Pārlūkot izejas kodu

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 gadus atpakaļ
vecāks
revīzija
6791beb2e4

+ 39
- 31
src/com/dmdirc/Server.java Parādīt failu

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

+ 5
- 0
src/com/dmdirc/ServerManager.java Parādīt failu

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

+ 12
- 5
test/com/dmdirc/ServerManagerTest.java Parādīt failu

34
 
34
 
35
 import java.net.URI;
35
 import java.net.URI;
36
 import java.util.Arrays;
36
 import java.util.Arrays;
37
+import java.util.concurrent.ScheduledExecutorService;
37
 
38
 
38
 import javax.inject.Provider;
39
 import javax.inject.Provider;
39
 
40
 
40
-import org.junit.After;
41
 import org.junit.Before;
41
 import org.junit.Before;
42
 import org.junit.Test;
42
 import org.junit.Test;
43
 import org.junit.runner.RunWith;
43
 import org.junit.runner.RunWith;
46
 import org.mockito.Mock;
46
 import org.mockito.Mock;
47
 import org.mockito.runners.MockitoJUnitRunner;
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
 @RunWith(MockitoJUnitRunner.class)
58
 @RunWith(MockitoJUnitRunner.class)
53
 public class ServerManagerTest {
59
 public class ServerManagerTest {
81
         when(profile.isProfile()).thenReturn(true);
87
         when(profile.isProfile()).thenReturn(true);
82
 
88
 
83
         when(serverFactoryImpl.getServer(eq(configProviderMigrator), any(CommandParser.class),
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
     @Test
94
     @Test
216
         verify(server, never()).addRaw();
223
         verify(server, never()).addRaw();
217
     }
224
     }
218
 
225
 
219
-}
226
+}

+ 3
- 0
test/com/dmdirc/ServerTest.java Parādīt failu

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

Notiek ielāde…
Atcelt
Saglabāt