mirror of
https://github.com/python/cpython
synced 2026-09-29 12:10:30 +03:00
gh-99108: Release the GIL around hashlib built-in computation (#104675)
This matches the GIL releasing behavior of our existing `_hashopenssl` module, extending it to the HACL* built-ins. Includes adding comments to better describe the ENTER/LEAVE macros purpose and explain the lock strategy in both existing and new code.
This commit is contained in:
parent
988c1f68ce
commit
2e5d8a90aa
7 changed files with 207 additions and 15 deletions
|
|
@ -49,7 +49,9 @@ 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;
|
||||
Hacl_Streaming_MD5_state *hash_state;
|
||||
} MD5object;
|
||||
|
||||
|
|
@ -72,6 +74,7 @@ static MD5object *
|
|||
newMD5object(MD5State * st)
|
||||
{
|
||||
MD5object *md5 = (MD5object *)PyObject_GC_New(MD5object, st->md5_type);
|
||||
md5->lock = NULL;
|
||||
PyObject_GC_Track(md5);
|
||||
return md5;
|
||||
}
|
||||
|
|
@ -88,6 +91,9 @@ 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(ptr);
|
||||
PyObject_GC_UnTrack(ptr);
|
||||
PyObject_GC_Del(ptr);
|
||||
|
|
@ -115,7 +121,9 @@ MD5Type_copy_impl(MD5object *self, PyTypeObject *cls)
|
|||
if ((newobj = newMD5object(st))==NULL)
|
||||
return NULL;
|
||||
|
||||
ENTER_HASHLIB(self);
|
||||
newobj->hash_state = Hacl_Streaming_MD5_legacy_copy(self->hash_state);
|
||||
LEAVE_HASHLIB(self);
|
||||
return (PyObject *)newobj;
|
||||
}
|
||||
|
||||
|
|
@ -130,7 +138,9 @@ MD5Type_digest_impl(MD5object *self)
|
|||
/*[clinic end generated code: output=eb691dc4190a07ec input=bc0c4397c2994be6]*/
|
||||
{
|
||||
unsigned char digest[MD5_DIGESTSIZE];
|
||||
ENTER_HASHLIB(self);
|
||||
Hacl_Streaming_MD5_legacy_finish(self->hash_state, digest);
|
||||
LEAVE_HASHLIB(self);
|
||||
return PyBytes_FromStringAndSize((const char *)digest, MD5_DIGESTSIZE);
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +155,9 @@ MD5Type_hexdigest_impl(MD5object *self)
|
|||
/*[clinic end generated code: output=17badced1f3ac932 input=b60b19de644798dd]*/
|
||||
{
|
||||
unsigned char digest[MD5_DIGESTSIZE];
|
||||
ENTER_HASHLIB(self);
|
||||
Hacl_Streaming_MD5_legacy_finish(self->hash_state, digest);
|
||||
LEAVE_HASHLIB(self);
|
||||
return _Py_strhex((const char*)digest, MD5_DIGESTSIZE);
|
||||
}
|
||||
|
||||
|
|
@ -177,7 +189,18 @@ MD5Type_update(MD5object *self, PyObject *obj)
|
|||
|
||||
GET_BUFFER_VIEW_OR_ERROUT(obj, &buf);
|
||||
|
||||
update(self->hash_state, buf.buf, buf.len);
|
||||
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);
|
||||
update(self->hash_state, buf.buf, buf.len);
|
||||
PyThread_release_lock(self->lock);
|
||||
Py_END_ALLOW_THREADS
|
||||
} else {
|
||||
update(self->hash_state, buf.buf, buf.len);
|
||||
}
|
||||
|
||||
PyBuffer_Release(&buf);
|
||||
Py_RETURN_NONE;
|
||||
|
|
@ -279,7 +302,15 @@ _md5_md5_impl(PyObject *module, PyObject *string, int usedforsecurity)
|
|||
return NULL;
|
||||
}
|
||||
if (string) {
|
||||
update(new->hash_state, buf.buf, buf.len);
|
||||
if (buf.len >= HASHLIB_GIL_MINSIZE) {
|
||||
/* We do not initialize self->lock here as this is the constructor
|
||||
* where it is not yet possible to have concurrent access. */
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
update(new->hash_state, buf.buf, buf.len);
|
||||
Py_END_ALLOW_THREADS
|
||||
} else {
|
||||
update(new->hash_state, buf.buf, buf.len);
|
||||
}
|
||||
PyBuffer_Release(&buf);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue