@@ -814,23 +814,6 @@ static std::vector<X509*>& GetSystemStoreCACertificates() {
814
814
return system_store_certs;
815
815
}
816
816
817
- 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
-
834
817
static std::vector<X509*> InitializeExtraCACertificates () {
835
818
std::vector<X509*> extra_certs;
836
819
unsigned long err = LoadCertsFromFile ( // NOLINT(runtime/int)
@@ -854,6 +837,53 @@ static std::vector<X509*>& GetExtraCACertificates() {
854
837
return extra_certs;
855
838
}
856
839
840
+ 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
+
857
887
// Due to historical reasons the various options of CA certificates
858
888
// may invalid one another. The current rule is:
859
889
// 1. If the configure-time option --openssl-use-def-ca-store is NOT used
@@ -942,9 +972,12 @@ void CleanupCachedRootCertificates() {
942
972
X509_free (cert);
943
973
}
944
974
}
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);
948
981
}
949
982
}
950
983
@@ -1233,6 +1266,10 @@ void SecureContext::Initialize(Environment* env, Local<Object> target) {
1233
1266
SetMethod (context, target, " resetRootCertStore" , ResetRootCertStore);
1234
1267
SetMethodNoSideEffect (
1235
1268
context, target, " getUserRootCertificates" , GetUserRootCertificates);
1269
+ SetMethod (context,
1270
+ target,
1271
+ " startLoadingCertificatesOffThread" ,
1272
+ StartLoadingCertificatesOffThread);
1236
1273
}
1237
1274
1238
1275
void SecureContext::RegisterExternalReferences (
@@ -1277,6 +1314,7 @@ void SecureContext::RegisterExternalReferences(
1277
1314
registry->Register (GetExtraCACertificates);
1278
1315
registry->Register (ResetRootCertStore);
1279
1316
registry->Register (GetUserRootCertificates);
1317
+ registry->Register (StartLoadingCertificatesOffThread);
1280
1318
}
1281
1319
1282
1320
SecureContext* SecureContext::Create (Environment* env) {
0 commit comments