bug 1555854 - avoid creating transient threads in PSM (particularly CryptoTask) r=KevinJacobs

CryptoTask is a helper class that makes it easier to implement code that runs on
a background thread and then notifies completion on the main thread (this is
useful for not blocking the main thread with long-running cryptography or I/O).
Before this patch, each CryptoTask would create a new thread each time it ran,
which was inefficient. This patch updates CryptoTask to use the stream transport
service (which is essentially a pool of threads for doing exactly these kinds of
things and notably is not to be confused with the socket transport service) to
run each task. Additionally, there were a few places in PSM where we
unnecessarily created new threads to perform similar tasks. These now use the
stream transport service as well.

Differential Revision: https://phabricator.services.mozilla.com/D33534

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Dana Keeler 2019-06-03 23:47:48 +00:00
parent 73edc6621b
commit 098bc1f91c
9 changed files with 45 additions and 76 deletions

View file

@ -1368,5 +1368,5 @@ nsNSSCertificateDB::OpenSignedAppFileAsync(
SignaturePolicy policy(policyInt);
RefPtr<OpenSignedAppFileTask> task(
new OpenSignedAppFileTask(aTrustedRoot, aJarFile, policy, aCallback));
return task->Dispatch("SignedJAR");
return task->Dispatch();
}

View file

