Skip to content

Commit 0f3c830

Browse files
alexmalyshevmeta-codesync[bot]
authored andcommitted
Move classloader_cache and classloader_cache_module_to_keys into ModuleState
Summary: Move the `classloader_cache` and `classloader_cache_module_to_keys` file-scope globals from `StaticPython/type.c` into the `ModuleState` class as `Ref<>` members with proper GC traversal. C accessor functions are provided in `module_c_state.h/cpp` for use from the C code in `type.c`. Reviewed By: yoney Differential Revision: D96742317 fbshipit-source-id: 08be7c4ef4f14a6e31933f8f42be4c007ee05948
1 parent 9a51738 commit 0f3c830

5 files changed

Lines changed: 91 additions & 34 deletions

File tree

cinderx/StaticPython/type.c

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,12 @@
88
#include "cinderx/StaticPython/generic_type.h"
99
#include "cinderx/StaticPython/typed_method_def.h"
1010
#include "cinderx/UpstreamBorrow/borrowed.h"
11+
#include "cinderx/module_c_state.h"
1112

1213
#if PY_VERSION_HEX < 0x030C0000
1314
#include "cinder/exports.h"
1415
#endif
1516

16-
static PyObject* classloader_cache;
17-
static PyObject* classloader_cache_module_to_keys;
18-
1917
Py_ssize_t _PyClassLoader_PrimitiveTypeToSize(int primitive_type) {
2018
switch (primitive_type) {
2119
case TYPED_INT8:
@@ -375,11 +373,13 @@ _PyClassLoader_ResolveType(PyObject* descr, int* optional, int* exact) {
375373
last = PyTuple_GET_ITEM(descr, items - 1);
376374
}
377375

378-
if (classloader_cache != NULL) {
379-
PyObject* cache = PyDict_GetItem(classloader_cache, descr);
380-
if (cache != NULL) {
381-
Py_INCREF(cache);
382-
return (PyTypeObject*)cache;
376+
// Fast path that hits the cache.
377+
PyObject* cl_cache = Ci_GetClassLoaderCache();
378+
if (cl_cache != NULL) {
379+
PyObject* cached = PyDict_GetItem(cl_cache, descr);
380+
if (cached != NULL) {
381+
Py_INCREF(cached);
382+
return (PyTypeObject*)cached;
383383
}
384384
}
385385

@@ -389,39 +389,37 @@ _PyClassLoader_ResolveType(PyObject* descr, int* optional, int* exact) {
389389
return NULL;
390390
}
391391

392-
if (classloader_cache == NULL) {
393-
classloader_cache = PyDict_New();
394-
if (classloader_cache == NULL) {
395-
Py_DECREF(res);
396-
return NULL;
397-
}
392+
// Ensure type resolution cache is set up.
393+
cl_cache = _PyClassLoader_GetCache();
394+
if (cl_cache == NULL) {
395+
Py_DECREF(res);
396+
return NULL;
398397
}
399398

400-
if (classloader_cache_module_to_keys == NULL) {
401-
classloader_cache_module_to_keys = PyDict_New();
402-
if (classloader_cache_module_to_keys == NULL) {
399+
PyObject* mod_to_keys = Ci_GetClassLoaderCacheModuleToKeys();
400+
if (mod_to_keys == NULL) {
401+
mod_to_keys = PyDict_New();
402+
if (mod_to_keys == NULL) {
403403
Py_DECREF(res);
404404
return NULL;
405405
}
406+
Ci_SetClassLoaderCacheModuleToKeys((PyDictObject*)mod_to_keys);
407+
Py_DECREF(mod_to_keys);
406408
}
407409

408-
if (PyDict_SetItem(classloader_cache, descr, res)) {
410+
if (PyDict_SetItem(cl_cache, descr, res)) {
409411
Py_DECREF(res);
410412
return NULL;
411413
}
412414
PyObject* module_key = PyTuple_GET_ITEM(descr, 0);
413-
PyObject* existing_modules_to_keys =
414-
PyDict_GetItem(classloader_cache_module_to_keys, module_key);
415+
PyObject* existing_modules_to_keys = PyDict_GetItem(mod_to_keys, module_key);
415416
if (existing_modules_to_keys == NULL) {
416417
existing_modules_to_keys = PyList_New(0);
417418
if (existing_modules_to_keys == NULL) {
418419
Py_DECREF(res);
419420
return NULL;
420421
}
421-
if (PyDict_SetItem(
422-
classloader_cache_module_to_keys,
423-
module_key,
424-
existing_modules_to_keys) < 0) {
422+
if (PyDict_SetItem(mod_to_keys, module_key, existing_modules_to_keys) < 0) {
425423
Py_DECREF(res);
426424
return NULL;
427425
}
@@ -441,34 +439,40 @@ int _PyClassLoader_CheckModuleChange(PyDictObject* dict, PyObject* key) {
441439
if (((PyObject*)dict) != modules_dict) {
442440
return 0;
443441
}
444-
if (classloader_cache_module_to_keys == NULL) {
442+
PyObject* mod_to_keys = Ci_GetClassLoaderCacheModuleToKeys();
443+
if (mod_to_keys == NULL) {
445444
return 0;
446445
}
447-
PyObject* keys_to_invalidate =
448-
PyDict_GetItem(classloader_cache_module_to_keys, key);
446+
PyObject* keys_to_invalidate = PyDict_GetItem(mod_to_keys, key);
449447
if (keys_to_invalidate == NULL) {
450448
return 0;
451449
}
450+
PyObject* cl_cache = Ci_GetClassLoaderCache();
452451
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(keys_to_invalidate); i++) {
453452
PyObject* key_to_invalidate = PyList_GET_ITEM(keys_to_invalidate, i);
454-
if (PyDict_DelItem(classloader_cache, key_to_invalidate) < 0) {
453+
if (PyDict_DelItem(cl_cache, key_to_invalidate) < 0) {
455454
return 0;
456455
}
457456
}
458-
PyDict_DelItem(classloader_cache_module_to_keys, key);
457+
PyDict_DelItem(mod_to_keys, key);
459458
return 0;
460459
}
461460

462461
void _PyClassLoader_ClearCache() {
463-
Py_CLEAR(classloader_cache);
464-
Py_CLEAR(classloader_cache_module_to_keys);
462+
Ci_ClearClassLoaderCache();
463+
Ci_ClearClassLoaderCacheModuleToKeys();
465464
}
466465

467466
PyObject* _PyClassLoader_GetCache() {
468-
if (classloader_cache == NULL) {
469-
classloader_cache = PyDict_New();
467+
PyObject* cl_cache = Ci_GetClassLoaderCache();
468+
if (cl_cache == NULL) {
469+
cl_cache = PyDict_New();
470+
if (cl_cache != NULL) {
471+
Ci_SetClassLoaderCache((PyDictObject*)cl_cache);
472+
Py_DECREF(cl_cache);
473+
}
470474
}
471-
return classloader_cache;
475+
return cl_cache;
472476
}
473477

474478
/* Resolve a tuple type descr to a `prim_type` integer (`TYPED_*`); return -1

cinderx/module_c_state.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,39 @@ void Ci_ClearGenericInstCache(void) {
3232
}
3333
}
3434

35+
PyObject* Ci_GetClassLoaderCache(void) {
36+
auto state = cinderx::getModuleState();
37+
return state != nullptr ? state->classloader_cache.getObj() : nullptr;
38+
}
39+
40+
void Ci_SetClassLoaderCache(PyDictObject* cache) {
41+
cinderx::getModuleState()->classloader_cache =
42+
Ref<PyDictObject>::create(cache);
43+
}
44+
45+
void Ci_ClearClassLoaderCache(void) {
46+
if (auto state = cinderx::getModuleState(); state != nullptr) {
47+
state->classloader_cache.reset();
48+
}
49+
}
50+
51+
PyObject* Ci_GetClassLoaderCacheModuleToKeys(void) {
52+
auto state = cinderx::getModuleState();
53+
return state != nullptr ? state->classloader_cache_module_to_keys.getObj()
54+
: nullptr;
55+
}
56+
57+
void Ci_SetClassLoaderCacheModuleToKeys(PyDictObject* cache) {
58+
cinderx::getModuleState()->classloader_cache_module_to_keys =
59+
Ref<PyDictObject>::create(cache);
60+
}
61+
62+
void Ci_ClearClassLoaderCacheModuleToKeys(void) {
63+
if (auto state = cinderx::getModuleState(); state != nullptr) {
64+
state->classloader_cache_module_to_keys.reset();
65+
}
66+
}
67+
3568
int Ci_Watchers_WatchDict(PyObject* dict) {
3669
return cinderx::getModuleState()->watcher_state.watchDict(dict);
3770
}

cinderx/module_c_state.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,16 @@ PyObject* Ci_GetGenericInstCache(void);
2828
void Ci_SetGenericInstCache(PyDictObject* cache);
2929
void Ci_ClearGenericInstCache(void);
3030

31+
// Class loader type resolution cache.
32+
PyObject* Ci_GetClassLoaderCache(void);
33+
void Ci_SetClassLoaderCache(PyDictObject* cache);
34+
void Ci_ClearClassLoaderCache(void);
35+
36+
// Class loader module-to-keys mapping for cache invalidation.
37+
PyObject* Ci_GetClassLoaderCacheModuleToKeys(void);
38+
void Ci_SetClassLoaderCacheModuleToKeys(PyDictObject* cache);
39+
void Ci_ClearClassLoaderCacheModuleToKeys(void);
40+
3141
// WatcherState.
3242

3343
int Ci_Watchers_WatchDict(PyObject* dict);

cinderx/module_state.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,17 @@ ModuleState* s_cinderx_state;
1515
int ModuleState::traverse(visitproc visit, void* arg) {
1616
Py_VISIT(static_type_error);
1717
Py_VISIT(genericinst_cache);
18+
Py_VISIT(classloader_cache);
19+
Py_VISIT(classloader_cache_module_to_keys);
1820
Py_VISIT(builtin_next);
1921
return 0;
2022
}
2123

2224
int ModuleState::clear() {
2325
static_type_error.reset();
2426
genericinst_cache.reset();
27+
classloader_cache.reset();
28+
classloader_cache_module_to_keys.reset();
2529
sys_clear_caches.reset();
2630
builtin_next.reset();
2731
return 0;

cinderx/module_state.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ struct ModuleState {
6161
// Cache for generic type instantiations (e.g. list[int]).
6262
Ref<PyDictObject> genericinst_cache;
6363

64+
// Cache for the Static Python class loader.
65+
Ref<PyDictObject> classloader_cache;
66+
67+
// Mapping from module name to classloader cache keys for that module.
68+
Ref<PyDictObject> classloader_cache_module_to_keys;
69+
6470
// Snapshotted member dicts for standard builtin types (int, str, list, etc.)
6571
// so the JIT optimizer can look up methods during multithreaded compilation
6672
// without calling PyType_Lookup (which isn't safe off the main thread).

0 commit comments

Comments
 (0)