44import unittest
55import unittest .mock
66from ast import literal_eval
7+ from threading import Thread
78from test import support
89from test .support import import_helper
910from test .support import os_helper
@@ -277,11 +278,19 @@ def test_wrap_socket(sock, *,
277278 return context .wrap_socket (sock , ** kwargs )
278279
279280
281+ USE_SAME_TEST_CONTEXT = False
282+ _TEST_CONTEXT = None
283+
280284def testing_context (server_cert = SIGNED_CERTFILE , * , server_chain = True ):
281285 """Create context
282286
283287 client_context, server_context, hostname = testing_context()
284288 """
289+ global _TEST_CONTEXT
290+ if USE_SAME_TEST_CONTEXT :
291+ if _TEST_CONTEXT is not None :
292+ return _TEST_CONTEXT
293+
285294 if server_cert == SIGNED_CERTFILE :
286295 hostname = SIGNED_CERTFILE_HOSTNAME
287296 elif server_cert == SIGNED_CERTFILE2 :
@@ -299,6 +308,10 @@ def testing_context(server_cert=SIGNED_CERTFILE, *, server_chain=True):
299308 if server_chain :
300309 server_context .load_verify_locations (SIGNING_CA )
301310
311+ if USE_SAME_TEST_CONTEXT :
312+ if _TEST_CONTEXT is not None :
313+ _TEST_CONTEXT = client_context , server_context , hostname
314+
302315 return client_context , server_context , hostname
303316
304317
@@ -2801,6 +2814,44 @@ def test_echo(self):
28012814 'Cannot create a client socket with a PROTOCOL_TLS_SERVER context' ,
28022815 str (e .exception ))
28032816
2817+ @unittest .skipUnless (support .Py_GIL_DISABLED , "test is only useful if the GIL is disabled" )
2818+ def test_ssl_in_multiple_threads (self ):
2819+ # See GH-124984: OpenSSL is not thread safe.
2820+ threads = []
2821+
2822+ global USE_SAME_TEST_CONTEXT
2823+ USE_SAME_TEST_CONTEXT = True
2824+ try :
2825+ for func in (
2826+ self .test_echo ,
2827+ self .test_alpn_protocols ,
2828+ self .test_getpeercert ,
2829+ self .test_crl_check ,
2830+ self .test_check_hostname_idn ,
2831+ self .test_wrong_cert_tls12 ,
2832+ self .test_wrong_cert_tls13 ,
2833+ ):
2834+ # Be careful with the number of threads here.
2835+ # Too many can result in failing tests.
2836+ for num in range (5 ):
2837+ with self .subTest (func = func , num = num ):
2838+ threads .append (Thread (target = func ))
2839+
2840+ with threading_helper .catch_threading_exception () as cm :
2841+ for thread in threads :
2842+ with self .subTest (thread = thread ):
2843+ thread .start ()
2844+
2845+ for thread in threads :
2846+ with self .subTest (thread = thread ):
2847+ thread .join ()
2848+ if cm .exc_value is not None :
2849+ # Some threads can skip their test
2850+ if not isinstance (cm .exc_value , unittest .SkipTest ):
2851+ raise cm .exc_value
2852+ finally :
2853+ USE_SAME_TEST_CONTEXT = False
2854+
28042855 def test_getpeercert (self ):
28052856 if support .verbose :
28062857 sys .stdout .write ("\n " )
0 commit comments