]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-111916: Make hashlib related modules thread-safe without the GIL (#111981)
authorTomas R <tomas.roun8@gmail.com>
Wed, 15 Nov 2023 23:53:38 +0000 (00:53 +0100)
committerGitHub <noreply@github.com>
Wed, 15 Nov 2023 23:53:38 +0000 (23:53 +0000)
Always use an individual lock on hash objects when in free-threaded builds.

Fixes #111916

Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst [new file with mode: 0644]
Modules/_blake2/blake2b_impl.c
Modules/_blake2/blake2s_impl.c
Modules/_hashopenssl.c
Modules/clinic/md5module.c.h
Modules/hashlib.h
Modules/md5module.c
Modules/sha1module.c
Modules/sha2module.c
Modules/sha3module.c

diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst b/Misc/NEWS.d/next/Core and Builtins/2023-11-14-22-12-11.gh-issue-111916.ZGCayL.rst
new file mode 100644 (file)
index 0000000..b381482
--- /dev/null
@@ -0,0 +1 @@
+Make hashlib related modules thread-safe without the GIL
index c2cac98c7529eb5cc62fc39931381511d825e4e7..0c3ae5a2fac2751af822ad60ff03fb30d9da1944 100644 (file)
@@ -17,6 +17,7 @@
 #  define Py_BUILD_CORE_MODULE 1
 #endif
 
+#include <stdbool.h>
 #include "Python.h"
 #include "pycore_strhex.h"       // _Py_strhex()
 
@@ -42,7 +43,8 @@ typedef struct {
     PyObject_HEAD
     blake2b_param    param;
     blake2b_state    state;
-    PyThread_type_lock lock;
+    bool use_mutex;
+    PyMutex mutex;
 } BLAKE2bObject;
 
 #include "clinic/blake2b_impl.c.h"
@@ -59,9 +61,11 @@ new_BLAKE2bObject(PyTypeObject *type)
 {
     BLAKE2bObject *self;
     self = (BLAKE2bObject *)type->tp_alloc(type, 0);
-    if (self != NULL) {
-        self->lock = NULL;
+    if (self == NULL) {
+        return NULL;
     }
+    HASHLIB_INIT_MUTEX(self);
+
     return self;
 }
 
@@ -278,18 +282,19 @@ _blake2_blake2b_update(BLAKE2bObject *self, PyObject *data)
 
     GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
 
-    if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
-        self->lock = PyThread_allocate_lock();
-
-    if (self->lock != NULL) {
-       Py_BEGIN_ALLOW_THREADS
-       PyThread_acquire_lock(self->lock, 1);
-       blake2b_update(&self->state, buf.buf, buf.len);
-       PyThread_release_lock(self->lock);
-       Py_END_ALLOW_THREADS
+    if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
+    }
+    if (self->use_mutex) {
+        Py_BEGIN_ALLOW_THREADS
+        PyMutex_Lock(&self->mutex);
+        blake2b_update(&self->state, buf.buf, buf.len);
+        PyMutex_Unlock(&self->mutex);
+        Py_END_ALLOW_THREADS
     } else {
         blake2b_update(&self->state, buf.buf, buf.len);
     }
+
     PyBuffer_Release(&buf);
 
     Py_RETURN_NONE;
@@ -389,10 +394,6 @@ py_blake2b_dealloc(PyObject *self)
     /* Try not to leave state in memory. */
     secure_zero_memory(&obj->param, sizeof(obj->param));
     secure_zero_memory(&obj->state, sizeof(obj->state));
-    if (obj->lock) {
-        PyThread_free_lock(obj->lock);
-        obj->lock = NULL;
-    }
 
     PyTypeObject *type = Py_TYPE(self);
     PyObject_Free(self);
index 1c47328ece13e84c2178d3b07b38aa09991c2268..3014773ab5233156c10dd66b8f0738181b38bf66 100644 (file)
@@ -17,6 +17,7 @@
 #  define Py_BUILD_CORE_MODULE 1
 #endif
 
+#include <stdbool.h>
 #include "Python.h"
 #include "pycore_strhex.h"       // _Py_strhex()
 
@@ -42,7 +43,8 @@ typedef struct {
     PyObject_HEAD
     blake2s_param    param;
     blake2s_state    state;
-    PyThread_type_lock lock;
+    bool use_mutex;
+    PyMutex mutex;
 } BLAKE2sObject;
 
 #include "clinic/blake2s_impl.c.h"
@@ -59,9 +61,11 @@ new_BLAKE2sObject(PyTypeObject *type)
 {
     BLAKE2sObject *self;
     self = (BLAKE2sObject *)type->tp_alloc(type, 0);
-    if (self != NULL) {
-        self->lock = NULL;
+    if (self == NULL) {
+        return NULL;
     }
+    HASHLIB_INIT_MUTEX(self);
+
     return self;
 }
 
@@ -278,18 +282,19 @@ _blake2_blake2s_update(BLAKE2sObject *self, PyObject *data)
 
     GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
 
-    if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE)
-        self->lock = PyThread_allocate_lock();
-
-    if (self->lock != NULL) {
-       Py_BEGIN_ALLOW_THREADS
-       PyThread_acquire_lock(self->lock, 1);
-       blake2s_update(&self->state, buf.buf, buf.len);
-       PyThread_release_lock(self->lock);
-       Py_END_ALLOW_THREADS
+    if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
+    }
+    if (self->use_mutex) {
+        Py_BEGIN_ALLOW_THREADS
+        PyMutex_Lock(&self->mutex);
+        blake2s_update(&self->state, buf.buf, buf.len);
+        PyMutex_Unlock(&self->mutex);
+        Py_END_ALLOW_THREADS
     } else {
         blake2s_update(&self->state, buf.buf, buf.len);
     }
+
     PyBuffer_Release(&buf);
 
     Py_RETURN_NONE;
@@ -389,10 +394,6 @@ py_blake2s_dealloc(PyObject *self)
     /* Try not to leave state in memory. */
     secure_zero_memory(&obj->param, sizeof(obj->param));
     secure_zero_memory(&obj->state, sizeof(obj->state));
-    if (obj->lock) {
-        PyThread_free_lock(obj->lock);
-        obj->lock = NULL;
-    }
 
     PyTypeObject *type = Py_TYPE(self);
     PyObject_Free(self);
index ee6fb8b4b03643557c736a82b3e9378cfd69ab17..0e230f332ff6cb091dea44167308a4a6034a42b3 100644 (file)
@@ -22,6 +22,7 @@
 #  define Py_BUILD_CORE_MODULE 1
 #endif
 
+#include <stdbool.h>
 #include "Python.h"
 #include "pycore_hashtable.h"
 #include "pycore_pyhash.h"        // _Py_HashBytes()
@@ -227,16 +228,16 @@ typedef struct {
     PyObject_HEAD
     EVP_MD_CTX          *ctx;   /* OpenSSL message digest context */
     // Prevents undefined behavior via multiple threads entering the C API.
-    // The lock will be NULL before threaded access has been enabled.
-    PyThread_type_lock   lock;  /* OpenSSL context lock */
+    bool use_mutex;
+    PyMutex mutex;  /* OpenSSL context lock */
 } EVPobject;
 
 typedef struct {
     PyObject_HEAD
     HMAC_CTX *ctx;            /* OpenSSL hmac context */
     // Prevents undefined behavior via multiple threads entering the C API.
-    // The lock will be NULL before threaded access has been enabled.
-    PyThread_type_lock lock;  /* HMAC context lock */
+    bool use_mutex;
+    PyMutex mutex;  /* HMAC context lock */
 } HMACobject;
 
 #include "clinic/_hashopenssl.c.h"
@@ -414,8 +415,7 @@ newEVPobject(PyTypeObject *type)
     if (retval == NULL) {
         return NULL;
     }
-
-    retval->lock = NULL;
+    HASHLIB_INIT_MUTEX(retval);
 
     retval->ctx = EVP_MD_CTX_new();
     if (retval->ctx == NULL) {
@@ -453,8 +453,6 @@ static void
 EVP_dealloc(EVPobject *self)
 {
     PyTypeObject *tp = Py_TYPE(self);
-    if (self->lock != NULL)
-        PyThread_free_lock(self->lock);
     EVP_MD_CTX_free(self->ctx);
     PyObject_Free(self);
     Py_DECREF(tp);
@@ -582,16 +580,14 @@ EVP_update(EVPobject *self, PyObject *obj)
 
     GET_BUFFER_VIEW_OR_ERROUT(obj, &view);
 
-    if (self->lock == NULL && view.len >= HASHLIB_GIL_MINSIZE) {
-        self->lock = PyThread_allocate_lock();
-        /* fail? lock = NULL and we fail over to non-threaded code. */
+    if (!self->use_mutex && view.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
     }
-
-    if (self->lock != NULL) {
+    if (self->use_mutex) {
         Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->lock, 1);
+        PyMutex_Lock(&self->mutex);
         result = EVP_hash(self, view.buf, view.len);
-        PyThread_release_lock(self->lock);
+        PyMutex_Unlock(&self->mutex);
         Py_END_ALLOW_THREADS
     } else {
         result = EVP_hash(self, view.buf, view.len);
@@ -1540,7 +1536,7 @@ _hashlib_hmac_new_impl(PyObject *module, Py_buffer *key, PyObject *msg_obj,
     }
 
     self->ctx = ctx;
-    self->lock = NULL;
+    HASHLIB_INIT_MUTEX(self);
 
     if ((msg_obj != NULL) && (msg_obj != Py_None)) {
         if (!_hmac_update(self, msg_obj))
@@ -1582,16 +1578,14 @@ _hmac_update(HMACobject *self, PyObject *obj)
 
     GET_BUFFER_VIEW_OR_ERROR(obj, &view, return 0);
 
-    if (self->lock == NULL && view.len >= HASHLIB_GIL_MINSIZE) {
-        self->lock = PyThread_allocate_lock();
-        /* fail? lock = NULL and we fail over to non-threaded code. */
+    if (!self->use_mutex && view.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
     }
-
-    if (self->lock != NULL) {
+    if (self->use_mutex) {
         Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->lock, 1);
+        PyMutex_Lock(&self->mutex);
         r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len);
-        PyThread_release_lock(self->lock);
+        PyMutex_Unlock(&self->mutex);
         Py_END_ALLOW_THREADS
     } else {
         r = HMAC_Update(self->ctx, (const unsigned char*)view.buf, view.len);
@@ -1633,7 +1627,7 @@ _hashlib_HMAC_copy_impl(HMACobject *self)
         return NULL;
     }
     retval->ctx = ctx;
-    retval->lock = NULL;
+    HASHLIB_INIT_MUTEX(retval);
 
     return (PyObject *)retval;
 }
@@ -1642,9 +1636,6 @@ static void
 _hmac_dealloc(HMACobject *self)
 {
     PyTypeObject *tp = Py_TYPE(self);
-    if (self->lock != NULL) {
-        PyThread_free_lock(self->lock);
-    }
     HMAC_CTX_free(self->ctx);
     PyObject_Free(self);
     Py_DECREF(tp);
index 1d98c574929f6f78856a37bb51f65abb819dc12b..7d4d3108dab9b6ca7fec08b27c2759ce36b8a0da 100644 (file)
@@ -2,6 +2,12 @@
 preserve
 [clinic start generated code]*/
 
+#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+#  include "pycore_gc.h"          // PyGC_Head
+#  include "pycore_runtime.h"     // _Py_ID()
+#endif
+#include "pycore_modsupport.h"    // _PyArg_UnpackKeywords()
+
 PyDoc_STRVAR(MD5Type_copy__doc__,
 "copy($self, /)\n"
 "--\n"
@@ -9,7 +15,7 @@ PyDoc_STRVAR(MD5Type_copy__doc__,
 "Return a copy of the hash object.");
 
 #define MD5TYPE_COPY_METHODDEF    \
-    {"copy", (PyCFunction)(void(*)(void))MD5Type_copy, METH_METHOD|METH_FASTCALL|METH_KEYWORDS, MD5Type_copy__doc__},
+    {"copy", _PyCFunction_CAST(MD5Type_copy), METH_METHOD|METH_FASTCALL|METH_KEYWORDS, MD5Type_copy__doc__},
 
 static PyObject *
 MD5Type_copy_impl(MD5object *self, PyTypeObject *cls);
@@ -76,25 +82,70 @@ PyDoc_STRVAR(_md5_md5__doc__,
 "Return a new MD5 hash object; optionally initialized with a string.");
 
 #define _MD5_MD5_METHODDEF    \
-    {"md5", (PyCFunction)(void(*)(void))_md5_md5, METH_VARARGS|METH_KEYWORDS, _md5_md5__doc__},
+    {"md5", _PyCFunction_CAST(_md5_md5), METH_FASTCALL|METH_KEYWORDS, _md5_md5__doc__},
 
 static PyObject *
 _md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity);
 
 static PyObject *
-_md5_md5(PyObject *module, PyObject *args, PyObject *kwargs)
+_md5_md5(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
 {
     PyObject *return_value = NULL;
-    static char *_keywords[] = {"string", "usedforsecurity", NULL};
+    #if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)
+
+    #define NUM_KEYWORDS 2
+    static struct {
+        PyGC_Head _this_is_not_used;
+        PyObject_VAR_HEAD
+        PyObject *ob_item[NUM_KEYWORDS];
+    } _kwtuple = {
+        .ob_base = PyVarObject_HEAD_INIT(&PyTuple_Type, NUM_KEYWORDS)
+        .ob_item = { &_Py_ID(string), &_Py_ID(usedforsecurity), },
+    };
+    #undef NUM_KEYWORDS
+    #define KWTUPLE (&_kwtuple.ob_base.ob_base)
+
+    #else  // !Py_BUILD_CORE
+    #  define KWTUPLE NULL
+    #endif  // !Py_BUILD_CORE
+
+    static const char * const _keywords[] = {"string", "usedforsecurity", NULL};
+    static _PyArg_Parser _parser = {
+        .keywords = _keywords,
+        .fname = "md5",
+        .kwtuple = KWTUPLE,
+    };
+    #undef KWTUPLE
+    PyObject *argsbuf[2];
+    Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 0;
     PyObject *string = NULL;
     int usedforsecurity = 1;
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|O$p:md5", _keywords,
-        &string, &usedforsecurity))
+    args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 0, 1, 0, argsbuf);
+    if (!args) {
         goto exit;
+    }
+    if (!noptargs) {
+        goto skip_optional_pos;
+    }
+    if (args[0]) {
+        string = args[0];
+        if (!--noptargs) {
+            goto skip_optional_pos;
+        }
+    }
+skip_optional_pos:
+    if (!noptargs) {
+        goto skip_optional_kwonly;
+    }
+    usedforsecurity = PyObject_IsTrue(args[1]);
+    if (usedforsecurity < 0) {
+        goto exit;
+    }
+skip_optional_kwonly:
     return_value = _md5_md5_impl(module, string, usedforsecurity);
 
 exit:
     return return_value;
 }
-/*[clinic end generated code: output=81702ec915f36236 input=a9049054013a1b77]*/
+/*[clinic end generated code: output=bfadda44914804a8 input=a9049054013a1b77]*/
index a8bad9dd87a939657e9c6b45ecf3e3b47f1b5b66..25b2aaadfb2868846e4a320374bb0e2c86b0e101 100644 (file)
@@ -1,5 +1,7 @@
 /* Common code for use by all hashlib related modules. */
 
+#include "pycore_lock.h"        // PyMutex
+
 /*
  * Given a PyObject* obj, fill in the Py_buffer* viewp with the result
  * of PyObject_GetBuffer.  Sets an exception and issues the erraction
 
 #include "pythread.h"
 #define ENTER_HASHLIB(obj) \
-    if ((obj)->lock) { \
-        if (!PyThread_acquire_lock((obj)->lock, 0)) { \
-            Py_BEGIN_ALLOW_THREADS \
-            PyThread_acquire_lock((obj)->lock, 1); \
-            Py_END_ALLOW_THREADS \
-        } \
+    if ((obj)->use_mutex) { \
+        PyMutex_Lock(&(obj)->mutex); \
     }
 #define LEAVE_HASHLIB(obj) \
-    if ((obj)->lock) { \
-        PyThread_release_lock((obj)->lock); \
+    if ((obj)->use_mutex) { \
+        PyMutex_Unlock(&(obj)->mutex); \
     }
 
+#ifdef Py_NOGIL
+#define HASHLIB_INIT_MUTEX(obj) \
+    do { \
+        (obj)->mutex = (PyMutex){0}; \
+        (obj)->use_mutex = true; \
+    } while (0)
+#else
+#define HASHLIB_INIT_MUTEX(obj) \
+    do { \
+        (obj)->mutex = (PyMutex){0}; \
+        (obj)->use_mutex = false; \
+    } while (0)
+#endif
+
 /* TODO(gpshead): We should make this a module or class attribute
  * to allow the user to optimize based on the platform they're using. */
 #define HASHLIB_GIL_MINSIZE 2048
index 7883a8cbe6e246744a3be1581cdf208039f67c25..7d2b3275f213fd8eb23ef1b310cf8378742e3121 100644 (file)
 
 /* MD5 objects */
 
-#ifndef _MSC_VER
-#include "pyconfig.h"   // Py_NOGIL
-#endif
-
-#ifndef Py_NOGIL
-// Need limited C API version 3.12 for Py_MOD_PER_INTERPRETER_GIL_SUPPORTED
-#define Py_LIMITED_API 0x030c0000
+#ifndef Py_BUILD_CORE_BUILTIN
+#  define Py_BUILD_CORE_MODULE 1
 #endif
 
 #include "Python.h"
@@ -54,8 +49,8 @@ typedef long long MD5_INT64; /* 64-bit integer */
 typedef struct {
     PyObject_HEAD
     // Prevents undefined behavior via multiple threads entering the C API.
-    // The lock will be NULL before threaded access has been enabled.
-    PyThread_type_lock lock;
+    bool use_mutex;
+    PyMutex mutex;
     Hacl_Streaming_MD5_state *hash_state;
 } MD5object;
 
@@ -78,7 +73,11 @@ static MD5object *
 newMD5object(MD5State * st)
 {
     MD5object *md5 = (MD5object *)PyObject_GC_New(MD5object, st->md5_type);
-    md5->lock = NULL;
+    if (!md5) {
+        return NULL;
+    }
+    HASHLIB_INIT_MUTEX(md5);
+
     PyObject_GC_Track(md5);
     return md5;
 }
@@ -95,9 +94,6 @@ static void
 MD5_dealloc(MD5object *ptr)
 {
     Hacl_Streaming_MD5_legacy_free(ptr->hash_state);
-    if (ptr->lock != NULL) {
-        PyThread_free_lock(ptr->lock);
-    }
     PyTypeObject *tp = Py_TYPE((PyObject*)ptr);
     PyObject_GC_UnTrack(ptr);
     PyObject_GC_Del(ptr);
@@ -202,14 +198,14 @@ MD5Type_update(MD5object *self, PyObject *obj)
 
     GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
 
-    if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
-        self->lock = PyThread_allocate_lock();
+    if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
     }
-    if (self->lock != NULL) {
+    if (self->use_mutex) {
         Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->lock, 1);
+        PyMutex_Lock(&self->mutex);
         update(self->hash_state, buf.buf, buf.len);
-        PyThread_release_lock(self->lock);
+        PyMutex_Unlock(&self->mutex);
         Py_END_ALLOW_THREADS
     } else {
         update(self->hash_state, buf.buf, buf.len);
index 3fd53123229ac4e87be3b2934e900f8a829d3659..eda6b5608d52f7606b44e604b9b55a0a90a4a555 100644 (file)
@@ -49,7 +49,8 @@ typedef long long SHA1_INT64;        /* 64-bit integer */
 typedef struct {
     PyObject_HEAD
     // Prevents undefined behavior via multiple threads entering the C API.
-    // The lock will be NULL before threaded access has been enabled.
+    bool use_mutex;
+    PyMutex mutex;
     PyThread_type_lock lock;
     Hacl_Streaming_SHA1_state *hash_state;
 } SHA1object;
@@ -76,7 +77,8 @@ newSHA1object(SHA1State *st)
     if (sha == NULL) {
         return NULL;
     }
-    sha->lock = NULL;
+    HASHLIB_INIT_MUTEX(sha);
+
     PyObject_GC_Track(sha);
     return sha;
 }
@@ -94,9 +96,6 @@ static void
 SHA1_dealloc(SHA1object *ptr)
 {
     Hacl_Streaming_SHA1_legacy_free(ptr->hash_state);
-    if (ptr->lock != NULL) {
-        PyThread_free_lock(ptr->lock);
-    }
     PyTypeObject *tp = Py_TYPE(ptr);
     PyObject_GC_UnTrack(ptr);
     PyObject_GC_Del(ptr);
@@ -192,14 +191,14 @@ SHA1Type_update(SHA1object *self, PyObject *obj)
 
     GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
 
-    if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
-        self->lock = PyThread_allocate_lock();
+    if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
     }
-    if (self->lock != NULL) {
+    if (self->use_mutex) {
         Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->lock, 1);
+        PyMutex_Lock(&self->mutex);
         update(self->hash_state, buf.buf, buf.len);
-        PyThread_release_lock(self->lock);
+        PyMutex_Unlock(&self->mutex);
         Py_END_ALLOW_THREADS
     } else {
         update(self->hash_state, buf.buf, buf.len);
index 6ad1ff2e05bfd840bfaaca41afd0ff893b3ee883..968493ba51b50d82fd179def127496c12ce36300 100644 (file)
@@ -53,8 +53,8 @@ typedef struct {
     PyObject_HEAD
     int digestsize;
     // Prevents undefined behavior via multiple threads entering the C API.
-    // The lock will be NULL before threaded access has been enabled.
-    PyThread_type_lock lock;
+    bool use_mutex;
+    PyMutex mutex;
     Hacl_Streaming_SHA2_state_sha2_256 *state;
 } SHA256object;
 
@@ -62,8 +62,8 @@ typedef struct {
     PyObject_HEAD
     int digestsize;
     // Prevents undefined behavior via multiple threads entering the C API.
-    // The lock will be NULL before threaded access has been enabled.
-    PyThread_type_lock lock;
+    bool use_mutex;
+    PyMutex mutex;
     Hacl_Streaming_SHA2_state_sha2_512 *state;
 } SHA512object;
 
@@ -106,7 +106,8 @@ newSHA224object(sha2_state *state)
     if (!sha) {
         return NULL;
     }
-    sha->lock = NULL;
+    HASHLIB_INIT_MUTEX(sha);
+
     PyObject_GC_Track(sha);
     return sha;
 }
@@ -119,7 +120,8 @@ newSHA256object(sha2_state *state)
     if (!sha) {
         return NULL;
     }
-    sha->lock = NULL;
+    HASHLIB_INIT_MUTEX(sha);
+
     PyObject_GC_Track(sha);
     return sha;
 }
@@ -132,7 +134,8 @@ newSHA384object(sha2_state *state)
     if (!sha) {
         return NULL;
     }
-    sha->lock = NULL;
+    HASHLIB_INIT_MUTEX(sha);
+
     PyObject_GC_Track(sha);
     return sha;
 }
@@ -145,7 +148,8 @@ newSHA512object(sha2_state *state)
     if (!sha) {
         return NULL;
     }
-    sha->lock = NULL;
+    HASHLIB_INIT_MUTEX(sha);
+
     PyObject_GC_Track(sha);
     return sha;
 }
@@ -163,9 +167,6 @@ static void
 SHA256_dealloc(SHA256object *ptr)
 {
     Hacl_Streaming_SHA2_free_256(ptr->state);
-    if (ptr->lock != NULL) {
-        PyThread_free_lock(ptr->lock);
-    }
     PyTypeObject *tp = Py_TYPE(ptr);
     PyObject_GC_UnTrack(ptr);
     PyObject_GC_Del(ptr);
@@ -176,9 +177,6 @@ static void
 SHA512_dealloc(SHA512object *ptr)
 {
     Hacl_Streaming_SHA2_free_512(ptr->state);
-    if (ptr->lock != NULL) {
-        PyThread_free_lock(ptr->lock);
-    }
     PyTypeObject *tp = Py_TYPE(ptr);
     PyObject_GC_UnTrack(ptr);
     PyObject_GC_Del(ptr);
@@ -376,14 +374,14 @@ SHA256Type_update(SHA256object *self, PyObject *obj)
 
     GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
 
-    if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
-        self->lock = PyThread_allocate_lock();
+    if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
     }
-    if (self->lock != NULL) {
+    if (self->use_mutex) {
         Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->lock, 1);
+        PyMutex_Lock(&self->mutex);
         update_256(self->state, buf.buf, buf.len);
-        PyThread_release_lock(self->lock);
+        PyMutex_Unlock(&self->mutex);
         Py_END_ALLOW_THREADS
     } else {
         update_256(self->state, buf.buf, buf.len);
@@ -410,14 +408,14 @@ SHA512Type_update(SHA512object *self, PyObject *obj)
 
     GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
 
-    if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
-        self->lock = PyThread_allocate_lock();
+    if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
     }
-    if (self->lock != NULL) {
+    if (self->use_mutex) {
         Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->lock, 1);
+        PyMutex_Lock(&self->mutex);
         update_512(self->state, buf.buf, buf.len);
-        PyThread_release_lock(self->lock);
+        PyMutex_Unlock(&self->mutex);
         Py_END_ALLOW_THREADS
     } else {
         update_512(self->state, buf.buf, buf.len);
index 558d2005cff617483010da2d438da5be96641e91..d9d2f6c385a68b4dbb72e4330a64709edd1bf635 100644 (file)
@@ -61,8 +61,8 @@ class _sha3.shake_256 "SHA3object *" "&SHAKE256type"
 typedef struct {
     PyObject_HEAD
     // Prevents undefined behavior via multiple threads entering the C API.
-    // The lock will be NULL before threaded access has been enabled.
-    PyThread_type_lock lock;
+    bool use_mutex;
+    PyMutex mutex;
     Hacl_Streaming_Keccak_state *hash_state;
 } SHA3object;
 
@@ -76,7 +76,8 @@ newSHA3object(PyTypeObject *type)
     if (newobj == NULL) {
         return NULL;
     }
-    newobj->lock = NULL;
+    HASHLIB_INIT_MUTEX(newobj);
+
     return newobj;
 }
 
@@ -169,9 +170,6 @@ static void
 SHA3_dealloc(SHA3object *self)
 {
     Hacl_Streaming_Keccak_free(self->hash_state);
-    if (self->lock != NULL) {
-        PyThread_free_lock(self->lock);
-    }
     PyTypeObject *tp = Py_TYPE(self);
     PyObject_Free(self);
     Py_DECREF(tp);
@@ -257,19 +255,22 @@ _sha3_sha3_224_update(SHA3object *self, PyObject *data)
 /*[clinic end generated code: output=d3223352286ed357 input=a887f54dcc4ae227]*/
 {
     Py_buffer buf;
+
     GET_BUFFER_VIEW_OR_ERROUT(data, &buf);
-    if (self->lock == NULL && buf.len >= HASHLIB_GIL_MINSIZE) {
-        self->lock = PyThread_allocate_lock();
+
+    if (!self->use_mutex && buf.len >= HASHLIB_GIL_MINSIZE) {
+        self->use_mutex = true;
     }
-    if (self->lock != NULL) {
+    if (self->use_mutex) {
         Py_BEGIN_ALLOW_THREADS
-        PyThread_acquire_lock(self->lock, 1);
+        PyMutex_Lock(&self->mutex);
         sha3_update(self->hash_state, buf.buf, buf.len);
-        PyThread_release_lock(self->lock);
+        PyMutex_Unlock(&self->mutex);
         Py_END_ALLOW_THREADS
     } else {
         sha3_update(self->hash_state, buf.buf, buf.len);
     }
+
     PyBuffer_Release(&buf);
     Py_RETURN_NONE;
 }