Skip to content

Commit 9ca2d56

Browse files
alexmalyshevmeta-codesync[bot]
authored andcommitted
Move dlopen_cache, dlsym_cache, and invoke_native_helper into ModuleState
Summary: Move the `dlopen_cache`, `dlsym_cache`, and `invoke_native_helper` file-scope globals from `StaticPython/classloader.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 `classloader.c`. Reviewed By: czardoz Differential Revision: D96742344 fbshipit-source-id: 57d751310a10910cd04cc0ae5d812ef9afca2f52
1 parent 9894ec1 commit 9ca2d56

5 files changed

Lines changed: 99 additions & 35 deletions

File tree

cinderx/StaticPython/classloader.c

Lines changed: 44 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -22,16 +22,6 @@
2222

2323
#ifndef WIN32
2424
#include <dlfcn.h>
25-
26-
// This is a dict containing a mapping of lib name to "handle"
27-
// as returned by `dlopen()`.
28-
// Dict[str, int]
29-
static PyObject* dlopen_cache;
30-
31-
// This is a dict containing a mapping of (lib_name, symbol_name) to
32-
// the raw address as returned by `dlsym()`.
33-
// Dict[Tuple[str, str], int]
34-
static PyObject* dlsym_cache;
3525
#endif
3626

3727
int used_in_vtable(PyObject* value);
@@ -638,20 +628,29 @@ int _PyClassLoader_HasPrimitiveArgs(PyCodeObject* code) {
638628
}
639629

640630
#ifndef WIN32
641-
static PyObject* invoke_native_helper = NULL;
642631

643632
static inline int import_invoke_native() {
644-
if (__builtin_expect(invoke_native_helper == NULL, 0)) {
633+
if (__builtin_expect(Ci_GetInvokeNativeHelper() == NULL, 0)) {
645634
PyObject* native_utils = PyImport_ImportModule("__static__.native_utils");
646635
if (native_utils == NULL) {
647636
return -1;
648637
}
649-
invoke_native_helper =
650-
PyObject_GetAttrString(native_utils, "invoke_native");
638+
PyObject* helper = PyObject_GetAttrString(native_utils, "invoke_native");
651639
Py_DECREF(native_utils);
652-
if (invoke_native_helper == NULL) {
640+
if (helper == NULL) {
641+
return -1;
642+
}
643+
if (!PyFunction_Check(helper)) {
644+
PyErr_Format(
645+
PyExc_TypeError,
646+
"Expected __static__.native_utils.invoke_native to be a function, "
647+
"got %s",
648+
Py_TYPE(helper)->tp_name);
649+
Py_DECREF(helper);
653650
return -1;
654651
}
652+
Ci_SetInvokeNativeHelper((PyFunctionObject*)helper);
653+
Py_DECREF(helper);
655654
}
656655
return 0;
657656
}
@@ -707,7 +706,7 @@ PyObject* _PyClassloader_InvokeNativeFunction(
707706
return NULL;
708707
}
709708
PyObject* res = PyObject_CallFunction(
710-
invoke_native_helper,
709+
Ci_GetInvokeNativeHelper(),
711710
"OOOO",
712711
lib_name,
713712
symbol_name,
@@ -720,41 +719,45 @@ PyObject* _PyClassloader_InvokeNativeFunction(
720719

721720
// Returns the size of the dlsym_cache dict (0 if uninitialized)
722721
PyObject* _PyClassloader_SizeOf_DlSym_Cache() {
723-
if (dlsym_cache == NULL) {
722+
PyObject* cache = Ci_GetDlsymCache();
723+
if (cache == NULL) {
724724
return PyLong_FromLong(0);
725725
}
726-
Py_ssize_t size = PyDict_Size(dlsym_cache);
726+
Py_ssize_t size = PyDict_Size(cache);
727727
return PyLong_FromSsize_t(size);
728728
}
729729

730730
// Returns the size of the dlopen_cache dict (0 if uninitialized)
731731
PyObject* _PyClassloader_SizeOf_DlOpen_Cache() {
732-
if (dlopen_cache == NULL) {
732+
PyObject* cache = Ci_GetDlopenCache();
733+
if (cache == NULL) {
733734
return PyLong_FromLong(0);
734735
}
735-
Py_ssize_t size = PyDict_Size(dlopen_cache);
736+
Py_ssize_t size = PyDict_Size(cache);
736737
return PyLong_FromSsize_t(size);
737738
}
738739

739740
// Clears the dlsym_cache dict
740741
void _PyClassloader_Clear_DlSym_Cache() {
741-
if (dlsym_cache != NULL) {
742-
PyDict_Clear(dlsym_cache);
742+
PyObject* cache = Ci_GetDlsymCache();
743+
if (cache != NULL) {
744+
PyDict_Clear(cache);
743745
}
744746
}
745747

746748
// Clears the dlopen_cache dict
747749
void _PyClassloader_Clear_DlOpen_Cache() {
748-
if (dlopen_cache != NULL) {
750+
PyObject* cache = Ci_GetDlopenCache();
751+
if (cache != NULL) {
749752
PyObject *name, *handle;
750753
Py_ssize_t i = 0;
751-
while (PyDict_Next(dlopen_cache, &i, &name, &handle)) {
754+
while (PyDict_Next(cache, &i, &name, &handle)) {
752755
void* raw_handle = PyLong_AsVoidPtr(handle);
753756
// Ignore errors - we can't do much even if they occur
754757
dlclose(raw_handle);
755758
}
756759

757-
PyDict_Clear(dlopen_cache);
760+
PyDict_Clear(cache);
758761
}
759762
}
760763

@@ -784,14 +787,17 @@ static void* classloader_lookup_sharedlib(PyObject* lib_name) {
784787
PyObject* val = NULL;
785788

786789
// Ensure cache exists
787-
if (dlopen_cache == NULL) {
788-
dlopen_cache = PyDict_New();
789-
if (dlopen_cache == NULL) {
790+
PyObject* dl_cache = Ci_GetDlopenCache();
791+
if (dl_cache == NULL) {
792+
dl_cache = PyDict_New();
793+
if (dl_cache == NULL) {
790794
return NULL;
791795
}
796+
Ci_SetDlopenCache((PyDictObject*)dl_cache);
797+
Py_DECREF(dl_cache);
792798
}
793799

794-
val = PyDict_GetItem(dlopen_cache, lib_name);
800+
val = PyDict_GetItem(dl_cache, lib_name);
795801
if (val != NULL) {
796802
// Cache hit
797803
return PyLong_AsVoidPtr(val);
@@ -808,7 +814,7 @@ static void* classloader_lookup_sharedlib(PyObject* lib_name) {
808814
if (val == NULL) {
809815
return NULL;
810816
}
811-
int res = PyDict_SetItem(dlopen_cache, lib_name, val);
817+
int res = PyDict_SetItem(dl_cache, lib_name, val);
812818
Py_DECREF(val);
813819
if (res < 0) {
814820
return NULL;
@@ -875,19 +881,22 @@ void* _PyClassloader_LookupSymbol(PyObject* lib_name, PyObject* symbol_name) {
875881
}
876882

877883
// Ensure cache exists
878-
if (dlsym_cache == NULL) {
879-
dlsym_cache = PyDict_New();
880-
if (dlsym_cache == NULL) {
884+
PyObject* sym_cache = Ci_GetDlsymCache();
885+
if (sym_cache == NULL) {
886+
sym_cache = PyDict_New();
887+
if (sym_cache == NULL) {
881888
return NULL;
882889
}
890+
Ci_SetDlsymCache((PyDictObject*)sym_cache);
891+
Py_DECREF(sym_cache);
883892
}
884893

885894
PyObject* key = PyTuple_Pack(2, lib_name, symbol_name);
886895
if (key == NULL) {
887896
return NULL;
888897
}
889898

890-
PyObject* res = PyDict_GetItem(dlsym_cache, key);
899+
PyObject* res = PyDict_GetItem(sym_cache, key);
891900

892901
if (res != NULL) {
893902
Py_DECREF(key);
@@ -900,7 +909,7 @@ void* _PyClassloader_LookupSymbol(PyObject* lib_name, PyObject* symbol_name) {
900909
return NULL;
901910
}
902911

903-
if (PyDict_SetItem(dlsym_cache, key, res) < 0) {
912+
if (PyDict_SetItem(sym_cache, key, res) < 0) {
904913
Py_DECREF(key);
905914
Py_DECREF(res);
906915
return NULL;

cinderx/module_c_state.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,34 @@ void Ci_AddTypeIndexOffset(int32_t offset) {
103103
cinderx::getModuleState()->type_index_offset += offset;
104104
}
105105

106+
PyObject* Ci_GetDlopenCache(void) {
107+
auto state = cinderx::getModuleState();
108+
return state != nullptr ? state->dlopen_cache.getObj() : nullptr;
109+
}
110+
111+
void Ci_SetDlopenCache(PyDictObject* cache) {
112+
cinderx::getModuleState()->dlopen_cache = Ref<PyDictObject>::create(cache);
113+
}
114+
115+
PyObject* Ci_GetDlsymCache(void) {
116+
auto state = cinderx::getModuleState();
117+
return state != nullptr ? state->dlsym_cache.getObj() : nullptr;
118+
}
119+
120+
void Ci_SetDlsymCache(PyDictObject* cache) {
121+
cinderx::getModuleState()->dlsym_cache = Ref<PyDictObject>::create(cache);
122+
}
123+
124+
PyObject* Ci_GetInvokeNativeHelper(void) {
125+
auto state = cinderx::getModuleState();
126+
return state != nullptr ? state->invoke_native_helper.getObj() : nullptr;
127+
}
128+
129+
void Ci_SetInvokeNativeHelper(PyFunctionObject* helper) {
130+
cinderx::getModuleState()->invoke_native_helper =
131+
Ref<PyFunctionObject>::create(helper);
132+
}
133+
106134
int Ci_Watchers_WatchDict(PyObject* dict) {
107135
return cinderx::getModuleState()->watcher_state.watchDict(dict);
108136
}

cinderx/module_c_state.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,18 @@ void Ci_ClearValueIndices(void);
5252
int32_t Ci_GetTypeIndexOffset(void);
5353
void Ci_AddTypeIndexOffset(int32_t offset);
5454

55+
// dlopen handle cache (lib_name -> handle).
56+
PyObject* Ci_GetDlopenCache(void);
57+
void Ci_SetDlopenCache(PyDictObject* cache);
58+
59+
// dlsym address cache ((lib_name, symbol_name) -> address).
60+
PyObject* Ci_GetDlsymCache(void);
61+
void Ci_SetDlsymCache(PyDictObject* cache);
62+
63+
// Cached reference to __static__.native_utils.invoke_native.
64+
PyObject* Ci_GetInvokeNativeHelper(void);
65+
void Ci_SetInvokeNativeHelper(PyFunctionObject* helper);
66+
5567
// WatcherState.
5668

5769
int Ci_Watchers_WatchDict(PyObject* dict);

cinderx/module_state.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ int ModuleState::traverse(visitproc visit, void* arg) {
1919
Py_VISIT(classloader_cache_module_to_keys);
2020
Py_VISIT(value_cache);
2121
Py_VISIT(value_indices);
22+
Py_VISIT(dlopen_cache);
23+
Py_VISIT(dlsym_cache);
24+
Py_VISIT(invoke_native_helper);
2225
Py_VISIT(builtin_next);
2326
return 0;
2427
}
@@ -30,6 +33,9 @@ int ModuleState::clear() {
3033
classloader_cache_module_to_keys.reset();
3134
value_cache.reset();
3235
value_indices.reset();
36+
dlopen_cache.reset();
37+
dlsym_cache.reset();
38+
invoke_native_helper.reset();
3339
sys_clear_caches.reset();
3440
builtin_next.reset();
3541
return 0;

cinderx/module_state.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,15 @@ struct ModuleState {
7676
// Running offset for assigning type indices to Static Python types.
7777
int32_t type_index_offset{0};
7878

79+
// Cache of dlopen'd shared library handles for invoke_native.
80+
Ref<PyDictObject> dlopen_cache;
81+
82+
// Cache of dlsym'd function pointers for invoke_native.
83+
Ref<PyDictObject> dlsym_cache;
84+
85+
// Python helper function used by the invoke_native implementation.
86+
Ref<PyFunctionObject> invoke_native_helper;
87+
7988
// Snapshotted member dicts for standard builtin types (int, str, list, etc.)
8089
// so the JIT optimizer can look up methods during multithreaded compilation
8190
// without calling PyType_Lookup (which isn't safe off the main thread).

0 commit comments

Comments
 (0)