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. In GIL environment those rendered as a scope (i.e. `{` and `}`).
32+
33+ NB: `Py_BEGIN_ALLOW_THREADS` / `Py_END_ALLOW_THREADS` are still required to
34+ unblock the stop-the-world GC.
35+ */
36+ #ifdef Py_GIL_DISABLED
37+ #if PY_MAJOR_VERSION < 3 || (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION < 13 )
38+ #error "Critical sections are only available in Python 3.13+"
39+ #endif
40+ #define BROTLI_CRITICAL_START Py_BEGIN_CRITICAL_SECTION(self)
41+ #define BROTLI_CRITICAL_END Py_END_CRITICAL_SECTION()
42+ #else
43+ #define BROTLI_CRITICAL_START {
44+ #define BROTLI_CRITICAL_END }
45+ #endif
46+
2547static const char kErrorAttr [] = "error" ;
2648static const char kModuleAttr [] = "_module" ;
2749
@@ -449,6 +471,33 @@ static void brotli_Compressor_dealloc(PyBrotli_Compressor* self) {
449471 Py_TYPE (self )-> tp_free ((PyObject * )self );
450472}
451473
474+ static int brotli_compressor_enter (PyBrotli_Compressor * self ) {
475+ PyObject * self_type = (PyObject * )Py_TYPE ((PyObject * )self );
476+ int ok = 1 ;
477+
478+ BROTLI_CRITICAL_START ;
479+ if (self -> healthy == 0 ) {
480+ set_brotli_exception (self_type , kCompressUnhealthyError );
481+ ok = 0 ;
482+ }
483+ if (self -> processing != 0 ) {
484+ set_brotli_exception (self_type , kCompressConcurrentError );
485+ ok = 0 ;
486+ }
487+ if (ok ) {
488+ self -> processing = 1 ;
489+ }
490+ BROTLI_CRITICAL_END ;
491+ return ok ;
492+ }
493+
494+ static void brotli_compressor_leave (PyBrotli_Compressor * self ) {
495+ BROTLI_CRITICAL_START ;
496+ assert (self -> processing == 1 );
497+ self -> processing = 0 ;
498+ BROTLI_CRITICAL_END ;
499+ }
500+
452501/*
453502 Compress "utility knife" used for process / flush / finish.
454503
@@ -522,66 +571,49 @@ static PyObject* brotli_Compressor_process(PyBrotli_Compressor* self,
522571 PyObject * ret = NULL ;
523572 PyObject * input_object = NULL ;
524573 Py_buffer input ;
574+ int ok = 1 ;
525575
526- if (self -> healthy == 0 ) {
527- set_brotli_exception (self_type , kCompressUnhealthyError );
528- return NULL ;
529- }
530- if (self -> processing != 0 ) {
531- set_brotli_exception (self_type , kCompressConcurrentError );
532- return NULL ;
533- }
576+ if (!brotli_compressor_enter (self )) return NULL ;
534577
535578 if (!PyArg_ParseTuple (args , "O:process" , & input_object )) {
536- return NULL ;
579+ ok = 0 ;
537580 }
538- if (!get_data_view (input_object , & input )) {
581+ if (ok && !get_data_view (input_object , & input )) {
582+ ok = 0 ;
583+ }
584+ if (!ok ) {
585+ self -> healthy = 0 ;
586+ brotli_compressor_leave (self );
539587 return NULL ;
540588 }
541589
542- self -> processing = 1 ;
543590 ret = compress_stream (self , BROTLI_OPERATION_PROCESS , (uint8_t * )input .buf ,
544591 input .len );
545592 PyBuffer_Release (& input );
546- self -> processing = 0 ;
593+ brotli_compressor_leave (self );
594+
547595 return ret ;
548596}
549597
550598static PyObject * brotli_Compressor_flush (PyBrotli_Compressor * self ) {
551599 PyObject * self_type = (PyObject * )Py_TYPE ((PyObject * )self );
552600 PyObject * ret = NULL ;
553601
554- if (self -> healthy == 0 ) {
555- set_brotli_exception (self_type , kCompressUnhealthyError );
556- return NULL ;
557- }
558- if (self -> processing != 0 ) {
559- set_brotli_exception (self_type , kCompressConcurrentError );
560- return NULL ;
561- }
562-
563- self -> processing = 1 ;
602+ if (!brotli_compressor_enter (self )) return NULL ;
564603 ret = compress_stream (self , BROTLI_OPERATION_FLUSH , NULL , 0 );
565- self -> processing = 0 ;
604+ brotli_compressor_leave (self );
605+
566606 return ret ;
567607}
568608
569609static PyObject * brotli_Compressor_finish (PyBrotli_Compressor * self ) {
570610 PyObject * self_type = (PyObject * )Py_TYPE ((PyObject * )self );
571611 PyObject * ret = NULL ;
572612
573- if (self -> healthy == 0 ) {
574- set_brotli_exception (self_type , kCompressUnhealthyError );
575- return NULL ;
576- }
577- if (self -> processing != 0 ) {
578- set_brotli_exception (self_type , kCompressConcurrentError );
579- return NULL ;
580- }
581-
582- self -> processing = 1 ;
613+ if (!brotli_compressor_enter (self )) return NULL ;
583614 ret = compress_stream (self , BROTLI_OPERATION_FINISH , NULL , 0 );
584- self -> processing = 0 ;
615+ brotli_compressor_leave (self );
616+
585617 if (ret != NULL ) {
586618 assert (BrotliEncoderIsFinished (self -> enc ));
587619 }
@@ -639,6 +671,33 @@ static int brotli_Decompressor_init(PyBrotli_Decompressor* self, PyObject* args,
639671 return 0 ;
640672}
641673
674+ static int brotli_decompressor_enter (PyBrotli_Decompressor * self ) {
675+ PyObject * self_type = (PyObject * )Py_TYPE ((PyObject * )self );
676+ int ok = 1 ;
677+
678+ BROTLI_CRITICAL_START ;
679+ if (self -> healthy == 0 ) {
680+ set_brotli_exception (self_type , kDecompressUnhealthyError );
681+ ok = 0 ;
682+ }
683+ if (self -> processing != 0 ) {
684+ set_brotli_exception (self_type , kDecompressConcurrentError );
685+ ok = 0 ;
686+ }
687+ if (ok ) {
688+ self -> processing = 1 ;
689+ }
690+ BROTLI_CRITICAL_END ;
691+ return ok ;
692+ }
693+
694+ static void brotli_decompressor_leave (PyBrotli_Decompressor * self ) {
695+ BROTLI_CRITICAL_START ;
696+ assert (self -> processing == 1 );
697+ self -> processing = 0 ;
698+ BROTLI_CRITICAL_END ;
699+ }
700+
642701static void brotli_Decompressor_dealloc (PyBrotli_Decompressor * self ) {
643702 if (self -> dec ) BrotliDecoderDestroyInstance (self -> dec );
644703 if (self -> unconsumed_data ) {
@@ -664,26 +723,24 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
664723 uint8_t * new_tail = NULL ;
665724 size_t new_tail_length = 0 ;
666725 int oom = 0 ;
726+ int ok = 1 ;
667727
668- if (self -> healthy == 0 ) {
669- set_brotli_exception (self_type , kDecompressUnhealthyError );
670- return NULL ;
671- }
672- if (self -> processing != 0 ) {
673- set_brotli_exception (self_type , kDecompressConcurrentError );
674- return NULL ;
675- }
728+ if (!brotli_decompressor_enter (self )) return NULL ;
676729
677730 if (!PyArg_ParseTupleAndKeywords (args , keywds , "O|n:process" , (char * * )kwlist ,
678731 & input_object , & output_buffer_limit )) {
679- return NULL ;
732+ ok = 0 ;
680733 }
681- if (!get_data_view (input_object , & input )) {
734+ if (ok && !get_data_view (input_object , & input )) {
735+ ok = 0 ;
736+ }
737+ if (!ok ) {
738+ self -> healthy = 0 ;
739+ brotli_decompressor_leave (self );
682740 return NULL ;
683741 }
684742
685743 Buffer_Init (& buffer );
686- self -> processing = 1 ;
687744
688745 if (self -> unconsumed_data_length > 0 ) {
689746 if (input .len > 0 ) {
@@ -769,21 +826,17 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
769826 assert (ret == NULL );
770827 self -> healthy = 0 ;
771828 }
772- self -> processing = 0 ;
829+ brotli_decompressor_leave (self );
830+
773831 return ret ;
774832}
775833
776834static PyObject * brotli_Decompressor_is_finished (PyBrotli_Decompressor * self ) {
777- PyObject * self_type = (PyObject * )Py_TYPE ((PyObject * )self );
778- if (self -> healthy == 0 ) {
779- set_brotli_exception (self_type , kDecompressUnhealthyError );
780- return NULL ;
781- }
782- if (self -> processing != 0 ) {
783- set_brotli_exception (self_type , kDecompressConcurrentError );
784- return NULL ;
785- }
786- if (BrotliDecoderIsFinished (self -> dec )) {
835+ int result ;
836+ if (!brotli_decompressor_enter (self )) return NULL ;
837+ result = BrotliDecoderIsFinished (self -> dec );
838+ brotli_decompressor_leave (self );
839+ if (result ) {
787840 Py_RETURN_TRUE ;
788841 } else {
789842 Py_RETURN_FALSE ;
@@ -792,16 +845,11 @@ static PyObject* brotli_Decompressor_is_finished(PyBrotli_Decompressor* self) {
792845
793846static PyObject * brotli_Decompressor_can_accept_more_data (
794847 PyBrotli_Decompressor * self ) {
795- PyObject * self_type = (PyObject * )Py_TYPE ((PyObject * )self );
796- if (self -> healthy == 0 ) {
797- set_brotli_exception (self_type , kDecompressUnhealthyError );
798- return NULL ;
799- }
800- if (self -> processing != 0 ) {
801- set_brotli_exception (self_type , kDecompressConcurrentError );
802- return NULL ;
803- }
804- if (self -> unconsumed_data_length > 0 ) {
848+ int result ;
849+ if (!brotli_decompressor_enter (self )) return NULL ;
850+ result = (self -> unconsumed_data_length > 0 );
851+ brotli_decompressor_leave (self );
852+ if (result ) {
805853 Py_RETURN_FALSE ;
806854 } else {
807855 Py_RETURN_TRUE ;
@@ -1003,7 +1051,9 @@ static PyMethodDef brotli_methods[] = {
10031051
10041052static PyModuleDef_Slot brotli_mod_slots [] = {
10051053 {Py_mod_exec , brotli_init_mod },
1006- #if (PY_MAJOR_VERSION > 3 ) || (PY_MINOR_VERSION >= 12 )
1054+ #ifdef Py_GIL_DISABLED
1055+ {Py_mod_gil , Py_MOD_GIL_NOT_USED },
1056+ #elif (PY_MAJOR_VERSION > 3 ) || (PY_MINOR_VERSION >= 12 )
10071057 {Py_mod_multiple_interpreters , Py_MOD_PER_INTERPRETER_GIL_SUPPORTED },
10081058#endif
10091059 {0 , NULL }};
0 commit comments