Bug 1063877 - Change nsVolumeService to use sync IPC for initial request. r=khuey, a=bajaj
authorDave Hylands <dhylands@mozilla.com>
Mon, 15 Sep 2014 11:51:25 -0700
changeset 224967 9ef7e2446ea80734c3a561df2fd7a44df5b193a3
parent 224966 afafa9e042316bf2f0348ea1522dff2a278d8810
child 224968 630bf4e1fb38281cb14f3c097f67640d274b3ec4
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskhuey, bajaj
bugs1063877
milestone34.0a2
Bug 1063877 - Change nsVolumeService to use sync IPC for initial request. r=khuey, a=bajaj
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/system/gonk/nsIVolumeService.idl
dom/system/gonk/nsVolumeMountLock.cpp
dom/system/gonk/nsVolumeMountLock.h
dom/system/gonk/nsVolumeService.cpp
dom/system/gonk/nsVolumeService.h
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -142,16 +142,17 @@
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
 # include "AndroidBridge.h"
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsIVolume.h"
+#include "nsVolumeService.h"
 #include "nsIVolumeService.h"
 #include "SpeakerManagerService.h"
 using namespace mozilla::system;
 #endif
 
 #ifdef MOZ_B2G_BT
 #include "BluetoothParent.h"
 #include "BluetoothService.h"
@@ -2427,27 +2428,24 @@ ContentParent::RecvDataStoreGetStores(
     return false;
   }
 
   mSendDataStoreInfos = true;
   return true;
 }
 
 bool
-ContentParent::RecvBroadcastVolume(const nsString& aVolumeName)
+ContentParent::RecvGetVolumes(InfallibleTArray<VolumeInfo>* aResult)
 {
 #ifdef MOZ_WIDGET_GONK
-    nsresult rv;
-    nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
-    if (vs) {
-        vs->BroadcastVolume(aVolumeName);
-    }
+    nsRefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
+    vs->GetVolumesForIPC(aResult);
     return true;
 #else
-    NS_WARNING("ContentParent::RecvBroadcastVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
+    NS_WARNING("ContentParent::RecvGetVolumes shouldn't be called when MOZ_WIDGET_GONK is not defined");
     return false;
 #endif
 }
 
 bool
 ContentParent::RecvNuwaReady()
 {
 #ifdef MOZ_NUWA_PROCESS
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -592,17 +592,17 @@ private:
                                                 const bool& aElementHidden,
                                                 const bool& aWithVideo) MOZ_OVERRIDE;
 
     virtual bool RecvAudioChannelChangedNotification() MOZ_OVERRIDE;
 
     virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                      const bool& aHidden) MOZ_OVERRIDE;
     virtual bool RecvGetSystemMemory(const uint64_t& getterId) MOZ_OVERRIDE;
-    virtual bool RecvBroadcastVolume(const nsString& aVolumeName) MOZ_OVERRIDE;
+    virtual bool RecvGetVolumes(InfallibleTArray<VolumeInfo>* aResult) MOZ_OVERRIDE;
 
     virtual bool RecvDataStoreGetStores(
                        const nsString& aName,
                        const nsString& aOwner,
                        const IPC::Principal& aPrincipal,
                        InfallibleTArray<DataStoreSetting>* aValue) MOZ_OVERRIDE;
 
     virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue) MOZ_OVERRIDE;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -284,16 +284,29 @@ struct PrefSetting {
 struct DataStoreSetting {
   nsString name;
   nsString originURL;
   nsString manifestURL;
   bool readOnly;
   bool enabled;
 };
 
+// Note: Any changes to this structure should also be changed in
+// FileSystemUpdate below.
+struct VolumeInfo {
+  nsString name;
+  nsString mountPoint;
+  int32_t volState;
+  int32_t mountGeneration;
+  bool isMediaPresent;
+  bool isSharing;
+  bool isFormatting;
+  bool isFake;
+};
+
 intr protocol PContent
 {
     parent opens PCompositor;
     parent opens PSharedBufferManager;
     parent opens PImageBridge;
     child opens PBackground;
 
     manages PAsmJSCacheEntry;
@@ -420,16 +433,18 @@ child:
     AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName);
 
     // Notify child that last-pb-context-exited notification was observed
     LastPrivateDocShellDestroyed();
 
     FilePathUpdate(nsString storageType, nsString storageName, nsString filepath,
                    nsCString reasons);
 
+    // Note: Any changes to this structure should also be changed in
+    // VolumeInfo above.
     FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
                      int32_t mountGeneration, bool isMediaPresent,
                      bool isSharing, bool isFormatting, bool isFake);
 
     // Ask the Nuwa process to create a new child process.
     NuwaFork();
 
     NotifyProcessPriorityChanged(ProcessPriority priority);
