Bug 1201037 - (Linux) squash network-change events during 1000ms, r=mcmanus
authorDaniel Stenberg <daniel@haxx.se>
Thu, 08 Oct 2015 03:50:00 +0200
changeset 266969 4e3c40a187a1b16afa0052123971e2567c4631e0
parent 266968 7f9eddce2499554e6a46bf6128599a316675865a
child 266970 5cf375bf524d54dab84c9d4ada093cf08d6bb7b1
push id29504
push usercbook@mozilla.com
push dateFri, 09 Oct 2015 09:43:23 +0000
treeherdermozilla-central@d01dd42e654b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1201037
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1201037 - (Linux) squash network-change events during 1000ms, r=mcmanus
netwerk/system/linux/nsNotifyAddrListener_Linux.cpp
netwerk/system/linux/nsNotifyAddrListener_Linux.h
--- a/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp
+++ b/netwerk/system/linux/nsNotifyAddrListener_Linux.cpp
@@ -31,16 +31,20 @@
 
 #ifdef MOZ_WIDGET_GONK
 #include <cutils/properties.h>
 #endif
 
 /* a shorter name that better explains what it does */
 #define EINTR_RETRY(x) MOZ_TEMP_FAILURE_RETRY(x)
 
+// period during which to absorb subsequent network change events, in
+// milliseconds
+static const unsigned int kNetworkChangeCoalescingPeriod  = 1000;
+
 using namespace mozilla;
 
 static PRLogModuleInfo *gNotifyAddrLog = nullptr;
 #define LOG(args) MOZ_LOG(gNotifyAddrLog, mozilla::LogLevel::Debug, args)
 
 #define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
 
 NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
@@ -48,16 +52,17 @@ NS_IMPL_ISUPPORTS(nsNotifyAddrListener,
                   nsIRunnable,
                   nsIObserver)
 
 nsNotifyAddrListener::nsNotifyAddrListener()
     : mLinkUp(true)  // assume true by default
     , mStatusKnown(false)
     , mAllowChangedEvent(true)
     , mChildThreadShutdown(false)
+    , mCoalescingActive(false)
 {
     mShutdownPipe[0] = -1;
     mShutdownPipe[1] = -1;
 }
 
 nsNotifyAddrListener::~nsNotifyAddrListener()
 {
     MOZ_ASSERT(!mThread, "nsNotifyAddrListener thread shutdown failed");
@@ -218,17 +223,17 @@ void nsNotifyAddrListener::OnNetlinkMess
             break;
 
         default:
             continue;
         }
     }
 
     if (networkChange && mAllowChangedEvent) {
-        SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
+        NetworkChanged();
     }
 
     if (networkChange) {
         checkLink();
     }
 }
 
 NS_IMETHODIMP
@@ -270,32 +275,46 @@ nsNotifyAddrListener::Run()
 #ifdef MOZ_WIDGET_GONK
     char propQemu[PROPERTY_VALUE_MAX];
     property_get("ro.kernel.qemu", propQemu, "");
     pollTimeout = !strncmp(propQemu, "1", 1) ? 100 : -1;
 #endif
 
     nsresult rv = NS_OK;
     bool shutdown = false;
