Skip to content

Commit f10c1de

Browse files
anandoleecopybara-github
authored andcommitted
Protobuf Python UPB Free Threading support.
Add obj_cache lock to pass current free threading tests on python upb. PiperOrigin-RevId: 864903528
1 parent b159d05 commit f10c1de

4 files changed

Lines changed: 56 additions & 6 deletions

File tree

python/descriptor_pool.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ static PyObject* PyUpb_DescriptorPool_DoCreateWithCache(
4949
pool->symtab = upb_DefPool_New();
5050
pool->db = db;
5151
Py_XINCREF(pool->db);
52-
PyUpb_WeakMap_Add(obj_cache, pool->symtab, &pool->ob_base);
52+
PyUpb_KnownObjCache_Add(obj_cache, pool->symtab, &pool->ob_base);
5353
return &pool->ob_base;
5454
}
5555

python/google/protobuf/internal/thread_safe_test.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,6 @@ def DoNothing():
7575

7676
self.RunThreads(thread_size, DoNothing)
7777

78-
@unittest.skipIf(
79-
api_implementation.Type() != 'cpp',
80-
'Only cpp supports free threading for now',
81-
)
8278
def testDescriptorPoolMap(self):
8379
thread_size = 20
8480
self.success_count = 0

python/protobuf.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,44 @@
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+
1943
static 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+
2157
static 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

203248
void 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

211258
PyObject* 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) ||

python/protobuf.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ void PyUpb_WeakMap_DeleteIter(PyUpb_WeakMap* map, intptr_t* iter);
148148
// The object cache is a global WeakMap for mapping upb objects to the
149149
// corresponding wrapper.
150150
void PyUpb_ObjCache_Add(const void* key, PyObject* py_obj);
151+
void PyUpb_KnownObjCache_Add(PyUpb_WeakMap* cache, const void* key,
152+
PyObject* py_obj);
151153
void PyUpb_ObjCache_Delete(const void* key);
152154
PyObject* PyUpb_ObjCache_Get(const void* key); // returns NULL if not present.
153155
PyUpb_WeakMap* PyUpb_ObjCache_Instance(void);

0 commit comments

Comments
 (0)