1616#include "python/repeated.h"
1717#include "python/unknown_fields.h"
1818
19+ // Mutex wrapper when GIL-disabled. Zero-cost when GIL-enabled.
20+ // NOTE: Protobuf Free-threading support is still experimental.
21+ // TODO: Remove mutex and make upb python lock free.
22+ #ifdef Py_GIL_DISABLED
23+ #ifdef _POSIX_THREADS
24+ #define ENABLE_MUTEX 1
25+ #include <pthread.h>
26+ #else
27+ #error "GIL is disabled but _POSIX_THREADS isn't available"
28+ #endif // _POSIX_THREADS
29+ #endif // Py_GIL_DISABLED
30+
31+ typedef struct {
32+ #ifdef ENABLE_MUTEX
33+ pthread_mutex_t mutex ;
34+ #endif
35+ } FreeThreadingMutex ;
36+
37+ #ifdef ENABLE_MUTEX
38+ static FreeThreadingMutex obj_cache_mutex = {PTHREAD_MUTEX_INITIALIZER };
39+ #else
40+ static FreeThreadingMutex obj_cache_mutex = {};
41+ #endif
42+
1943static upb_Arena * PyUpb_NewArena (void );
2044
45+ void FreeThreadingLock (FreeThreadingMutex * thread_mutex ) {
46+ #ifdef ENABLE_MUTEX
47+ pthread_mutex_lock (& (thread_mutex -> mutex ));
48+ #endif
49+ }
50+
51+ void FreeThreadingUnlock (FreeThreadingMutex * thread_mutex ) {
52+ #ifdef ENABLE_MUTEX
53+ pthread_mutex_unlock (& (thread_mutex -> mutex ));
54+ #endif
55+ }
56+
2157static void PyUpb_ModuleDealloc (void * module ) {
2258 PyUpb_ModuleState * s = PyModule_GetState (module );
2359 PyUpb_WeakMap_Free (s -> obj_cache );
@@ -197,15 +233,26 @@ void PyUpb_ObjCache_Add(const void* key, PyObject* py_obj) {
197233 if (!cache ) {
198234 return ;
199235 }
236+ FreeThreadingLock (& obj_cache_mutex );
237+ PyUpb_WeakMap_Add (cache , key , py_obj );
238+ FreeThreadingUnlock (& obj_cache_mutex );
239+ }
240+
241+ void PyUpb_KnownObjCache_Add (PyUpb_WeakMap * cache , const void * key ,
242+ PyObject * py_obj ) {
243+ FreeThreadingLock (& obj_cache_mutex );
200244 PyUpb_WeakMap_Add (cache , key , py_obj );
245+ FreeThreadingUnlock (& obj_cache_mutex );
201246}
202247
203248void PyUpb_ObjCache_Delete (const void * key ) {
204249 PyUpb_WeakMap * cache = PyUpb_ObjCache_MaybeInstance ();
205250 if (!cache ) {
206251 return ;
207252 }
253+ FreeThreadingLock (& obj_cache_mutex );
208254 PyUpb_WeakMap_Delete (cache , key );
255+ FreeThreadingUnlock (& obj_cache_mutex );
209256}
210257
211258PyObject * PyUpb_ObjCache_Get (const void * key ) {
@@ -231,7 +278,10 @@ PyObject* PyUpb_ObjCache_Get(const void* key) {
231278#endif
232279 return NULL ;
233280 }
234- return PyUpb_WeakMap_Get (cache , key );
281+ FreeThreadingLock (& obj_cache_mutex );
282+ PyObject * obj = PyUpb_WeakMap_Get (cache , key );
283+ FreeThreadingUnlock (& obj_cache_mutex );
284+ return obj ;
235285}
236286
237287// -----------------------------------------------------------------------------
@@ -449,7 +499,9 @@ PyMODINIT_FUNC PyInit__message(void) {
449499
450500 state -> allow_oversize_protos = false;
451501 state -> wkt_bases = NULL ;
502+ FreeThreadingLock (& obj_cache_mutex );
452503 state -> obj_cache = PyUpb_WeakMap_New ();
504+ FreeThreadingUnlock (& obj_cache_mutex );
453505 state -> c_descriptor_symtab = NULL ;
454506
455507 if (!PyUpb_InitDescriptorContainers (m ) || !PyUpb_InitDescriptorPool (m ) ||
0 commit comments