Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 31f30b1

Browse files
committed
Issue #4738: finer-grained locking in the zlib module.
1 parent e9f8bf0 commit 31f30b1

2 files changed

Lines changed: 67 additions & 61 deletions

File tree

Misc/NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@ Tools/Demos
175175
Extension Modules
176176
-----------------
177177

178+
- Issue #4738: Each zlib object now has a separate lock, allowing to compress
179+
or decompress several streams at once on multi-CPU systems. Also, the GIL
180+
is now released when computing the CRC of a large buffer. Patch by ebfe.
181+
178182
- Issue #1040026: Fix os.times result on systems where HZ is incorrect.
179183

180184
- Issues #3167, #3682: Fix test_math failures for log, log10 on Solaris,

Modules/zlibmodule.c

Lines changed: 63 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,15 @@
99
#include "zlib.h"
1010

1111
#ifdef WITH_THREAD
12-
#include "pythread.h"
13-
14-
/* #defs ripped off from _tkinter.c, even though the situation here is much
15-
simpler, because we don't have to worry about waiting for Tcl
16-
events! And, since zlib itself is threadsafe, we don't need to worry
17-
about re-entering zlib functions.
18-
19-
N.B.
20-
21-
Since ENTER_ZLIB and LEAVE_ZLIB only need to be called on functions
22-
that modify the components of preexisting de/compress objects, it
23-
could prove to be a performance gain on multiprocessor machines if
24-
there was an de/compress object-specific lock. However, for the
25-
moment the ENTER_ZLIB and LEAVE_ZLIB calls are global for ALL
26-
de/compress objects.
27-
*/
28-
29-
static PyThread_type_lock zlib_lock = NULL; /* initialized on module load */
30-
31-
#define ENTER_ZLIB \
32-
Py_BEGIN_ALLOW_THREADS \
33-
PyThread_acquire_lock(zlib_lock, 1); \
34-
Py_END_ALLOW_THREADS
35-
36-
#define LEAVE_ZLIB \
37-
PyThread_release_lock(zlib_lock);
38-
12+
#include "pythread.h"
13+
#define ENTER_ZLIB(obj) \
14+
Py_BEGIN_ALLOW_THREADS; \
15+
PyThread_acquire_lock((obj)->lock, 1); \
16+
Py_END_ALLOW_THREADS;
17+
#define LEAVE_ZLIB(obj) PyThread_release_lock((obj)->lock);
3918
#else
40-
41-
#define ENTER_ZLIB
42-
#define LEAVE_ZLIB
43-
19+
#define ENTER_ZLIB(obj)
20+
#define LEAVE_ZLIB(obj)
4421
#endif
4522

4623
/* The following parameters are copied from zutil.h, version 0.95 */
@@ -67,6 +44,9 @@ typedef struct
6744
PyObject *unused_data;
6845
PyObject *unconsumed_tail;
6946
int is_initialised;
47+
#ifdef WITH_THREAD
48+
PyThread_type_lock lock;
49+
#endif
7050
} compobject;
7151

7252
static void
@@ -106,6 +86,9 @@ newcompobject(PyTypeObject *type)
10686
Py_DECREF(self);
10787
return NULL;
10888
}
89+
#ifdef WITH_THREAD
90+
self->lock = PyThread_allocate_lock();
91+
#endif
10992
return self;
11093
}
11194

@@ -376,23 +359,30 @@ PyZlib_decompressobj(PyObject *selfptr, PyObject *args)
376359
}
377360

378361
static void
379-
Comp_dealloc(compobject *self)
362+
Dealloc(compobject *self)
380363
{
381-
if (self->is_initialised)
382-
deflateEnd(&self->zst);
364+
#ifdef WITH_THREAD
365+
PyThread_free_lock(self->lock);
366+
#endif
383367
Py_XDECREF(self->unused_data);
384368
Py_XDECREF(self->unconsumed_tail);
385369
PyObject_Del(self);
386370
}
387371

