Browse Source

Make timers in Server more sensible.

Pass in a ScheduledExecutorService and use it for who timers and
reconnect timers.

Only run the who timer when we're connected, not the whole time.
This stops the Server ctor starting a timer.

Change-Id: Iea92d7046f33eda7666b8a2b739374345e8ea20d
Reviewed-on: http://gerrit.dmdirc.com/3419
Automatic-Compile: DMDirc Build Manager
Reviewed-by: Greg Holmes <greg@dmdirc.com>
pull/1/head
Chris Smith 10 years ago
parent
commit
6791beb2e4

+ 39
- 31
src/com/dmdirc/Server.java View File

@@ -81,9 +81,10 @@ import java.util.List;
81 81
 import java.util.Map;
82 82
 import java.util.Random;
83 83
 import java.util.Set;
84
-import java.util.Timer;
85
-import java.util.TimerTask;
86 84
 import java.util.concurrent.ConcurrentSkipListMap;
85
+import java.util.concurrent.ScheduledExecutorService;
86
+import java.util.concurrent.ScheduledFuture;
87
+import java.util.concurrent.TimeUnit;
87 88
 import java.util.concurrent.locks.ReadWriteLock;
88 89
 import java.util.concurrent.locks.ReentrantReadWriteLock;
89 90
 
@@ -138,10 +139,6 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
138 139
     private final Object myStateLock = new Object();
139 140
     /** The current state of this server. */
140 141
     private final ServerStatus myState = new ServerStatus(this, myStateLock);
141
-    /** The timer we're using to delay reconnects. */
142
-    private Timer reconnectTimer;
143
-    /** The timer we're using to send WHO requests. */
144
-    private final Timer whoTimer;
145 142
     /** The tabcompleter used for this server. */
146 143
     private final TabCompleter tabCompleter;
147 144
     /** Our reason for being away, if any. */
@@ -180,6 +177,12 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
180 177
     private final ConfigProvider userSettings;
181 178
     /** The manager to use to add status bar messages. */
182 179
     private final StatusBarManager statusBarManager;
180
+    /** Executor service to use to schedule repeated events. */
181
+    private final ScheduledExecutorService executorService;
182
+    /** The future used when a who timer is scheduled. */
183
+    private ScheduledFuture<?> whoTimerFuture;
184
+    /** The future used when a reconnect timer is scheduled. */
185
+    private ScheduledFuture<?> reconnectTimerFuture;
183 186
 
184 187
     /**
185 188
      * Creates a new server which will connect to the specified URL with the specified profile.
@@ -201,6 +204,7 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
201 204
      * @param urlBuilder          The URL builder to use when finding icons.
202 205
      * @param eventBus            The event bus to despatch events onto.
203 206
      * @param userSettings        The config provider to write user settings to.
207
+     * @param executorService     The service to use to schedule events.
204 208
      * @param uri                 The address of the server to connect to
205 209
      * @param profile             The profile to use
206 210
      */
@@ -220,6 +224,7 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
220 224
             final URLBuilder urlBuilder,
221 225
             final EventBus eventBus,
222 226
             @SuppressWarnings("qualifiers") @UserConfig final ConfigProvider userSettings,
227
+            @Unbound final ScheduledExecutorService executorService,
223 228
             @Unbound final URI uri,