@ -17,6 +17,7 @@
#include "mozilla/dom/Promise.h"
#include "nsCOMPtr.h"
#include "nsPromiseFlatString.h"
#include "nsProxyRelease.h"
#include "nsSecurityHeaderParser.h"
#include "nsWhitespaceTokenizer.h"
#include "mozpkix/pkix.h"
@ -50,7 +51,8 @@ class VerifyContentSignatureTask : public CryptoTask {
mCertChain(aCertChain),
mHostname(aHostname),
mSignatureVerified(false),
mPromise(aPromise) {}
mPromise(new nsMainThreadPtrHolder<Promise>(
"VerifyContentSignatureTask::mPromise", aPromise)) {}
private:
virtual nsresult CalculateResult() override;
@ -61,7 +63,7 @@ class VerifyContentSignatureTask : public CryptoTask {
nsCString mCertChain;
nsCString mHostname;
bool mSignatureVerified;
RefPtr<Promise> mPromise;
nsMainThreadPtrHandle<Promise> mPromise;
};
NS_IMETHODIMP
@ -84,7 +86,7 @@ ContentSignatureVerifier::AsyncVerifyContentSignature(
RefPtr<VerifyContentSignatureTask> task(new VerifyContentSignatureTask(
aData, aCSHeader, aCertChain, aHostname, promise));
nsresult rv = task->Dispatch("ContentSig");
nsresult rv = task->Dispatch();
if (NS_FAILED(rv)) {
return rv;
}

View file

@ -6,28 +6,26 @@
#include "CryptoTask.h"
#include "nsNSSComponent.h"
#include "nsNetCID.h"
namespace mozilla {
nsresult CryptoTask::Dispatch(const nsACString& taskThreadName) {
MOZ_ASSERT(taskThreadName.Length() <= 15);
nsresult CryptoTask::Dispatch() {
// Ensure that NSS is initialized, since presumably CalculateResult
// will use NSS functions
if (!EnsureNSSInitializedChromeOrContent()) {
return NS_ERROR_FAILURE;
}
// Can't add 'this' as the event to run, since mThread may not be set yet
nsresult rv =
NS_NewNamedThread(taskThreadName, getter_AddRefs(mThread), nullptr,
nsIThreadManager::DEFAULT_STACK_SIZE);
if (NS_FAILED(rv)) {
return rv;
// The stream transport service (note: not the socket transport service) can
// be used to perform background tasks or I/O that would otherwise block the
// main thread.
nsCOMPtr<nsIEventTarget> target(
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
if (!target) {
return NS_ERROR_FAILURE;
}
// Note: event must not null out mThread!
return mThread->Dispatch(this, NS_DISPATCH_NORMAL);
return target->Dispatch(this, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
@ -37,21 +35,8 @@ CryptoTask::Run() {
NS_DispatchToMainThread(this);
} else {
// back on the main thread
CallCallback(mRv);
// Not all uses of CryptoTask use a transient thread
if (mThread) {
// Don't leak threads!
mThread->Shutdown(); // can't Shutdown from the thread itself, darn
// Don't null out mThread!
// See bug 999104. We must hold a ref to the thread across Dispatch()
// since the internal mThread ref could be released while processing
// the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
// assumes the caller does.
}
}
return NS_OK;
}

View file

@ -23,13 +23,7 @@ namespace mozilla {
*/
class CryptoTask : public Runnable {
public:
template <size_t LEN>
nsresult Dispatch(const char (&taskThreadName)[LEN]) {
static_assert(LEN <= 15, "Thread name must be no more than 15 characters");
return Dispatch(nsDependentCString(taskThreadName, LEN - 1));
}
nsresult Dispatch(const nsACString& taskThreadName);
nsresult Dispatch();
protected:
CryptoTask() : Runnable("CryptoTask"), mRv(NS_ERROR_NOT_INITIALIZED) {}
@ -51,7 +45,6 @@ class CryptoTask : public Runnable {
NS_IMETHOD Run() final;
nsresult mRv;
nsCOMPtr<nsIThread> mThread;
};
} // namespace mozilla

View file

@ -402,7 +402,7 @@ LocalCertService::GetOrCreateCert(const nsACString& aNickname,
}
RefPtr<LocalCertGetTask> task(new LocalCertGetTask(aNickname, aCallback));
return task->Dispatch("LocalCertGet");
return task->Dispatch();
}
NS_IMETHODIMP
@ -424,7 +424,7 @@ LocalCertService::RemoveCert(const nsACString& aNickname,
RefPtr<LocalCertRemoveTask> task(
new LocalCertRemoveTask(aNickname, aCallback));
return task->Dispatch("LocalCertRm");
return task->Dispatch();
}
NS_IMETHODIMP

View file

@ -7,6 +7,7 @@
#include "OSReauthenticator.h"
#include "OSKeyStore.h"
#include "nsNetCID.h"
NS_IMPL_ISUPPORTS(OSReauthenticator, nsIOSReauthenticator)
@ -240,9 +241,12 @@ OSReauthenticator::AsyncReauthenticateUser(const nsACString& aPrompt,
BackgroundReauthenticateUser(promiseHandle, aPrompt);
}));
nsCOMPtr<nsIThread> thread;
rv = NS_NewNamedThread(NS_LITERAL_CSTRING("ReauthenticateUserThread"),
getter_AddRefs(thread), runnable);
nsCOMPtr<nsIEventTarget> target(
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
if (!target) {
return NS_ERROR_FAILURE;
}
rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View file

@ -20,6 +20,7 @@
#include "nsITokenPasswordDialogs.h"
#include "nsNSSComponent.h"
#include "nsNSSHelper.h"
#include "nsNetCID.h"
#include "nsPK11TokenDB.h"
#include "pk11func.h"
#include "pk11sdr.h" // For PK11SDR_Encrypt, PK11SDR_Decrypt
@ -170,9 +171,12 @@ SecretDecoderRing::AsyncEncryptStrings(uint32_t plaintextsCount,
BackgroundSdrEncryptStrings(plaintextsUtf8, promise);
}));
nsCOMPtr<nsIThread> encryptionThread;
nsresult rv = NS_NewNamedThread("AsyncSDRThread",
getter_AddRefs(encryptionThread), runnable);
nsCOMPtr<nsIEventTarget> target(
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
if (!target) {
return NS_ERROR_FAILURE;
}
nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}

View file

@ -1223,7 +1223,7 @@ nsNSSCertificateDB::AsyncVerifyCertAtTime(
nsICertVerificationCallback* aCallback) {
RefPtr<VerifyCertAtTimeTask> task(new VerifyCertAtTimeTask(
aCert, aUsage, aFlags, aHostname, aTime, aCallback));
return task->Dispatch("VerifyCert");
return task->Dispatch();
}
NS_IMETHODIMP

View file

@ -44,6 +44,7 @@
#include "nsLiteralString.h"
#include "nsNSSCertificateDB.h"
#include "nsNSSHelper.h"
#include "nsNetCID.h"
#include "nsPK11TokenDB.h"
#include "nsPrintfCString.h"
#include "nsServiceManagerUtils.h"
@ -529,7 +530,7 @@ void nsNSSComponent::MaybeImportEnterpriseRoots() {
if (importEnterpriseRoots) {
RefPtr<BackgroundImportEnterpriseCertsTask> task =
new BackgroundImportEnterpriseCertsTask(this);
Unused << task->Dispatch("EnterpriseCrts");
Unused << task->Dispatch();
}
}
@ -611,40 +612,22 @@ class LoadLoadableRootsTask final : public Runnable {
bool mImportEnterpriseRoots;
uint32_t mFamilySafetyMode;
Vector<nsCString> mPossibleLoadableRootsLocations;
nsCOMPtr<nsIThread> mThread;
};
nsresult LoadLoadableRootsTask::Dispatch() {
// Can't add 'this' as the event to run, since mThread may not be set yet
nsresult rv = NS_NewNamedThread("LoadRoots", getter_AddRefs(mThread), nullptr,
nsIThreadManager::DEFAULT_STACK_SIZE);
if (NS_FAILED(rv)) {
return rv;
// The stream transport service (note: not the socket transport service) can
// be used to perform background tasks or I/O that would otherwise block the
// main thread.
nsCOMPtr<nsIEventTarget> target(
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID));
if (!target) {
return NS_ERROR_FAILURE;
}
// Note: event must not null out mThread!
return mThread->Dispatch(this, NS_DISPATCH_NORMAL);
return target->Dispatch(this, NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
LoadLoadableRootsTask::Run() {
// First we Run() on the "LoadRoots" thread, do our work, and then we Run()
// again on the main thread so we can shut down the thread (since we don't
// need it any more). We can't shut down the thread while we're *on* the
// thread, which is why we do the dispatch to the main thread. CryptoTask.cpp
// (which informs this code) says that we can't null out mThread. This appears
// to be because its refcount could be decreased while this dispatch is being
// processed, so it might get prematurely destroyed. I'm not sure this makes
// sense but it'll get cleaned up in our destructor anyway, so it's fine to
// not null it out here (as long as we don't run twice on the main thread,
// which shouldn't be possible).
if (NS_IsMainThread()) {
if (mThread) {
mThread->Shutdown();
}
return NS_OK;
}
nsresult loadLoadableRootsResult = LoadLoadableRoots();
if (NS_WARN_IF(NS_FAILED(loadLoadableRootsResult))) {
MOZ_LOG(gPIPNSSLog, LogLevel::Error, ("LoadLoadableRoots failed"));
@ -686,9 +669,7 @@ LoadLoadableRootsTask::Run() {
("failed to notify loadable roots loaded monitor"));
}
}
// Go back to the main thread to clean up this worker thread.
return NS_DispatchToMainThread(this);
return NS_OK;
}
NS_IMETHODIMP