372+
static void
373+
Comp_dealloc(compobject *self)
374+
{
375+
if (self->is_initialised)
376+
deflateEnd(&self->zst);
377+
Dealloc(self);
378+
}
379+
388380
static void
389381
Decomp_dealloc(compobject *self)
390382
{
391383
if (self->is_initialised)
392-
inflateEnd(&self->zst);
393-
Py_XDECREF(self->unused_data);
394-
Py_XDECREF(self->unconsumed_tail);
395-
PyObject_Del(self);
384+
inflateEnd(&self->zst);
385+
Dealloc(self);
396386
}
397387

398388
PyDoc_STRVAR(comp_compress__doc__,
@@ -422,7 +412,7 @@ PyZlib_objcompress(compobject *self, PyObject *args)
422412
return NULL;
423413
}
424414

425-
ENTER_ZLIB
415+
ENTER_ZLIB(self);
426416

427417
start_total_out = self->zst.total_out;
428418
self->zst.avail_in = inplen;
@@ -468,7 +458,7 @@ PyZlib_objcompress(compobject *self, PyObject *args)
468458
}
469459

470460
error:
471-
LEAVE_ZLIB
461+
LEAVE_ZLIB(self);
472462
PyBuffer_Release(&pinput);
473463
return RetVal;
474464
}
@@ -514,7 +504,7 @@ PyZlib_objdecompress(compobject *self, PyObject *args)
514504
return NULL;
515505
}
516506

517-
ENTER_ZLIB
507+
ENTER_ZLIB(self);
518508

519509
start_total_out = self->zst.total_out;
520510
self->zst.avail_in = inplen;
@@ -600,7 +590,7 @@ PyZlib_objdecompress(compobject *self, PyObject *args)
600590
}
601591

602592
error:
603-
LEAVE_ZLIB
593+
LEAVE_ZLIB(self);
604594
PyBuffer_Release(&pinput);
605595
return RetVal;
606596
}
@@ -633,7 +623,7 @@ PyZlib_flush(compobject *self, PyObject *args)
633623
if (!(RetVal = PyBytes_FromStringAndSize(NULL, length)))
634624
return NULL;
635625

636-
ENTER_ZLIB
626+
ENTER_ZLIB(self);
637627

638628
start_total_out = self->zst.total_out;
639629
self->zst.avail_in = 0;
@@ -693,7 +683,7 @@ PyZlib_flush(compobject *self, PyObject *args)
693683
}
694684

695685
error:
696-
LEAVE_ZLIB
686+
LEAVE_ZLIB(self);
697687

698688
return RetVal;
699689
}
@@ -714,7 +704,7 @@ PyZlib_copy(compobject *self)
714704
/* Copy the zstream state
715705
* We use ENTER_ZLIB / LEAVE_ZLIB to make this thread-safe
716706
*/
717-
ENTER_ZLIB
707+
ENTER_ZLIB(self);
718708
err = deflateCopy(&retval->zst, &self->zst);
719709
switch(err) {
720710
case(Z_OK):
@@ -730,7 +720,6 @@ PyZlib_copy(compobject *self)
730720
zlib_error(self->zst, err, "while copying compression object");
731721
goto error;
732722
}
733-
734723
Py_INCREF(self->unused_data);
735724
Py_INCREF(self->unconsumed_tail);
736725
Py_XDECREF(retval->unused_data);
@@ -741,11 +730,11 @@ PyZlib_copy(compobject *self)
741730
/* Mark it as being initialized */
742731
retval->is_initialised = 1;
743732

744-
LEAVE_ZLIB
733+
LEAVE_ZLIB(self);
745734
return (PyObject *)retval;
746735

