Bug 614286 - Don't initialize the socket transport service unless we know that we're online; r=bzbarsky a=blocking-beta8+
☠☠ backed out by e0bb5d9067f1 ☠ ☠
authorEhsan Akhgari <ehsan@mozilla.com>
Thu, 25 Nov 2010 00:20:11 -0500
changeset 58375 630b08a7fe632dd311f78b23e17e4c78458d6cbd
parent 58374 b2dad520d4aebf5806a86201d24a4c6c18161f02
child 58376 0ebeeb2fae57ff3445a37062174916e52e6ac307
child 58389 e0bb5d9067f1611a51de87326ea5422776930238
push idunknown
push userunknown
push dateunknown
reviewersbzbarsky, blocking-beta8
bugs614286
milestone2.0b8pre
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 614286 - Don't initialize the socket transport service unless we know that we're online; r=bzbarsky a=blocking-beta8+
netwerk/base/src/nsIOService.cpp
netwerk/base/src/nsIOService.h
netwerk/base/src/nsSocketTransportService2.cpp
netwerk/dns/nsDNSService2.cpp
netwerk/protocol/http/nsHttpConnectionMgr.cpp
netwerk/protocol/http/nsHttpConnectionMgr.h
netwerk/test/unit/test_offline_status.js
netwerk/test/unit/test_sockettransportsvc_available.js
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -169,48 +169,38 @@ static const char kProfileChangeNetResto
 // Necko buffer cache
 nsIMemory* nsIOService::gBufferCache = nsnull;
 PRUint32   nsIOService::gDefaultSegmentSize = 4096;
 PRUint32   nsIOService::gDefaultSegmentCount = 24;
 
 ////////////////////////////////////////////////////////////////////////////////
 
 nsIOService::nsIOService()
-    : mOffline(PR_FALSE)
+    : mOffline(PR_TRUE)
     , mOfflineForProfileChange(PR_FALSE)
     , mManageOfflineStatus(PR_TRUE)
     , mSettingOffline(PR_FALSE)
     , mSetOfflineValue(PR_FALSE)
     , mShutdown(PR_FALSE)
     , mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY)
     , mContentSniffers(NS_CONTENT_SNIFFER_CATEGORY)
 {
 }
 
 nsresult
 nsIOService::Init()
 {
     NS_TIME_FUNCTION;
 
     nsresult rv;
-    
-    // We need to get references to these services so that we can shut them
+
+    // We need to get references to the DNS service so that we can shut it
     // down later. If we wait until the nsIOService is being shut down,
     // GetService will fail at that point.
 
-    // TODO(darin): Load the Socket and DNS services lazily.
-
-    mSocketTransportService = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
-    if (NS_FAILED(rv)) {
-        NS_WARNING("failed to get socket transport service");
-        return rv;
-    }
-
-    NS_TIME_FUNCTION_MARK("got SocketTransportService");
-
     mDNSService = do_GetService(NS_DNSSERVICE_CONTRACTID, &rv);
     if (NS_FAILED(rv)) {
         NS_WARNING("failed to get DNS service");
         return rv;
     }
 
     NS_TIME_FUNCTION_MARK("got DNS Service");
 
@@ -288,17 +278,39 @@ nsIOService::Init()
 
     return NS_OK;
 }
 
 
 nsIOService::~nsIOService()
 {
     gIOService = nsnull;
-}   
+}
+
+nsresult
+nsIOService::InitializeSocketTransportService()
+{
+    NS_TIME_FUNCTION;
+
+    nsresult rv = NS_OK;
+
+    if (!mSocketTransportService) {
+        mSocketTransportService = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+        if (NS_FAILED(rv)) {
+            NS_WARNING("failed to get socket transport service");
+        }
+    }
+
+    if (mSocketTransportService) {
+        rv = mSocketTransportService->Init();
+        NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
+    }
+
+    return rv;
+}
 
 nsIOService*
 nsIOService::GetInstance() {
     if (!gIOService) {
         gIOService = new nsIOService();
         if (!gIOService)
             return nsnull;
         NS_ADDREF(gIOService);
@@ -737,20 +749,17 @@ nsIOService::SetOffline(PRBool offline)
                                                  offlineString.get());
         }
         else if (!offline && mOffline) {
             // go online
             if (mDNSService) {
                 rv = mDNSService->Init();
                 NS_ASSERTION(NS_SUCCEEDED(rv), "DNS service init failed");
             }
-            if (mSocketTransportService) {
-                rv = mSocketTransportService->Init();
-                NS_ASSERTION(NS_SUCCEEDED(rv), "socket transport service init failed");
-            }
+            InitializeSocketTransportService();
             mOffline = PR_FALSE;    // indicate success only AFTER we've
                                     // brought up the services
 
             // trigger a PAC reload when we come back online
             if (mProxyService)
                 mProxyService->ReloadPAC();
 
             // don't care if notification fails
--- a/netwerk/base/src/nsIOService.h
+++ b/netwerk/base/src/nsIOService.h
@@ -103,16 +103,20 @@ public:
     // Gets the array of registered content sniffers
     const nsCOMArray<nsIContentSniffer>& GetContentSniffers() {
       return mContentSniffers.GetEntries();
     }
 
     PRBool IsOffline() { return mOffline; }
     PRBool IsLinkUp();
 
+    PRBool IsComingOnline() const {
+      return mOffline && mSettingOffline && !mSetOfflineValue;
+    }
+
 private:
     // These shouldn't be called directly:
     // - construct using GetInstance
     // - destroy using Release
     nsIOService() NS_HIDDEN;
     ~nsIOService() NS_HIDDEN;
 
     NS_HIDDEN_(nsresult) TrackNetworkLinkStatusForOffline();
@@ -124,16 +128,18 @@ private:
     NS_HIDDEN_(nsresult) CacheProtocolHandler(const char *scheme,
                                               nsIProtocolHandler* hdlr);
 
     // Prefs wrangling
     NS_HIDDEN_(void) PrefsChanged(nsIPrefBranch *prefs, const char *pref = nsnull);
     NS_HIDDEN_(void) GetPrefBranch(nsIPrefBranch2 **);
     NS_HIDDEN_(void) ParsePortList(nsIPrefBranch *prefBranch, const char *pref, PRBool remove);
 
+    nsresult InitializeSocketTransportService();
+
 private:
     PRPackedBool                         mOffline;
     PRPackedBool                         mOfflineForProfileChange;
     PRPackedBool                         mManageOfflineStatus;
 
     // Used to handle SetOffline() reentrancy.  See the comment in
     // SetOffline() for more details.
     PRPackedBool                         mSettingOffline;
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -47,16 +47,17 @@
 #include "nsNetError.h"
 #include "prnetdb.h"
 #include "prlock.h"
 #include "prerror.h"
 #include "plstr.h"
 #include "nsIPrefService.h"
 #include "nsIPrefBranch2.h"
 #include "nsServiceManagerUtils.h"
+#include "nsIOService.h"
 
 #include "mozilla/FunctionTimer.h"
 
 #if defined(PR_LOGGING)
 PRLogModuleInfo *gSocketTransportLog = nsnull;
 #endif
 
 nsSocketTransportService *gSocketTransportService = nsnull;
@@ -390,16 +391,20 @@ nsSocketTransportService::Init()
     }
 
     if (mInitialized)
         return NS_OK;
 
     if (mShuttingDown)
         return NS_ERROR_UNEXPECTED;
 
