Skip to content

Commit 39264c7

Browse files
committed
fixed racing condition in websocket server stop
1 parent ecb2c8a commit 39264c7

File tree

1 file changed

+41
-8
lines changed

1 file changed

+41
-8
lines changed

src/main/java/org/java_websocket/server/WebSocketServer.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.util.Set;
2222
import java.util.concurrent.BlockingQueue;
2323
import java.util.concurrent.LinkedBlockingQueue;
24+
import java.util.concurrent.atomic.AtomicBoolean;
2425
import java.util.concurrent.atomic.AtomicInteger;
2526

2627
import org.java_websocket.SocketChannelIOHelper;
@@ -69,6 +70,8 @@ public abstract class WebSocketServer extends WebSocketAdapter implements Runnab
6970

7071
private Thread selectorthread;
7172

73+
private volatile AtomicBoolean isclosed = new AtomicBoolean( false );
74+
7275
private List<WebSocketWorker> decoders;
7376

7477
private BlockingQueue<WebSocketImpl> oqueue;
@@ -167,26 +170,53 @@ public void start() {
167170

168171
/**
169172
* Closes all connected clients sockets, then closes the underlying
170-
* ServerSocketChannel, effectively killing the server socket selectorthread and
171-
* freeing the port the server was bound to.
173+
* ServerSocketChannel, effectively killing the server socket selectorthread,
174+
* freeing the port the server was bound to and stops all internal workerthreads.
175+
*
176+
* If this method is called before the server is started it will never start.
177+
*
178+
* Therefore
179+
*
180+
* Additionally
181+
*
182+
* @param timeout
183+
* Specifies how many milliseconds shall pass between initiating the close handshakes with the connected clients and closing the servers socket channel.
172184
*
173185
* @throws IOException
174186
* When {@link ServerSocketChannel}.close throws an IOException
175187
* @throws InterruptedException
176188
*/
177-
public void stop() throws IOException , InterruptedException {
189+
public void stop( int timeout ) throws IOException , InterruptedException {
190+
if( !isclosed.compareAndSet( false, true ) ) {
191+
return;
192+
}
193+
178194
synchronized ( connections ) {
179195
for( WebSocket ws : connections ) {
180196
ws.close( CloseFrame.NORMAL );
181197
}
182198
}
183-
selectorthread.interrupt();
184-
selectorthread.join();
185-
for( WebSocketWorker w : decoders ) {
186-
w.interrupt();
199+
synchronized ( this ) {
200+
if( selectorthread != null ) {
201+
if( Thread.currentThread() != selectorthread ) {
202+
203+
}
204+
selectorthread.interrupt();
205+
selectorthread.join();
206+
}
207+
if( decoders != null ) {
208+
for( WebSocketWorker w : decoders ) {
209+
w.interrupt();
210+
}
211+
}
212+
if( server != null ) {
213+
server.close();
214+
}
187215
}
188-
this.server.close();
216+
}
189217

218+
public void stop() throws IOException , InterruptedException {
219+
stop( 0 );
190220
}
191221

192222
/**
@@ -237,6 +267,9 @@ public void run() {
237267
if( selectorthread != null )
238268
throw new IllegalStateException( getClass().getName() + " can only be started once." );
239269
selectorthread = Thread.currentThread();
270+
if( isclosed.get() ) {
271+
return;
272+
}
240273
}
241274
selectorthread.setName( "WebsocketSelector" + selectorthread.getId() );
242275
try {

0 commit comments

Comments
 (0)