2222#error "Only Python 3.10+ is supported"
2323#endif
2424
25+ /*
26+ Decoder / encoder nature does not support concurrent access. Attempt to enter
27+ concurrently will result in an exception.
28+
29+ "Critical" parts used in prologues to ensure that only one thread enters.
30+ For consistency, we use them in epilogues as well. "Critical" is essential for
31+ free-threaded support and should be no-op in GIL environment (since methods are
32+ entered with GIL already held).
33+
34+ "Native" parts denote code that does not interact with Python and thus GIL could
35+ be released. "Native" are important for GIL environment (for performance), but
36+ should be no-op in free-threaded environment, since prologue/epilogue should
37+ guarantee exclusive access.
38+ */
39+ #ifdef Py_GIL_DISABLED
40+ #define BROTLI_CRITICAL_START Py_BEGIN_CRITICAL_SECTION(self)
41+ #define BROTLI_CRITICAL_END Py_END_CRITICAL_SECTION()
42+ #define BROTLI_NATIVE_START
43+ #define BROTLI_NATIVE_END
44+ #else
45+ #define BROTLI_CRITICAL_START
46+ #define BROTLI_CRITICAL_END
47+ #define BROTLI_NATIVE_START Py_BEGIN_ALLOW_THREADS
48+ #define BROTLI_NATIVE_END Py_END_ALLOW_THREADS
49+ #endif
50+
2551static const char kErrorAttr[] = "error";
2652static const char kModuleAttr[] = "_module";
2753
@@ -475,7 +501,7 @@ static PyObject* compress_stream(PyBrotli_Compressor* self,
475501 goto error;
476502 }
477503
478- Py_BEGIN_ALLOW_THREADS ;
504+ BROTLI_NATIVE_START ;
479505 while (1) {
480506 ok = BrotliEncoderCompressStream(enc, op, &available_in, &next_in,
481507 &buffer.avail_out, &buffer.next_out, NULL);
@@ -492,7 +518,7 @@ static PyObject* compress_stream(PyBrotli_Compressor* self,
492518 }
493519 break;
494520 }
495- Py_END_ALLOW_THREADS ;
521+ BROTLI_NATIVE_END ;
496522
497523 if (oom) goto error;
498524 if (ok) {
@@ -522,7 +548,9 @@ static PyObject* brotli_Compressor_process(PyBrotli_Compressor* self,
522548 PyObject* ret = NULL;
523549 PyObject* input_object = NULL;
524550 Py_buffer input;
551+ int ok = 1;
525552
553+ BROTLI_CRITICAL_START;
526554 if (self->healthy == 0) {
527555 set_brotli_exception(self_type, kCompressUnhealthyError);
528556 return NULL;
@@ -531,6 +559,13 @@ static PyObject* brotli_Compressor_process(PyBrotli_Compressor* self,
531559 set_brotli_exception(self_type, kCompressConcurrentError);
532560 return NULL;
533561 }
562+ if (ok) {
563+ self->processing = 1;
564+ }
565+ BROTLI_CRITICAL_END;
566+ if (!ok) {
567+ return NULL;
568+ }
534569
535570 if (!PyArg_ParseTuple(args, "O:process", &input_object)) {
536571 return NULL;
@@ -539,49 +574,76 @@ static PyObject* brotli_Compressor_process(PyBrotli_Compressor* self,
539574 return NULL;
540575 }
541576
542- self->processing = 1;
543577 ret = compress_stream(self, BROTLI_OPERATION_PROCESS, (uint8_t*)input.buf,
544578 input.len);
545579 PyBuffer_Release(&input);
580+
581+ BROTLI_CRITICAL_START;
546582 self->processing = 0;
583+ BROTLI_CRITICAL_END;
584+
547585 return ret;
548586}
549587
550588static PyObject* brotli_Compressor_flush(PyBrotli_Compressor* self) {
551589 PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
552590 PyObject* ret = NULL;
591+ int ok = 1;
553592
593+ BROTLI_CRITICAL_START;
554594 if (self->healthy == 0) {
555595 set_brotli_exception(self_type, kCompressUnhealthyError);
556- return NULL ;
596+ ok = 0 ;
557597 }
558598 if (self->processing != 0) {
559599 set_brotli_exception(self_type, kCompressConcurrentError);
600+ ok = 0;
601+ }
602+ if (ok) {
603+ self->processing = 1;
604+ }
605+ BROTLI_CRITICAL_END;
606+ if (!ok) {
560607 return NULL;
561608 }
562609
563- self->processing = 1;
564610 ret = compress_stream(self, BROTLI_OPERATION_FLUSH, NULL, 0);
611+
612+ BROTLI_CRITICAL_START;
565613 self->processing = 0;
614+ BROTLI_CRITICAL_END;
615+
566616 return ret;
567617}
568618
569619static PyObject* brotli_Compressor_finish(PyBrotli_Compressor* self) {
570620 PyObject* self_type = (PyObject*)Py_TYPE((PyObject*)self);
571621 PyObject* ret = NULL;
622+ int ok = 1;
572623
624+ BROTLI_CRITICAL_START;
573625 if (self->healthy == 0) {
574626 set_brotli_exception(self_type, kCompressUnhealthyError);
575- return NULL ;
627+ ok = 0 ;
576628 }
577629 if (self->processing != 0) {
578630 set_brotli_exception(self_type, kCompressConcurrentError);
631+ ok = 0;
632+ }
633+ if (ok) {
634+ self->processing = 1;
635+ }
636+ BROTLI_CRITICAL_END;
637+ if (!ok) {
579638 return NULL;
580639 }
581640
582- self->processing = 1;
583641 ret = compress_stream(self, BROTLI_OPERATION_FINISH, NULL, 0);
642+
643+ BROTLI_CRITICAL_START;
584644 self->processing = 0;
645+ BROTLI_CRITICAL_END;
646+
585647 if (ret != NULL) {
586648 assert(BrotliEncoderIsFinished(self->enc));
587649 }
@@ -664,13 +726,22 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
664726 uint8_t* new_tail = NULL;
665727 size_t new_tail_length = 0;
666728 int oom = 0;
729+ int ok = 1;
667730
731+ BROTLI_CRITICAL_START;
668732 if (self->healthy == 0) {
669733 set_brotli_exception(self_type, kDecompressUnhealthyError);
670- return NULL ;
734+ ok = 0 ;
671735 }
672736 if (self->processing != 0) {
673737 set_brotli_exception(self_type, kDecompressConcurrentError);
738+ ok = 0;
739+ }
740+ if (ok) {
741+ self->processing = 1;
742+ }
743+ BROTLI_CRITICAL_END;
744+ if (!ok) {
674745 return NULL;
675746 }
676747
@@ -683,7 +754,6 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
683754 }
684755
685756 Buffer_Init(&buffer);
686- self->processing = 1;
687757
688758 if (self->unconsumed_data_length > 0) {
689759 if (input.len > 0) {
@@ -703,7 +773,7 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
703773 goto finally;
704774 }
705775
706- Py_BEGIN_ALLOW_THREADS ;
776+ BROTLI_NATIVE_START ;
707777 while (1) {
708778 result = BrotliDecoderDecompressStream(self->dec, &avail_in, &next_in,
709779 &buffer.avail_out, &buffer.next_out,
@@ -721,7 +791,7 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
721791 }
722792 break;
723793 }
724- Py_END_ALLOW_THREADS ;
794+ BROTLI_NATIVE_END ;
725795
726796 if (oom) {
727797 goto finally;
@@ -759,17 +829,22 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
759829 }
760830 PyBuffer_Release(&input);
761831 Buffer_Cleanup(&buffer);
832+
833+ ok = (PyErr_Occurred() == NULL) ? 1 : 0;
834+ BROTLI_CRITICAL_START;
762835 if (self->unconsumed_data) {
763836 free(self->unconsumed_data);
764837 self->unconsumed_data = NULL;
765838 }
766839 self->unconsumed_data = new_tail;
767840 self->unconsumed_data_length = new_tail_length;
768- if (PyErr_Occurred() != NULL ) {
841+ if (!ok ) {
769842 assert(ret == NULL);
770843 self->healthy = 0;
771844 }
772845 self->processing = 0;
846+ BROTLI_CRITICAL_END;
847+
773848 return ret;
774849}
775850
@@ -848,7 +923,7 @@ static PyObject* brotli_decompress(PyObject* m, PyObject* args,
848923 goto finally;
849924 }
850925
851- Py_BEGIN_ALLOW_THREADS ;
926+ BROTLI_NATIVE_START ;
852927 while (1) {
853928 result = BrotliDecoderDecompressStream(
854929 state, &available_in, &next_in, &buffer.avail_out, &buffer.next_out, 0);
@@ -862,7 +937,7 @@ static PyObject* brotli_decompress(PyObject* m, PyObject* args,
862937 }
863938 break;
864939 }
865- Py_END_ALLOW_THREADS ;
940+ BROTLI_NATIVE_END ;
866941
867942 if (oom) {
868943 goto finally;
@@ -1003,7 +1078,9 @@ static PyMethodDef brotli_methods[] = {
10031078
10041079static PyModuleDef_Slot brotli_mod_slots[] = {
10051080 {Py_mod_exec, brotli_init_mod},
1006- #if (PY_MAJOR_VERSION > 3) || (PY_MINOR_VERSION >= 12)
1081+ #ifdef Py_GIL_DISABLED
1082+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
1083+ #elif (PY_MAJOR_VERSION > 3) || (PY_MINOR_VERSION >= 12)
10071084 {Py_mod_multiple_interpreters, Py_MOD_PER_INTERPRETER_GIL_SUPPORTED},
10081085#endif
10091086 {0, NULL}};
0 commit comments