@@ -612,18 +627,18 @@ parent:
 
     sync DataStoreGetStores(nsString aName, nsString aOwner, Principal aPrincipal)
         returns (DataStoreSetting[] dataStores);
 
     async FilePathUpdateNotify(nsString aType,
                                nsString aStorageName,
                                nsString aFilepath,
                                nsCString aReason);
-    // get nsIVolumeService to broadcast volume information
-    async BroadcastVolume(nsString volumeName);
+
+    sync GetVolumes() returns (VolumeInfo[] volumes);
 
     // Notify the parent that the child has finished handling a system message.
     async SystemMessageHandled();
 
     NuwaReady();
 
     sync AddNewProcess(uint32_t pid, ProtocolFdMapping[] aFds);
 
--- a/dom/system/gonk/nsIVolumeService.idl
+++ b/dom/system/gonk/nsIVolumeService.idl
@@ -3,25 +3,23 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 #include "nsIVolume.idl"
 #include "nsIVolumeMountLock.idl"
 
 interface nsIArray;
 
-[scriptable, uuid(cab99ab4-542e-4387-bd40-db6ef30e4f5f)]
+[scriptable, uuid(c31b182c-61a3-449c-bba8-fd45044499c2)]
 interface nsIVolumeService : nsISupports
 {
     nsIVolume getVolumeByName(in DOMString volName);
     nsIVolume getVolumeByPath(in DOMString path);
     nsIVolume createOrGetVolumeByPath(in DOMString path);
 
-    void BroadcastVolume(in DOMString volName);
-
     nsIVolumeMountLock createMountLock(in DOMString volName);
 
     nsIArray getVolumeNames();
 
     /* for test case only to simulate sdcard insertion/removal */
     void createFakeVolume(in DOMString name, in DOMString path);
     void SetFakeVolumeState(in DOMString name, in long state);
 };
--- a/dom/system/gonk/nsVolumeMountLock.cpp
+++ b/dom/system/gonk/nsVolumeMountLock.cpp
@@ -60,28 +60,29 @@ nsresult nsVolumeMountLock::Init()
       NS_LossyConvertUTF16toASCII(mVolumeName).get());
 
   // Add ourselves as an Observer. It's important that we use a weak
   // reference here. If we used a strong reference, then that reference
   // would prevent this object from being destructed.
   nsCOMPtr<nsIObserverService> obs = GetObserverService();
   obs->AddObserver(this, NS_VOLUME_STATE_CHANGED, true /*weak*/);
 
-  // Request the sdcard info, so we know the state/generation without having
-  // to wait for a state change.
-  if (XRE_GetProcessType() == GeckoProcessType_Content) {
-    ContentChild::GetSingleton()->SendBroadcastVolume(mVolumeName);
-    return NS_OK;
-  }
+  // Get the initial mountGeneration and grab a lock.
   nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID);
   NS_ENSURE_TRUE(vs, NS_ERROR_FAILURE);
 
-  vs->BroadcastVolume(mVolumeName);
+  nsCOMPtr<nsIVolume> vol;
+  nsresult rv = vs->GetVolumeByName(mVolumeName, getter_AddRefs(vol));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  rv = vol->GetMountGeneration(&mVolumeGeneration);
+  NS_ENSURE_SUCCESS(rv, rv);
 
