Bug 1046245 - IO on STS thread + cleanup BackgroundChild on shutdown. r=jesup
authorJan-Ivar Bruaroey <jib@mozilla.com>
Sun, 29 Mar 2015 13:43:43 -0400
changeset 265304 76f9de2a6a4fd5d60f3f2672bde9231a6383fcd4
parent 265303 05ed094a0ab2fa9b6709b8e093eb9315b940066e
child 265305 d935ebcbfc63fa810c1cf8b8946d01d3f8061fc5
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup
bugs1046245
milestone39.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 1046245 - IO on STS thread + cleanup BackgroundChild on shutdown. r=jesup
dom/media/MediaManager.cpp
dom/media/systemservices/MediaChild.cpp
dom/media/systemservices/MediaChild.h
dom/media/systemservices/MediaParent.cpp
dom/media/systemservices/MediaParent.h
dom/media/systemservices/MediaUtils.cpp
dom/media/systemservices/MediaUtils.h
dom/media/systemservices/PMedia.ipdl
ipc/glue/BackgroundChildImpl.cpp
ipc/glue/BackgroundParentImpl.cpp
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -36,16 +36,17 @@
 #include "mozilla/PeerIdentity.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/MediaStreamBinding.h"
 #include "mozilla/dom/MediaStreamTrackBinding.h"
 #include "mozilla/dom/GetUserMediaRequestBinding.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Base64.h"
+#include "mozilla/ipc/BackgroundChild.h"
 #include "mozilla/media/MediaChild.h"
 #include "MediaTrackConstraints.h"
 #include "VideoUtils.h"
 #include "Latency.h"
 
 // For PR_snprintf
 #include "prprf.h"
 
@@ -1492,34 +1493,37 @@ public:
       nsTArray<nsRefPtr<AudioDevice>> sources;
       GetSources(backend, GetInvariant(mConstraints.mAudio),
                  &MediaEngine::EnumerateAudioDevices, sources,
                  mLoopbackAudioDevice.get());
       for (auto& source : sources) {
         result->AppendElement(source);
       }
     }
-    nsRefPtr<DeviceSuccessCallbackRunnable> runnable
-        (new DeviceSuccessCallbackRunnable(mWindowId, mOnSuccess, mOnFailure,
-                                           result.forget()));
+    // In the case of failure with this newly allocated runnable, we
+    // intentionally leak the runnable, because we've pawned mOnSuccess and
+    // mOnFailure onto it which are main thread objects unsafe to release here.
+    DeviceSuccessCallbackRunnable* runnable =
+        new DeviceSuccessCallbackRunnable(mWindowId, mOnSuccess, mOnFailure,
+                                          result.forget());
     if (mPrivileged) {
       NS_DispatchToMainThread(runnable);
     } else {
       // Get persistent origin-unique uuid to anonymize deviceIds back on main.
       //
       // GetOriginKey is an async API that returns a pledge (as promise-like
       // pattern). We use .Then() to pass in a lambda to run back on this
       // thread once GetOriginKey resolves asynchronously . The "runnable"
-      // nsRefPtr is "captured" (passed by value) into the lambda.
+      // pointer is "captured" (passed by value) into the lambda.
       nsRefPtr<media::ChildPledge<nsCString>> p =
           media::GetOriginKey(mOrigin, mInPrivateBrowsing);
       p->Then([runnable](nsCString result) mutable {
         runnable->mOriginKey = result;
         NS_DispatchToMainThread(runnable);
-      }, [](nsresult rv){});
+      });
     }
     // One of the Runnables have taken these.
     MOZ_ASSERT(!mOnSuccess && !mOnFailure);
   }
 
 private:
   MediaStreamConstraints mConstraints;
   nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> mOnSuccess;
