Index: Modules/_hashopenssl.c =================================================================== --- Modules/_hashopenssl.c (Revision 67926) +++ Modules/_hashopenssl.c (Arbeitskopie) @@ -26,15 +26,29 @@ #define HASH_OBJ_CONSTRUCTOR 0 #endif +#ifdef WITH_THREAD + #include "pythread.h" + + #define ENTER_HASHLIB \ + Py_BEGIN_ALLOW_THREADS \ + PyThread_acquire_lock(self->lock, 1); \ + Py_END_ALLOW_THREADS + + #define LEAVE_HASHLIB \ + PyThread_release_lock(self->lock); + #else + + #define ENTER_HASHLIB + #define LEAVE_HASHLIB +#endif + typedef struct { PyObject_HEAD PyObject *name; /* name of this hash algorithm */ EVP_MD_CTX ctx; /* OpenSSL message digest context */ - /* - * TODO investigate performance impact of including a lock for this object - * here and releasing the Python GIL while hash updates are in progress. - * (perhaps only release GIL if input length will take long to process?) - */ + #ifdef WITH_THREAD + PyThread_type_lock lock; + #endif } EVPobject; @@ -63,19 +77,41 @@ if (retval != NULL) { Py_INCREF(name); retval->name = name; + #ifdef WITH_THREAD + retval->lock = PyThread_allocate_lock(); + #endif } return retval; } +static void +EVP_hash(EVPobject *self, void *cp, Py_ssize_t len) +{ + if (len > 0 && len <= MUNCH_SIZE) { + EVP_DigestUpdate(&self->ctx, cp, len); + } else { + Py_ssize_t offset = 0, sublen = len; + while (sublen) { + unsigned int process = sublen > MUNCH_SIZE ? MUNCH_SIZE : sublen; + EVP_DigestUpdate(&self->ctx, (unsigned char*)cp + offset, process); + sublen -= process; + offset += process; + } + } +} + /* Internal methods for a hash object */ static void -EVP_dealloc(PyObject *ptr) +EVP_dealloc(EVPobject *self) { - EVP_MD_CTX_cleanup(&((EVPobject *)ptr)->ctx); - Py_XDECREF(((EVPobject *)ptr)->name); - PyObject_Del(ptr); + EVP_MD_CTX_cleanup(&self->ctx); + Py_XDECREF(self->name); + #ifdef WITH_THREAD + PyThread_free_lock(self->lock); + #endif + PyObject_Del(self); } @@ -106,7 +142,9 @@ PyObject *retval; unsigned int digest_size; + ENTER_HASHLIB EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + LEAVE_HASHLIB digest_size = EVP_MD_CTX_size(&temp_ctx); EVP_DigestFinal(&temp_ctx, digest, NULL); @@ -128,7 +166,9 @@ unsigned int i, j, digest_size; /* Get the raw (binary) digest value */ + ENTER_HASHLIB EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + LEAVE_HASHLIB digest_size = EVP_MD_CTX_size(&temp_ctx); EVP_DigestFinal(&temp_ctx, digest, NULL); @@ -155,8 +195,13 @@ } #define MY_GET_BUFFER_VIEW_OR_ERROUT(obj, viewp) do { \ - if (PyUnicode_Check(obj) || !PyObject_CheckBuffer((obj))) { \ + if (PyUnicode_Check((obj))) { \ PyErr_SetString(PyExc_TypeError, \ + "Unicode-objects must be encoded before hashing");\ + return NULL; \ + } \ + if (!PyObject_CheckBuffer((obj))) { \ + PyErr_SetString(PyExc_TypeError, \ "object supporting the buffer API required"); \ return NULL; \ } \ @@ -182,19 +227,25 @@ if (!PyArg_ParseTuple(args, "O:update", &obj)) return NULL; - - MY_GET_BUFFER_VIEW_OR_ERROUT(obj, &view); - if (view.len > 0 && view.len <= MUNCH_SIZE) { - EVP_DigestUpdate(&self->ctx, view.buf, view.len); + + // See if the passed objects is a bytes-object. If it is, we can trust the buffer's locking and release the GIL + if (PyBytes_CheckExact(obj)) + { + if (!PyArg_ParseTuple(args, "y*:update", &view)) + return NULL; + ENTER_HASHLIB + Py_BEGIN_ALLOW_THREADS + EVP_hash(self, view.buf, view.len); + Py_END_ALLOW_THREADS + LEAVE_HASHLIB } else { - Py_ssize_t offset = 0, len = view.len; - while (len) { - unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; - EVP_DigestUpdate(&self->ctx, (unsigned char*)view.buf + offset, process); - len -= process; - offset += process; - } + // All other objects may be unsafe to hash without having the GIL... + MY_GET_BUFFER_VIEW_OR_ERROUT(obj, &view); + ENTER_HASHLIB + EVP_hash(self, view.buf, view.len); + LEAVE_HASHLIB } + PyBuffer_Release(&view); Py_INCREF(Py_None); @@ -246,11 +297,13 @@ static PyObject * -EVP_repr(PyObject *self) +EVP_repr(EVPobject *self) { char buf[100]; + ENTER_HASHLIB PyOS_snprintf(buf, sizeof(buf), "<%s HASH object @ %p>", - _PyUnicode_AsString(((EVPobject *)self)->name), self); + _PyUnicode_AsString(self->name), self); + LEAVE_HASHLIB return PyUnicode_FromString(buf); } @@ -293,18 +346,7 @@ Py_INCREF(self->name); if (data_obj) { - if (len > 0 && len <= MUNCH_SIZE) { - EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, - unsigned int)); - } else { - Py_ssize_t offset = 0, len = view.len; - while (len) { - unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; - EVP_DigestUpdate(&self->ctx, (unsigned char*)view.buf + offset, process); - len -= process; - offset += process; - } - } + EVP_hash(self, view.buf, view.len); PyBuffer_Release(&view); } @@ -394,20 +436,8 @@ EVP_DigestInit(&self->ctx, digest); } - if (cp && len) { - if (len > 0 && len <= MUNCH_SIZE) { - EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, - unsigned int)); - } else { - Py_ssize_t offset = 0; - while (len) { - unsigned int process = len > MUNCH_SIZE ? MUNCH_SIZE : len; - EVP_DigestUpdate(&self->ctx, cp + offset, process); - len -= process; - offset += process; - } - } - } + if (cp && len) + EVP_hash(self, (void*)cp, len); return (PyObject *)self; }