Bug 1131557 - Serve multiple xpcom events in one poll iteration. r=mcmanus
authorDragana Damjanovic <dd.mozilla@gmail.com>
Wed, 04 Mar 2015 14:17:00 +0100
changeset 231986 7d6ee599c58b249b9bc0220bba7ff39ec787265f
parent 231985 c6d4680f1f5a7494ffbbe61dba8196c06ee60b2d
child 231987 54eee85f489fb01da314d4242fed398fdad72b29
push id56423
push usercbook@mozilla.com
push dateThu, 05 Mar 2015 11:30:52 +0000
treeherdermozilla-inbound@1968388d42c2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1131557
milestone39.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 1131557 - Serve multiple xpcom events in one poll iteration. r=mcmanus
modules/libpref/init/all.js
netwerk/base/nsSocketTransportService2.cpp
netwerk/base/nsSocketTransportService2.h
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1355,16 +1355,22 @@ pref("network.http.enforce-framing.soft"
 
 // default values for FTP
 // in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,
 // Section 4.8 "High-Throughput Data Service Class", and 80 (0x50, or AF22)
 // per Section 4.7 "Low-Latency Data Service Class".
 pref("network.ftp.data.qos", 0);
 pref("network.ftp.control.qos", 0);
 
+// If this pref is false only one xpcom event will be served per poll
+// iteration. This is the original behavior.
+// If it is true multiple events will be served.
+pref("network.sts.serve_multiple_events_per_poll_iteration", true);
+// The max time to spend on xpcom events between two polls in ms.
+pref("network.sts.max_time_for_events_between_two_polls", 100);
 // </http>
 
 // 2147483647 == PR_INT32_MAX == ~2 GB
 pref("network.websocket.max-message-size", 2147483647);
 
 // Should we automatically follow http 3xx redirects during handshake
 pref("network.websocket.auto-follow-http-redirects", false);
 
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -14,16 +14,17 @@
 #include "NetworkActivityMonitor.h"
 #include "nsIObserverService.h"
 #include "mozilla/Services.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Likely.h"
 #include "mozilla/PublicSSL.h"
 #include "mozilla/ChaosMode.h"
 #include "mozilla/PodOperations.h"
+#include "mozilla/TimeStamp.h"
 #include "nsThreadUtils.h"
 #include "nsIFile.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 
 #if defined(PR_LOGGING)
 PRLogModuleInfo *gSocketTransportLog = nullptr;
@@ -35,16 +36,18 @@ PRThread                 *gSocketThread 
 #define SEND_BUFFER_PREF "network.tcp.sendbuffer"
 #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled"
 #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time"
 #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval"
 #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
 #define SOCKET_LIMIT_TARGET 550U
 #define SOCKET_LIMIT_MIN     50U
 #define BLIP_INTERVAL_PREF "network.activity.blipIntervalMilliseconds"
+#define SERVE_MULTIPLE_EVENTS_PREF "network.sts.serve_multiple_events_per_poll_iteration"
+#define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
 
 uint32_t nsSocketTransportService::gMaxCount;
 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
 
 //-----------------------------------------------------------------------------
 // ctor/dtor (called on the main/UI thread by the service manager)
 
 nsSocketTransportService::nsSocketTransportService()
@@ -62,16 +65,19 @@ nsSocketTransportService::nsSocketTransp
     , mIdleCount(0)
     , mSentBytesCount(0)
     , mReceivedBytesCount(0)
     , mSendBufferSize(0)
     , mKeepaliveIdleTimeS(600)
     , mKeepaliveRetryIntervalS(1)
     , mKeepaliveProbeCount(kDefaultTCPKeepCount)
     , mKeepaliveEnabledPref(false)
+    , mServeMultipleEventsPerPollIter(true)
+    , mServingPendingQueue(false)
+    , mMaxTimePerPollIter(100)
     , mProbedMaxCount(false)
 {
 #if defined(PR_LOGGING)
     gSocketTransportLog = PR_NewLogModule("nsSocketTransport");
 #endif
 
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
@@ -476,16 +482,18 @@ nsSocketTransportService::Init()
 
     nsCOMPtr<nsIPrefBranch> tmpPrefService = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (tmpPrefService) {
         tmpPrefService->AddObserver(SEND_BUFFER_PREF, this, false);
         tmpPrefService->AddObserver(KEEPALIVE_ENABLED_PREF, this, false);
         tmpPrefService->AddObserver(KEEPALIVE_IDLE_TIME_PREF, this, false);
         tmpPrefService->AddObserver(KEEPALIVE_RETRY_INTERVAL_PREF, this, false);
         tmpPrefService->AddObserver(KEEPALIVE_PROBE_COUNT_PREF, this, false);
+        tmpPrefService->AddObserver(SERVE_MULTIPLE_EVENTS_PREF, this, false);
+        tmpPrefService->AddObserver(MAX_TIME_BETWEEN_TWO_POLLS, this, false);
     }
     UpdatePrefs();
 
     nsCOMPtr<nsIObserverService> obsSvc = services::GetObserverService();
     if (obsSvc) {
         obsSvc->AddObserver(this, "profile-initial-state", false);
         obsSvc->AddObserver(this, "last-pb-context-exited", false);
     }
@@ -681,16 +689,22 @@ nsSocketTransportService::OnProcessNextE
 NS_IMETHODIMP
 nsSocketTransportService::AfterProcessNextEvent(nsIThreadInternal* thread,
                                                 uint32_t depth,
                                                 bool eventWasProcessed)
 {
     return NS_OK;
 }
 
+void
+nsSocketTransportService::MarkTheLastElementOfPendingQueue()
+{
+    mServingPendingQueue = false;
+}
+
 #ifdef MOZ_NUWA_PROCESS
 #include "ipc/Nuwa.h"
 #endif
 
 NS_IMETHODIMP
 nsSocketTransportService::Run()
 {
     PR_SetCurrentThreadName("Socket Thread");
@@ -725,25 +739,49 @@ nsSocketTransportService::Run()
     for (;;) {
         bool pendingEvents = false;
         thread->HasPendingEvents(&pendingEvents);
 
         do {
             // If there are pending events for this thread then
             // DoPollIteration() should service the network without blocking.
             DoPollIteration(!pendingEvents);
-            
+
             // If nothing was pending before the poll, it might be now
-            if (!pendingEvents)
+            if (!pendingEvents) {
                 thread->HasPendingEvents(&pendingEvents);
+            }
 
             if (pendingEvents) {
-                NS_ProcessNextEvent(thread);
-                pendingEvents = false;
-                thread->HasPendingEvents(&pendingEvents);
+                if (mServeMultipleEventsPerPollIter) {
+                    if (!mServingPendingQueue) {
+                        nsresult rv = Dispatch(NS_NewRunnableMethod(this,
+                            &nsSocketTransportService::MarkTheLastElementOfPendingQueue),
+                            nsIEventTarget::DISPATCH_NORMAL);
+                        if (NS_FAILED(rv)) {
+                            NS_WARNING("Could not dispatch a new event on the "
+                                       "socket thread.");
+                        } else {
+                            mServingPendingQueue = true;
+                        }
+                    }
+                    TimeStamp eventQueueStart = TimeStamp::NowLoRes();
+                    do {
+                        NS_ProcessNextEvent(thread);
+                        pendingEvents = false;
+                        thread->HasPendingEvents(&pendingEvents);
+                    } while (pendingEvents && mServingPendingQueue &&
+                             ((TimeStamp::NowLoRes() -
+                               eventQueueStart).ToMilliseconds() <
+                              mMaxTimePerPollIter));
+                } else {
+                    NS_ProcessNextEvent(thread);
+                    pendingEvents = false;
+                    thread->HasPendingEvents(&pendingEvents);
+                }
             }
         } while (pendingEvents);
 
         bool goingOffline = false;
         // now that our event queue is empty, check to see if we should exit
         {
             MutexAutoLock lock(mLock);
             if (mShuttingDown)
@@ -977,16 +1015,30 @@ nsSocketTransportService::UpdatePrefs()
                                            1, kMaxTCPKeepCount);
         bool keepaliveEnabled = false;
         rv = tmpPrefService->GetBoolPref(KEEPALIVE_ENABLED_PREF,
                                          &keepaliveEnabled);
         if (NS_SUCCEEDED(rv) && keepaliveEnabled != mKeepaliveEnabledPref) {
             mKeepaliveEnabledPref = keepaliveEnabled;
             OnKeepaliveEnabledPrefChange();
         }
+
+        bool serveMultiplePref = false;
+        rv = tmpPrefService->GetBoolPref(SERVE_MULTIPLE_EVENTS_PREF,
+                                         &serveMultiplePref);
+        if (NS_SUCCEEDED(rv)) {
+            mServeMultipleEventsPerPollIter = serveMultiplePref;
+        }
+
+        int32_t maxTimePref;
+        rv = tmpPrefService->GetIntPref(MAX_TIME_BETWEEN_TWO_POLLS,
+                                        &maxTimePref);
+        if (NS_SUCCEEDED(rv) && maxTimePref >= 0) {
+            mMaxTimePerPollIter = maxTimePref;
+        }
     }
     
     return NS_OK;
 }
 
 void
 nsSocketTransportService::OnKeepaliveEnabledPrefChange()
 {
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -12,16 +12,17 @@
 #include "nsEventQueue.h"
 #include "nsCOMPtr.h"
 #include "prinrval.h"
 #include "prlog.h"
 #include "prinit.h"
 #include "nsIObserver.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/net/DashboardTypes.h"
+#include "mozilla/Atomics.h"
 
 class nsASocketHandler;
 struct PRPollDesc;
 
 //-----------------------------------------------------------------------------
 
 #if defined(PR_LOGGING)
 //
@@ -212,30 +213,36 @@ private:
     int32_t     mKeepaliveIdleTimeS;
     // Number of seconds between retries should keepalive pings fail.
     int32_t     mKeepaliveRetryIntervalS;
     // Number of keepalive probes to send.
     int32_t     mKeepaliveProbeCount;
     // True if TCP keepalive is enabled globally.
     bool        mKeepaliveEnabledPref;
 
+    bool                   mServeMultipleEventsPerPollIter;
+    mozilla::Atomic<bool>  mServingPendingQueue;
+    int32_t                mMaxTimePerPollIter;
+
     void OnKeepaliveEnabledPrefChange();
     void NotifyKeepaliveEnabledPrefChange(SocketContext *sock);
 
     // Socket thread only for dynamically adjusting max socket size
 #if defined(XP_WIN)
     void ProbeMaxCount();
 #endif
     bool mProbedMaxCount;
 
     void AnalyzeConnection(nsTArray<mozilla::net::SocketInfo> *data,
                            SocketContext *context, bool aActive);
 
     void ClosePrivateConnections();
     void DetachSocketWithGuard(bool aGuardLocals,
                                SocketContext *socketList,
                                int32_t index);
+
+    void MarkTheLastElementOfPendingQueue();
 };
 
 extern nsSocketTransportService *gSocketTransportService;
 extern PRThread                 *gSocketThread;
 
 #endif // !nsSocketTransportService_h__