@@ -2200,31 +2204,57 @@ MediaManager::Observe(nsISupports* aSubj
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (prefs) {
       prefs->RemoveObserver("media.navigator.video.default_width", this);
       prefs->RemoveObserver("media.navigator.video.default_height", this);
       prefs->RemoveObserver("media.navigator.video.default_fps", this);
       prefs->RemoveObserver("media.navigator.video.default_minfps", this);
     }
 
-    // Close off any remaining active windows.
+    // Because mMediaThread is not an nsThread, we must dispatch to it so it can
+    // clean up BackgroundChild. Continue stopping thread once this is done.
+
+    class ShutdownTask : public Task
     {
+    public:
+      explicit ShutdownTask(nsRunnable* aReply) : mReply(aReply) {}
+    private:
+      virtual void
+      Run()
+      {
+        MOZ_ASSERT(MediaManager::IsInMediaThread());
+        mozilla::ipc::BackgroundChild::CloseForCurrentThread();
+        NS_DispatchToMainThread(mReply);
+      }
+      nsRefPtr<nsRunnable> mReply;
+    };
+
+    // Post ShutdownTask to execute on mMediaThread and pass in a lambda
+    // callback to be executed back on this thread once it is done.
+    //
+    // The lambda callback "captures" the 'this' pointer for member access.
+    // This is safe since this is guaranteed to be here since sSingleton isn't
+    // cleared until the lambda function clears it.
+
+    MediaManager::GetMessageLoop()->PostTask(FROM_HERE, new ShutdownTask(
+        media::CallbackRunnable::New([this]() mutable {
+      // Close off any remaining active windows.
       MutexAutoLock lock(mMutex);
       GetActiveWindows()->Clear();
       mActiveCallbacks.Clear();
       mCallIds.Clear();
       LOG(("Releasing MediaManager singleton and thread"));
       // Note: won't be released immediately as the Observer has a ref to us
       sSingleton = nullptr;
       if (mMediaThread) {
         mMediaThread->Stop();
       }
       mBackend = nullptr;
-    }
-
+      return NS_OK;
+    })));
     return NS_OK;
 
   } else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
     nsString key(aData);
     nsAutoPtr<GetUserMediaTask> task;
     mActiveCallbacks.RemoveAndForget(key, task);
     if (!task) {
       return NS_OK;
--- a/dom/media/systemservices/MediaChild.cpp
+++ b/dom/media/systemservices/MediaChild.cpp
@@ -19,32 +19,26 @@ PRLogModuleInfo *gMediaChildLog;
 #else
 #define LOG(args)
 #endif
 
 
 namespace mozilla {
 namespace media {
 
-static PMediaChild* sChild = nullptr;
-
-template<typename ValueType>
-ChildPledge<ValueType>::ChildPledge()
-{
-  MOZ_ASSERT(MediaManager::IsInMediaThread());
-}
+static Child* sChild;
 
 template<typename ValueType> void
 ChildPledge<ValueType>::ActorCreated(PBackgroundChild* aActor)
 {
   if (!sChild) {
     // Create PMedia by sending a message to the parent
-    sChild = aActor->SendPMediaConstructor();
+    sChild = static_cast<Child*>(aActor->SendPMediaConstructor());
   }
-  Run(static_cast<Child*>(sChild));
+  Run(sChild);
 }
 
 template<typename ValueType> void
 ChildPledge<ValueType>::ActorFailed()
 {
   Pledge<ValueType>::Reject(NS_ERROR_UNEXPECTED);
 }
 
@@ -59,21 +53,22 @@ GetOriginKey(const nsCString& aOrigin, b
 {
   class Pledge : public ChildPledge<nsCString>
   {
   public:
     explicit Pledge(const nsCString& aOrigin, bool aPrivateBrowsing)
     : mOrigin(aOrigin), mPrivateBrowsing(aPrivateBrowsing) {}
   private:
     ~Pledge() {}
-    void Run(PMediaChild* aMedia)
+    void Run(PMediaChild* aChild)
     {
-      aMedia->SendGetOriginKey(mOrigin, mPrivateBrowsing, &mValue);
-      LOG(("GetOriginKey for %s, result:", mOrigin.get(), mValue.get()));
-      Resolve();
+      Child* child = static_cast<Child*>(aChild);
+
+      uint32_t id = child->AddRequestPledge(*this);
+      child->SendGetOriginKey(id, mOrigin, mPrivateBrowsing);
     }
     const nsCString mOrigin;
     const bool mPrivateBrowsing;
   };
 
   nsRefPtr<ChildPledge<nsCString>> p = new Pledge(aOrigin, aPrivateBrowsing);
   nsCOMPtr<nsIIPCBackgroundChildCreateCallback> cb = do_QueryObject(p);
   bool ok = ipc::BackgroundChild::GetOrCreateForCurrentThread(cb);
@@ -119,16 +114,61 @@ Child::Child()
 
 Child::~Child()
 {
   LOG(("~media::Child: %p", this));
   sChild = nullptr;
   MOZ_COUNT_DTOR(Child);
 }
 
+uint32_t Child::sRequestCounter = 0;
+
+uint32_t
+Child::AddRequestPledge(ChildPledge<nsCString>& aPledge)
+{
+  uint32_t id = ++sRequestCounter;
+  nsRefPtr<ChildPledge<nsCString>> ptr(&aPledge);
+  mRequestPledges.AppendElement(PledgeEntry(id, ptr));
+  return id;
+}
+
+already_AddRefed<ChildPledge<nsCString>>
+Child::RemoveRequestPledge(uint32_t aRequestId)
+{
+  for (PledgeEntry& entry : mRequestPledges) {
+    if (entry.first == aRequestId) {
+      nsRefPtr<ChildPledge<nsCString>> ref;
+      ref.swap(entry.second);
+      mRequestPledges.RemoveElement(entry);
+      return ref.forget();
+    }
+  }
+  MOZ_ASSERT_UNREACHABLE("Received response with no matching media::ChildPledge!");
+  return nullptr;
+}
+
+bool
+Child::RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey)
+{
+  nsRefPtr<ChildPledge<nsCString>> pledge = RemoveRequestPledge(aRequestId);
+  if (pledge) {
+    pledge->Resolve(aKey);
+  }
+  return true;
+}
+
 PMediaChild*
-CreateMediaChild()
+AllocPMediaChild()
 {
-  return new Child();
+  Child* obj = new Child();
+  obj->AddRef();
+  return obj;
+}
+
+bool
+DeallocPMediaChild(media::PMediaChild *aActor)
+{
+  static_cast<Child*>(aActor)->Release();
+  return true;
 }
 
 }
 }
--- a/dom/media/systemservices/MediaChild.h
+++ b/dom/media/systemservices/MediaChild.h
@@ -20,40 +20,55 @@ namespace media {
 // functions, for the moment just:
 //
 // GetOriginKey() - get a cookie-like persisted unique key for a given origin.
 // SanitizeOriginKeys() - reset persisted unique keys.
 
 // GetOriginKey and SanitizeOriginKeys are asynchronous APIs that return pledges
 // (promise-like objects) with the future value. Use pledge.Then(func) to access.
 
+class Child;
+
 template<typename ValueType>
 class ChildPledge : public Pledge<ValueType>,
                     public nsIIPCBackgroundChildCreateCallback
 {
+  friend Child;
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECL_ISUPPORTS
 public:
-  explicit ChildPledge();
+  explicit ChildPledge() {};
 protected:
   virtual ~ChildPledge() {}
   virtual void Run(PMediaChild* aMedia) = 0;
 };
 
 already_AddRefed<ChildPledge<nsCString>>
 GetOriginKey(const nsCString& aOrigin, bool aPrivateBrowsing);
 
 already_AddRefed<ChildPledge<bool>>
 SanitizeOriginKeys(const uint64_t& aSinceWhen);
 
 class Child : public PMediaChild
 {
+  NS_INLINE_DECL_REFCOUNTING(Child)
 public:
   Child();
+
+  bool RecvGetOriginKeyResponse(const uint32_t& aRequestId, const nsCString& aKey);
+
+  uint32_t AddRequestPledge(ChildPledge<nsCString>& aPledge);
+  already_AddRefed<ChildPledge<nsCString>> RemoveRequestPledge(uint32_t aRequestId);
+private:
   virtual ~Child();
+
+  typedef std::pair<uint32_t,nsRefPtr<ChildPledge<nsCString>>> PledgeEntry;
+  static uint32_t sRequestCounter;
+  nsTArray<PledgeEntry> mRequestPledges;
 };
 
-PMediaChild* CreateMediaChild();
+PMediaChild* AllocPMediaChild();
+bool DeallocPMediaChild(PMediaChild *aActor);
 
 } // namespace media
 } // namespace mozilla
 
 #endif  // mozilla_MediaChild_h
--- a/dom/media/systemservices/MediaParent.cpp
+++ b/dom/media/systemservices/MediaParent.cpp
@@ -2,16 +2,17 @@
 /* vim: set sw=2 ts=8 et ft=cpp : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaParent.h"
 
 #include "mozilla/Base64.h"
+#include <mozilla/StaticMutex.h>
 
 #include "MediaUtils.h"
 #include "MediaEngine.h"
 #include "VideoUtils.h"
 #include "nsThreadUtils.h"
 #include "nsNetUtil.h"
 #include "nsILineInputStream.h"
 #include "nsAppDirectoryServiceDefs.h"
@@ -30,21 +31,22 @@ PRLogModuleInfo *gMediaParentLog;
 // deviceIds to be unique per origin, to avoid them being supercookies.
 
 #define ORIGINKEYS_FILE "enumerate_devices.txt"
 #define ORIGINKEYS_VERSION "1"
 
 namespace mozilla {
 namespace media {
 
+static StaticMutex gMutex;
 static ParentSingleton* sParentSingleton = nullptr;
 
 class ParentSingleton : public nsISupports
 {
-  NS_DECL_ISUPPORTS
+  NS_DECL_THREADSAFE_ISUPPORTS
 
   class OriginKey
   {
   public:
     static const size_t DecodedLength = 18;
 
     OriginKey(const nsACString& aKey, int64_t aSecondsStamp)
     : mKey(aKey)
@@ -327,58 +329,102 @@ private:
   {
     sParentSingleton = nullptr;
     LOG((__FUNCTION__));
   }
 
 public:
   static ParentSingleton* Get()
   {
+    // Protect creation of singleton and access from multiple Background threads.
+    //
+    // Multiple Background threads happen because sanitize.js calls us from the
+    // chrome process and gets a thread separate from the one servicing ipc from
+    // the content process.
+
+    StaticMutexAutoLock lock(gMutex);
     if (!sParentSingleton) {
       sParentSingleton = new ParentSingleton();
     }
     return sParentSingleton;
   }
 
   OriginKeysLoader mOriginKeys;
   OriginKeysTable mPrivateBrowsingOriginKeys;
 };
 
 NS_IMPL_ISUPPORTS0(ParentSingleton)
 
 bool
-Parent::RecvGetOriginKey(const nsCString& aOrigin,
-                         const bool& aPrivateBrowsing,
-                         nsCString* aKey)
+Parent::RecvGetOriginKey(const uint32_t& aRequestId,
+                         const nsCString& aOrigin,
+                         const bool& aPrivateBrowsing)
 {
-  if (aPrivateBrowsing) {
-    mSingleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, *aKey);
-  } else {
-    mSingleton->mOriginKeys.GetOriginKey(aOrigin, *aKey);
+  // Hand over to stream-transport thread.
+
+  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(sts);
+  nsRefPtr<ParentSingleton> singleton(mSingleton);
+
+  nsRefPtr<PledgeRunnable<nsCString>> p = PledgeRunnable<nsCString>::New(
+      [singleton, aOrigin, aPrivateBrowsing](nsCString& aResult) {
+    if (aPrivateBrowsing) {
+      singleton->mPrivateBrowsingOriginKeys.GetOriginKey(aOrigin, aResult);
+    } else {
+      singleton->mOriginKeys.GetOriginKey(aOrigin, aResult);
+    }
+    return NS_OK;
+  });
+  nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
   }
+  nsRefPtr<media::Parent> keepAlive(this);
+  p->Then([this, keepAlive, aRequestId](const nsCString& aKey) mutable {
+    if (!mDestroyed) {
+      unused << SendGetOriginKeyResponse(aRequestId, aKey);
+    }
+    return NS_OK;
+  });
   return true;
 }
 
 bool
 Parent::RecvSanitizeOriginKeys(const uint64_t& aSinceWhen)
 {
-  mSingleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
-  mSingleton->mOriginKeys.Clear(aSinceWhen);
+  // Hand over to stream-transport thread.
+
+  nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+  MOZ_ASSERT(sts);
+  nsRefPtr<ParentSingleton> singleton(mSingleton);
+
+  nsRefPtr<PledgeRunnable<bool>> p = PledgeRunnable<bool>::New(
+      [singleton, aSinceWhen](bool) {
+    singleton->mPrivateBrowsingOriginKeys.Clear(aSinceWhen);
+    singleton->mOriginKeys.Clear(aSinceWhen);
+    return NS_OK;
+  });
+  nsresult rv = sts->Dispatch(p, NS_DISPATCH_NORMAL);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return false;
+  }
   return true;
 }
 
 void
 Parent::ActorDestroy(ActorDestroyReason aWhy)
 {
   // No more IPC from here
+  mDestroyed = true;
   LOG((__FUNCTION__));
 }
 
 Parent::Parent()
   : mSingleton(ParentSingleton::Get())
+  , mDestroyed(false)
 {
 #if defined(PR_LOGGING)
   if (!gMediaParentLog)
     gMediaParentLog = PR_NewLogModule("MediaParent");
 #endif
   LOG(("media::Parent: %p", this));
 
   MOZ_COUNT_CTOR(Parent);
@@ -386,15 +432,25 @@ Parent::Parent()
 
 Parent::~Parent()
 {
   LOG(("~media::Parent: %p", this));
 
   MOZ_COUNT_DTOR(Parent);
 }
 
-PMediaParent* CreateParent()
+PMediaParent*
+AllocPMediaParent()
 {
-  return new Parent();
+  Parent* obj = new Parent();
+  obj->AddRef();
+  return obj;
+}
+
+bool
+DeallocPMediaParent(media::PMediaParent *aActor)
+{
+  static_cast<Parent*>(aActor)->Release();
+  return true;
 }
 
 }
 }
--- a/dom/media/systemservices/MediaParent.h
+++ b/dom/media/systemservices/MediaParent.h
@@ -16,27 +16,31 @@ namespace mozilla {
 namespace media {
 
 // media::Parent implements the chrome-process side of ipc for media::Child APIs
 
 class ParentSingleton;
 
 class Parent : public PMediaParent
 {
+  NS_INLINE_DECL_REFCOUNTING(Parent)
 public:
-  virtual bool RecvGetOriginKey(const nsCString& aOrigin,
-                                const bool& aPrivateBrowsing,
-                                nsCString* aKey) override;
+  virtual bool RecvGetOriginKey(const uint32_t& aRequestId,
+                                const nsCString& aOrigin,
+                                const bool& aPrivateBrowsing) override;
   virtual bool RecvSanitizeOriginKeys(const uint64_t& aSinceWhen) override;
   virtual void ActorDestroy(ActorDestroyReason aWhy) override;
 
   Parent();
+private:
   virtual ~Parent();
-private:
+
   nsRefPtr<ParentSingleton> mSingleton;
+  bool mDestroyed;
 };
 
-PMediaParent* CreateParent();
+PMediaParent* AllocPMediaParent();
+bool DeallocPMediaParent(PMediaParent *aActor);
 
 } // namespace media
 } // namespace mozilla
 
 #endif  // mozilla_MediaParent_h
--- a/dom/media/systemservices/MediaUtils.cpp
+++ b/dom/media/systemservices/MediaUtils.cpp
@@ -1,23 +1,13 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set sw=2 ts=8 et ft=cpp : */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "MediaUtils.h"
 
-#include "mozilla/MediaManager.h"
-
 namespace mozilla {
 namespace media {
 
-template<typename ValueType>
-Pledge<ValueType>::Pledge()
-  : mDone(false)
-  , mResult(NS_OK)
-{
-  MOZ_ASSERT(MediaManager::IsInMediaThread());
-}
-
 }
 }
--- a/dom/media/systemservices/MediaUtils.h
+++ b/dom/media/systemservices/MediaUtils.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_MediaUtils_h
 #define mozilla_MediaUtils_h
 
 #include "nsAutoPtr.h"
+#include "nsThreadUtils.h"
 
 namespace mozilla {
 namespace media {
 
 // A media::Pledge is a promise-like pattern for c++ that takes lambda functions.
 //
 // Asynchronous APIs that proxy to the chrome process and back, may return a
 // pledge to allow callers to use pledge.Then() to specify a lambda function to
@@ -31,17 +32,23 @@ class Pledge
   public:
     FunctorsBase() {}
     virtual void Succeed(const ValueType& result) = 0;
     virtual void Fail(nsresult rv) = 0;
     virtual ~FunctorsBase() {};
   };
 
 public:
-  explicit Pledge();
+  explicit Pledge() : mDone(false), mResult(NS_OK) {}
+
+  template<typename OnSuccessType>
+  void Then(OnSuccessType aOnSuccess)
+  {
+    Then(aOnSuccess, [](nsresult){});
+  }
 
   template<typename OnSuccessType, typename OnFailureType>
   void Then(OnSuccessType aOnSuccess, OnFailureType aOnFailure)
   {
     class F : public FunctorsBase
     {
     public:
       F(OnSuccessType& aOnSuccess, OnFailureType& aOnFailure)
@@ -66,16 +73,22 @@ public:
         mFunctors->Succeed(mValue);
       } else {
         mFunctors->Fail(mResult);
       }
     }
   }
 
 protected:
+  void Resolve(const ValueType& aValue)
+  {
+    mValue = aValue;
+    Resolve();
+  }
+
   void Resolve()
   {
     if (!mDone) {
       mDone = true;
       MOZ_ASSERT(mResult == NS_OK);
       if (mFunctors) {
         mFunctors->Succeed(mValue);
       }
@@ -89,18 +102,96 @@ protected:
       mResult = rv;
       if (mFunctors) {
         mFunctors->Fail(mResult);
       }
     }
   }
 
   ValueType mValue;
-private:
+protected:
   bool mDone;
   nsresult mResult;
+private:
   nsAutoPtr<FunctorsBase> mFunctors;
 };
 