224 229
             @Unbound final ConfigProvider profile) {
225 230
         super("server-disconnected",
@@ -242,6 +247,7 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
242 247
         this.channelFactory = channelFactory;
243 248
         this.queryFactory = queryFactory;
244 249
         this.rawFactory = rawFactory;
250
+        this.executorService = executorService;
245 251
         this.eventBus = eventBus;
246 252
         this.userSettings = userSettings;
247 253
         this.statusBarManager = statusBarManager;
@@ -253,17 +259,6 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
253 259
 
254 260
         updateIcon();
255 261
 
256
-        // TODO: Don't start timers in the constructor!
257
-        whoTimer = new Timer("Server Who Timer");
258
-        whoTimer.schedule(new TimerTask() {
259
-            @Override
260
-            public void run() {
261
-                for (Channel channel : channels.values()) {
262
-                    channel.checkWho();
263
-                }
264
-            }
265
-        }, 0, getConfigManager().getOptionInt(DOMAIN_GENERAL, "whotime"));
266
-
267 262
         getConfigManager().addChangeListener("formatter", "serverName", this);
268 263
         getConfigManager().addChangeListener("formatter", "serverTitle", this);
269 264
     }
@@ -312,7 +307,9 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
312 307
             switch (myState.getState()) {
313 308
                 case RECONNECT_WAIT:
314 309
                     log.debug("Cancelling reconnection timer");
315
-                    reconnectTimer.cancel();
310
+                    if (reconnectTimerFuture != null) {
311
+                        reconnectTimerFuture.cancel(false);
312
+                    }
316 313
                     break;
317 314
                 case CLOSING:
318 315
                     // Ignore the connection attempt
@@ -418,7 +415,9 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
418 415
                     return;
419 416
                 case RECONNECT_WAIT:
420 417
                     log.debug("Cancelling reconnection timer");
421
-                    reconnectTimer.cancel();
418
+                    if (reconnectTimerFuture != null) {
419
+                        reconnectTimerFuture.cancel(false);
420
+                    }
422 421
                     break;
423 422
                 default:
424 423
                     break;
@@ -473,12 +472,9 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
473 472
 
474 473
             handleNotification("connectRetry", getAddress(), delay / 1000);
475 474
 
476
-            reconnectTimer = new Timer("Server Reconnect Timer");
477
-            reconnectTimer.schedule(new TimerTask() {
475
+            reconnectTimerFuture = executorService.schedule(new Runnable() {
478 476
                 @Override
479 477
                 public void run() {
480
-                    reconnectTimer.cancel();
481
-
482 478
                     synchronized (myStateLock) {
483 479
                         log.debug("Reconnect task executing, state: {}",
484 480
                                 myState.getState());
@@ -488,7 +484,7 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
488 484
                         }
489 485
                     }
490 486
                 }
491
-            }, delay);
487
+            }, delay, TimeUnit.MILLISECONDS);
492 488
 
493 489
             log.info("Scheduling reconnect task for delay of {}", delay);
494 490
 
@@ -1036,7 +1032,7 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
1036 1032
             // Remove any callbacks or listeners
1037 1033
             eventHandler.unregisterCallbacks();
1038 1034
             getConfigManager().removeListener(this);
1039
-            whoTimer.cancel();
1035
+            executorService.shutdown();
1040 1036
 
1041 1037
             // Trigger any actions neccessary
1042 1038
             disconnect();
@@ -1201,8 +1197,7 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
1201 1197
         }
1202 1198
 
1203 1199
         ActionManager.getActionManager().triggerEvent(
1204
-                CoreActionType.SERVER_NUMERIC, target, this,
1205
-                Integer.valueOf(numeric), tokens);
1200
+                CoreActionType.SERVER_NUMERIC, target, this, numeric, tokens);
1206 1201
 
1207 1202
         handleNotification(target.toString(), (Object[]) tokens);
1208 1203
     }
@@ -1213,16 +1208,19 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
1213 1208
     public void onSocketClosed() {
1214 1209
         log.info("Received socket closed event, state: {}", myState.getState());
1215 1210
 
1211
+        if (whoTimerFuture != null) {
1212
+            whoTimerFuture.cancel(false);
1213
+        }
1214
+
1216 1215
         if (Thread.holdsLock(myStateLock)) {
1217 1216
             log.info("State lock contended: rerunning on a new thread");
1218 1217
 
1219
-            new Thread(new Runnable() {
1220
-
1218
+            executorService.schedule(new Runnable() {
1221 1219
                 @Override
1222 1220
                 public void run() {
1223 1221
                     onSocketClosed();
1224 1222
                 }
1225
-            }, "Socket closed deferred thread").start();
1223
+            }, 0, TimeUnit.SECONDS);
1226 1224
             return;
1227 1225
         }
1228 1226
 
@@ -1360,7 +1358,7 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
1360 1358
 
1361 1359
         ActionManager.getActionManager().triggerEvent(
1362 1360
                 CoreActionType.SERVER_NOPING, null, this,
1363
-                Long.valueOf(parser.getPingTime()));
1361
+                parser.getPingTime());
1364 1362
 
1365 1363
         if (parser.getPingTime()
1366 1364
                 >= getConfigManager().getOptionInt(DOMAIN_SERVER, "pingtimeout")) {
@@ -1407,6 +1405,16 @@ public class Server extends WritableFrameContainer implements ConfigChangeListen
1407 1405
             join(requests.toArray(new ChannelJoinRequest[requests.size()]));
1408 1406
 
1409 1407
             checkModeAliases();
1408
+
1409
+            final int whoTime = getConfigManager().getOptionInt(DOMAIN_GENERAL, "whotime");
1410
+            whoTimerFuture = executorService.scheduleAtFixedRate(new Runnable() {
1411
+                @Override
1412
+                public void run() {
1413
+                    for (Channel channel : channels.values()) {
1414
+                        channel.checkWho();
1415
+                    }
1416
+                }
1417
+            }, whoTime, whoTime, TimeUnit.MILLISECONDS);
1410 1418
         }