747736
error:
748-
LEAVE_ZLIB
737+
LEAVE_ZLIB(self);
749738
Py_XDECREF(retval);
750739
return NULL;
751740
}
@@ -765,7 +754,7 @@ PyZlib_uncopy(compobject *self)
765754
/* Copy the zstream state
766755
* We use ENTER_ZLIB / LEAVE_ZLIB to make this thread-safe
767756
*/
768-
ENTER_ZLIB
757+
ENTER_ZLIB(self);
769758
err = inflateCopy(&retval->zst, &self->zst);
770759
switch(err) {
771760
case(Z_OK):
@@ -792,11 +781,11 @@ PyZlib_uncopy(compobject *self)
792781
/* Mark it as being initialized */
793782
retval->is_initialised = 1;
794783

795-
LEAVE_ZLIB
784+
LEAVE_ZLIB(self);
796785
return (PyObject *)retval;
797786

798787
error:
799-
LEAVE_ZLIB
788+
LEAVE_ZLIB(self);
800789
Py_XDECREF(retval);
801790
return NULL;
802791
}
@@ -826,7 +815,7 @@ PyZlib_unflush(compobject *self, PyObject *args)
826815
return NULL;
827816

828817

829-
ENTER_ZLIB
818+
ENTER_ZLIB(self);
830819

831820
start_total_out = self->zst.total_out;
832821
self->zst.avail_out = length;
@@ -873,7 +862,7 @@ PyZlib_unflush(compobject *self, PyObject *args)
873862

874863
error:
875864

876-
LEAVE_ZLIB
865+
LEAVE_ZLIB(self);
877866

878867
return retval;
879868
}
@@ -921,12 +910,20 @@ static PyObject *
921910
PyZlib_adler32(PyObject *self, PyObject *args)
922911
{
923912
unsigned int adler32val = 1; /* adler32(0L, Z_NULL, 0) */
924-
Byte *buf;
925-
int len;
913+
Py_buffer pbuf;
926914

927-
if (!PyArg_ParseTuple(args, "s#|I:adler32", &buf, &len, &adler32val))
915+
if (!PyArg_ParseTuple(args, "s*|I:adler32", &pbuf, &adler32val))
928916
return NULL;
929-
adler32val = adler32(adler32val, buf, len);
917+
/* Releasing the GIL for very small buffers is inefficient
918+
and may lower performance */
919+
if (pbuf.len > 1024*5) {
920+
Py_BEGIN_ALLOW_THREADS
921+
adler32val = adler32(adler32val, pbuf.buf, pbuf.len);
922+
Py_END_ALLOW_THREADS
923+
} else {
924+
adler32val = adler32(adler32val, pbuf.buf, pbuf.len);
925+
}
926+
PyBuffer_Release(&pbuf);
930927
return PyLong_FromUnsignedLong(adler32val & 0xffffffffU);
931928
}
932929

@@ -945,7 +942,15 @@ PyZlib_crc32(PyObject *self, PyObject *args)
945942

946943
if (!PyArg_ParseTuple(args, "s*|I:crc32", &pbuf, &crc32val))
947944
return NULL;
948-
signed_val = crc32(crc32val, pbuf.buf, pbuf.len);
945+
/* Releasing the GIL for very small buffers is inefficient
946+
and may lower performance */
947+
if (pbuf.len > 1024*5) {
948+
Py_BEGIN_ALLOW_THREADS
949+
signed_val = crc32(crc32val, pbuf.buf, pbuf.len);
950+
Py_END_ALLOW_THREADS
951+
} else {
952+
signed_val = crc32(crc32val, pbuf.buf, pbuf.len);
953+
}
949954
PyBuffer_Release(&pbuf);
950955
return PyLong_FromUnsignedLong(signed_val & 0xffffffffU);
951956
}
@@ -1096,8 +1101,5 @@ PyInit_zlib(void)
10961101

10971102
PyModule_AddStringConstant(m, "__version__", "1.0");
10981103

1099-
#ifdef WITH_THREAD
1100-
zlib_lock = PyThread_allocate_lock();
1101-
#endif /* WITH_THREAD */
11021104
return m;
11031105
}

0 commit comments

Comments
 (0)