-  return NS_OK;
+  return Lock(vol);
 }
 
 /* void unlock (); */
 NS_IMETHODIMP nsVolumeMountLock::Unlock()
 {
   LOG("nsVolumeMountLock released for '%s'",
       NS_LossyConvertUTF16toASCII(mVolumeName).get());
 
@@ -137,22 +138,28 @@ NS_IMETHODIMP nsVolumeMountLock::Observe
 
   // The generation changed, which means that any wakelock we may have
   // been holding is now invalid. Grab a new wakelock for the new generation
   // number.
 
   mWakeLock = nullptr;
   mVolumeGeneration = mountGeneration;
 
+  return Lock(vol);
+}
+
+nsresult
+nsVolumeMountLock::Lock(nsIVolume* aVolume)
+{
   nsRefPtr<power::PowerManagerService> pmService =
     power::PowerManagerService::GetInstance();
   NS_ENSURE_TRUE(pmService, NS_ERROR_FAILURE);
 
   nsString mountLockName;
-  vol->GetMountLockName(mountLockName);
+  aVolume->GetMountLockName(mountLockName);
 
   ErrorResult err;
   mWakeLock = pmService->NewWakeLock(mountLockName, nullptr, err);
   if (err.Failed()) {
     return err.ErrorCode();
   }
 
   LOG("nsVolumeMountLock acquired for '%s' gen %d",
--- a/dom/system/gonk/nsVolumeMountLock.h
+++ b/dom/system/gonk/nsVolumeMountLock.h
@@ -9,16 +9,18 @@
 
 #include "mozilla/dom/WakeLock.h"
 #include "nsIObserver.h"
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h"
 #include "nsWeakReference.h"
 
+class nsIVolume;
+
 namespace mozilla {
 namespace system {
 
 /* The VolumeMountLock is designed so that it can be used in the Child or
  * Parent process. While the VolumeMountLock object exists, then the
  * VolumeManager/AutoMounter will prevent a mounted volume from being
  * shared with the PC.
  */
@@ -36,16 +38,17 @@ public:
 
   const nsString& VolumeName() const  { return mVolumeName; }
 
 private:
   nsVolumeMountLock(const nsAString& aVolumeName);
   ~nsVolumeMountLock();
 
   nsresult Init();
+  nsresult Lock(nsIVolume* aVolume);
 
   nsRefPtr<dom::WakeLock>  mWakeLock;
   nsString                 mVolumeName;
   int32_t                  mVolumeGeneration;
   bool                     mUnlocked;
 };
 
 } // namespace system
--- a/dom/system/gonk/nsVolumeService.cpp
+++ b/dom/system/gonk/nsVolumeService.cpp
@@ -81,23 +81,24 @@ nsVolumeService::Shutdown()
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(ShutdownVolumeServiceIOThread));
 
   sSingleton = nullptr;
 }
 
 nsVolumeService::nsVolumeService()
