Bug 962581 - Notice disconnections faster after waking up from sleep. r=clokep
authoraleth <aleth@instantbird.org>
Thu, 23 Jan 2014 13:04:49 +0100
changeset 19340 df0b77fa9170dcecb3a72037ab4b737be7917985
parent 19339 cb2e8c1d0782938f41a9a2a1df7170e41579d1fe
child 19341 52e6bc2710750c6da29ec18f445f1b64cf1825e8
push id1103
push usermbanner@mozilla.com
push dateTue, 18 Mar 2014 07:44:06 +0000
treeherdercomm-beta@50c6279a0af0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersclokep
bugs962581
Bug 962581 - Notice disconnections faster after waking up from sleep. r=clokep
chat/modules/socket.jsm
--- a/chat/modules/socket.jsm
+++ b/chat/modules/socket.jsm
@@ -137,16 +137,19 @@ const Socket = {
    ******************************* Public methods ******************************
    *****************************************************************************
    */
   // Synchronously open a connection.
   connect: function(aHost, aPort, aSecurity, aProxy) {
     if (Services.io.offline)
       throw Cr.NS_ERROR_FAILURE;
 
+    // This won't work for Linux due to bug 758848.
+    Services.obs.addObserver(this, "wake_notification", false);
+
     this.LOG("Connecting to: " + aHost + ":" + aPort);
     this.host = aHost;
     this.port = aPort;
     this.disconnected = false;
 
     this._pendingData = [];
     delete this._stopRequestStatus;
 
@@ -206,16 +209,19 @@ const Socket = {
 
     if (this._pingTimer) {
       clearTimeout(this._pingTimer);
       delete this._pingTimer;
       delete this._resetPingTimerPending;
     }
     this.cancelDisconnectTimer();
 
+    delete this._lastAliveTime;
+    Services.obs.removeObserver(this, "wake_notification");
+
     this.disconnected = true;
   },
 
   // Listen for a connection on a port.
   // XXX take a timeout and then call stopListening
   listen: function(port) {
     this.LOG("Listening on port " + port);
 
@@ -277,41 +283,67 @@ const Socket = {
     this.transport.securityInfo.QueryInterface(Ci.nsISSLSocketControl).StartTLS();
   },
 
   // If using the ping functionality, this should be called whenever a message is
   // received (e.g. when it is known the socket is still open). Calling this for
   // the first time enables the ping functionality.
   resetPingTimer: function() {
     // Clearing and setting timeouts is expensive, so we do it at most
-      // once per eventloop spin cycle.
+    // once per eventloop spin cycle.
     if (this._resetPingTimerPending)
       return;
     this._resetPingTimerPending = true;
     executeSoon(this._delayedResetPingTimer.bind(this));
   },
+  kTimeBeforePing: 120000, // 2 min
+  kTimeAfterPingBeforeDisconnect: 30000, // 30 s
   _delayedResetPingTimer: function() {
     if (!this._resetPingTimerPending)
       return;
     delete this._resetPingTimerPending;
     if (this._pingTimer)
       clearTimeout(this._pingTimer);
     // Send a ping every 2 minutes if there's no traffic on the socket.
-    this._pingTimer = setTimeout(this._sendPing.bind(this), 120000);
+    this._pingTimer = setTimeout(this._sendPing.bind(this), this.kTimeBeforePing);
   },
 
   // If using the ping functionality, this should be called when a ping receives
   // a response.
   cancelDisconnectTimer: function() {
     if (!this._disconnectTimer)
       return;
     clearTimeout(this._disconnectTimer);
     delete this._disconnectTimer;
   },
 
+  // Plenty of time may have elapsed if the computer wakes from sleep, so check
+  // if we should reconnect immediately.
+  _lastAliveTime: null,
+  observe: function(aSubject, aTopic, aData) {
+    if (aTopic != "wake_notification")
+      return;
+    let elapsedTime = Date.now() - this._lastAliveTime;
+    // If there never was any activity before we went to sleep,
+    // or if we've been waiting for a ping response for over 30s,
+    // or if the last activity on the socket is longer ago than we usually
+    //   allow before we timeout,
+    // declare the connection timed out immediately.
+    if (!this._lastAliveTime ||
+        (this._disconnectTimer && elapsedTime > this.kTimeAfterPingBeforeDisconnect) ||
+        elapsedTime > this.kTimeBeforePing + this.kTimeAfterPingBeforeDisconnect)
+      this.onConnectionTimedOut();
+    else if (this._pingTimer) {
+      // If there was a ping timer running when the computer went to sleep,
+      // ping immediately to discover if we are still connected.
+      clearTimeout(this._pingTimer);
+      this._sendPing();
+    }
+  },
+
   /*
    *****************************************************************************
    ***************************** Interface methods *****************************
    *****************************************************************************
    */
   /*
    * nsIProtocolProxyCallback methods
    */
@@ -363,16 +395,17 @@ const Socket = {
   /*
    * nsIStreamListener methods
    */
   // onDataAvailable, called by Mozilla's networking code.
   // Buffers the data, and parses it into discrete messages.
   onDataAvailable: function(aRequest, aContext, aInputStream, aOffset, aCount) {
     if (this.disconnected)
       return;
+    this._lastAliveTime = Date.now();
     if (this.binaryMode) {
       // Load the data from the stream
       this._incomingDataBuffer = this._incomingDataBuffer
                                      .concat(this._binaryInputStream
                                                  .readByteArray(aCount));
 
       let size = this._incomingDataBuffer.length;
 
@@ -606,17 +639,17 @@ const Socket = {
   },
 
   _pingTimer: null,
   _disconnectTimer: null,
   _sendPing: function() {
     delete this._pingTimer;
     this.sendPing();
     this._disconnectTimer = setTimeout(this.onConnectionTimedOut.bind(this),
-                                       30000);
+                                       this.kTimeAfterPingBeforeDisconnect);
   },
 
   /*
    *****************************************************************************
    ********************* Methods for subtypes to override **********************
    *****************************************************************************
    */
   LOG: function(aString) { },