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
@@ -522,13 +544,22 @@ static PyObject* brotli_Compressor_process(PyBrotli_Compressor* self,
522544 PyObject * ret = NULL ;
523545 PyObject * input_object = NULL ;
524546 Py_buffer input ;
547+ int ok = 1 ;
525548
549+ BROTLI_CRITICAL_START ;
526550 if (self -> healthy == 0 ) {
527551 set_brotli_exception (self_type , kCompressUnhealthyError );
528- return NULL ;
552+ ok = 0 ;
529553 }
530554 if (self -> processing != 0 ) {
531555 set_brotli_exception (self_type , kCompressConcurrentError );
556+ ok = 0 ;
557+ }
558+ if (ok ) {
559+ self -> processing = 1 ;
560+ }
561+ BROTLI_CRITICAL_END ;
562+ if (!ok ) {
532563 return NULL ;
533564 }
534565
@@ -539,49 +570,76 @@ static PyObject* brotli_Compressor_process(PyBrotli_Compressor* self,
539570 return NULL ;
540571 }
541572
542- self -> processing = 1 ;
543573 ret = compress_stream (self , BROTLI_OPERATION_PROCESS , (uint8_t * )input .buf ,
544574 input .len );
545575 PyBuffer_Release (& input );
576+
577+ BROTLI_CRITICAL_START ;
546578 self -> processing = 0 ;
579+ BROTLI_CRITICAL_END ;
580+
547581 return ret ;
548582}
549583
550584static PyObject * brotli_Compressor_flush (PyBrotli_Compressor * self ) {
551585 PyObject * self_type = (PyObject * )Py_TYPE ((PyObject * )self );
552586 PyObject * ret = NULL ;
587+ int ok = 1 ;
553588
589+ BROTLI_CRITICAL_START ;
554590 if (self -> healthy == 0 ) {
555591 set_brotli_exception (self_type , kCompressUnhealthyError );
556- return NULL ;
592+ ok = 0 ;
557593 }
558594 if (self -> processing != 0 ) {
559595 set_brotli_exception (self_type , kCompressConcurrentError );
596+ ok = 0 ;
597+ }
598+ if (ok ) {
599+ self -> processing = 1 ;
600+ }
601+ BROTLI_CRITICAL_END ;
602+ if (!ok ) {
560603 return NULL ;
561604 }
562605
563- self -> processing = 1 ;
564606 ret = compress_stream (self , BROTLI_OPERATION_FLUSH , NULL , 0 );
607+
608+ BROTLI_CRITICAL_START ;
565609 self -> processing = 0 ;
610+ BROTLI_CRITICAL_END ;
611+
566612 return ret ;
567613}
568614
569615static PyObject * brotli_Compressor_finish (PyBrotli_Compressor * self ) {
570616 PyObject * self_type = (PyObject * )Py_TYPE ((PyObject * )self );
571617 PyObject * ret = NULL ;
618+ int ok = 1 ;
572619
620+ BROTLI_CRITICAL_START ;
573621 if (self -> healthy == 0 ) {
574622 set_brotli_exception (self_type , kCompressUnhealthyError );
575- return NULL ;
623+ ok = 0 ;
576624 }
577625 if (self -> processing != 0 ) {
578626 set_brotli_exception (self_type , kCompressConcurrentError );
627+ ok = 0 ;
628+ }
629+ if (ok ) {
630+ self -> processing = 1 ;
631+ }
632+ BROTLI_CRITICAL_END ;
633+ if (!ok ) {
579634 return NULL ;
580635 }
581636
582- self -> processing = 1 ;
583637 ret = compress_stream (self , BROTLI_OPERATION_FINISH , NULL , 0 );
638+
639+ BROTLI_CRITICAL_START ;
584640 self -> processing = 0 ;
641+ BROTLI_CRITICAL_END ;
642+
585643 if (ret != NULL ) {
586644 assert (BrotliEncoderIsFinished (self -> enc ));
587645 }
@@ -664,13 +722,22 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
664722 uint8_t * new_tail = NULL ;
665723 size_t new_tail_length = 0 ;
666724 int oom = 0 ;
725+ int ok = 1 ;
667726
727+ BROTLI_CRITICAL_START ;
668728 if (self -> healthy == 0 ) {
669729 set_brotli_exception (self_type , kDecompressUnhealthyError );
670- return NULL ;
730+ ok = 0 ;
671731 }
672732 if (self -> processing != 0 ) {
673733 set_brotli_exception (self_type , kDecompressConcurrentError );
734+ ok = 0 ;
735+ }
736+ if (ok ) {
737+ self -> processing = 1 ;
738+ }
739+ BROTLI_CRITICAL_END ;
740+ if (!ok ) {
674741 return NULL ;
675742 }
676743
@@ -683,7 +750,6 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
683750 }
684751
685752 Buffer_Init (& buffer );
686- self -> processing = 1 ;
687753
688754 if (self -> unconsumed_data_length > 0 ) {
689755 if (input .len > 0 ) {
@@ -759,17 +825,22 @@ static PyObject* brotli_Decompressor_process(PyBrotli_Decompressor* self,
759825 }
760826 PyBuffer_Release (& input );
761827 Buffer_Cleanup (& buffer );
828+
829+ ok = (PyErr_Occurred () == NULL ) ? 1 : 0 ;
830+ BROTLI_CRITICAL_START ;
762831 if (self -> unconsumed_data ) {
763832 free (self -> unconsumed_data );
764833 self -> unconsumed_data = NULL ;
765834 }
766835 self -> unconsumed_data = new_tail ;
767836 self -> unconsumed_data_length = new_tail_length ;
768- if (PyErr_Occurred () != NULL ) {
837+ if (! ok ) {
769838 assert (ret == NULL );
770839 self -> healthy = 0 ;
771840 }
772841 self -> processing = 0 ;
842+ BROTLI_CRITICAL_END ;
843+
773844 return ret ;
774845}
775846
@@ -1003,7 +1074,9 @@ static PyMethodDef brotli_methods[] = {
10031074
10041075static PyModuleDef_Slot brotli_mod_slots [] = {
10051076 {Py_mod_exec , brotli_init_mod },
1006- #if (PY_MAJOR_VERSION > 3 ) || (PY_MINOR_VERSION >= 12 )
1077+ #ifdef Py_GIL_DISABLED
1078+ {Py_mod_gil , Py_MOD_GIL_NOT_USED },
1079+ #elif (PY_MAJOR_VERSION > 3 ) || (PY_MINOR_VERSION >= 12 )
10071080 {Py_mod_multiple_interpreters , Py_MOD_PER_INTERPRETER_GIL_SUPPORTED },
10081081#endif
10091082 {0 , NULL }};
0 commit comments