Bug 1109457 - Add persistent session to our ClearKey CDM. r=edwin
☠☠ backed out by 3e0d52c8f679 ☠ ☠
authorChris Pearce <cpearce@mozilla.com>
Thu, 18 Dec 2014 13:23:27 +1300
changeset 247816 dc6994acf4e38744c1da0de44ad07f0db45b46bf
parent 247815 f0e3e6f8866d6747ebb748d080efc97e1276a68c
child 247817 157b8c310478e7a6b1e8c2c13fa88b7cb1d4be0e
push id698
push userjlund@mozilla.com
push dateMon, 23 Mar 2015 22:08:11 +0000
treeherdermozilla-release@b0c0ae7b02a3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin
bugs1109457
milestone37.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 1109457 - Add persistent session to our ClearKey CDM. r=edwin
dom/media/gmp/gmp-api/gmp-storage.h
dom/media/test/eme.js
dom/media/test/mochitest.ini
dom/media/test/test_eme_persistent_sessions.html
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
media/gmp-clearkey/0.1/ClearKeyPersistence.h
media/gmp-clearkey/0.1/ClearKeySession.cpp
media/gmp-clearkey/0.1/ClearKeySession.h
media/gmp-clearkey/0.1/ClearKeyStorage.cpp
media/gmp-clearkey/0.1/ClearKeyStorage.h
media/gmp-clearkey/0.1/ClearKeyUtils.cpp
media/gmp-clearkey/0.1/ClearKeyUtils.h
media/gmp-clearkey/0.1/RefCounted.h
media/gmp-clearkey/0.1/gmp-task-utils-generated.h
media/gmp-clearkey/0.1/gmp-task-utils.h
media/gmp-clearkey/0.1/moz.build
--- a/dom/media/gmp/gmp-api/gmp-storage.h
+++ b/dom/media/gmp/gmp-api/gmp-storage.h
@@ -113,16 +113,17 @@ class GMPRecordClient {
 // Iterates over the records that are available. Note: this list maintains
 // a snapshot of the records that were present when the iterator was created.
 // Create by calling the GMPCreateRecordIteratorPtr function on the
 // GMPPlatformAPI struct.
 // Iteration is in alphabetical order.
 class GMPRecordIterator {
 public:
   // Retrieve the name for the current record.
+  // aOutName is null terminated at character  at index (*aOutNameLength).
   // Returns GMPNoErr if successful, or GMPEndOfEnumeration if iteration has
   // reached the end.
   virtual GMPErr GetName(const char ** aOutName, uint32_t * aOutNameLength) = 0;
 
   // Advance iteration to the next record.
   // Returns GMPNoErr if successful, or GMPEndOfEnumeration if iteration has
   // reached the end.
   virtual GMPErr NextRecord() = 0;
--- a/dom/media/test/eme.js
+++ b/dom/media/test/eme.js
@@ -1,16 +1,19 @@
 const KEYSYSTEM_TYPE = "org.w3.clearkey";
 
 function bail(message)
 {
   return function(err) {
+    if (err) {
+      message +=  "; " + String(err)
+    }
     ok(false, message);
     if (err) {
-      info(err);
+      info(String(err));
     }
     SimpleTest.finish();
   }
 }
 
 function ArrayBufferToString(arr)
 {
   var str = '';
@@ -65,23 +68,23 @@ function TimeStamp(token) {
              (ms < 10 ? "  " : (ms < 100 ? " " : ""));
   return token ? (time + " " + token) : time;
 }
 
 function Log(token, msg) {
   info(TimeStamp(token) + " " + msg);
 }
 
-function UpdateSessionFunc(test, token) {
+function UpdateSessionFunc(test, token, sessionType) {
   return function(ev) {
     var msgStr = ArrayBufferToString(ev.message);
     var msg = JSON.parse(msgStr);
 
     Log(token, "got message from CDM: " + msgStr);
-    is(msg.type, test.sessionType, TimeStamp(token) + " key session type should match");
+    is(msg.type, sessionType, TimeStamp(token) + " key session type should match");
     ok(msg.kids, TimeStamp(token) + " message event should contain key ID array");
 
     var outKeys = [];
 
     for (var i = 0; i < msg.kids.length; i++) {
       var id64 = msg.kids[i];
       var idHex = Base64ToHex(msg.kids[i]).toLowerCase();
       var key = test.keys[idHex];
@@ -206,32 +209,32 @@ function SetupEME(test, token, params)
         initDataType: ev.initDataType,
         videoType: test.type,
       }
     ];
     navigator.requestMediaKeySystemAccess(KEYSYSTEM_TYPE, options)
       .then(function(keySystemAccess) {
         return keySystemAccess.createMediaKeys();
       }, bail(token + " Failed to request key system access."))
-      
+
       .then(function(mediaKeys) {
         Log(token, "created MediaKeys object ok");
         mediaKeys.sessions = [];
         return v.setMediaKeys(mediaKeys);
       }, bail("failed to create MediaKeys object"))
-      
+
       .then(function() {
         Log(token, "set MediaKeys on <video> element ok");
-
-        var session = v.mediaKeys.createSession(test.sessionType);
+        var sessionType = (params && params.sessionType) ? params.sessionType : "temporary";
+        var session = v.mediaKeys.createSession(sessionType);
         if (params && params.onsessioncreated) {
           params.onsessioncreated(session);
         }
-        session.addEventListener("message", UpdateSessionFunc(test, token));
+        session.addEventListener("message", UpdateSessionFunc(test, token, sessionType));
         return session.generateRequest(ev.initDataType, ev.initData);
       }, onSetKeysFail)
-      
+
       .then(function() {
         Log(token, "generated request");
       }, bail(token + " Failed to request key system access2."));
   });
   return v;
 }
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -359,16 +359,18 @@ skip-if = (toolkit == 'android' && proce
 [test_currentTime.html]
 [test_decode_error.html]
 [test_decoder_disable.html]
 [test_defaultMuted.html]
 [test_delay_load.html]
 skip-if = buildapp == 'b2g' && toolkit != 'gonk' # bug 1082984
 [test_eme_canvas_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
+[test_eme_persistent_sessions.html]
+skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_playback.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_requestKeySystemAccess.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_eme_stream_capture_blocked.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # bug 1043403, bug 1057908
 [test_empty_resource.html]
 [test_error_in_video_document.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_eme_persistent_sessions.html
@@ -0,0 +1,156 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test Encrypted Media Extensions</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+  <script type="text/javascript" src="eme.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+var manager = new MediaTestManager;
+
+function UsableKeyIdsMatch(usableKeyIds, expectedKeyIds) {
+  var hexKeyIds = usableKeyIds.map(function(keyId) {
+    return Base64ToHex(window.btoa(ArrayBufferToString(keyId)));
+  }).sort();
+  var expected = Object.keys(expectedKeyIds).sort();
+  if (expected.length != hexKeyIds.length) {
+    return false;
+  }
+  for (var i = 0; i < hexKeyIds.length; i++) {
+    if (hexKeyIds[i] != expected[i]){
+      return false;
+    }
+  }
+  return true;
+}
+
+function AwaitAllKeysUsable(session, keys, token) {
+  return new Promise(function(resolve, reject) {
+    function listener(event) {
+      session.getUsableKeyIds().then(function(usableKeyIds) {
+        var u = UsableKeyIdsMatch(usableKeyIds, keys);
+        if (UsableKeyIdsMatch(usableKeyIds, keys)) {
+          Log(token, "resolving AwaitAllKeysUsable promise");
+          session.removeEventListener("keyschange", listener);
+          resolve();
+        }
+      }, bail(token + " failed to get usableKeyIds"));
+    }
+    session.addEventListener("keyschange", listener);
+  });
+}
+
+function AwaitAllKeysNotUsable(session, token) {
+  return new Promise(function(resolve, reject) {
+    function listener(event) {
+      session.getUsableKeyIds().then(function(usableKeyIds) {
+        if (usableKeyIds.length == 0) {
+          session.removeEventListener("keyschange", listener);
+          resolve();
+        }
+      }, bail(token + " failed to get usableKeyIds"));
+    }
+    session.addEventListener("keyschange", listener);
+  });
+}
+
+function startTest(test, token)
+{
+  manager.started(token);
+
+  var recreatedSession; // will have remove() called on it.
+
+  var keySystemAccess;
+
+  var v = SetupEME(test, token,
+    {
+      onsessioncreated: function(session) {
+        Log(token, "Session created");
+        var sessionId;
+        initialSession = session;
+
+        // Once the session has loaded and has all its keys usable, close
+        // all sessions without calling remove() on them.
+        AwaitAllKeysUsable(session, test.keys, token).then(
+          function() {
+            sessionId = session.sessionId;
+            Log(token, "Closing session with id=" + sessionId);
+            session.close();
+          }
+        );
+
+        // Once the session is closed, reload the MediaKeys and reload the session
+        session.closed.then(function() {
+          return navigator.requestMediaKeySystemAccess(KEYSYSTEM_TYPE)
+        }, bail("close promise rejected"))
+
+        .then(function(requestedKeySystemAccess) {
+          keySystemAccess = requestedKeySystemAccess;
+          return keySystemAccess.createMediaKeys();
+        }, bail(token + " Failed to request key system access."))
+
+        .then(function(mediaKeys) {
+          Log(token, "re-created MediaKeys object ok");
+          recreatedSession = mediaKeys.createSession("persistent");
+          Log(token, "Created recreatedSession, loading sessionId=" + sessionId);
+          return Promise.all([AwaitAllKeysUsable(recreatedSession, test.keys, token), recreatedSession.load(sessionId)]);
+        }, bail(token + " failed to create mediaKeys"))
+
+        .then(function() {
+          Log(token, "re-loaded persistent session, all keys still usable");
+          return Promise.all([AwaitAllKeysNotUsable(recreatedSession, token), recreatedSession.remove()]);
+        }, bail(token + " failed to get reload session or keys"))
+
+        .then(function() {
+          Log(token, "removed session, all keys unusable.");
+          // Attempt to recreate the session, the attempt should fail.
+          return keySystemAccess.createMediaKeys();
+        }, bail(token + " failed to remove session"))
+
+        .then(function(mediaKeys) {
+          Log(token, "re-re-created MediaKeys object ok");
+          // Trying to load the removed persistent session should fail.
+          return mediaKeys.createSession("persistent").load(sessionId);
+        }, bail(token + " failed to create mediaKeys"))
+
+        .then(function(suceeded) {
+          is(suceeded, false, token + " we expect the third session creation to fail, as the session should have been removed.");
+          manager.finished(token);
+        }, bail(token + " failure to load session."));
+
+      },
+      sessionType: "persistent",
+    }
+  );
+
+  v.addEventListener("error", bail(token + " got error event"));
+
+  LoadTest(test, v, token);
+}
+
+function beginTest() {
+  manager.runTests(gEMETests, startTest);
+}
+
+var prefs = [
+  [ "media.mediasource.enabled", true ],
+  [ "media.mediasource.mp4.enabled", true ],
+];
+
+if (/Linux/.test(navigator.userAgent) ||
+    !document.createElement('video').canPlayType("video/mp4")) {
+  // XXX remove once we have mp4 PlatformDecoderModules on all platforms.
+  prefs.push([ "media.fragmented-mp4.exposed", true ]);
+  prefs.push([ "media.fragmented-mp4.use-blank-decoder", true ]);
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({ "set" : prefs }, beginTest);
+</script>
+</pre>
+</body>
+</html>
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.cpp
@@ -1,39 +1,42 @@
 /* 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 <sstream>
 #include <stdint.h>
 #include <stdio.h>
 
 #include "ClearKeyDecryptionManager.h"
 #include "ClearKeyUtils.h"
+#include "ClearKeyStorage.h"
+#include "ClearKeyPersistence.h"
+#include "gmp-task-utils.h"
 
 #include "mozilla/Assertions.h"
 #include "mozilla/NullPtr.h"
 
 using namespace mozilla;
 using namespace std;
 
-
 class ClearKeyDecryptor
 {
 public:
   ClearKeyDecryptor(GMPDecryptorCallback* aCallback, const Key& aKey);
   ~ClearKeyDecryptor();
 
   void InitKey();
 
   void QueueDecrypt(GMPBuffer* aBuffer, GMPEncryptedBufferMetadata* aMetadata);
 
   uint32_t AddRef();
   uint32_t Release();
 
+  const Key& DecryptionKey() const { return mKey; }
+
 private:
   struct DecryptTask : public GMPTask
   {
     DecryptTask(ClearKeyDecryptor* aTarget, GMPBuffer* aBuffer,
                 GMPEncryptedBufferMetadata* aMetadata)
       : mTarget(aTarget), mBuffer(aBuffer), mMetadata(aMetadata) { }
 
     virtual void Run() MOZ_OVERRIDE
@@ -74,47 +77,42 @@ private:
   uint32_t mRefCnt;
 
   GMPDecryptorCallback* mCallback;
   GMPThread* mThread;
 
   Key mKey;
 };
 
-
 ClearKeyDecryptionManager::ClearKeyDecryptionManager()
 {
   CK_LOGD("ClearKeyDecryptionManager ctor");
+
+  // The ClearKeyDecryptionManager maintains a self reference which is
+  // removed when the host is finished with the interface and calls
+  // DecryptingComplete(). We make ClearKeyDecryptionManager refcounted so
+  // that the tasks to that we dispatch to call functions on it won't end up
+  // derefing a null reference after DecryptingComplete() is called.
+  AddRef();
 }
 
 ClearKeyDecryptionManager::~ClearKeyDecryptionManager()
 {
   CK_LOGD("ClearKeyDecryptionManager dtor");
+  MOZ_ASSERT(mRefCount == 1);
 }
 
 void
 ClearKeyDecryptionManager::Init(GMPDecryptorCallback* aCallback)
 {
   CK_LOGD("ClearKeyDecryptionManager::Init");
   mCallback = aCallback;
   mCallback->SetCapabilities(GMP_EME_CAP_DECRYPT_AUDIO |
                              GMP_EME_CAP_DECRYPT_VIDEO);
-}
-
-static string
-GetNewSessionId()
-{
-  static uint32_t sNextSessionId = 0;
-
-  string sessionId;
-  stringstream ss;
-  ss << ++sNextSessionId;
-  ss >> sessionId;
-
-  return sessionId;
+  ClearKeyPersistence::EnsureInitialized();
 }
 
 void
 ClearKeyDecryptionManager::CreateSession(uint32_t aPromiseId,
                                          const char* aInitDataType,
                                          uint32_t aInitDataTypeSize,
                                          const uint8_t* aInitData,
                                          uint32_t aInitDataSize,
@@ -124,20 +122,28 @@ ClearKeyDecryptionManager::CreateSession
 
   // initDataType must be "cenc".
   if (strcmp("cenc", aInitDataType)) {
     mCallback->RejectPromise(aPromiseId, kGMPNotSupportedError,
                              nullptr /* message */, 0 /* messageLen */);
     return;
   }
 
-  string sessionId = GetNewSessionId();
+  if (ClearKeyPersistence::DeferCreateSessionIfNotReady(this,
+                                                        aPromiseId,
+                                                        aInitData,
+                                                        aInitDataSize,
+                                                        aSessionType)) {
+    return;
+  }
+
+  string sessionId = ClearKeyPersistence::GetNewSessionId(aSessionType);
   MOZ_ASSERT(mSessions.find(sessionId) == mSessions.end());
 
-  ClearKeySession* session = new ClearKeySession(sessionId, mCallback);
+  ClearKeySession* session = new ClearKeySession(sessionId, mCallback, aSessionType);
   session->Init(aPromiseId, aInitData, aInitDataSize);
   mSessions[sessionId] = session;
 
   const vector<KeyId>& sessionKeys = session->GetKeyIds();
   vector<KeyId> neededKeys;
   for (auto it = sessionKeys.begin(); it != sessionKeys.end(); it++) {
     if (mDecryptors.find(*it) == mDecryptors.end()) {
       // Need to request this key ID from the client.
@@ -152,109 +158,253 @@ ClearKeyDecryptionManager::CreateSession
 
   if (neededKeys.empty()) {
     CK_LOGD("No keys needed from client.");
     return;
   }
 
   // Send a request for needed key data.
   string request;
-  ClearKeyUtils::MakeKeyRequest(neededKeys, request);
+  ClearKeyUtils::MakeKeyRequest(neededKeys, request, aSessionType);
   mCallback->SessionMessage(&sessionId[0], sessionId.length(),
                             (uint8_t*)&request[0], request.length(),
                             "" /* destination url */, 0);
 }
 
 void
 ClearKeyDecryptionManager::LoadSession(uint32_t aPromiseId,
                                        const char* aSessionId,
                                        uint32_t aSessionIdLength)
 {
-  // TODO implement "persistent" sessions.
-  mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+  CK_LOGD("ClearKeyDecryptionManager::LoadSession");
+
+  if (!ClearKeyUtils::IsValidSessionId(aSessionId, aSessionIdLength)) {
+    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+    return;
+  }
+
+  if (ClearKeyPersistence::DeferLoadSessionIfNotReady(this,
+                                                      aPromiseId,
+                                                      aSessionId,
+                                                      aSessionIdLength)) {
+    return;
+  }
+
+  string sid(aSessionId, aSessionId + aSessionIdLength);
+  if (!ClearKeyPersistence::IsPersistentSessionId(sid)) {
+    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+    return;
+  }
+
+  // Callsback PersistentSessionDataLoaded with results...
+  ClearKeyPersistence::LoadSessionData(this, sid, aPromiseId);
+}
 
-  CK_LOGD("ClearKeyDecryptionManager::LoadSession");
+void
+ClearKeyDecryptionManager::PersistentSessionDataLoaded(GMPErr aStatus,
+                                                       uint32_t aPromiseId,
+                                                       const string& aSessionId,
+                                                       const uint8_t* aKeyData,
+                                                       uint32_t aKeyDataSize)
+{
+  if (GMP_FAILED(aStatus) ||
+      Contains(mSessions, aSessionId) ||
+      (aKeyDataSize % (2 * CLEARKEY_KEY_LEN)) != 0) {
+    mCallback->ResolveLoadSessionPromise(aPromiseId, false);
+    return;
+  }
+
+  ClearKeySession* session = new ClearKeySession(aSessionId,
+                                                 mCallback,
+                                                 kGMPPersistentSession);
+  mSessions[aSessionId] = session;
+
+  // TODO: currently we have to resolve the load-session promise before we
+  // can mark the keys as usable. We should really do this before marking
+  // the keys usable, but we need to fix Gecko first.
+  mCallback->ResolveLoadSessionPromise(aPromiseId, true);
+
+  uint32_t numKeys = aKeyDataSize / (2 * CLEARKEY_KEY_LEN);
+  for (uint32_t i = 0; i < numKeys; i ++) {
+    const uint8_t* base = aKeyData + 2 * CLEARKEY_KEY_LEN * i;
+
+    KeyId keyId(base, base + CLEARKEY_KEY_LEN);
+    MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
+
+    Key key(base + CLEARKEY_KEY_LEN, base + 2 * CLEARKEY_KEY_LEN);
+    MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
+
+    session->AddKeyId(keyId);
+
+    if (!Contains(mDecryptors, keyId)) {
+      mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, key);
+    }
+    mDecryptors[keyId]->AddRef();
+    mCallback->KeyIdUsable(aSessionId.c_str(), aSessionId.size(),
+                           &keyId[0], keyId.size());
+  }
 }
 
 void
 ClearKeyDecryptionManager::UpdateSession(uint32_t aPromiseId,
                                          const char* aSessionId,
                                          uint32_t aSessionIdLength,
                                          const uint8_t* aResponse,
                                          uint32_t aResponseSize)
 {
   CK_LOGD("ClearKeyDecryptionManager::UpdateSession");
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
 
-  if (mSessions.find(sessionId) == mSessions.end() || !mSessions[sessionId]) {
+  auto itr = mSessions.find(sessionId);
+  if (itr == mSessions.end() || !(itr->second)) {
     CK_LOGW("ClearKey CDM couldn't resolve session ID in UpdateSession.");
     mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
     return;
   }
+  ClearKeySession* session = itr->second;
 
   // Parse the response for any (key ID, key) pairs.
   vector<KeyIdPair> keyPairs;
-  if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs)) {
+  if (!ClearKeyUtils::ParseJWK(aResponse, aResponseSize, keyPairs, session->Type())) {
     CK_LOGW("ClearKey CDM failed to parse JSON Web Key.");
     mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError, nullptr, 0);
     return;
   }
-  mCallback->ResolvePromise(aPromiseId);
 
   for (auto it = keyPairs.begin(); it != keyPairs.end(); it++) {
     KeyId& keyId = it->mKeyId;
 
     if (mDecryptors.find(keyId) != mDecryptors.end()) {
       mDecryptors[keyId] = new ClearKeyDecryptor(mCallback, it->mKey);
       mCallback->KeyIdUsable(aSessionId, aSessionIdLength,
                              &keyId[0], keyId.size());
     }
 
     mDecryptors[keyId]->AddRef();
   }
+
+  if (session->Type() != kGMPPersistentSession) {
+    mCallback->ResolvePromise(aPromiseId);
+    return;
+  }
+
+  // Store the keys on disk. We store a record whose name is the sessionId,
+  // and simply append each keyId followed by its key.
+  vector<uint8_t> keydata;
+  Serialize(session, keydata);
+  GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
+  static const char* message = "Couldn't store cenc key init data";
+  GMPTask* reject = WrapTask(mCallback,
+                             &GMPDecryptorCallback::RejectPromise,
+                             aPromiseId,
+                             kGMPInvalidStateError,
+                             message,
+                             strlen(message));
+  StoreData(sessionId, keydata, resolve, reject);
+}
+
+void
+ClearKeyDecryptionManager::Serialize(const ClearKeySession* aSession,
+                                     std::vector<uint8_t>& aOutKeyData)
+{
+  const std::vector<KeyId>& keyIds = aSession->GetKeyIds();
+  for (size_t i = 0; i < keyIds.size(); i++) {
+    const KeyId& keyId = keyIds[i];
+    if (!Contains(mDecryptors, keyId)) {
+      continue;
+    }
+    MOZ_ASSERT(keyId.size() == CLEARKEY_KEY_LEN);
+    aOutKeyData.insert(aOutKeyData.end(), keyId.begin(), keyId.end());
+    const Key& key = mDecryptors[keyId]->DecryptionKey();
+    MOZ_ASSERT(key.size() == CLEARKEY_KEY_LEN);
+    aOutKeyData.insert(aOutKeyData.end(), key.begin(), key.end());
+  }
 }
 
 void
 ClearKeyDecryptionManager::CloseSession(uint32_t aPromiseId,
                                         const char* aSessionId,
                                         uint32_t aSessionIdLength)
 {
   CK_LOGD("ClearKeyDecryptionManager::CloseSession");
 
   string sessionId(aSessionId, aSessionId + aSessionIdLength);
-  ClearKeySession* session = mSessions[sessionId];
+  auto itr = mSessions.find(sessionId);
+  if (itr == mSessions.end()) {
+    CK_LOGW("ClearKey CDM couldn't close non-existent session.");
+    mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
+    return;
+  }
 
+  ClearKeySession* session = itr->second;
   MOZ_ASSERT(session);
 
-  const vector<KeyId>& keyIds = session->GetKeyIds();
+  ClearInMemorySessionData(session);
+  mCallback->ResolvePromise(aPromiseId);
+  mCallback->SessionClosed(aSessionId, aSessionIdLength);
+}
+
+void
+ClearKeyDecryptionManager::ClearInMemorySessionData(ClearKeySession* aSession)
+{
+  MOZ_ASSERT(aSession);
+
+  const vector<KeyId>& keyIds = aSession->GetKeyIds();
   for (auto it = keyIds.begin(); it != keyIds.end(); it++) {
     MOZ_ASSERT(mDecryptors.find(*it) != mDecryptors.end());
 
     if (!mDecryptors[*it]->Release()) {
       mDecryptors.erase(*it);
-      mCallback->KeyIdNotUsable(aSessionId, aSessionIdLength,
+      mCallback->KeyIdNotUsable(aSession->Id().c_str(), aSession->Id().size(),
                                 &(*it)[0], it->size());
     }
   }
 
-  mSessions.erase(sessionId);
-  delete session;
-
-  mCallback->ResolvePromise(aPromiseId);
+  mSessions.erase(aSession->Id());
+  delete aSession;
 }
 
 void
 ClearKeyDecryptionManager::RemoveSession(uint32_t aPromiseId,
                                          const char* aSessionId,
                                          uint32_t aSessionIdLength)
 {
-  // TODO implement "persistent" sessions.
-  CK_LOGD("ClearKeyDecryptionManager::RemoveSession");
-  mCallback->RejectPromise(aPromiseId, kGMPInvalidAccessError,
-                           nullptr /* message */, 0 /* messageLen */);
+  string sessionId(aSessionId, aSessionId + aSessionIdLength);
+  auto itr = mSessions.find(sessionId);
+  if (itr == mSessions.end()) {
+    CK_LOGW("ClearKey CDM couldn't remove non-existent session.");
+    mCallback->RejectPromise(aPromiseId, kGMPNotFoundError, nullptr, 0);
+    return;
+  }
+
+  ClearKeySession* session = itr->second;
+  MOZ_ASSERT(session);
+  string sid = session->Id();
+  bool isPersistent = session->Type() == kGMPPersistentSession;
+  ClearInMemorySessionData(session);
+
+  if (!isPersistent) {
+    mCallback->ResolvePromise(aPromiseId);
+    return;
+  }
+
+  ClearKeyPersistence::PersistentSessionRemoved(sid);
+
+  // Overwrite the record storing the sessionId's key data with a zero
+  // length record to delete it.
+  vector<uint8_t> emptyKeydata;
+  GMPTask* resolve = WrapTask(mCallback, &GMPDecryptorCallback::ResolvePromise, aPromiseId);
+  static const char* message = "Could not remove session";
+  GMPTask* reject = WrapTask(mCallback,
+                             &GMPDecryptorCallback::RejectPromise,
+                             aPromiseId,
+                             kGMPInvalidAccessError,
+                             message,
+                             strlen(message));
+  StoreData(sessionId, emptyKeydata, resolve, reject);
 }
 
 void
 ClearKeyDecryptionManager::SetServerCertificate(uint32_t aPromiseId,
                                                 const uint8_t* aServerCert,
                                                 uint32_t aServerCertSize)
 {
   // ClearKey CDM doesn't support this method by spec.
@@ -265,18 +415,21 @@ ClearKeyDecryptionManager::SetServerCert
 
 void
 ClearKeyDecryptionManager::Decrypt(GMPBuffer* aBuffer,
                                    GMPEncryptedBufferMetadata* aMetadata)
 {
   CK_LOGD("ClearKeyDecryptionManager::Decrypt");
   KeyId keyId(aMetadata->KeyId(), aMetadata->KeyId() + aMetadata->KeyIdSize());
 
-  if (mDecryptors.find(keyId) == mDecryptors.end() || !mDecryptors[keyId]) {
+  auto itr = mDecryptors.find(keyId);
+  if (itr == mDecryptors.end() || !(itr->second)) {
+    CK_LOGD("ClearKeyDecryptionManager::Decrypt GMPNoKeyErr");
     mCallback->Decrypted(aBuffer, GMPNoKeyErr);
+    return;
   }
 
   mDecryptors[keyId]->QueueDecrypt(aBuffer, aMetadata);
 }
 
 void
 ClearKeyDecryptionManager::DecryptingComplete()
 {
@@ -285,17 +438,17 @@ ClearKeyDecryptionManager::DecryptingCom
   for (auto it = mSessions.begin(); it != mSessions.end(); it++) {
     delete it->second;
   }
 
   for (auto it = mDecryptors.begin(); it != mDecryptors.end(); it++) {
     delete it->second;
   }
 
-  delete this;
+  Release();
 }
 
 void
 ClearKeyDecryptor::QueueDecrypt(GMPBuffer* aBuffer,
                                 GMPEncryptedBufferMetadata* aMetadata)
 {
   CK_LOGD("ClearKeyDecryptor::QueueDecrypt");
   mThread->Post(new DecryptTask(this, aBuffer, aMetadata));
--- a/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
+++ b/media/gmp-clearkey/0.1/ClearKeyDecryptionManager.h
@@ -8,24 +8,24 @@
 #include <map>
 #include <string>
 #include <vector>
 
 #include "ClearKeySession.h"
 #include "ClearKeyUtils.h"
 #include "gmp-api/gmp-decryption.h"
 #include "ScopedNSSTypes.h"
+#include "RefCounted.h"
 
 class ClearKeyDecryptor;
-
 class ClearKeyDecryptionManager MOZ_FINAL : public GMPDecryptor
+                                          , public RefCounted
 {
 public:
   ClearKeyDecryptionManager();
-  ~ClearKeyDecryptionManager();
 
   virtual void Init(GMPDecryptorCallback* aCallback) MOZ_OVERRIDE;
 
   virtual void CreateSession(uint32_t aPromiseId,
                              const char* aInitDataType,
                              uint32_t aInitDataTypeSize,
                              const uint8_t* aInitData,
                              uint32_t aInitDataSize,
@@ -53,16 +53,27 @@ public:
                                     const uint8_t* aServerCert,
                                     uint32_t aServerCertSize) MOZ_OVERRIDE;
 
   virtual void Decrypt(GMPBuffer* aBuffer,
                        GMPEncryptedBufferMetadata* aMetadata) MOZ_OVERRIDE;
 
   virtual void DecryptingComplete() MOZ_OVERRIDE;
 
+  void PersistentSessionDataLoaded(GMPErr aStatus,
+                                   uint32_t aPromiseId,
+                                   const std::string& aSessionId,
+                                   const uint8_t* aKeyData,
+                                   uint32_t aKeyDataSize);
+
 private:
+  ~ClearKeyDecryptionManager();
+
+  void ClearInMemorySessionData(ClearKeySession* aSession);
+  void Serialize(const ClearKeySession* aSession, std::vector<uint8_t>& aOutKeyData);
+
   GMPDecryptorCallback* mCallback;
 
   std::map<KeyId, ClearKeyDecryptor*> mDecryptors;
   std::map<std::string, ClearKeySession*> mSessions;
 };
 
 #endif // __ClearKeyDecryptor_h__
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.cpp
@@ -0,0 +1,236 @@
+/* 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 "ClearKeyPersistence.h"
+#include "ClearKeyUtils.h"
+#include "ClearKeyStorage.h"
+#include "ClearKeyDecryptionManager.h"
+#include "mozilla/RefPtr.h"
+
+#include <stdint.h>
+#include <set>
+#include <vector>
+#include <sstream>
+
+using namespace mozilla;
+using namespace std;
+
+// Whether we've loaded the persistent session ids from GMPStorage yet.
+enum PersistentKeyState {
+  UNINITIALIZED,
+  LOADING,
+  LOADED
+};
+static PersistentKeyState sPersistentKeyState = UNINITIALIZED;
+
+// Set of session Ids of the persistent sessions created or residing in
+// storage.
+static set<uint32_t> sPersistentSessionIds;
+
+static vector<GMPTask*> sTasksBlockedOnSessionIdLoad;
+
+static void
+ReadAllRecordsFromIterator(GMPRecordIterator* aRecordIterator,
+                           void* aUserArg,
+                           GMPErr aStatus)
+{
+  MOZ_ASSERT(sPersistentKeyState == LOADING);
+  if (GMP_SUCCEEDED(aStatus)) {
+    // Extract the record names which are valid uint32_t's; they're
+    // the persistent session ids.
+    const char* name = nullptr;
+    uint32_t len = 0;
+    while (GMP_SUCCEEDED(aRecordIterator->GetName(&name, &len))) {
+      if (ClearKeyUtils::IsValidSessionId(name, len)) {
+        MOZ_ASSERT(name[len] == 0);
+        sPersistentSessionIds.insert(atoi(name));
+      }
+      aRecordIterator->NextRecord();
+    }
+  }
+  sPersistentKeyState = LOADED;
+  aRecordIterator->Close();
+
+  for (size_t i = 0; i < sTasksBlockedOnSessionIdLoad.size(); i++) {
+    sTasksBlockedOnSessionIdLoad[i]->Run();
+    sTasksBlockedOnSessionIdLoad[i]->Destroy();
+  }
+  sTasksBlockedOnSessionIdLoad.clear();
+}
+
+/* static */ void
+ClearKeyPersistence::EnsureInitialized()
+{
+  if (sPersistentKeyState == UNINITIALIZED) {
+    sPersistentKeyState = LOADING;
+    if (GMP_FAILED(EnumRecordNames(&ReadAllRecordsFromIterator))) {
+      sPersistentKeyState = LOADED;
+    }
+  }
+}
+
+/* static */ string
+ClearKeyPersistence::GetNewSessionId(GMPSessionType aSessionType)
+{
+  static uint32_t sNextSessionId = 1;
+
+  // Ensure we don't re-use a session id that was persisted.
+  while (Contains(sPersistentSessionIds, sNextSessionId)) {
+    sNextSessionId++;
+  }
+
+  string sessionId;
+  stringstream ss;
+  ss << sNextSessionId;
+  ss >> sessionId;
+
+  if (aSessionType == kGMPPersistentSession) {
+    sPersistentSessionIds.insert(sNextSessionId);
+  }
+
+  sNextSessionId++;
+
+  return sessionId;
+}
+
+
+class CreateSessionTask : public GMPTask {
+public:
+  CreateSessionTask(ClearKeyDecryptionManager* aTarget,
+                    uint32_t aPromiseId,
+                    const uint8_t* aInitData,
+                    uint32_t aInitDataSize,
+                    GMPSessionType aSessionType)
+    : mTarget(aTarget)
+    , mPromiseId(aPromiseId)
+    , mSessionType(aSessionType)
+  {
+    mInitData.insert(mInitData.end(),
+                     aInitData,
+                     aInitData + aInitDataSize);
+  }
+  virtual void Run() MOZ_OVERRIDE {
+    mTarget->CreateSession(mPromiseId,
+                           "cenc",
+                           strlen("cenc"),
+                           &mInitData.front(),
+                           mInitData.size(),
+                           mSessionType);
+  }
+  virtual void Destroy() MOZ_OVERRIDE {
+    delete this;
+  }
+private:
+  RefPtr<ClearKeyDecryptionManager> mTarget;
+  uint32_t mPromiseId;
+  vector<uint8_t> mInitData;
+  GMPSessionType mSessionType;
+};
+
+
+/* static */ bool
+ClearKeyPersistence::DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+                                                  uint32_t aPromiseId,
+                                                  const uint8_t* aInitData,
+                                                  uint32_t aInitDataSize,
+                                                  GMPSessionType aSessionType)
+{
+  if (sPersistentKeyState >= LOADED)  {
+    return false;
+  }
+  GMPTask* t = new CreateSessionTask(aInstance,
+                                     aPromiseId,
+                                     aInitData,
+                                     aInitDataSize,
+                                     aSessionType);
+  sTasksBlockedOnSessionIdLoad.push_back(t);
+  return true;
+}
+
+class LoadSessionTask : public GMPTask {
+public:
+  LoadSessionTask(ClearKeyDecryptionManager* aTarget,
+                  uint32_t aPromiseId,
+                  const char* aSessionId,
+                  uint32_t aSessionIdLength)
+    : mTarget(aTarget)
+    , mPromiseId(aPromiseId)
+    , mSessionId(aSessionId, aSessionId + aSessionIdLength)
+  {
+  }
+  virtual void Run() MOZ_OVERRIDE {
+    mTarget->LoadSession(mPromiseId,
+                         mSessionId.c_str(),
+                         mSessionId.size());
+  }
+  virtual void Destroy() MOZ_OVERRIDE {
+    delete this;
+  }
+private:
+  RefPtr<ClearKeyDecryptionManager> mTarget;
+  uint32_t mPromiseId;
+  string mSessionId;
+};
+
+/* static */ bool
+ClearKeyPersistence::DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+                                                uint32_t aPromiseId,
+                                                const char* aSessionId,
+                                                uint32_t aSessionIdLength)
+{
+  if (sPersistentKeyState >= LOADED)  {
+    return false;
+  }
+  GMPTask* t = new LoadSessionTask(aInstance,
+                                   aPromiseId,
+                                   aSessionId,
+                                   aSessionIdLength);
+  sTasksBlockedOnSessionIdLoad.push_back(t);
+  return true;
+}
+
+/* static */ bool
+ClearKeyPersistence::IsPersistentSessionId(const string& aSessionId)
+{
+  return Contains(sPersistentSessionIds, atoi(aSessionId.c_str()));
+}
+
+class LoadSessionFromKeysTask : public ReadContinuation {
+public:
+  LoadSessionFromKeysTask(ClearKeyDecryptionManager* aTarget,
+                          const string& aSessionId,
+                          uint32_t aPromiseId)
+    : mTarget(aTarget)
+    , mSessionId(aSessionId)
+    , mPromiseId(aPromiseId)
+  {
+  }
+
+  virtual void ReadComplete(GMPErr aStatus,
+                            const uint8_t* aData,
+                            uint32_t aLength) MOZ_OVERRIDE
+  {
+    mTarget->PersistentSessionDataLoaded(aStatus, mPromiseId, mSessionId, aData, aLength);
+  }
+private:
+  RefPtr<ClearKeyDecryptionManager> mTarget;
+  string mSessionId;
+  uint32_t mPromiseId;
+};
+
+/* static */ void
+ClearKeyPersistence::LoadSessionData(ClearKeyDecryptionManager* aInstance,
+                                     const string& aSid,
+                                     uint32_t aPromiseId)
+{
+  LoadSessionFromKeysTask* loadTask =
+    new LoadSessionFromKeysTask(aInstance, aSid, aPromiseId);
+  ReadData(aSid, loadTask);
+}
+
+/* static */ void
+ClearKeyPersistence::PersistentSessionRemoved(const string& aSessionId)
+{
+  sPersistentSessionIds.erase(atoi(aSessionId.c_str()));
+}
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyPersistence.h
@@ -0,0 +1,39 @@
+/* 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 __ClearKeyPersistence_h__
+#define __ClearKeyPersistence_h__
+
+#include <string>
+#include "gmp-decryption.h"
+
+class ClearKeyDecryptionManager;
+
+class ClearKeyPersistence {
+public:
+  static void EnsureInitialized();
+
+  static std::string GetNewSessionId(GMPSessionType aSessionType);
+
+  static bool DeferCreateSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+                                           uint32_t aPromiseId,
+                                           const uint8_t* aInitData,
+                                           uint32_t aInitDataSize,
+                                           GMPSessionType aSessionType);
+
+  static bool DeferLoadSessionIfNotReady(ClearKeyDecryptionManager* aInstance,
+                                         uint32_t aPromiseId,
+                                         const char* aSessionId,
+                                         uint32_t aSessionIdLength);
+
+  static bool IsPersistentSessionId(const std::string& aSid);
+
+  static void LoadSessionData(ClearKeyDecryptionManager* aInstance,
+                              const std::string& aSid,
+                              uint32_t aPromiseId);
+
+  static void PersistentSessionRemoved(const std::string& aSid);
+};
+
+#endif // __ClearKeyPersistence_h__
--- a/media/gmp-clearkey/0.1/ClearKeySession.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeySession.cpp
@@ -1,25 +1,29 @@
 /* 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 "ClearKeySession.h"
 #include "ClearKeyUtils.h"
+#include "ClearKeyStorage.h"
+#include "gmp-task-utils.h"
 
 #include "gmp-api/gmp-decryption.h"
 #include "mozilla/Endian.h"
 #include "pk11pub.h"
 
 using namespace mozilla;
 
 ClearKeySession::ClearKeySession(const std::string& aSessionId,
-                                 GMPDecryptorCallback* aCallback)
+                                 GMPDecryptorCallback* aCallback,
+                                 GMPSessionType aSessionType)
   : mSessionId(aSessionId)
   , mCallback(aCallback)
+  , mSessionType(aSessionType)
 {
   CK_LOGD("ClearKeySession ctor %p", this);
 }
 
 ClearKeySession::~ClearKeySession()
 {
   CK_LOGD("ClearKeySession dtor %p", this);
 }
@@ -31,12 +35,23 @@ ClearKeySession::Init(uint32_t aPromiseI
   CK_LOGD("ClearKeySession::Init");
 
   ClearKeyUtils::ParseInitData(aInitData, aInitDataSize, mKeyIds);
   if (!mKeyIds.size()) {
     const char message[] = "Couldn't parse cenc key init data";
     mCallback->RejectPromise(aPromiseId, kGMPAbortError, message, strlen(message));
     return;
   }
-
   mCallback->ResolveNewSessionPromise(aPromiseId,
                                       mSessionId.data(), mSessionId.length());
 }
+
+GMPSessionType
+ClearKeySession::Type() const
+{
+  return mSessionType;
+}
+
+void
+ClearKeySession::AddKeyId(const KeyId& aKeyId)
+{
+  mKeyIds.push_back(aKeyId);
+}
--- a/media/gmp-clearkey/0.1/ClearKeySession.h
+++ b/media/gmp-clearkey/0.1/ClearKeySession.h
@@ -1,39 +1,44 @@
 /* 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 __ClearKeySession_h__
 #define __ClearKeySession_h__
 
 #include "ClearKeyUtils.h"
+#include "gmp-decryption.h"
 
 class GMPBuffer;
 class GMPDecryptorCallback;
 class GMPDecryptorHost;
 class GMPEncryptedBufferMetadata;
 
-/**
- * Currently useless; will be fleshed out later with support for persistent
- * key sessions.
- */
-
 class ClearKeySession
 {
 public:
-  ClearKeySession(const std::string& aSessionId,
-                  GMPDecryptorCallback* aCallback);
+  explicit ClearKeySession(const std::string& aSessionId,
+                           GMPDecryptorCallback* aCallback,
+                           GMPSessionType aSessionType);
 
   ~ClearKeySession();
 
-  const std::vector<KeyId>& GetKeyIds() { return mKeyIds; }
+  const std::vector<KeyId>& GetKeyIds() const { return mKeyIds; }
 
   void Init(uint32_t aPromiseId,
             const uint8_t* aInitData, uint32_t aInitDataSize);
+
+  GMPSessionType Type() const;
+
+  void AddKeyId(const KeyId& aKeyId);
+
+  const std::string Id() const { return mSessionId; }
+
 private:
-  std::string mSessionId;
+  const std::string mSessionId;
   std::vector<KeyId> mKeyIds;
 
   GMPDecryptorCallback* mCallback;
+  const GMPSessionType mSessionType;
 };
 
 #endif // __ClearKeySession_h__
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyStorage.cpp
@@ -0,0 +1,174 @@
+/* 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 "ClearKeyStorage.h"
+#include "ClearKeyUtils.h"
+
+#include "gmp-task-utils.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/NullPtr.h"
+#include "mozilla/ArrayUtils.h"
+
+#include <vector>
+
+static GMPErr
+RunOnMainThread(GMPTask* aTask)
+{
+  return GetPlatform()->runonmainthread(aTask);
+}
+
+class WriteRecordClient : public GMPRecordClient {
+public:
+  GMPErr Init(GMPRecord* aRecord,
+              GMPTask* aOnSuccess,
+              GMPTask* aOnFailure,
+              const uint8_t* aData,
+              uint32_t aDataSize) {
+    mRecord = aRecord;
+    mOnSuccess = aOnSuccess;
+    mOnFailure = aOnFailure;
+    mData.insert(mData.end(), aData, aData + aDataSize);
+    return mRecord->Open();
+  }
+
+  virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
+    if (GMP_FAILED(aStatus) ||
+        GMP_FAILED(mRecord->Write(&mData.front(), mData.size()))) {
+      RunOnMainThread(mOnFailure);
+      mOnSuccess->Destroy();
+    }
+  }
+
+  virtual void ReadComplete(GMPErr aStatus,
+                            const uint8_t* aData,
+                            uint32_t aDataSize) MOZ_OVERRIDE {
+    MOZ_ASSERT(false, "Should not reach here.");
+  }
+
+  virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE {
+    // Note: Call Close() before running continuation, in case the
+    // continuation tries to open the same record; if we call Close()
+    // after running the continuation, the Close() call will arrive
+    // just after the Open() call succeeds, immediately closing the
+    // record we just opened.
+    mRecord->Close();
+    if (GMP_SUCCEEDED(aStatus)) {
+      RunOnMainThread(mOnSuccess);
+      mOnFailure->Destroy();
+    } else {
+      RunOnMainThread(mOnFailure);
+      mOnSuccess->Destroy();
+    }
+    delete this;
+  }
+
+private:
+  GMPRecord* mRecord;
+  GMPTask* mOnSuccess;
+  GMPTask* mOnFailure;
+  std::vector<uint8_t> mData;
+};
+
+GMPErr
+OpenRecord(const char* aName,
+           uint32_t aNameLength,
+           GMPRecord** aOutRecord,
+           GMPRecordClient* aClient)
+{
+  return GetPlatform()->createrecord(aName, aNameLength, aOutRecord, aClient);
+}
+
+void
+StoreData(const std::string& aRecordName,
+          const std::vector<uint8_t>& aData,
+          GMPTask* aOnSuccess,
+          GMPTask* aOnFailure)
+{
+  GMPRecord* record;
+  WriteRecordClient* client = new WriteRecordClient();
+  if (GMP_FAILED(OpenRecord(aRecordName.c_str(),
+                            aRecordName.size(),
+                            &record,
+                            client)) ||
+      GMP_FAILED(client->Init(record,
+                              aOnSuccess,
+                              aOnFailure,
+                              &aData.front(),
+                              aData.size()))) {
+    RunOnMainThread(aOnFailure);
+    aOnSuccess->Destroy();
+  }
+}
+
+class ReadRecordClient : public GMPRecordClient {
+public:
+  ReadRecordClient()
+    : mRecord(nullptr)
+    , mContinuation(nullptr)
+  {}
+  ~ReadRecordClient() {
+    delete mContinuation;
+  }
+
+  GMPErr Init(GMPRecord* aRecord,
+              ReadContinuation* aContinuation) {
+    mRecord = aRecord;
+    mContinuation = aContinuation;
+    return mRecord->Open();
+  }
+
+  virtual void OpenComplete(GMPErr aStatus) MOZ_OVERRIDE {
+    auto err = mRecord->Read();
+    if (GMP_FAILED(err)) {
+      mContinuation->ReadComplete(err, nullptr, 0);
+      delete this;
+    }
+  }
+
+  virtual void ReadComplete(GMPErr aStatus,
+                            const uint8_t* aData,
+                            uint32_t aDataSize) MOZ_OVERRIDE {
+    // Note: Call Close() before running continuation, in case the
+    // continuation tries to open the same record; if we call Close()
+    // after running the continuation, the Close() call will arrive
+    // just after the Open() call succeeds, immediately closing the
+    // record we just opened.
+    mRecord->Close();
+    mContinuation->ReadComplete(GMPNoErr, aData, aDataSize);
+    delete this;
+  }
+
+  virtual void WriteComplete(GMPErr aStatus) MOZ_OVERRIDE {
+    MOZ_ASSERT(false, "Should not reach here.");
+  }
+
+private:
+  GMPRecord* mRecord;
+  ReadContinuation* mContinuation;
+};
+
+void
+ReadData(const std::string& aRecordName,
+         ReadContinuation* aContinuation)
+{
+  MOZ_ASSERT(aContinuation);
+  GMPRecord* record;
+  ReadRecordClient* client = new ReadRecordClient();
+  auto err = OpenRecord(aRecordName.c_str(),
+                        aRecordName.size(),
+                        &record,
+                        client);
+  if (GMP_FAILED(err) ||
+      GMP_FAILED(client->Init(record, aContinuation))) {
+    aContinuation->ReadComplete(err, nullptr, 0);
+    delete aContinuation;
+  }
+}
+
+GMPErr
+EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc)
+{
+  return GetPlatform()->getrecordenumerator(aRecvIteratorFunc, nullptr);
+}
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/ClearKeyStorage.h
@@ -0,0 +1,36 @@
+/* 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 __ClearKeyStorage_h__
+#define __ClearKeyStorage_h__
+
+#include "gmp-errors.h"
+#include "gmp-platform.h"
+#include <string>
+#include <vector>
+#include <stdint.h>
+
+class GMPTask;
+
+// Responsible for ensuring that both aOnSuccess and aOnFailure are destroyed.
+void StoreData(const std::string& aRecordName,
+               const std::vector<uint8_t>& aData,
+               GMPTask* aOnSuccess,
+               GMPTask* aOnFailure);
+
+class ReadContinuation {
+public:
+  virtual void ReadComplete(GMPErr aStatus,
+                            const uint8_t* aData,
+                            uint32_t aLength) = 0;
+  virtual ~ReadContinuation() {}
+};
+
+// Deletes aContinuation after running it to report the result.
+void ReadData(const std::string& aSessionId,
+              ReadContinuation* aContinuation);
+
+GMPErr EnumRecordNames(RecvGMPRecordIteratorPtr aRecvIteratorFunc);
+
+#endif // __ClearKeyStorage_h__
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.cpp
@@ -172,17 +172,18 @@ ClearKeyUtils::ParseInitData(const uint8
       aOutKeys.push_back(KeyId(data, data + CLEARKEY_KEY_LEN));
       data += CLEARKEY_KEY_LEN;
     }
   }
 }
 
 /* static */ void
 ClearKeyUtils::MakeKeyRequest(const vector<KeyId>& aKeyIDs,
-                              string& aOutRequest)
+                              string& aOutRequest,
+                              GMPSessionType aSessionType)
 {
   MOZ_ASSERT(aKeyIDs.size() && aOutRequest.empty());
 
   aOutRequest.append("{ \"kids\":[");
   for (size_t i = 0; i < aKeyIDs.size(); i++) {
     if (i) {
       aOutRequest.append(",");
     }
@@ -190,19 +191,20 @@ ClearKeyUtils::MakeKeyRequest(const vect
 
     string base64key;
     EncodeBase64Web(aKeyIDs[i], base64key);
     aOutRequest.append(base64key);
 
     aOutRequest.append("\"");
   }
   aOutRequest.append("], \"type\":");
-  // TODO implement "persistent" session type
-  aOutRequest.append("\"temporary\"");
-  aOutRequest.append("}");
+
+  aOutRequest.append("\"");
+  aOutRequest.append(SessionTypeToString(aSessionType));
+  aOutRequest.append("\"}");
 }
 
 #define EXPECT_SYMBOL(CTX, X) do { \
   if (GetNextSymbol(CTX) != (X)) { \
     CK_LOGE("Unexpected symbol in JWK parser"); \
     return false; \
   } \
 } while (false)
@@ -504,17 +506,18 @@ ParseKeys(ParserContext& aCtx, vector<Ke
     EXPECT_SYMBOL(aCtx, ',');
   }
 
   return GetNextSymbol(aCtx) == ']';
 }
 
 /* static */ bool
 ClearKeyUtils::ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
-                        vector<KeyIdPair>& aOutKeys)
+                        vector<KeyIdPair>& aOutKeys,
+                        GMPSessionType aSessionType)
 {
   ParserContext ctx;
   ctx.mIter = aKeyData;
   ctx.mEnd = aKeyData + aKeyDataSize;
 
   // Consume '{' from start of object.
   EXPECT_SYMBOL(ctx, '{');
 
@@ -526,18 +529,17 @@ ClearKeyUtils::ParseJWK(const uint8_t* a
 
     if (label == "keys") {
       // Parse "keys" array.
       if (!ParseKeys(ctx, aOutKeys)) return false;
     } else if (label == "type") {
       // Consume type string.
       string type;
       if (!GetNextLabel(ctx, type)) return false;
-      // XXX todo support "persistent" session type
-      if (type != "temporary") {
+      if (type != SessionTypeToString(aSessionType)) {
         return false;
       }
     } else {
       SkipToken(ctx);
     }
 
     // Check for end of object.
     if (PeekSymbol(ctx) == '}') {
@@ -548,8 +550,37 @@ ClearKeyUtils::ParseJWK(const uint8_t* a
     EXPECT_SYMBOL(ctx, ',');
   }
 
   // Consume '}' from end of object.
   EXPECT_SYMBOL(ctx, '}');
 
   return true;
 }
+
+/* static */ const char*
+ClearKeyUtils::SessionTypeToString(GMPSessionType aSessionType)
+{
+  switch (aSessionType) {
+    case kGMPTemporySession: return "temporary";
+    case kGMPPersistentSession: return "persistent";
+    default: {
+      MOZ_ASSERT(false, "Should not reach here.");
+      return "invalid";
+    }
+  }
+}
+
+/* static */ bool
+ClearKeyUtils::IsValidSessionId(const char* aBuff, uint32_t aLength)
+{
+  if (aLength > 10) {
+    // 10 is the max number of characters in UINT32_MAX when
+    // represented as a string; ClearKey session ids are integers.
+    return false;
+  }
+  for (uint32_t i = 0; i < aLength; i++) {
+    if (!isdigit(aBuff[i])) {
+      return false;
+    }
+  }
+  return true;
+}
--- a/media/gmp-clearkey/0.1/ClearKeyUtils.h
+++ b/media/gmp-clearkey/0.1/ClearKeyUtils.h
@@ -3,16 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __ClearKeyUtils_h__
 #define __ClearKeyUtils_h__
 
 #include <stdint.h>
 #include <string>
 #include <vector>
+#include "gmp-decryption.h"
 
 #define CLEARKEY_KEY_LEN ((size_t)16)
 
 #if 0
 void CK_Log(const char* aFmt, ...);
 #define CK_LOGE(...) CK_Log(__VA_ARGS__)
 #define CK_LOGD(...) CK_Log(__VA_ARGS__)
 #define CK_LOGW(...) CK_Log(__VA_ARGS__)
@@ -39,15 +40,27 @@ class ClearKeyUtils
 public:
   static void DecryptAES(const std::vector<uint8_t>& aKey,
                          std::vector<uint8_t>& aData, std::vector<uint8_t>& aIV);
 
   static void ParseInitData(const uint8_t* aInitData, uint32_t aInitDataSize,
                             std::vector<Key>& aOutKeys);
 
   static void MakeKeyRequest(const std::vector<KeyId>& aKeyIds,
-                             std::string& aOutRequest);
+                             std::string& aOutRequest,
+                             GMPSessionType aSessionType);
 
   static bool ParseJWK(const uint8_t* aKeyData, uint32_t aKeyDataSize,
-                       std::vector<KeyIdPair>& aOutKeys);
+                       std::vector<KeyIdPair>& aOutKeys,
+                       GMPSessionType aSessionType);
+  static const char* SessionTypeToString(GMPSessionType aSessionType);
+
+  static bool IsValidSessionId(const char* aBuff, uint32_t aLength);
 };
 
+template<class Container, class Element>
+inline bool
+Contains(const Container& aContainer, const Element& aElement)
+{
+  return aContainer.find(aElement) != aContainer.end();
+}
+
 #endif // __ClearKeyUtils_h__
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/RefCounted.h
@@ -0,0 +1,34 @@
+/* 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 __RefCount_h__
+#define __RefCount_h__
+
+// Note: Not thread safe!
+class RefCounted {
+public:
+  void AddRef() {
+    ++mRefCount;
+  }
+
+  void Release() {
+    if (mRefCount == 1) {
+      delete this;
+    } else {
+      --mRefCount;
+    }
+  }
+
+protected:
+  RefCounted()
+    : mRefCount(0)
+  {
+  }
+  virtual ~RefCounted()
+  {
+  }
+  uint32_t mRefCount;
+};
+
+#endif // __RefCount_h__
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/gmp-task-utils-generated.h
@@ -0,0 +1,1898 @@
+/* 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/. */
+
+
+// 0 arguments --
+template<typename M> class gmp_task_args_nm_0 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_0(M m) :
+    m_(m)  {}
+
+  void Run() {
+    m_();
+  }
+
+ private:
+  M m_;
+};
+
+
+
+// 0 arguments --
+template<typename M, typename R> class gmp_task_args_nm_0_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_0_ret(M m, R *r) :
+    m_(m), r_(r)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_();
+  }
+
+ private:
+  M m_;
+  R* r_;
+};
+
+
+
+// 0 arguments --
+template<typename C, typename M> class gmp_task_args_m_0 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_0(C o, M m) :
+    o_(o), m_(m)  {}
+
+  void Run() {
+    ((*o_).*m_)();
+  }
+
+ private:
+  C o_;
+  M m_;
+};
+
+
+
+// 0 arguments --
+template<typename C, typename M, typename R> class gmp_task_args_m_0_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_0_ret(C o, M m, R *r) :
+    o_(o), m_(m), r_(r)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)();
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+};
+
+
+
+// 1 arguments --
+template<typename M, typename A0> class gmp_task_args_nm_1 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_1(M m, A0 a0) :
+    m_(m), a0_(a0)  {}
+
+  void Run() {
+    m_(a0_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+};
+
+
+
+// 1 arguments --
+template<typename M, typename A0, typename R> class gmp_task_args_nm_1_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_1_ret(M m, A0 a0, R *r) :
+    m_(m), r_(r), a0_(a0)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+};
+
+
+
+// 1 arguments --
+template<typename C, typename M, typename A0> class gmp_task_args_m_1 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_1(C o, M m, A0 a0) :
+    o_(o), m_(m), a0_(a0)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+};
+
+
+
+// 1 arguments --
+template<typename C, typename M, typename A0, typename R> class gmp_task_args_m_1_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_1_ret(C o, M m, A0 a0, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+};
+
+
+
+// 2 arguments --
+template<typename M, typename A0, typename A1> class gmp_task_args_nm_2 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_2(M m, A0 a0, A1 a1) :
+    m_(m), a0_(a0), a1_(a1)  {}
+
+  void Run() {
+    m_(a0_, a1_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+};
+
+
+
+// 2 arguments --
+template<typename M, typename A0, typename A1, typename R> class gmp_task_args_nm_2_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_2_ret(M m, A0 a0, A1 a1, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+};
+
+
+
+// 2 arguments --
+template<typename C, typename M, typename A0, typename A1> class gmp_task_args_m_2 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_2(C o, M m, A0 a0, A1 a1) :
+    o_(o), m_(m), a0_(a0), a1_(a1)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+};
+
+
+
+// 2 arguments --
+template<typename C, typename M, typename A0, typename A1, typename R> class gmp_task_args_m_2_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_2_ret(C o, M m, A0 a0, A1 a1, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+};
+
+
+
+// 3 arguments --
+template<typename M, typename A0, typename A1, typename A2> class gmp_task_args_nm_3 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_3(M m, A0 a0, A1 a1, A2 a2) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+};
+
+
+
+// 3 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename R> class gmp_task_args_nm_3_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_3_ret(M m, A0 a0, A1 a1, A2 a2, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+};
+
+
+
+// 3 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2> class gmp_task_args_m_3 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_3(C o, M m, A0 a0, A1 a1, A2 a2) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+};
+
+
+
+// 3 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename R> class gmp_task_args_m_3_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_3_ret(C o, M m, A0 a0, A1 a1, A2 a2, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+};
+
+
+
+// 4 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3> class gmp_task_args_nm_4 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_4(M m, A0 a0, A1 a1, A2 a2, A3 a3) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+};
+
+
+
+// 4 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename R> class gmp_task_args_nm_4_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_4_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+};
+
+
+
+// 4 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3> class gmp_task_args_m_4 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_4(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+};
+
+
+
+// 4 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename R> class gmp_task_args_m_4_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_4_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+};
+
+
+
+// 5 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4> class gmp_task_args_nm_5 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_5(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+};
+
+
+
+// 5 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> class gmp_task_args_nm_5_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_5_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+};
+
+
+
+// 5 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4> class gmp_task_args_m_5 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_5(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+};
+
+
+
+// 5 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R> class gmp_task_args_m_5_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_5_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+};
+
+
+
+// 6 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> class gmp_task_args_nm_6 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_6(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+};
+
+
+
+// 6 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> class gmp_task_args_nm_6_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_6_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+};
+
+
+
+// 6 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5> class gmp_task_args_m_6 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_6(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+};
+
+
+
+// 6 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R> class gmp_task_args_m_6_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_6_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+};
+
+
+
+// 7 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> class gmp_task_args_nm_7 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_7(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+};
+
+
+
+// 7 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> class gmp_task_args_nm_7_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_7_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+};
+
+
+
+// 7 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6> class gmp_task_args_m_7 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_7(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+};
+
+
+
+// 7 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R> class gmp_task_args_m_7_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_7_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+};
+
+
+
+// 8 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> class gmp_task_args_nm_8 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_8(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+};
+
+
+
+// 8 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> class gmp_task_args_nm_8_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_8_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+};
+
+
+
+// 8 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7> class gmp_task_args_m_8 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_8(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+};
+
+
+
+// 8 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R> class gmp_task_args_m_8_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_8_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+};
+
+
+
+// 9 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> class gmp_task_args_nm_9 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_9(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+};
+
+
+
+// 9 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> class gmp_task_args_nm_9_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_9_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+};
+
+
+
+// 9 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8> class gmp_task_args_m_9 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_9(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+};
+
+
+
+// 9 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R> class gmp_task_args_m_9_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_9_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+};
+
+
+
+// 10 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> class gmp_task_args_nm_10 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_10(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+};
+
+
+
+// 10 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R> class gmp_task_args_nm_10_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_10_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+};
+
+
+
+// 10 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9> class gmp_task_args_m_10 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_10(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+};
+
+
+
+// 10 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R> class gmp_task_args_m_10_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_10_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+};
+
+
+
+// 11 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> class gmp_task_args_nm_11 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_11(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+};
+
+
+
+// 11 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R> class gmp_task_args_nm_11_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_11_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+};
+
+
+
+// 11 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10> class gmp_task_args_m_11 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_11(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+};
+
+
+
+// 11 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R> class gmp_task_args_m_11_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_11_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+};
+
+
+
+// 12 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11> class gmp_task_args_nm_12 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_12(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+};
+
+
+
+// 12 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R> class gmp_task_args_nm_12_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_12_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+};
+
+
+
+// 12 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11> class gmp_task_args_m_12 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_12(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+};
+
+
+
+// 12 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R> class gmp_task_args_m_12_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_12_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+};
+
+
+
+// 13 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12> class gmp_task_args_nm_13 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_13(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+  A12 a12_;
+};
+
+
+
+// 13 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R> class gmp_task_args_nm_13_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_13_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+  A12 a12_;
+};
+
+
+
+// 13 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12> class gmp_task_args_m_13 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_13(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+  A12 a12_;
+};
+
+
+
+// 13 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R> class gmp_task_args_m_13_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_13_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+  A12 a12_;
+};
+
+
+
+// 14 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13> class gmp_task_args_nm_14 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_14(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) :
+    m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13)  {}
+
+  void Run() {
+    m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_);
+  }
+
+ private:
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+  A12 a12_;
+  A13 a13_;
+};
+
+
+
+// 14 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R> class gmp_task_args_nm_14_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_nm_14_ret(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R *r) :
+    m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = m_(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_);
+  }
+
+ private:
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+  A12 a12_;
+  A13 a13_;
+};
+
+
+
+// 14 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13> class gmp_task_args_m_14 : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_14(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) :
+    o_(o), m_(m), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13)  {}
+
+  void Run() {
+    ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+  A12 a12_;
+  A13 a13_;
+};
+
+
+
+// 14 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R> class gmp_task_args_m_14_ret : public gmp_task_args_base {
+ public:
+  explicit gmp_task_args_m_14_ret(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R *r) :
+    o_(o), m_(m), r_(r), a0_(a0), a1_(a1), a2_(a2), a3_(a3), a4_(a4), a5_(a5), a6_(a6), a7_(a7), a8_(a8), a9_(a9), a10_(a10), a11_(a11), a12_(a12), a13_(a13)  {}
+  virtual bool returns_value() const { return true; }
+
+  void Run() {
+    *r_ = ((*o_).*m_)(a0_, a1_, a2_, a3_, a4_, a5_, a6_, a7_, a8_, a9_, a10_, a11_, a12_, a13_);
+  }
+
+ private:
+  C o_;
+  M m_;
+  R* r_;
+  A0 a0_;
+  A1 a1_;
+  A2 a2_;
+  A3 a3_;
+  A4 a4_;
+  A5 a5_;
+  A6 a6_;
+  A7 a7_;
+  A8 a8_;
+  A9 a9_;
+  A10 a10_;
+  A11 a11_;
+  A12 a12_;
+  A13 a13_;
+};
+
+
+
+
+
+
+// 0 arguments --
+template<typename M>
+gmp_task_args_nm_0<M>* WrapTaskNM(M m) {
+  return new gmp_task_args_nm_0<M>
+    (m);
+}
+
+// 0 arguments --
+template<typename M, typename R>
+gmp_task_args_nm_0_ret<M, R>* WrapTaskNMRet(M m, R* r) {
+  return new gmp_task_args_nm_0_ret<M, R>
+    (m, r);
+}
+
+// 0 arguments --
+template<typename C, typename M>
+gmp_task_args_m_0<C, M>* WrapTask(C o, M m) {
+  return new gmp_task_args_m_0<C, M>
+    (o, m);
+}
+
+// 0 arguments --
+template<typename C, typename M, typename R>
+gmp_task_args_m_0_ret<C, M, R>* WrapTaskRet(C o, M m, R* r) {
+  return new gmp_task_args_m_0_ret<C, M, R>
+    (o, m, r);
+}
+
+// 1 arguments --
+template<typename M, typename A0>
+gmp_task_args_nm_1<M, A0>* WrapTaskNM(M m, A0 a0) {
+  return new gmp_task_args_nm_1<M, A0>
+    (m, a0);
+}
+
+// 1 arguments --
+template<typename M, typename A0, typename R>
+gmp_task_args_nm_1_ret<M, A0, R>* WrapTaskNMRet(M m, A0 a0, R* r) {
+  return new gmp_task_args_nm_1_ret<M, A0, R>
+    (m, a0, r);
+}
+
+// 1 arguments --
+template<typename C, typename M, typename A0>
+gmp_task_args_m_1<C, M, A0>* WrapTask(C o, M m, A0 a0) {
+  return new gmp_task_args_m_1<C, M, A0>
+    (o, m, a0);
+}
+
+// 1 arguments --
+template<typename C, typename M, typename A0, typename R>
+gmp_task_args_m_1_ret<C, M, A0, R>* WrapTaskRet(C o, M m, A0 a0, R* r) {
+  return new gmp_task_args_m_1_ret<C, M, A0, R>
+    (o, m, a0, r);
+}
+
+// 2 arguments --
+template<typename M, typename A0, typename A1>
+gmp_task_args_nm_2<M, A0, A1>* WrapTaskNM(M m, A0 a0, A1 a1) {
+  return new gmp_task_args_nm_2<M, A0, A1>
+    (m, a0, a1);
+}
+
+// 2 arguments --
+template<typename M, typename A0, typename A1, typename R>
+gmp_task_args_nm_2_ret<M, A0, A1, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, R* r) {
+  return new gmp_task_args_nm_2_ret<M, A0, A1, R>
+    (m, a0, a1, r);
+}
+
+// 2 arguments --
+template<typename C, typename M, typename A0, typename A1>
+gmp_task_args_m_2<C, M, A0, A1>* WrapTask(C o, M m, A0 a0, A1 a1) {
+  return new gmp_task_args_m_2<C, M, A0, A1>
+    (o, m, a0, a1);
+}
+
+// 2 arguments --
+template<typename C, typename M, typename A0, typename A1, typename R>
+gmp_task_args_m_2_ret<C, M, A0, A1, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, R* r) {
+  return new gmp_task_args_m_2_ret<C, M, A0, A1, R>
+    (o, m, a0, a1, r);
+}
+
+// 3 arguments --
+template<typename M, typename A0, typename A1, typename A2>
+gmp_task_args_nm_3<M, A0, A1, A2>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2) {
+  return new gmp_task_args_nm_3<M, A0, A1, A2>
+    (m, a0, a1, a2);
+}
+
+// 3 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename R>
+gmp_task_args_nm_3_ret<M, A0, A1, A2, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, R* r) {
+  return new gmp_task_args_nm_3_ret<M, A0, A1, A2, R>
+    (m, a0, a1, a2, r);
+}
+
+// 3 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2>
+gmp_task_args_m_3<C, M, A0, A1, A2>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2) {
+  return new gmp_task_args_m_3<C, M, A0, A1, A2>
+    (o, m, a0, a1, a2);
+}
+
+// 3 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename R>
+gmp_task_args_m_3_ret<C, M, A0, A1, A2, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, R* r) {
+  return new gmp_task_args_m_3_ret<C, M, A0, A1, A2, R>
+    (o, m, a0, a1, a2, r);
+}
+
+// 4 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3>
+gmp_task_args_nm_4<M, A0, A1, A2, A3>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3) {
+  return new gmp_task_args_nm_4<M, A0, A1, A2, A3>
+    (m, a0, a1, a2, a3);
+}
+
+// 4 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename R>
+gmp_task_args_nm_4_ret<M, A0, A1, A2, A3, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, R* r) {
+  return new gmp_task_args_nm_4_ret<M, A0, A1, A2, A3, R>
+    (m, a0, a1, a2, a3, r);
+}
+
+// 4 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3>
+gmp_task_args_m_4<C, M, A0, A1, A2, A3>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3) {
+  return new gmp_task_args_m_4<C, M, A0, A1, A2, A3>
+    (o, m, a0, a1, a2, a3);
+}
+
+// 4 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename R>
+gmp_task_args_m_4_ret<C, M, A0, A1, A2, A3, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, R* r) {
+  return new gmp_task_args_m_4_ret<C, M, A0, A1, A2, A3, R>
+    (o, m, a0, a1, a2, a3, r);
+}
+
+// 5 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4>
+gmp_task_args_nm_5<M, A0, A1, A2, A3, A4>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) {
+  return new gmp_task_args_nm_5<M, A0, A1, A2, A3, A4>
+    (m, a0, a1, a2, a3, a4);
+}
+
+// 5 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R>
+gmp_task_args_nm_5_ret<M, A0, A1, A2, A3, A4, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R* r) {
+  return new gmp_task_args_nm_5_ret<M, A0, A1, A2, A3, A4, R>
+    (m, a0, a1, a2, a3, a4, r);
+}
+
+// 5 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4>
+gmp_task_args_m_5<C, M, A0, A1, A2, A3, A4>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4) {
+  return new gmp_task_args_m_5<C, M, A0, A1, A2, A3, A4>
+    (o, m, a0, a1, a2, a3, a4);
+}
+
+// 5 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename R>
+gmp_task_args_m_5_ret<C, M, A0, A1, A2, A3, A4, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, R* r) {
+  return new gmp_task_args_m_5_ret<C, M, A0, A1, A2, A3, A4, R>
+    (o, m, a0, a1, a2, a3, a4, r);
+}
+
+// 6 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
+gmp_task_args_nm_6<M, A0, A1, A2, A3, A4, A5>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+  return new gmp_task_args_nm_6<M, A0, A1, A2, A3, A4, A5>
+    (m, a0, a1, a2, a3, a4, a5);
+}
+
+// 6 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R>
+gmp_task_args_nm_6_ret<M, A0, A1, A2, A3, A4, A5, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R* r) {
+  return new gmp_task_args_nm_6_ret<M, A0, A1, A2, A3, A4, A5, R>
+    (m, a0, a1, a2, a3, a4, a5, r);
+}
+
+// 6 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5>
+gmp_task_args_m_6<C, M, A0, A1, A2, A3, A4, A5>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5) {
+  return new gmp_task_args_m_6<C, M, A0, A1, A2, A3, A4, A5>
+    (o, m, a0, a1, a2, a3, a4, a5);
+}
+
+// 6 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename R>
+gmp_task_args_m_6_ret<C, M, A0, A1, A2, A3, A4, A5, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, R* r) {
+  return new gmp_task_args_m_6_ret<C, M, A0, A1, A2, A3, A4, A5, R>
+    (o, m, a0, a1, a2, a3, a4, a5, r);
+}
+
+// 7 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
+gmp_task_args_nm_7<M, A0, A1, A2, A3, A4, A5, A6>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+  return new gmp_task_args_nm_7<M, A0, A1, A2, A3, A4, A5, A6>
+    (m, a0, a1, a2, a3, a4, a5, a6);
+}
+
+// 7 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R>
+gmp_task_args_nm_7_ret<M, A0, A1, A2, A3, A4, A5, A6, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R* r) {
+  return new gmp_task_args_nm_7_ret<M, A0, A1, A2, A3, A4, A5, A6, R>
+    (m, a0, a1, a2, a3, a4, a5, a6, r);
+}
+
+// 7 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
+gmp_task_args_m_7<C, M, A0, A1, A2, A3, A4, A5, A6>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6) {
+  return new gmp_task_args_m_7<C, M, A0, A1, A2, A3, A4, A5, A6>
+    (o, m, a0, a1, a2, a3, a4, a5, a6);
+}
+
+// 7 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename R>
+gmp_task_args_m_7_ret<C, M, A0, A1, A2, A3, A4, A5, A6, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, R* r) {
+  return new gmp_task_args_m_7_ret<C, M, A0, A1, A2, A3, A4, A5, A6, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, r);
+}
+
+// 8 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
+gmp_task_args_nm_8<M, A0, A1, A2, A3, A4, A5, A6, A7>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
+  return new gmp_task_args_nm_8<M, A0, A1, A2, A3, A4, A5, A6, A7>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7);
+}
+
+// 8 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R>
+gmp_task_args_nm_8_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R* r) {
+  return new gmp_task_args_nm_8_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, R>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, r);
+}
+
+// 8 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
+gmp_task_args_m_8<C, M, A0, A1, A2, A3, A4, A5, A6, A7>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7) {
+  return new gmp_task_args_m_8<C, M, A0, A1, A2, A3, A4, A5, A6, A7>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7);
+}
+
+// 8 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename R>
+gmp_task_args_m_8_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, R* r) {
+  return new gmp_task_args_m_8_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, r);
+}
+
+// 9 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
+gmp_task_args_nm_9<M, A0, A1, A2, A3, A4, A5, A6, A7, A8>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) {
+  return new gmp_task_args_nm_9<M, A0, A1, A2, A3, A4, A5, A6, A7, A8>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8);
+}
+
+// 9 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R>
+gmp_task_args_nm_9_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R* r) {
+  return new gmp_task_args_nm_9_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, r);
+}
+
+// 9 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
+gmp_task_args_m_9<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8) {
+  return new gmp_task_args_m_9<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8);
+}
+
+// 9 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename R>
+gmp_task_args_m_9_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, R* r) {
+  return new gmp_task_args_m_9_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, r);
+}
+
+// 10 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
+gmp_task_args_nm_10<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) {
+  return new gmp_task_args_nm_10<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+}
+
+// 10 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R>
+gmp_task_args_nm_10_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R* r) {
+  return new gmp_task_args_nm_10_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, r);
+}
+
+// 10 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9>
+gmp_task_args_m_10<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9) {
+  return new gmp_task_args_m_10<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9);
+}
+
+// 10 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename R>
+gmp_task_args_m_10_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, R* r) {
+  return new gmp_task_args_m_10_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, r);
+}
+
+// 11 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
+gmp_task_args_nm_11<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) {
+  return new gmp_task_args_nm_11<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
+}
+
+// 11 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R>
+gmp_task_args_nm_11_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R* r) {
+  return new gmp_task_args_nm_11_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, r);
+}
+
+// 11 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10>
+gmp_task_args_m_11<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10) {
+  return new gmp_task_args_m_11<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10);
+}
+
+// 11 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename R>
+gmp_task_args_m_11_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, R* r) {
+  return new gmp_task_args_m_11_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, r);
+}
+
+// 12 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
+gmp_task_args_nm_12<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) {
+  return new gmp_task_args_nm_12<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+}
+
+// 12 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R>
+gmp_task_args_nm_12_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R* r) {
+  return new gmp_task_args_nm_12_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, r);
+}
+
+// 12 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11>
+gmp_task_args_m_12<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11) {
+  return new gmp_task_args_m_12<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
+}
+
+// 12 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename R>
+gmp_task_args_m_12_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, R* r) {
+  return new gmp_task_args_m_12_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, r);
+}
+
+// 13 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
+gmp_task_args_nm_13<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) {
+  return new gmp_task_args_nm_13<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
+}
+
+// 13 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R>
+gmp_task_args_nm_13_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R* r) {
+  return new gmp_task_args_nm_13_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, r);
+}
+
+// 13 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12>
+gmp_task_args_m_13<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12) {
+  return new gmp_task_args_m_13<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12);
+}
+
+// 13 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename R>
+gmp_task_args_m_13_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, R* r) {
+  return new gmp_task_args_m_13_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, r);
+}
+
+// 14 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
+gmp_task_args_nm_14<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>* WrapTaskNM(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) {
+  return new gmp_task_args_nm_14<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13);
+}
+
+// 14 arguments --
+template<typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R>
+gmp_task_args_nm_14_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>* WrapTaskNMRet(M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R* r) {
+  return new gmp_task_args_nm_14_ret<M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>
+    (m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, r);
+}
+
+// 14 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13>
+gmp_task_args_m_14<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>* WrapTask(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13) {
+  return new gmp_task_args_m_14<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13);
+}
+
+// 14 arguments --
+template<typename C, typename M, typename A0, typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8, typename A9, typename A10, typename A11, typename A12, typename A13, typename R>
+gmp_task_args_m_14_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>* WrapTaskRet(C o, M m, A0 a0, A1 a1, A2 a2, A3 a3, A4 a4, A5 a5, A6 a6, A7 a7, A8 a8, A9 a9, A10 a10, A11 a11, A12 a12, A13 a13, R* r) {
+  return new gmp_task_args_m_14_ret<C, M, A0, A1, A2, A3, A4, A5, A6, A7, A8, A9, A10, A11, A12, A13, R>
+    (o, m, a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, r);
+}
+
new file mode 100644
--- /dev/null
+++ b/media/gmp-clearkey/0.1/gmp-task-utils.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* 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/. */
+
+// Original author: ekr@rtfm.com
+
+#ifndef gmp_task_utils_h_
+#define gmp_task_utils_h_
+
+#include "gmp-platform.h"
+
+class gmp_task_args_base : public GMPTask {
+public:
+  virtual void Destroy() { delete this; }
+  virtual void Run() = 0;
+};
+
+// The generated file contains four major function templates
+// (in variants for arbitrary numbers of arguments up to 10,
+// which is why it is machine generated). The four templates
+// are:
+//
+// WrapTask(o, m, ...) -- wraps a member function m of an object ptr o
+// WrapTaskRet(o, m, ..., r) -- wraps a member function m of an object ptr o
+//                                  the function returns something that can
+//                                  be assigned to *r
+// WrapTaskNM(f, ...) -- wraps a function f
+// WrapTaskNMRet(f, ..., r) -- wraps a function f that returns something
+//                                 that can be assigned to *r
+//
+// All of these template functions return a GMPTask* which can be passed
+// to DispatchXX().
+#include "gmp-task-utils-generated.h"
+
+#endif // gmp_task_utils_h_
--- a/media/gmp-clearkey/0.1/moz.build
+++ b/media/gmp-clearkey/0.1/moz.build
@@ -5,17 +5,19 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 SharedLibrary('clearkey')
 
 FINAL_TARGET = 'dist/bin/gmp-clearkey/0.1'
 
 UNIFIED_SOURCES += [
     'ClearKeyDecryptionManager.cpp',
+    'ClearKeyPersistence.cpp',
     'ClearKeySession.cpp',
+    'ClearKeyStorage.cpp',
     'ClearKeyUtils.cpp',
     'gmp-clearkey.cpp',
     'openaes/oaes_lib.c',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/media/gmp',
 ]