+// General purpose runnable that also acts as a Pledge for the resulting value.
+// Use PledgeRunnable<>::New() factory function to use with lambdas.
+
+template<typename ValueType>
+class PledgeRunnable : public Pledge<ValueType>, public nsRunnable
+{
+public:
+  template<typename OnRunType>
+  static PledgeRunnable<ValueType>*
+  New(OnRunType aOnRun)
+  {
+    class P : public PledgeRunnable<ValueType>
+    {
+    public:
+      explicit P(OnRunType& aOnRun)
+      : mOriginThread(NS_GetCurrentThread())
+      , mOnRun(aOnRun)
+      , mHasRun(false) {}
+    private:
+      virtual ~P() {}
+      NS_IMETHODIMP
+      Run()
+      {
+        if (!mHasRun) {
+          P::mResult = mOnRun(P::mValue);
+          mHasRun = true;
+          return mOriginThread->Dispatch(this, NS_DISPATCH_NORMAL);
+        }
+        bool on;
+        MOZ_RELEASE_ASSERT(NS_SUCCEEDED(mOriginThread->IsOnCurrentThread(&on)));
+        MOZ_RELEASE_ASSERT(on);
+
+        if (NS_SUCCEEDED(P::mResult)) {
+          P::Resolve();
+        } else {
+          P::Reject(P::mResult);
+        }
+        return NS_OK;
+      }
+      nsCOMPtr<nsIThread> mOriginThread;
+      OnRunType mOnRun;
+      bool mHasRun;
+    };
+
+    return new P(aOnRun);
+  }
+
+protected:
+  virtual ~PledgeRunnable() {}
+};
+
+// General purpose runnable with an eye toward lambdas
+
+namespace CallbackRunnable
+{
+template<typename OnRunType>
+class Impl : public nsRunnable
+{
+public:
+  explicit Impl(OnRunType& aOnRun) : mOnRun(aOnRun) {}
+private:
+  NS_IMETHODIMP
+  Run()
+  {
+    return mOnRun();
+  }
+  OnRunType mOnRun;
+};
+
+template<typename OnRunType>
+Impl<OnRunType>*
+New(OnRunType aOnRun)
+{
+  return new Impl<OnRunType>(aOnRun);
+}
+}
+
 }
 }
 
 #endif // mozilla_MediaUtils_h