+    int pollWait = pollTimeout;
     while (!shutdown) {
-        int rc = EINTR_RETRY(poll(fds, 2, pollTimeout));
+        int rc = EINTR_RETRY(poll(fds, 2, pollWait));
 
         if (rc > 0) {
             if (fds[0].revents & POLLIN) {
                 // shutdown, abort the loop!
                 LOG(("thread shutdown received, dying...\n"));
                 shutdown = true;
             } else if (fds[1].revents & POLLIN) {
                 LOG(("netlink message received, handling it...\n"));
                 OnNetlinkMessage(netlinkSocket);
             }
         } else if (rc < 0) {
             rv = NS_ERROR_FAILURE;
             break;
         }
+        if (mCoalescingActive) {
+            // check if coalescing period should continue
+            double period = (TimeStamp::Now() - mChangeTime).ToMilliseconds();
+            if (period >= kNetworkChangeCoalescingPeriod) {
+                SendEvent(NS_NETWORK_LINK_DATA_CHANGED);
+                mCoalescingActive = false;
+                pollWait = pollTimeout; // restore to default
+            } else {
+                // wait no longer than to the end of the period
+                pollWait = static_cast<int>
+                    (kNetworkChangeCoalescingPeriod - period);
+            }
+        }
         if (mChildThreadShutdown) {
             LOG(("thread shutdown via variable, dying...\n"));
             shutdown = true;
         }
     }
 
     EINTR_RETRY(close(netlinkSocket));
 
@@ -382,16 +401,37 @@ nsNotifyAddrListener::Shutdown(void)
     // Have to break the cycle here, otherwise nsNotifyAddrListener holds
     // onto the thread and the thread holds onto the nsNotifyAddrListener
     // via its mRunnable
     mThread = nullptr;
 
     return rv;
 }
 
+
+/*
+ * A network event has been registered. Delay the actual sending of the event
+ * for a while and absorb subsequent events in the mean time in an effort to
+ * squash potentially many triggers into a single event.
+ * Only ever called from the same thread.
+ */
+nsresult
+nsNotifyAddrListener::NetworkChanged()
+{
+    if (mCoalescingActive) {
+        LOG(("NetworkChanged: absorbed an event (coalescing active)\n"));
+    } else {
+        // A fresh trigger!
+        mChangeTime = TimeStamp::Now();
+        mCoalescingActive = true;
+        LOG(("NetworkChanged: coalescing period started\n"));
+    }
+    return NS_OK;
+}
+
 /* Sends the given event.  Assumes aEventID never goes out of scope (static
  * strings are ideal).
  */
 nsresult
 nsNotifyAddrListener::SendEvent(const char *aEventID)
 {
     if (!aEventID)
         return NS_ERROR_NULL_POINTER;
--- a/netwerk/system/linux/nsNotifyAddrListener_Linux.h
+++ b/netwerk/system/linux/nsNotifyAddrListener_Linux.h
@@ -17,16 +17,17 @@
 
 #include "nsINetworkLinkService.h"
 #include "nsIRunnable.h"
 #include "nsIObserver.h"
 #include "nsThreadUtils.h"
 #include "nsCOMPtr.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/TimeStamp.h"
+#include "nsITimer.h"
 
 class nsNotifyAddrListener : public nsINetworkLinkService,
                              public nsIRunnable,
                              public nsIObserver
 {
     virtual ~nsNotifyAddrListener();
 
 public:
@@ -48,16 +49,19 @@ private:
     private:
         nsCOMPtr<nsINetworkLinkService> mService;
         const char *mEventID;
     };
 
     // Called when xpcom-shutdown-threads is received.
     nsresult Shutdown(void);
 
+    // Called when a network change was detected
+    nsresult NetworkChanged();
+
     // Sends the network event.
     nsresult SendEvent(const char *aEventID);
 
     // Checks if there's a network "link"
     void checkLink(void);
 
     // Deals with incoming NETLINK messages.
     void OnNetlinkMessage(int NetlinkSocket);
@@ -73,11 +77,17 @@ private:
     // A pipe to signal shutdown with.
     int mShutdownPipe[2];
 
     // Network changed events are enabled
     bool mAllowChangedEvent;
 
     // Flag to signal child thread kill with
     mozilla::Atomic<bool, mozilla::Relaxed> mChildThreadShutdown;
-};
+
+    // Flag set while coalescing change events
+    bool mCoalescingActive;
+
+    // Time stamp for first event during coalescing
+    mozilla::TimeStamp mChangeTime;
+ };
 
 #endif /* NSNOTIFYADDRLISTENER_LINUX_H_ */