Bug 1513057 - P9: socket oepration should wait until socket process launch r=mayhemer,dragana,kershaw
authorJunior Hsu <juhsu@mozilla.com>
Sat, 12 Jan 2019 01:00:26 +0000
changeset 453588 d5da954d4dd2
parent 453587 44ff151d7111
child 453589 fbc1cb5a84a9
push id35360
push usernbeleuzu@mozilla.com
push dateSat, 12 Jan 2019 09:39:47 +0000
treeherdermozilla-central@cb35977ae7a4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmayhemer, dragana, kershaw
bugs1513057
milestone66.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 1513057 - P9: socket oepration should wait until socket process launch r=mayhemer,dragana,kershaw Differential Revision: https://phabricator.services.mozilla.com/D14510
netwerk/base/nsIOService.cpp
netwerk/base/nsIOService.h
netwerk/ipc/SocketProcessHost.cpp
--- a/netwerk/base/nsIOService.cpp
+++ b/netwerk/base/nsIOService.cpp
@@ -187,16 +187,17 @@ bool nsIOService::sBlockFTPSubresources 
 nsIOService::nsIOService()
     : mOffline(true),
       mOfflineForProfileChange(false),
       mManageLinkStatus(false),
       mConnectivity(true),
       mOfflineMirrorsConnectivity(true),
       mSettingOffline(false),
       mSetOfflineValue(false),
+      mSocketProcessLaunchComplete(false),
       mShutdown(false),
       mHttpHandlerAlreadyShutingDown(false),
       mNetworkLinkServiceInitialized(false),
       mChannelEventSinks(NS_CHANNEL_EVENT_SINK_CATEGORY),
       mNetworkNotifyChanged(true),
       mTotalRequests(0),
       mCacheWon(0),
       mNetWon(0),