+    // Don't initialize inside the offline mode
+    if (gIOService->IsOffline() && !gIOService->IsComingOnline())
+        return NS_ERROR_OFFLINE;
+
     if (!mThreadEvent) {
         mThreadEvent = PR_NewPollableEvent();
         //
         // NOTE: per bug 190000, this failure could be caused by Zone-Alarm
         // or similar software.
         //
         // NOTE: per bug 191739, this failure could also be caused by lack
         // of a loopback device on Windows and OS/2 platforms (NSPR creates
--- a/netwerk/dns/nsDNSService2.cpp
+++ b/netwerk/dns/nsDNSService2.cpp
@@ -52,16 +52,17 @@
 #include "nsNetError.h"
 #include "nsDNSPrefetch.h"
 #include "nsIProtocolProxyService.h"
 #include "prsystem.h"
 #include "prnetdb.h"
 #include "prmon.h"
 #include "prio.h"
 #include "plstr.h"
+#include "nsIOService.h"
 
 #include "mozilla/FunctionTimer.h"
 
 static const char kPrefDnsCacheEntries[]    = "network.dnsCacheEntries";
 static const char kPrefDnsCacheExpiration[] = "network.dnsCacheExpiration";
 static const char kPrefEnableIDN[]          = "network.enableIDN";
 static const char kPrefIPv4OnlyDomains[]    = "network.dns.ipv4OnlyDomains";
 static const char kPrefDisableIPv6[]        = "network.dns.disableIPv6";
@@ -377,33 +378,38 @@ nsDNSService::Init()
     }
 
     // we have to null out mIDN since we might be getting re-initialized
     // as a result of a pref change.
     nsCOMPtr<nsIIDNService> idn;
     if (enableIDN)
         idn = do_GetService(NS_IDNSERVICE_CONTRACTID);
 
