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 id1
push usershaver@mozilla.com
push dateTue, 04 Jan 2011 17:58:04 +0000
reviewersbzbarsky, blocking-beta8
bugs614286
milestone2.0b8pre
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);
+}