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