--- a/dom/media/systemservices/PMedia.ipdl
+++ b/dom/media/systemservices/PMedia.ipdl
@@ -3,31 +3,33 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PContent;
 include protocol PBackground;
 
 namespace mozilla {
 namespace media {
 
-sync protocol PMedia
+protocol PMedia
 {
   manager PBackground;
 
 parent:
   /**
-   * Returns a persistent unique secret key for each origin.
+   * Requests a persistent unique secret key for each origin.
    * Has no expiry, but is cleared by age along with cookies.
    * This is needed by mediaDevices.enumerateDevices() to produce persistent
    * deviceIds that wont work cross-origin.
    */
-  sync GetOriginKey(nsCString origin, bool privateBrowsing) returns (nsCString key);
+  GetOriginKey(uint32_t aRequestId, nsCString aOrigin, bool aPrivateBrowsing);
 
   /**
-   * On clear cookies.
+   * On clear cookies. Fire and forget.
    */
-  sync SanitizeOriginKeys(uint64_t sinceWhen);
+  SanitizeOriginKeys(uint64_t aSinceWhen);
 
-  async __delete__();
+child:
+  GetOriginKeyResponse(uint32_t aRequestId, nsCString key);
+  __delete__();
 };
 
 } // namespace media
 } // namespace mozilla
