1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/dom/WebCryptoThreadPool.h"
8 
9 #include "MainThreadUtils.h"
10 #include "mozilla/Services.h"
11 #include "mozilla/StaticPtr.h"
12 #include "nsComponentManagerUtils.h"
13 #include "nsNSSComponent.h"
14 #include "nsXPCOMCIDInternal.h"
15 #include "nsXPCOMPrivate.h"
16 #include "nsIObserverService.h"
17 #include "nsIThreadPool.h"
18 
19 namespace mozilla {
20 namespace dom {
21 
22 StaticRefPtr<WebCryptoThreadPool> gInstance;
23 
NS_IMPL_ISUPPORTS(WebCryptoThreadPool,nsIObserver)24 NS_IMPL_ISUPPORTS(WebCryptoThreadPool, nsIObserver)
25 
26 /* static */ void WebCryptoThreadPool::Initialize() {
27   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
28   MOZ_ASSERT(!gInstance, "More than one instance!");
29 
30   gInstance = new WebCryptoThreadPool();
31   NS_WARNING_ASSERTION(gInstance, "Failed create thread pool!");
32 
33   if (gInstance && NS_FAILED(gInstance->Init())) {
34     NS_WARNING("Failed to initialize thread pool!");
35     gInstance = nullptr;
36   }
37 }
38 
Dispatch(nsIRunnable * aRunnable)39 /* static */ nsresult WebCryptoThreadPool::Dispatch(nsIRunnable* aRunnable) {
40   if (gInstance) {
41     return gInstance->DispatchInternal(aRunnable);
42   }
43 
44   // Fail if called on shutdown.
45   return NS_ERROR_FAILURE;
46 }
47 
Init()48 nsresult WebCryptoThreadPool::Init() {
49   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
50 
51   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
52   NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
53 
54   // Need this observer to know when to shut down the thread pool.
55   return obs->AddObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
56 }
57 
DispatchInternal(nsIRunnable * aRunnable)58 nsresult WebCryptoThreadPool::DispatchInternal(nsIRunnable* aRunnable) {
59   MutexAutoLock lock(mMutex);
60 
61   if (mShutdown) {
62     return NS_ERROR_FAILURE;
63   }
64 
65   if (!mPool) {
66     NS_ENSURE_TRUE(EnsureNSSInitializedChromeOrContent(), NS_ERROR_FAILURE);
67 
68     nsCOMPtr<nsIThreadPool> pool(do_CreateInstance(NS_THREADPOOL_CONTRACTID));
69     NS_ENSURE_TRUE(pool, NS_ERROR_FAILURE);
70 
71     nsresult rv = pool->SetName(NS_LITERAL_CSTRING("SubtleCrypto"));
72     NS_ENSURE_SUCCESS(rv, rv);
73 
74     pool.swap(mPool);
75   }
76 
77   return mPool->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
78 }
79 
Shutdown()80 void WebCryptoThreadPool::Shutdown() {
81   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
82 
83   // Limit the scope of locking to avoid deadlocking if DispatchInternal ends
84   // up getting called during shutdown event processing.
85   nsCOMPtr<nsIThreadPool> pool;
86   {
87     MutexAutoLock lock(mMutex);
88     if (mShutdown) {
89       return;
90     }
91     pool = mPool;
92     mShutdown = true;
93   }
94 
95   if (pool) {
96     pool->Shutdown();
97   }
98 
99   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
100   NS_WARNING_ASSERTION(obs, "Failed to retrieve observer service!");
101 
102   if (obs) {
103     if (NS_FAILED(
104             obs->RemoveObserver(this, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID))) {
105       NS_WARNING("Failed to remove shutdown observer!");
106     }
107   }
108 }
109 
110 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)111 WebCryptoThreadPool::Observe(nsISupports* aSubject, const char* aTopic,
112                              const char16_t* aData) {
113   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
114 
115   if (gInstance) {
116     gInstance->Shutdown();
117     gInstance = nullptr;
118   }
119 
120   return NS_OK;
121 }
122 
123 }  // namespace dom
124 }  // namespace mozilla
125