@@ -436,35 +437,61 @@ void nsIOService::DestroySocketProcess()
 
 bool nsIOService::SocketProcessReady() {
   return mSocketProcess && mSocketProcess->IsConnected();
 }
 
 void nsIOService::NotifySocketProcessPrefsChanged(const char *aName) {
   MOZ_ASSERT(NS_IsMainThread());
 
-  if (!SocketProcessReady()) {
-    mQueuedPrefNames.AppendElement(aName);
+  if (!XRE_IsParentProcess()) {
     return;
   }
 
   dom::Pref pref(nsCString(aName), /* isLocked */ false, null_t(), null_t());
   Preferences::GetPreference(&pref);
-  Unused << mSocketProcess->GetActor()->SendPreferenceUpdate(pref);
+  auto sendPrefUpdate = [pref]() {
+    Unused << gIOService->mSocketProcess->GetActor()->SendPreferenceUpdate(pref);
+  };
+  CallOrWaitForSocketProcess(sendPrefUpdate);
 }
 
 void nsIOService::OnProcessLaunchComplete(SocketProcessHost *aHost,
                                           bool aSucceeded) {
   MOZ_ASSERT(NS_IsMainThread());
 
   LOG(("nsIOService::OnProcessLaunchComplete aSucceeded=%d\n", aSucceeded));
-  for (auto name : mQueuedPrefNames) {
-    NotifySocketProcessPrefsChanged(name);
+
+  mSocketProcessLaunchComplete = true;
+
+  if (mShutdown || !SocketProcessReady()) {
+    return;
+  }
+
+  if (!mPendingEvents.IsEmpty()) {
+    nsTArray<std::function<void()>> pendingEvents;
+    mPendingEvents.SwapElements(pendingEvents);
+    for (auto& func : pendingEvents) {
+      func();
+    }
   }
-  mQueuedPrefNames.Clear();
+}
+
+void nsIOService::CallOrWaitForSocketProcess(const std::function<void()>& aFunc) {
+  MOZ_ASSERT(NS_IsMainThread());
+  if (IsSocketProcessLaunchComplete() && SocketProcessReady()) {
+    aFunc();
+  } else {
+    mPendingEvents.AppendElement(aFunc);  // infallible
+  }
+}
+
+bool nsIOService::IsSocketProcessLaunchComplete() {
+  MOZ_ASSERT(NS_IsMainThread());
+  return mSocketProcessLaunchComplete;
 }
 
 void nsIOService::OnProcessUnexpectedShutdown(SocketProcessHost *aHost) {
   MOZ_ASSERT(NS_IsMainThread());
 
   LOG(("nsIOService::OnProcessUnexpectedShutdown\n"));
   DestroySocketProcess();
 }
@@ -1378,31 +1405,37 @@ nsIOService::Observe(nsISupports *subjec
       SetOffline(true);
     }
   } else if (!strcmp(topic, kProfileChangeNetRestoreTopic)) {
     if (mOfflineForProfileChange) {
       mOfflineForProfileChange = false;
       SetOffline(false);
     }
   } else if (!strcmp(topic, kProfileDoChange)) {
-    if (data && NS_LITERAL_STRING("startup").Equals(data)) {
+    if (!data) {
+      return NS_OK;
+    }
+    if (NS_LITERAL_STRING("startup").Equals(data)) {
       // Lazy initialization of network link service (see bug 620472)
       InitializeNetworkLinkService();
       // Set up the initilization flag regardless the actuall result.
       // If we fail here, we will fail always on.
       mNetworkLinkServiceInitialized = true;
 
       // And now reflect the preference setting
       PrefsChanged(MANAGE_OFFLINE_STATUS_PREF);
 
       // Bug 870460 - Read cookie database at an early-as-possible time
       // off main thread. Hence, we have more chance to finish db query
       // before something calls into the cookie service.
       nsCOMPtr<nsISupports> cookieServ =
           do_GetService(NS_COOKIESERVICE_CONTRACTID);
+    } else if (NS_LITERAL_STRING("xpcshell-do-get-profile").Equals(data)) {
+      // xpcshell doesn't read user profile.
+      LaunchSocketProcess();
     }
   } 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 (!mHttpHandlerAlreadyShutingDown && !mOfflineForProfileChange) {
--- a/netwerk/base/nsIOService.h
+++ b/netwerk/base/nsIOService.h
@@ -116,16 +116,23 @@ class nsIOService final : public nsIIOSe
   // Used to trigger a recheck of the captive portal status
   nsresult RecheckCaptivePortal();
 
   void OnProcessLaunchComplete(SocketProcessHost* aHost, bool aSucceeded);
   void OnProcessUnexpectedShutdown(SocketProcessHost* aHost);
   bool SocketProcessReady();
   void NotifySocketProcessPrefsChanged(const char* aName);
 
+  bool IsSocketProcessLaunchComplete();
+
+  // Call func immediately if socket process is launched completely. Otherwise,
+  // |func| will be queued and then executed in the *main thread* once socket
+  // process is launced.
+  void CallOrWaitForSocketProcess(const std::function<void()>& aFunc);
+
   friend SocketProcessMemoryReporter;
   RefPtr<MemoryReportingProcess> GetSocketProcessMemoryReporter();
 
  private:
   // These shouldn't be called directly:
   // - construct using GetInstance
   // - destroy using Release
   nsIOService();
@@ -185,16 +192,18 @@ class nsIOService final : public nsIIOSe
   // meaning if !mConnectivity, GetOffline() will return true
   bool mOfflineMirrorsConnectivity;
 
   // Used to handle SetOffline() reentrancy.  See the comment in
   // SetOffline() for more details.
   bool mSettingOffline;
   bool mSetOfflineValue;
 
+  bool mSocketProcessLaunchComplete;
+
   mozilla::Atomic<bool, mozilla::Relaxed> mShutdown;
   mozilla::Atomic<bool, mozilla::Relaxed> mHttpHandlerAlreadyShutingDown;
 
   nsCOMPtr<nsPISocketTransportService> mSocketTransportService;
   nsCOMPtr<nsICaptivePortalService> mCaptivePortalService;
   nsCOMPtr<nsINetworkLinkService> mNetworkLinkService;
   bool mNetworkLinkServiceInitialized;
 
@@ -224,17 +233,21 @@ class nsIOService final : public nsIIOSe
   mozilla::Atomic<PRIntervalTime> mLastOfflineStateChange;
   mozilla::Atomic<PRIntervalTime> mLastConnectivityChange;
   mozilla::Atomic<PRIntervalTime> mLastNetworkLinkChange;
 
   // Time a network tearing down started.
   mozilla::Atomic<PRIntervalTime> mNetTearingDownStarted;
 
   SocketProcessHost* mSocketProcess;
-  nsTArray<const char*> mQueuedPrefNames;
+
+  // Events should be executed after the socket process is launched. Will
+  // dispatch these events while socket process fires OnProcessLaunchComplete.
+  // Note: this array is accessed only on the main thread.
+  nsTArray<std::function<void()>> mPendingEvents;
 
  public:
   // Used for all default buffer sizes that necko allocates.
   static uint32_t gDefaultSegmentSize;
   static uint32_t gDefaultSegmentCount;
 };
 
 /**
--- a/netwerk/ipc/SocketProcessHost.cpp
+++ b/netwerk/ipc/SocketProcessHost.cpp
@@ -23,16 +23,17 @@ class OfflineObserver final : public nsI
   NS_DECL_THREADSAFE_ISUPPORTS
   explicit OfflineObserver(SocketProcessHost* aProcessHost)
       : mProcessHost(aProcessHost) {
     MOZ_ASSERT(NS_IsMainThread());
 
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
       obs->AddObserver(this, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC, false);
+      obs->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, false);
     }
   }
 
   void Destroy() {
     MOZ_ASSERT(NS_IsMainThread());
 
     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
     if (obs) {
@@ -53,16 +54,21 @@ class OfflineObserver final : public nsI
     if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
       NS_ConvertUTF16toUTF8 dataStr(aData);
       const char* offline = dataStr.get();
       if (!mProcessHost->IsConnected() ||
           mProcessHost->GetActor()->SendSetOffline(
               !strcmp(offline, "true") ? true : false)) {
         return NS_ERROR_NOT_AVAILABLE;
       }
+    } else if (!strcmp(aTopic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
+      nsCOMPtr<nsIObserverService> obs =
+          mozilla::services::GetObserverService();
+        obs->RemoveObserver(this, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC);
+        obs->RemoveObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID);
     }
 
     return NS_OK;
   }
   virtual ~OfflineObserver() = default;
 
   SocketProcessHost* mProcessHost;
 };