--- a/ipc/glue/BackgroundChildImpl.cpp
+++ b/ipc/glue/BackgroundChildImpl.cpp
@@ -280,24 +280,23 @@ BackgroundChildImpl::DeallocPCacheStream
 {
   dom::cache::DeallocPCacheStreamControlChild(aActor);
   return true;
 }
 
 media::PMediaChild*
 BackgroundChildImpl::AllocPMediaChild()
 {
-  return media::CreateMediaChild();
+  return media::AllocPMediaChild();
 }
 
 bool
 BackgroundChildImpl::DeallocPMediaChild(media::PMediaChild *aActor)
 {
-  delete aActor;
-  return true;
+  return media::DeallocPMediaChild(aActor);
 }
 
 } // namespace ipc
 } // namespace mozilla
 
 bool
 TestChild::Recv__delete__(const nsCString& aTestArg)
 {
--- a/ipc/glue/BackgroundParentImpl.cpp
+++ b/ipc/glue/BackgroundParentImpl.cpp
@@ -359,24 +359,23 @@ BackgroundParentImpl::DeallocPBroadcastC
 
   delete static_cast<BroadcastChannelParent*>(aActor);
   return true;
 }
 
 media::PMediaParent*
 BackgroundParentImpl::AllocPMediaParent()
 {
-  return media::CreateParent();
+  return media::AllocPMediaParent();
 }
 
 bool
 BackgroundParentImpl::DeallocPMediaParent(media::PMediaParent *aActor)
 {
-  delete aActor;
-  return true;
+  return media::DeallocPMediaParent(aActor);
 }
 
 namespace {
 
 class RegisterServiceWorkerCallback final : public nsRunnable
 {
 public:
   explicit RegisterServiceWorkerCallback(