1411 1419
 
1412 1420
         ActionManager.getActionManager().triggerEvent(

+ 5
- 0
src/com/dmdirc/ServerManager.java View File

@@ -34,12 +34,15 @@ import com.dmdirc.logger.Logger;
34 34
 import com.dmdirc.parser.common.ChannelJoinRequest;
35 35
 import com.dmdirc.ui.WindowManager;
36 36
 
37
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
38
+
37 39
 import java.net.URI;
38 40
 import java.net.URISyntaxException;
39 41
 import java.util.ArrayList;
40 42
 import java.util.List;
41 43
 import java.util.Set;
42 44
 import java.util.concurrent.CopyOnWriteArraySet;
45
+import java.util.concurrent.Executors;
43 46
 
44 47
 import javax.inject.Inject;
45 48
 import javax.inject.Provider;
@@ -98,6 +101,8 @@ public class ServerManager implements ServerFactory {
98 101
         final Server server = serverFactoryImpl.getServer(
99 102
                 configProvider,
100 103
                 new ServerCommandParser(configProvider.getConfigProvider(), commandController.get()),
104
+                Executors.newScheduledThreadPool(1,
105
+                        new ThreadFactoryBuilder().setNameFormat("server-timer-%d").build()),
101 106
                 uri,
102 107
                 profile);
103 108
         registerServer(server);

+ 12
- 5
test/com/dmdirc/ServerManagerTest.java View File

@@ -34,10 +34,10 @@ import com.dmdirc.ui.WindowManager;
34 34
 
35 35
 import java.net.URI;
36 36
 import java.util.Arrays;
37
+import java.util.concurrent.ScheduledExecutorService;
37 38
 
38 39
 import javax.inject.Provider;
39 40
 
40
-import org.junit.After;
41 41
 import org.junit.Before;
42 42
 import org.junit.Test;
43 43
 import org.junit.runner.RunWith;
@@ -46,8 +46,14 @@ import org.mockito.Captor;
46 46
 import org.mockito.Mock;
47 47
 import org.mockito.runners.MockitoJUnitRunner;
48 48
 
49
-import static org.junit.Assert.*;
50
-import static org.mockito.Mockito.*;
49
+import static org.junit.Assert.assertEquals;
50
+import static org.mockito.Matchers.any;
51
+import static org.mockito.Matchers.anyString;
52
+import static org.mockito.Matchers.eq;
53
+import static org.mockito.Mockito.mock;
54
+import static org.mockito.Mockito.never;
55
+import static org.mockito.Mockito.verify;
56
+import static org.mockito.Mockito.when;
51 57
 
52 58
 @RunWith(MockitoJUnitRunner.class)
53 59
 public class ServerManagerTest {
@@ -81,7 +87,8 @@ public class ServerManagerTest {
81 87
         when(profile.isProfile()).thenReturn(true);
82 88
 
83 89
         when(serverFactoryImpl.getServer(eq(configProviderMigrator), any(CommandParser.class),
84
-                uriCaptor.capture(), eq(profile))).thenReturn(server);
90
+                any(ScheduledExecutorService.class), uriCaptor.capture(), eq(profile)))
91
+                .thenReturn(server);
85 92
     }
86 93
 
87 94
     @Test
@@ -216,4 +223,4 @@ public class ServerManagerTest {
216 223
         verify(server, never()).addRaw();
217 224
     }
218 225
 
219
-}
226
+}

+ 3
- 0
test/com/dmdirc/ServerTest.java View File

@@ -36,6 +36,7 @@ import com.dmdirc.util.URLBuilder;
36 36
 import com.google.common.eventbus.EventBus;
37 37
 
38 38
 import java.net.URI;
39
+import java.util.concurrent.ScheduledExecutorService;
39 40
 
40 41
 import org.junit.Before;
41 42
 import org.junit.Test;
@@ -65,6 +66,7 @@ public class ServerTest {
65 66
     @Mock private StatusBarManager statusBarManager;
66 67
     @Mock private URLBuilder urlBuilder;
67 68
     @Mock private EventBus eventBus;
69
+    @Mock private ScheduledExecutorService executorService;
68 70
 
69 71
     private Server server;
70 72
 
@@ -90,6 +92,7 @@ public class ServerTest {
90 92
                 urlBuilder,
91 93
                 eventBus,
92 94
                 userConfig,
95
+                executorService,
93 96
                 new URI("irc-test://255.255.255.255"),
94 97
                 profile);
95 98
     }

Loading…
Cancel
Save