Bug 1240481 - Limit PR_Close calls during shutdown. r=mcmanus
authorDragana Damjanovic <dd.mozilla@gmail.com>
Mon, 18 Jan 2016 08:20:00 +0100
changeset 317527 66bce0b7c669deccc20ff146297478c1bf33f6db
parent 317526 c12b2d098ae024cd48e5af8ea3ae2863ee1798da
child 317528 3e00d789f42b21d105b67ddf0e20b0dd900ba70d
push id1079
push userjlund@mozilla.com
push dateFri, 15 Apr 2016 21:02:33 +0000
treeherdermozilla-release@575fbf6786d5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmcmanus
bugs1240481
milestone46.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 1240481 - Limit PR_Close calls during shutdown. r=mcmanus
modules/libpref/init/all.js
netwerk/base/nsIOService.cpp
netwerk/base/nsIOService.h
netwerk/base/nsSocketTransport2.cpp
netwerk/base/nsSocketTransportService2.cpp
netwerk/base/nsSocketTransportService2.h
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1476,16 +1476,20 @@ pref("network.http.signed-packages.enabl
 // 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);
 
 // The max time to spend on xpcom events between two polls in ms.
 pref("network.sts.max_time_for_events_between_two_polls", 100);
+
+// During shutdown we limit PR_Close calls. If time exceeds this pref (in ms)
+// let sockets just leak.
+pref("network.sts.max_time_for_pr_close_during_shutdown", 5000);
 // </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/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -185,16 +185,17 @@ nsIOService::nsIOService()
     , mNetworkLinkServiceInitialized(false)
     , mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
     , mAutoDialEnabled(false)
     , mNetworkNotifyChanged(true)
     , mPreviousWifiState(-1)
     , mLastOfflineStateChange(PR_IntervalNow())
     , mLastConnectivityChange(PR_IntervalNow())
     , mLastNetworkLinkChange(PR_IntervalNow())
+    , mNetTearingDownStarted(0)
 {
 }
 
 nsresult
 nsIOService::Init()
 {
     nsresult rv;
 
@@ -1414,16 +1415,17 @@ nsIOService::Observe(nsISupports *subjec
 {
     if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
         nsCOMPtr<nsIPrefBranch> prefBranch = do_QueryInterface(subject);
         if (prefBranch)
             PrefsChanged(prefBranch, NS_ConvertUTF16toUTF8(data).get());
     } else if (!strcmp(topic, kProfileChangeNetTeardownTopic)) {
         if (!mOffline) {
             mOfflineForProfileChange = true;
+            mNetTearingDownStarted = PR_IntervalNow();
             if (gHttpHandler) {
                 gHttpHandler->ShutdownConnectionManager();
             }
             SetOffline(true);
         }
     } else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
         if (mOfflineForProfileChange) {
             mOfflineForProfileChange = false;
@@ -1442,17 +1444,19 @@ nsIOService::Observe(nsISupports *subjec
             GetPrefBranch(getter_AddRefs(prefBranch));
             PrefsChanged(prefBranch, MANAGE_OFFLINE_STATUS_PREF);
         }
     } else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
         // Remember we passed XPCOM shutdown notification to prevent any
         // changes of the offline status from now. We must not allow going
         // online after this point.
         mShutdown = true;
-
+        if (!mOfflineForProfileChange) {
+          mNetTearingDownStarted = PR_IntervalNow();
+        }
         if (gHttpHandler) {
             gHttpHandler->ShutdownConnectionManager();
         }
         SetOffline(true);
 
         if (mCaptivePortalService) {
             static_cast<CaptivePortalService*>(mCaptivePortalService.get())->Stop();
             mCaptivePortalService = nullptr;
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -80,16 +80,17 @@ public:
                                     uint32_t flags,
                                     nsAsyncRedirectVerifyHelper *helper);
 
     bool IsOffline() { return mOffline; }
     PRIntervalTime LastOfflineStateChange() { return mLastOfflineStateChange; }
     PRIntervalTime LastConnectivityChange() { return mLastConnectivityChange; }
     PRIntervalTime LastNetworkLinkChange() { return mLastNetworkLinkChange; }
     bool IsNetTearingDown() { return mShutdown || mOfflineForProfileChange; }
+    PRIntervalTime NetTearingDownStarted() { return mNetTearingDownStarted; }
     bool IsLinkUp();
 
     // Should only be called from NeckoChild. Use SetAppOffline instead.
     void SetAppOfflineInternal(uint32_t appId, int32_t status);
 
 private:
     // These shouldn't be called directly:
     // - construct using GetInstance
@@ -175,19 +176,22 @@ private:
     nsDataHashtable<nsUint32HashKey, int32_t> mAppsOfflineStatus;
 
     static bool                          sTelemetryEnabled;
 
     // These timestamps are needed for collecting telemetry on PR_Connect,
     // PR_ConnectContinue and PR_Close blocking time.  If we spend very long
     // time in any of these functions we want to know if and what network
     // change has happened shortly before.
-    mozilla::Atomic<PRIntervalTime>  mLastOfflineStateChange;
-    mozilla::Atomic<PRIntervalTime>  mLastConnectivityChange;
-    mozilla::Atomic<PRIntervalTime>  mLastNetworkLinkChange;
+    mozilla::Atomic<PRIntervalTime> mLastOfflineStateChange;
+    mozilla::Atomic<PRIntervalTime> mLastConnectivityChange;
+    mozilla::Atomic<PRIntervalTime> mLastNetworkLinkChange;
+
+    // Time a network tearing down started.
+    mozilla::Atomic<PRIntervalTime> mNetTearingDownStarted;
 public:
     // Used for all default buffer sizes that necko allocates.
     static uint32_t   gDefaultSegmentSize;
     static uint32_t   gDefaultSegmentCount;
 };
 
 /**
  * This class is passed as the subject to a NotifyObservers call for the
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -1743,17 +1743,22 @@ void
 nsSocketTransport::ReleaseFD_Locked(PRFileDesc *fd)
 {
     mLock.AssertCurrentThreadOwns();
 
     NS_ASSERTION(mFD == fd, "wrong fd");
     SOCKET_LOG(("JIMB: ReleaseFD_Locked: mFDref = %d\n", mFDref));
 
     if (--mFDref == 0) {
-        if (PR_GetCurrentThread() == gSocketThread) {
+        if (gIOService->IsNetTearingDown() &&
+            ((PR_IntervalNow() - gIOService->NetTearingDownStarted()) >
+             gSocketTransportService->MaxTimeForPrClosePref())) {
+          // If shutdown last to long, let the socket leak and do not close it.
+          SOCKET_LOG(("Intentional leak"));
+        } else if (PR_GetCurrentThread() == gSocketThread) {
             SOCKET_LOG(("nsSocketTransport: calling PR_Close [this=%p]\n", this));
             PR_Close(mFD);
         } else {
             // Can't PR_Close() a socket off STS thread. Thunk it to STS to die
             STS_PRCloseOnSocketTransport(mFD);
         }
         mFD = nullptr;
     }
--- a/netwerk/base/nsSocketTransportService2.cpp
+++ b/netwerk/base/nsSocketTransportService2.cpp
@@ -42,16 +42,17 @@ Atomic<PRThread*, Relaxed> gSocketThread
 #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 MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
 #define TELEMETRY_PREF "toolkit.telemetry.enabled"
+#define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN "network.sts.max_time_for_pr_close_during_shutdown"
 
 uint32_t nsSocketTransportService::gMaxCount;
 PRCallOnceType nsSocketTransportService::gMaxCountInitOnce;
 
 class DebugMutexAutoLock
 {
 public:
     explicit DebugMutexAutoLock(Mutex& mutex);
@@ -106,16 +107,17 @@ nsSocketTransportService::nsSocketTransp
     , mSendBufferSize(0)
     , mKeepaliveIdleTimeS(600)
     , mKeepaliveRetryIntervalS(1)
     , mKeepaliveProbeCount(kDefaultTCPKeepCount)
     , mKeepaliveEnabledPref(false)
     , mServingPendingQueue(false)
     , mMaxTimePerPollIter(100)
     , mTelemetryEnabledPref(false)
+    , mMaxTimeForPrClosePref(PR_SecondsToInterval(5))
     , mProbedMaxCount(false)
 {
     NS_ASSERTION(NS_IsMainThread(), "wrong thread");
 
     PR_CallOnce(&gMaxCountInitOnce, DiscoverMaxCount);
     mActiveList = (SocketContext *)
         moz_xmalloc(sizeof(SocketContext) * mActiveListSize);
     mIdleList = (SocketContext *)
@@ -545,16 +547,17 @@ nsSocketTransportService::Init()
     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(MAX_TIME_BETWEEN_TWO_POLLS, this, false);
         tmpPrefService->AddObserver(TELEMETRY_PREF, this, false);
+        tmpPrefService->AddObserver(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN, 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);
     }
@@ -1213,16 +1216,23 @@ nsSocketTransportService::UpdatePrefs()
         }
 
         bool telemetryPref = false;
         rv = tmpPrefService->GetBoolPref(TELEMETRY_PREF,
                                          &telemetryPref);
         if (NS_SUCCEEDED(rv)) {
             mTelemetryEnabledPref = telemetryPref;
         }
+
+        int32_t maxTimeForPrClosePref;
+        rv = tmpPrefService->GetIntPref(MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN,
+                                        &maxTimeForPrClosePref);
+        if (NS_SUCCEEDED(rv) && maxTimeForPrClosePref >=0) {
+            mMaxTimeForPrClosePref = PR_MillisecondsToInterval(maxTimeForPrClosePref);
+        }
     }
     
     return NS_OK;
 #endif // defined(MOZILLA_XPCOMRT_API)
 }
 
 void
 nsSocketTransportService::OnKeepaliveEnabledPrefChange()
--- a/netwerk/base/nsSocketTransportService2.h
+++ b/netwerk/base/nsSocketTransportService2.h
@@ -107,16 +107,17 @@ public:
     void GetSocketConnections(nsTArray<mozilla::net::SocketInfo> *);
     uint64_t GetSentBytes() { return mSentBytesCount; }
     uint64_t GetReceivedBytes() { return mReceivedBytesCount; }
 
     // Returns true if keepalives are enabled in prefs.
     bool IsKeepaliveEnabled() { return mKeepaliveEnabledPref; }
 
     bool IsTelemetryEnabled() { return mTelemetryEnabledPref; }
+    PRIntervalTime MaxTimeForPrClosePref() {return mMaxTimeForPrClosePref; }
 protected:
 
     virtual ~nsSocketTransportService();
 
 private:
 
     //-------------------------------------------------------------------------
     // misc (any thread)
@@ -232,16 +233,17 @@ private:
     // Number of keepalive probes to send.
     int32_t     mKeepaliveProbeCount;
     // True if TCP keepalive is enabled globally.
     bool        mKeepaliveEnabledPref;
 
     mozilla::Atomic<bool>  mServingPendingQueue;
     mozilla::Atomic<int32_t, mozilla::Relaxed> mMaxTimePerPollIter;
     mozilla::Atomic<bool, mozilla::Relaxed>  mTelemetryEnabledPref;
+    mozilla::Atomic<PRIntervalTime, mozilla::Relaxed> mMaxTimeForPrClosePref;
 
     void OnKeepaliveEnabledPrefChange();
     void NotifyKeepaliveEnabledPrefChange(SocketContext *sock);
 
     // Socket thread only for dynamically adjusting max socket size
 #if defined(XP_WIN)
     void ProbeMaxCount();
 #endif