tls: only do off-thread certificate loading on loading tls · nodejs/node@f5d3f91
@@ -814,23 +814,6 @@ static std::vector<X509*>& GetSystemStoreCACertificates() {
814814return system_store_certs;
815815}
816816817-static void LoadSystemCACertificates(void* data) {
818-GetSystemStoreCACertificates();
819-}
820-821-static uv_thread_t system_ca_thread;
822-static bool system_ca_thread_started = false;
823-int LoadSystemCACertificatesOffThread() {
824-// This is only run once during the initialization of the process, so
825-// it is safe to use a static thread here.
826-int r =
827-uv_thread_create(&system_ca_thread, LoadSystemCACertificates, nullptr);
828-if (r == 0) {
829- system_ca_thread_started = true;
830- }
831-return r;
832-}
833-834817static std::vector<X509*> InitializeExtraCACertificates() {
835818 std::vector<X509*> extra_certs;
836819unsigned long err = LoadCertsFromFile( // NOLINT(runtime/int)
@@ -854,6 +837,53 @@ static std::vector<X509*>& GetExtraCACertificates() {
854837return extra_certs;
855838}
856839840+static void LoadCACertificates(void* data) {
841+per_process::Debug(DebugCategory::CRYPTO,
842+"Started loading system root certificates off-thread\n");
843+GetSystemStoreCACertificates();
844+}
845+846+static std::atomic<bool> tried_cert_loading_off_thread = false;
847+static std::atomic<bool> cert_loading_thread_started = false;
848+static Mutex start_cert_loading_thread_mutex;
849+static uv_thread_t cert_loading_thread;
850+851+void StartLoadingCertificatesOffThread(
852+const FunctionCallbackInfo<Value>& args) {
853+// Load the CA certificates eagerly off the main thread to avoid
854+// blocking the main thread when the first TLS connection is made. We
855+// don't need to wait for the thread to finish with code here, as
856+// Get*CACertificates() functions has a function-local static and any
857+// actual user of it will wait for that to complete initialization.
858+859+ {
860+ Mutex::ScopedLock cli_lock(node::per_process::cli_options_mutex);
861+if (!per_process::cli_options->use_system_ca) {
862+return;
863+ }
864+ }
865+866+// Only try to start the thread once. If it ever fails, we won't try again.
867+if (tried_cert_loading_off_thread.load()) {
868+return;
869+ }
870+ {
871+ Mutex::ScopedLock lock(start_cert_loading_thread_mutex);
872+// Re-check under the lock.
873+if (tried_cert_loading_off_thread.load()) {
874+return;
875+ }
876+ tried_cert_loading_off_thread.store(true);
877+int r = uv_thread_create(&cert_loading_thread, LoadCACertificates, nullptr);
878+ cert_loading_thread_started.store(r == 0);
879+if (r != 0) {
880+FPrintF(stderr,
881+"Warning: Failed to load CA certificates off thread: %s\n",
882+uv_strerror(r));
883+ }
884+ }
885+}
886+857887// Due to historical reasons the various options of CA certificates
858888// may invalid one another. The current rule is:
859889// 1. If the configure-time option --openssl-use-def-ca-store is NOT used
@@ -942,9 +972,12 @@ void CleanupCachedRootCertificates() {
942972X509_free(cert);
943973 }
944974 }
945-if (system_ca_thread_started) {
946-uv_thread_join(&system_ca_thread);
947- system_ca_thread_started = false;
975+976+// Serialize with starter to avoid the race window.
977+ Mutex::ScopedLock lock(start_cert_loading_thread_mutex);
978+if (tried_cert_loading_off_thread.load() &&
979+ cert_loading_thread_started.load()) {
980+uv_thread_join(&cert_loading_thread);
948981 }
949982}
950983@@ -1233,6 +1266,10 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
12331266SetMethod(context, target, "resetRootCertStore", ResetRootCertStore);
12341267SetMethodNoSideEffect(
12351268 context, target, "getUserRootCertificates", GetUserRootCertificates);
1269+SetMethod(context,
1270+ target,
1271+"startLoadingCertificatesOffThread",
1272+ StartLoadingCertificatesOffThread);
12361273}
1237127412381275void SecureContext::RegisterExternalReferences(
@@ -1277,6 +1314,7 @@ void SecureContext::RegisterExternalReferences(
12771314 registry->Register(GetExtraCACertificates);
12781315 registry->Register(ResetRootCertStore);
12791316 registry->Register(GetUserRootCertificates);
1317+ registry->Register(StartLoadingCertificatesOffThread);
12801318}
1281131912821320SecureContext* SecureContext::Create(Environment* env) {