Skip to content

Commit 73c6805

Browse files
authored
Replace TimerTask with ScheduledExecutorService (TooTallNate#878)
Replace TimerTask with ScheduledExecutorService
2 parents 8a74a87 + a911973 commit 73c6805

File tree

4 files changed

+82
-32
lines changed

4 files changed

+82
-32
lines changed

‎src/main/java/org/java_websocket/AbstractWebSocket.java‎

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,16 @@
2626
packageorg.java_websocket;
2727

2828
importorg.java_websocket.framing.CloseFrame;
29+
importorg.java_websocket.util.NamedThreadFactory;
2930
importorg.slf4j.Logger;
3031
importorg.slf4j.LoggerFactory;
3132

3233
importjava.util.ArrayList;
3334
importjava.util.Collection;
34-
importjava.util.Timer;
35-
importjava.util.TimerTask;
35+
importjava.util.concurrent.Executors;
36+
importjava.util.concurrent.ScheduledExecutorService;
37+
importjava.util.concurrent.ScheduledFuture;
38+
importjava.util.concurrent.TimeUnit;
3639

3740

3841
/**
@@ -60,21 +63,21 @@ public abstract class AbstractWebSocket extends WebSocketAdapter{
6063
privatebooleanreuseAddr;
6164

6265
/**
63-
* Attribute for a timer allowing to check for lost connections
64-
* @since 1.3.4
66+
* Attribute for a service that triggers lost connection checking
67+
* @since 1.4.1
6568
*/
66-
privateTimerconnectionLostTimer;
69+
privateScheduledExecutorServiceconnectionLostCheckerService;
6770
/**
68-
* Attribute for a timertask allowing to check for lost connections
69-
* @since 1.3.4
71+
* Attribute for a task that checks for lost connections
72+
* @since 1.4.1
7073
*/
71-
privateTimerTaskconnectionLostTimerTask;
74+
privateScheduledFutureconnectionLostCheckerFuture;
7275

7376
/**
74-
* Attribute for the lost connection check interval
77+
* Attribute for the lost connection check interval in nanoseconds
7578
* @since 1.3.4
7679
*/
77-
privateintconnectionLostTimeout = 60;
80+
privatelongconnectionLostTimeout = TimeUnit.SECONDS.toNanos(60);
7881

7982
/**
8083
* Attribute to keep track if the WebSocket Server/Client is running/connected
@@ -89,12 +92,12 @@ public abstract class AbstractWebSocket extends WebSocketAdapter{
8992
/**
9093
* Get the interval checking for lost connections
9194
* Default is 60 seconds
92-
* @return the interval
95+
* @return the interval in seconds
9396
* @since 1.3.4
9497
*/
9598
publicintgetConnectionLostTimeout(){
9699
synchronized (syncConnectionLost){
97-
returnconnectionLostTimeout;
100+
return(int) TimeUnit.NANOSECONDS.toSeconds(connectionLostTimeout);
98101
}
99102
}
100103

@@ -107,7 +110,7 @@ public int getConnectionLostTimeout(){
107110
*/
108111
publicvoidsetConnectionLostTimeout( intconnectionLostTimeout ){
109112
synchronized (syncConnectionLost){
110-
this.connectionLostTimeout = connectionLostTimeout;
113+
this.connectionLostTimeout = TimeUnit.SECONDS.toNanos(connectionLostTimeout);
111114
if (this.connectionLostTimeout <= 0){
112115
log.trace("Connection lost timer stopped");
113116
cancelConnectionLostTimer();
@@ -139,7 +142,7 @@ public void setConnectionLostTimeout( int connectionLostTimeout ){
139142
*/
140143
protectedvoidstopConnectionLostTimer(){
141144
synchronized (syncConnectionLost){
142-
if (connectionLostTimer != null || connectionLostTimerTask != null){
145+
if (connectionLostCheckerService != null || connectionLostCheckerFuture != null){
143146
this.websocketRunning = false;
144147
log.trace("Connection lost timer stopped");
145148
cancelConnectionLostTimer();
@@ -168,8 +171,8 @@ protected void startConnectionLostTimer(){
168171
*/
169172
privatevoidrestartConnectionLostTimer(){
170173
cancelConnectionLostTimer();
171-
connectionLostTimer = newTimer("WebSocketTimer");
172-
connectionLostTimerTask= newTimerTask(){
174+
connectionLostCheckerService = Executors.newSingleThreadScheduledExecutor(newNamedThreadFactory("connectionLostChecker"));
175+
RunnableconnectionLostChecker= newRunnable(){
173176

174177
/**
175178
* Keep the connections in a separate list to not cause deadlocks
@@ -180,31 +183,31 @@ public void run(){
180183
connections.clear();
181184
try{
182185
connections.addAll( getConnections() );
183-
longcurrent = (System.currentTimeMillis() - ( connectionLostTimeout * 1500 ) );
186+
longminimumPongTime = (long) (System.nanoTime() - ( connectionLostTimeout * 1.5 ));
184187
for( WebSocketconn : connections ){
185-
executeConnectionLostDetection(conn, current);
188+
executeConnectionLostDetection(conn, minimumPongTime);
186189
}
187190
} catch ( Exceptione ){
188191
//Ignore this exception
189192
}
190193
connections.clear();
191194
}
192195
};
193-
connectionLostTimer.scheduleAtFixedRate( connectionLostTimerTask,1000L*connectionLostTimeout , 1000L*connectionLostTimeout );
194196

197+
connectionLostCheckerFuture = connectionLostCheckerService.scheduleAtFixedRate(connectionLostChecker, connectionLostTimeout, connectionLostTimeout, TimeUnit.NANOSECONDS);
195198
}
196199

197200
/**
198201
* Send a ping to the endpoint or close the connection since the other endpoint did not respond with a ping
199202
* @param webSocket the websocket instance
200-
* @param current the current time in milliseconds
203+
* @param minimumPongTime the lowest/oldest allowable last pong time (in nanoTime) before we consider the connection to be lost
201204
*/
202-
privatevoidexecuteConnectionLostDetection(WebSocketwebSocket, longcurrent){
205+
privatevoidexecuteConnectionLostDetection(WebSocketwebSocket, longminimumPongTime){
203206
if (!(webSocketinstanceofWebSocketImpl)){
204207
return;
205208
}
206209
WebSocketImplwebSocketImpl = (WebSocketImpl) webSocket;
207-
if( webSocketImpl.getLastPong() < current ){
210+
if( webSocketImpl.getLastPong() < minimumPongTime ){
208211
log.trace("Closing connection due to no pong received:{}", webSocketImpl);
209212
webSocketImpl.closeConnection( CloseFrame.ABNORMAL_CLOSE, "The connection was closed because the other endpoint did not respond with a pong in time. For more information check: https://github.com/TooTallNate/Java-WebSocket/wiki/Lost-connection-detection" );
210213
} else{
@@ -228,13 +231,13 @@ private void executeConnectionLostDetection(WebSocket webSocket, long current){
228231
* @since 1.3.4
229232
*/
230233
privatevoidcancelConnectionLostTimer(){
231-
if( connectionLostTimer != null ){
232-
connectionLostTimer.cancel();
233-
connectionLostTimer = null;
234+
if( connectionLostCheckerService != null ){
235+
connectionLostCheckerService.shutdownNow();
236+
connectionLostCheckerService = null;
234237
}
235-
if( connectionLostTimerTask != null ){
236-
connectionLostTimerTask.cancel();
237-
connectionLostTimerTask = null;
238+
if( connectionLostCheckerFuture != null ){
239+
connectionLostCheckerFuture.cancel(false);
240+
connectionLostCheckerFuture = null;
238241
}
239242
}
240243

‎src/main/java/org/java_websocket/WebSocketImpl.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ public class WebSocketImpl implements WebSocket{
151151
/**
152152
* Attribute, when the last pong was recieved
153153
*/
154-
privatelonglastPong = System.currentTimeMillis();
154+
privatelonglastPong = System.nanoTime();
155155

156156
/**
157157
* Attribut to synchronize the write
@@ -802,7 +802,7 @@ long getLastPong(){
802802
* Update the timestamp when the last pong was received
803803
*/
804804
publicvoidupdateLastPong(){
805-
this.lastPong = System.currentTimeMillis();
805+
this.lastPong = System.nanoTime();
806806
}
807807

808808
/**
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2010-2019 Nathan Rajlich
3+
*
4+
* Permission is hereby granted, free of charge, to any person
5+
* obtaining a copy of this software and associated documentation
6+
* files (the "Software"), to deal in the Software without
7+
* restriction, including without limitation the rights to use,
8+
* copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the
10+
* Software is furnished to do so, subject to the following
11+
* conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be
14+
* included in all copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18+
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20+
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21+
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22+
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23+
* OTHER DEALINGS IN THE SOFTWARE.
24+
*/
25+
26+
packageorg.java_websocket.util;
27+
28+
importjava.util.concurrent.Executors;
29+
importjava.util.concurrent.ThreadFactory;
30+
importjava.util.concurrent.atomic.AtomicInteger;
31+
32+
publicclassNamedThreadFactoryimplementsThreadFactory{
33+
privatefinalThreadFactorydefaultThreadFactory = Executors.defaultThreadFactory();
34+
privatefinalAtomicIntegerthreadNumber = newAtomicInteger(1);
35+
privatefinalStringthreadPrefix;
36+
37+
publicNamedThreadFactory(StringthreadPrefix){
38+
this.threadPrefix = threadPrefix;
39+
}
40+
41+
@Override
42+
publicThreadnewThread(Runnablerunnable){
43+
Threadthread = defaultThreadFactory.newThread(runnable);
44+
thread.setName(threadPrefix + "-" + threadNumber);
45+
returnthread;
46+
}
47+
}

‎src/test/java/org/java_websocket/issues/Issue666Test.java‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public void onStart(){
8080
}
8181
for( Threadthread : mapAfter.values() ){
8282
Stringname = thread.getName();
83-
if( !name.startsWith( "WebSocketSelector-" ) && !name.startsWith( "WebSocketWorker-" ) && !name.equals( "WebSocketTimer" ) ){
83+
if( !name.startsWith( "WebSocketSelector-" ) && !name.startsWith( "WebSocketWorker-" ) && !name.startsWith( "connectionLostChecker-" ) ){
8484
Assert.fail( "Thread not correctly named! Is: " + name );
8585
}
8686
}
@@ -145,7 +145,7 @@ public void onStart(){
145145
}
146146
for( Threadthread : mapAfter.values() ){
147147
Stringname = thread.getName();
148-
if( !name.equals( "WebSocketTimer" ) && !name.startsWith( "WebSocketWriteThread-" ) && !name.startsWith( "WebSocketConnectReadThread-" )){
148+
if( !name.startsWith( "connectionLostChecker-" ) && !name.startsWith( "WebSocketWriteThread-" ) && !name.startsWith( "WebSocketConnectReadThread-" )){
149149
Assert.fail( "Thread not correctly named! Is: " + name );
150150
}
151151
}

0 commit comments

Comments
(0)