Index: Modules/_hashopenssl.c =================================================================== --- Modules/_hashopenssl.c (révision 68165) +++ Modules/_hashopenssl.c (copie de travail) @@ -26,15 +26,33 @@ #define HASH_OBJ_CONSTRUCTOR 0 #endif +#define HASHLIB_GIL_MINSIZE 8192 + +#ifdef WITH_THREAD + #include "pythread.h" + + #define ENTER_HASHLIB(obj) \ + if ((obj)->lock) { \ + Py_BEGIN_ALLOW_THREADS \ + PyThread_acquire_lock((obj)->lock, 1); \ + Py_END_ALLOW_THREADS \ + } + #define LEAVE_HASHLIB(obj) \ + if ((obj)->lock) { \ + PyThread_release_lock((obj)->lock); \ + } +#else + #define ENTER_HASHLIB(obj) + #define LEAVE_HASHLIB(obj) +#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?) - */ + EVP_MD_CTX ctx; /* OpenSSL message digest context */ +#ifdef WITH_THREAD + PyThread_type_lock lock; /* OpenSSL context lock */ +#endif } EVPobject; @@ -63,19 +81,42 @@ if (retval != NULL) { Py_INCREF(name); retval->name = name; +#ifdef WITH_THREAD + retval->lock = NULL; +#endif } return retval; } +static void +EVP_hash(EVPobject *self, const void *vp, Py_ssize_t len) +{ + unsigned int process; + const unsigned char *cp = (const unsigned char *)vp; + while (0 < len) { + if (len > (Py_ssize_t)MUNCH_SIZE) + process = MUNCH_SIZE; + else + process = Py_SAFE_DOWNCAST(len, Py_ssize_t, unsigned int); + EVP_DigestUpdate(&self->ctx, (const void*)cp, process); + len -= process; + cp += 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); +#ifdef WITH_THREAD + if (self->lock != NULL) + PyThread_free_lock(self->lock); +#endif + EVP_MD_CTX_cleanup(&self->ctx); + Py_XDECREF(self->name); + PyObject_Del(self); } @@ -91,7 +132,9 @@ if ( (newobj = newEVPobject(self->name))==NULL) return NULL; + ENTER_HASHLIB(self); EVP_MD_CTX_copy(&newobj->ctx, &self->ctx); + LEAVE_HASHLIB(self); return (PyObject *)newobj; } @@ -106,7 +149,9 @@ PyObject *retval; unsigned int digest_size; + ENTER_HASHLIB(self); EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + LEAVE_HASHLIB(self); digest_size = EVP_MD_CTX_size(&temp_ctx); EVP_DigestFinal(&temp_ctx, digest, NULL); @@ -128,7 +173,9 @@ unsigned int i, j, digest_size; /* Get the raw (binary) digest value */ + ENTER_HASHLIB(self); EVP_MD_CTX_copy(&temp_ctx, &self->ctx); + LEAVE_HASHLIB(self); digest_size = EVP_MD_CTX_size(&temp_ctx); EVP_DigestFinal(&temp_ctx, digest, NULL); @@ -137,16 +184,16 @@ /* Allocate a new buffer */ hex_digest = PyMem_Malloc(digest_size * 2 + 1); if (!hex_digest) - return PyErr_NoMemory(); + return PyErr_NoMemory(); /* Make hex version of the digest */ for(i=j=0; i> 4) & 0xf; - c = (c>9) ? c+'a'-10 : c + '0'; + c = (c>9) ? c+'a'-10 : c + '0'; hex_digest[j++] = c; c = (digest[i] & 0xf); - c = (c>9) ? c+'a'-10 : c + '0'; + c = (c>9) ? c+'a'-10 : c + '0'; hex_digest[j++] = c; } retval = PyUnicode_FromStringAndSize(hex_digest, digest_size * 2); @@ -155,8 +202,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; \ } \ @@ -184,17 +236,20 @@ 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); + + if (self->lock == NULL && view.len >= HASHLIB_GIL_MINSIZE) + self->lock = PyThread_allocate_lock(); + + if (self->lock != NULL) { + Py_BEGIN_ALLOW_THREADS + PyThread_acquire_lock(self->lock, 1); + EVP_hash(self, view.buf, view.len); + PyThread_release_lock(self->lock); + Py_END_ALLOW_THREADS } 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); Py_INCREF(Py_None); @@ -212,13 +267,21 @@ static PyObject * EVP_get_block_size(EVPobject *self, void *closure) { - return PyLong_FromLong(EVP_MD_CTX_block_size(&((EVPobject *)self)->ctx)); + long block_size; + ENTER_HASHLIB(self); + block_size = EVP_MD_CTX_block_size(&self->ctx); + LEAVE_HASHLIB(self); + return PyLong_FromLong(block_size); } static PyObject * EVP_get_digest_size(EVPobject *self, void *closure) { - return PyLong_FromLong(EVP_MD_CTX_size(&((EVPobject *)self)->ctx)); + long size; + ENTER_HASHLIB(self); + size = EVP_MD_CTX_size(&self->ctx); + LEAVE_HASHLIB(self); + return PyLong_FromLong(size); } static PyMemberDef EVP_members[] = { @@ -246,11 +309,11 @@ static PyObject * -EVP_repr(PyObject *self) +EVP_repr(EVPobject *self) { char buf[100]; PyOS_snprintf(buf, sizeof(buf), "<%s HASH object @ %p>", - _PyUnicode_AsString(((EVPobject *)self)->name), self); + _PyUnicode_AsString(self->name), self); return PyUnicode_FromString(buf); } @@ -293,21 +356,16 @@ 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)); + if (view.len >= HASHLIB_GIL_MINSIZE) { + Py_BEGIN_ALLOW_THREADS + EVP_hash(self, view.buf, view.len); + Py_END_ALLOW_THREADS } 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); } - + return 0; } #endif @@ -332,15 +390,15 @@ static PyTypeObject EVPtype = { PyVarObject_HEAD_INIT(NULL, 0) "_hashlib.HASH", /*tp_name*/ - sizeof(EVPobject), /*tp_basicsize*/ + sizeof(EVPobject), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ - EVP_dealloc, /*tp_dealloc*/ - 0, /*tp_print*/ + (destructor)EVP_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ - EVP_repr, /*tp_repr*/ + (reprfunc)EVP_repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ @@ -395,17 +453,12 @@ } if (cp && len) { - if (len > 0 && len <= MUNCH_SIZE) { - EVP_DigestUpdate(&self->ctx, cp, Py_SAFE_DOWNCAST(len, Py_ssize_t, - unsigned int)); + if (len >= HASHLIB_GIL_MINSIZE) { + Py_BEGIN_ALLOW_THREADS + EVP_hash(self, cp, len); + Py_END_ALLOW_THREADS } 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; - } + EVP_hash(self, cp, len); } }