-  : mArrayMonitor("nsVolumeServiceArray")
+  : mArrayMonitor("nsVolumeServiceArray"),
+    mGotVolumesFromParent(false)
 {
   sSingleton = this;
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
-    // Request the initial state for all volumes.
-    ContentChild::GetSingleton()->SendBroadcastVolume(NS_LITERAL_STRING(""));
+    // VolumeServiceIOThread and the WakeLock listener should only run in the
+    // parent, so we return early.
     return;
   }
 
   // Startup the IOThread side of things. The actual volume changes
   // are captured by the IOThread and forwarded to main thread.
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(InitVolumeServiceIOThread, this));
@@ -117,83 +118,36 @@ nsVolumeService::~nsVolumeService()
 // Callback for nsIDOMMozWakeLockListener
 NS_IMETHODIMP
 nsVolumeService::Callback(const nsAString& aTopic, const nsAString& aState)
 {
   CheckMountLock(aTopic, aState);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsVolumeService::BroadcastVolume(const nsAString& aVolName)
-{
-  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
-
-  if (aVolName.EqualsLiteral("")) {
-    nsVolume::Array volumeArray;
-    {
-      // Copy the array since we don't want to call BroadcastVolume
-      // while we're holding the lock.
-      MonitorAutoLock autoLock(mArrayMonitor);
-      volumeArray = mVolumeArray;
-    }
-
-    // We treat being passed the empty string as "broadcast all volumes"
-    nsVolume::Array::size_type numVolumes = volumeArray.Length();
-    nsVolume::Array::index_type volIndex;
-    for (volIndex = 0; volIndex < numVolumes; volIndex++) {
-      const nsString& volName(volumeArray[volIndex]->Name());
-      if (!volName.EqualsLiteral("")) {
-        // Note: The volume service is the only entity that should be able to
-        // modify the array of volumes. So we shouldn't have any issues with
-        // the array being modified under our feet (Since we're the volume
-        // service the array can't change until after we finish iterating the
-        // the loop).
-        nsresult rv = BroadcastVolume(volName);
-        NS_ENSURE_SUCCESS(rv, rv);
-      }
-    }
-    return NS_OK;
-  }
-  nsRefPtr<nsVolume> vol;
-  {
-    MonitorAutoLock autoLock(mArrayMonitor);
-    vol = FindVolumeByName(aVolName);
-  }
-  if (!vol) {
-    ERR("BroadcastVolume: Unable to locate volume '%s'",
-        NS_LossyConvertUTF16toASCII(aVolName).get());
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
-  nsCOMPtr<nsIObserverService> obs = GetObserverService();
-  NS_ENSURE_TRUE(obs, NS_NOINTERFACE);
-
-  DBG("nsVolumeService::BroadcastVolume for '%s'", vol->NameStr().get());
-  NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
-  obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
-  return NS_OK;
-}
-
 NS_IMETHODIMP nsVolumeService::GetVolumeByName(const nsAString& aVolName, nsIVolume **aResult)
 {
+  GetVolumesFromParent();
+
   MonitorAutoLock autoLock(mArrayMonitor);
 
   nsRefPtr<nsVolume> vol = FindVolumeByName(aVolName);
   if (!vol) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   vol.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsVolumeService::GetVolumeByPath(const nsAString& aPath, nsIVolume **aResult)
 {
+  GetVolumesFromParent();
+
   NS_ConvertUTF16toUTF8 utf8Path(aPath);
   char realPathBuf[PATH_MAX];
 
   while (realpath(utf8Path.get(), realPathBuf) < 0) {
     if (errno != ENOENT) {
       ERR("GetVolumeByPath: realpath on '%s' failed: %d", utf8Path.get(), errno);
       return NSRESULT_FOR_ERRNO();
     }
@@ -234,16 +188,18 @@ nsVolumeService::GetVolumeByPath(const n
     }
   }
   return NS_ERROR_FILE_NOT_FOUND;
 }
 
 NS_IMETHODIMP
 nsVolumeService::CreateOrGetVolumeByPath(const nsAString& aPath, nsIVolume** aResult)
 {
+  GetVolumesFromParent();
+
   nsresult rv = GetVolumeByPath(aPath, aResult);
   if (rv == NS_OK) {
     return NS_OK;
   }
 
   // In order to support queries by the updater, we will fabricate a volume
   // from the pathname, so that the caller can determine the volume size.
   nsCOMPtr<nsIVolume> vol = new nsVolume(NS_LITERAL_STRING("fake"),
@@ -255,16 +211,18 @@ nsVolumeService::CreateOrGetVolumeByPath
                                          true  /* isFake */);
   vol.forget(aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsVolumeService::GetVolumeNames(nsIArray** aVolNames)
 {
+  GetVolumesFromParent();
+
   NS_ENSURE_ARG_POINTER(aVolNames);
   MonitorAutoLock autoLock(mArrayMonitor);
 
   *aVolNames = nullptr;
 
   nsresult rv;
   nsCOMPtr<nsIMutableArray> volNames =
     do_CreateInstance(NS_ARRAY_CONTRACTID, &rv);
@@ -284,16 +242,70 @@ nsVolumeService::GetVolumeNames(nsIArray
     rv = volNames->AppendElement(isupportsString, false);
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   NS_ADDREF(*aVolNames = volNames);
   return NS_OK;
 }
 
+void
+nsVolumeService::GetVolumesForIPC(nsTArray<VolumeInfo>* aResult)
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MonitorAutoLock autoLock(mArrayMonitor);
+
+  nsVolume::Array::size_type numVolumes = mVolumeArray.Length();
+  nsVolume::Array::index_type volIndex;
+  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
+    nsRefPtr<nsVolume> vol = mVolumeArray[volIndex];
+    VolumeInfo* volInfo = aResult->AppendElement();
+
+    volInfo->name()             = vol->mName;
+    volInfo->mountPoint()       = vol->mMountPoint;
+    volInfo->volState()         = vol->mState;
+    volInfo->mountGeneration()  = vol->mMountGeneration;
+    volInfo->isMediaPresent()   = vol->mIsMediaPresent;
+    volInfo->isSharing()        = vol->mIsSharing;
+    volInfo->isFormatting()     = vol->mIsFormatting;
+    volInfo->isFake()           = vol->mIsFake;
+  }
+}
+
+void
+nsVolumeService::GetVolumesFromParent()
+{
+  if (XRE_GetProcessType() == GeckoProcessType_Default) {
+    // We are the parent. Therefore our volumes are already correct.
+    return;
+  }
+  if (mGotVolumesFromParent) {
+    // We've already done this, no need to do it again.
+    return;
+  }
+  mGotVolumesFromParent = true;
+
+  nsTArray<VolumeInfo> result;
+  ContentChild::GetSingleton()->SendGetVolumes(&result);
+  for (uint32_t i = 0; i < result.Length(); i++) {
+    const VolumeInfo& volInfo(result[i]);
+    nsRefPtr<nsVolume> vol = new nsVolume(volInfo.name(),
+                                          volInfo.mountPoint(),
+                                          volInfo.volState(),
+                                          volInfo.mountGeneration(),
+                                          volInfo.isMediaPresent(),
+                                          volInfo.isSharing(),
+                                          volInfo.isFormatting(),
+                                          volInfo.isFake());
+    UpdateVolume(vol, false);
+  }
+}
+
 NS_IMETHODIMP
 nsVolumeService::CreateMountLock(const nsAString& aVolumeName, nsIVolumeMountLock **aResult)
 {
   nsCOMPtr<nsIVolumeMountLock> mountLock = nsVolumeMountLock::Create(aVolumeName);
   if (!mountLock) {
     return NS_ERROR_NOT_AVAILABLE;
   }
   mountLock.forget(aResult);
@@ -361,17 +373,17 @@ nsVolumeService::CreateOrFindVolumeByNam
   // Volume not found - add a new one
   vol = new nsVolume(aName);
   vol->SetIsFake(aIsFake);
   mVolumeArray.AppendElement(vol);
   return vol.forget();
 }
 
 void
-nsVolumeService::UpdateVolume(nsIVolume* aVolume)
+nsVolumeService::UpdateVolume(nsIVolume* aVolume, bool aNotifyObservers)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   nsString volName;
   aVolume->GetName(volName);
   bool aIsFake;
   aVolume->GetIsFake(&aIsFake);
   nsRefPtr<nsVolume> vol = CreateOrFindVolumeByName(volName, aIsFake);
@@ -381,16 +393,21 @@ nsVolumeService::UpdateVolume(nsIVolume*
   }
 
   if (!vol->IsFake() && aIsFake) {
     // Prevent an incoming fake volume from overriding an existing real volume.
     return;
   }
 
   vol->Set(aVolume);
+
+  if (!aNotifyObservers) {
+    return;
+  }
+
   nsCOMPtr<nsIObserverService> obs = GetObserverService();
   if (!obs) {
     return;
   }
   NS_ConvertUTF8toUTF16 stateStr(vol->StateStr());
   obs->NotifyObservers(vol, NS_VOLUME_STATE_CHANGED, stateStr.get());
 }
 
--- a/dom/system/gonk/nsVolumeService.h
+++ b/dom/system/gonk/nsVolumeService.h
@@ -10,16 +10,21 @@
 #include "mozilla/StaticPtr.h"
 #include "nsCOMPtr.h"
 #include "nsIDOMWakeLockListener.h"
 #include "nsIVolume.h"
 #include "nsIVolumeService.h"
 #include "nsVolume.h"
 
 namespace mozilla {
+
+namespace dom {
+class VolumeInfo;
+} // dom
+
 namespace system {
 
 class WakeLockCallback;
 class Volume;
 
 /***************************************************************************
 * The nsVolumeData class encapsulates the data that is updated/maintained
 * on the main thread in order to support the nsIVolume and nsIVolumeService
@@ -35,30 +40,34 @@ public:
   NS_DECL_NSIDOMMOZWAKELOCKLISTENER
 
   nsVolumeService();
 
   static already_AddRefed<nsVolumeService> GetSingleton();
   //static nsVolumeService* GetSingleton();
   static void Shutdown();
 
-  void UpdateVolume(nsIVolume* aVolume);
+  void UpdateVolume(nsIVolume* aVolume, bool aNotifyObservers = true);
   void UpdateVolumeIOThread(const Volume* aVolume);
 
+  void GetVolumesForIPC(nsTArray<dom::VolumeInfo>* aResult);
+
 private:
   ~nsVolumeService();
 
   void CheckMountLock(const nsAString& aMountLockName,
                       const nsAString& aMountLockState);
   already_AddRefed<nsVolume> FindVolumeByMountLockName(const nsAString& aMountLockName);
   already_AddRefed<nsVolume> FindVolumeByName(const nsAString& aName);
   already_AddRefed<nsVolume> CreateOrFindVolumeByName(const nsAString& aName, bool aIsFake = false);
+  void GetVolumesFromParent();
 
   Monitor mArrayMonitor;
   nsVolume::Array mVolumeArray;
 
   static StaticRefPtr<nsVolumeService> sSingleton;
+  bool mGotVolumesFromParent;
 };
 
 } // system
 } // mozilla
 
 #endif  // mozilla_system_nsvolumeservice_h__