+    nsDNSPrefetch::Initialize(this);
+
+    // Don't initialize the resolver if we're in offline mode.
+    // Later on, the IO service will reinitialize us when going online.
+    if (gIOService->IsOffline() && !gIOService->IsComingOnline())
+        return NS_OK;
+
     nsRefPtr<nsHostResolver> res;
     nsresult rv = nsHostResolver::Create(maxCacheEntries,
                                          maxCacheLifetime,
                                          getter_AddRefs(res));
     if (NS_SUCCEEDED(rv)) {
         // now, set all of our member variables while holding the lock
         nsAutoLock lock(mLock);
         mResolver = res;
         mIDN = idn;
         mIPv4OnlyDomains = ipv4OnlyDomains; // exchanges buffer ownership
         mDisableIPv6 = disableIPv6;
 
         // Disable prefetching either by explicit preference or if a manual proxy is configured 
         mDisablePrefetch = disablePrefetch || (proxyType == nsIProtocolProxyService::PROXYCONFIG_MANUAL);
     }
-    
-    nsDNSPrefetch::Initialize(this);
     return rv;
 }
 
 NS_IMETHODIMP
 nsDNSService::Shutdown()
 {
     nsRefPtr<nsHostResolver> res;
     {
@@ -566,18 +572,18 @@ nsDNSService::Observe(nsISupports *subje
     //
     // NOTE Shutting down and reinitializing the service like this is obviously
     // suboptimal if Observe gets called several times in a row, but we don't
     // expect that to be the case.
     //
 
     if (mResolver) {
         Shutdown();
-        Init();
     }
+    Init();
     return NS_OK;
 }
 
 PRUint16
 nsDNSService::GetAFForLookup(const nsACString &host)
 {
     if (mDisableIPv6)
         return PR_AF_INET;
--- a/netwerk/protocol/http/nsHttpConnectionMgr.cpp
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.cpp
@@ -38,16 +38,17 @@
 
 #include "nsHttpConnectionMgr.h"
 #include "nsHttpConnection.h"
 #include "nsHttpPipeline.h"
 #include "nsHttpHandler.h"
 #include "nsAutoLock.h"
 #include "nsNetCID.h"
 #include "nsCOMPtr.h"
+#include "nsNetUtil.h"
 
 #include "nsIServiceManager.h"
 
 #include "nsIObserverService.h"
 
 // defined by the socket transport service while active
 extern PRThread *gSocketThread;
 
@@ -96,48 +97,65 @@ nsHttpConnectionMgr::~nsHttpConnectionMg
 {
     LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
  
     if (mMonitor)
         nsAutoMonitor::DestroyMonitor(mMonitor);
 }
 
 nsresult
+nsHttpConnectionMgr::EnsureSocketThreadTargetIfOnline()
+{
+    nsresult rv;
+    nsCOMPtr<nsIEventTarget> sts;
+    nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
+    if (NS_SUCCEEDED(rv)) {
+        PRBool offline = PR_TRUE;
+        ioService->GetOffline(&offline);
+
+        if (!offline) {
+            sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+        }
+    }
+
+    nsAutoMonitor mon(mMonitor);
+
+    // do nothing if already initialized
+    if (mSocketThreadTarget)
+        return NS_OK;
+
+    mSocketThreadTarget = sts;
+
+    return rv;
+}
+
+nsresult
 nsHttpConnectionMgr::Init(PRUint16 maxConns,
                           PRUint16 maxConnsPerHost,
                           PRUint16 maxConnsPerProxy,
                           PRUint16 maxPersistConnsPerHost,
                           PRUint16 maxPersistConnsPerProxy,
                           PRUint16 maxRequestDelay,
                           PRUint16 maxPipelinedRequests)
 {
     LOG(("nsHttpConnectionMgr::Init\n"));
 
-    nsresult rv;
-    nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
-    if (NS_FAILED(rv)) return rv;
-
-    nsAutoMonitor mon(mMonitor);
-
-    // do nothing if already initialized
-    if (mSocketThreadTarget)
-        return NS_OK;
+    {
+        nsAutoMonitor mon(mMonitor);
 
-    // no need to do any special synchronization here since there cannot be
-    // any activity on the socket thread (because Shutdown is synchronous).
-    mMaxConns = maxConns;
-    mMaxConnsPerHost = maxConnsPerHost;
-    mMaxConnsPerProxy = maxConnsPerProxy;
-    mMaxPersistConnsPerHost = maxPersistConnsPerHost;
-    mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
-    mMaxRequestDelay = maxRequestDelay;
-    mMaxPipelinedRequests = maxPipelinedRequests;
+        mMaxConns = maxConns;
+        mMaxConnsPerHost = maxConnsPerHost;
+        mMaxConnsPerProxy = maxConnsPerProxy;
+        mMaxPersistConnsPerHost = maxPersistConnsPerHost;
+        mMaxPersistConnsPerProxy = maxPersistConnsPerProxy;
+        mMaxRequestDelay = maxRequestDelay;
+        mMaxPipelinedRequests = maxPipelinedRequests;
+    }
 
-    mSocketThreadTarget = sts;
-    return rv;
+    return EnsureSocketThreadTargetIfOnline();
 }
 
 nsresult
 nsHttpConnectionMgr::Shutdown()
 {
     LOG(("nsHttpConnectionMgr::Shutdown\n"));
 
     nsAutoMonitor mon(mMonitor);
@@ -161,16 +179,22 @@ nsHttpConnectionMgr::Shutdown()
     // wait for shutdown event to complete
     mon.Wait();
     return NS_OK;
 }
 
 nsresult
 nsHttpConnectionMgr::PostEvent(nsConnEventHandler handler, PRInt32 iparam, void *vparam)
 {
+    // This object doesn't get reinitialized if the offline state changes, so our
+    // socket thread target might be uninitialized if we were offline when this
+    // object was being initialized, and we go online later on.  This call takes
+    // care of initializing the socket thread target if that's the case.
+    EnsureSocketThreadTargetIfOnline();
+
     nsAutoMonitor mon(mMonitor);
 
     nsresult rv;
     if (!mSocketThreadTarget) {
         NS_WARNING("cannot post event if not initialized");
         rv = NS_ERROR_NOT_INITIALIZED;
     }
     else {
@@ -280,16 +304,22 @@ nsresult
 nsHttpConnectionMgr::PruneDeadConnections()
 {
     return PostEvent(&nsHttpConnectionMgr::OnMsgPruneDeadConnections);
 }
 
 nsresult
 nsHttpConnectionMgr::GetSocketThreadTarget(nsIEventTarget **target)
 {
+    // This object doesn't get reinitialized if the offline state changes, so our
+    // socket thread target might be uninitialized if we were offline when this
+    // object was being initialized, and we go online later on.  This call takes
+    // care of initializing the socket thread target if that's the case.
+    EnsureSocketThreadTargetIfOnline();
+
     nsAutoMonitor mon(mMonitor);
     NS_IF_ADDREF(*target = mSocketThreadTarget);
     return NS_OK;
 }
 
 void
 nsHttpConnectionMgr::AddTransactionToPipeline(nsHttpPipeline *pipeline)
 {
--- a/netwerk/protocol/http/nsHttpConnectionMgr.h
+++ b/netwerk/protocol/http/nsHttpConnectionMgr.h
@@ -210,16 +210,17 @@ private:
     static PRIntn PurgeExcessIdleConnectionsCB(nsHashKey *, void *, void *);
     PRBool   ProcessPendingQForEntry(nsConnectionEntry *);
     PRBool   AtActiveConnectionLimit(nsConnectionEntry *, PRUint8 caps);
     void     GetConnection(nsConnectionEntry *, PRUint8 caps, nsHttpConnection **);
     nsresult DispatchTransaction(nsConnectionEntry *, nsAHttpTransaction *,
                                  PRUint8 caps, nsHttpConnection *);
     PRBool   BuildPipeline(nsConnectionEntry *, nsAHttpTransaction *, nsHttpPipeline **);
     nsresult ProcessNewTransaction(nsHttpTransaction *);
+    nsresult EnsureSocketThreadTargetIfOnline();
 
     // message handlers have this signature
     typedef void (nsHttpConnectionMgr:: *nsConnEventHandler)(PRInt32, void *);
 
     // nsConnEvent
     //
     // subclass of nsRunnable used to marshall events to the socket transport
     // thread.  this class is used to implement PostEvent.
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_offline_status.js
@@ -0,0 +1,10 @@
+function run_test() {
+  var ioService = Components.classes["@mozilla.org/network/io-service;1"]
+                            .getService(Components.interfaces.nsIIOService);
+
+  var linkService = Components.classes["@mozilla.org/network/network-link-service;1"]
+                              .getService(Components.interfaces.nsINetworkLinkService);
+
+  // The offline status should depends on the link status
+  do_check_neq(ioService.offline, linkService.isLinkUp);
+}
new file mode 100644
--- /dev/null
+++ b/netwerk/test/unit/test_sockettransportsvc_available.js
@@ -0,0 +1,8 @@
+function run_test() {
+  try {
+    var sts = Components.classes["@mozilla.org/network/socket-transport-service;1"]
+                        .getService(Components.interfaces.nsISocketTransportService);
+  } catch(e) {}
+
+  do_check_true(!!sts);
+}