Skip to content

Commit 83f08a0

Browse files
committed
[FFI][BUGFIX] Grab GIL when check env signals
This PR updates the CheckSignals function to grab GIL. This is needed because we now explicitly release gil when calling any C functions. GIL will need to be obtained otherwise we will run into segfault when checking the signal. The update now enables us to run ctrl + C in long running C functions.
1 parent 30b7b1c commit 83f08a0

File tree

4 files changed

+27
-25
lines changed

4 files changed

+27
-25
lines changed

python/tvm/_ffi/_cython/base.pxi

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,10 @@ cdef inline void* c_handle(object handle):
201201
# python env API
202202
cdef extern from "Python.h":
203203
int PyErr_CheckSignals()
204+
void* PyGILState_Ensure()
205+
void PyGILState_Release(void*)
206+
void Py_IncRef(void*)
207+
void Py_DecRef(void*)
204208

205209
cdef extern from "tvm/runtime/c_backend_api.h":
206210
int TVMBackendRegisterEnvCAPI(const char* name, void* ptr)
@@ -210,11 +214,13 @@ cdef _init_env_api():
210214
# so backend can call tvm::runtime::EnvCheckSignals to check
211215
# signal when executing a long running function.
212216
#
213-
# This feature is only enabled in cython for now due to problems of calling
214-
# these functions in ctypes.
215-
#
216-
# When the functions are not registered, the signals will be handled
217-
# only when the FFI function returns.
217+
# Also registers the gil state release and ensure as PyErr_CheckSignals
218+
# function is called with gil released and we need to regrab the gil
218219
CHECK_CALL(TVMBackendRegisterEnvCAPI(c_str("PyErr_CheckSignals"), <void*>PyErr_CheckSignals))
220+
CHECK_CALL(TVMBackendRegisterEnvCAPI(c_str("PyGILState_Ensure"), <void*>PyGILState_Ensure))
221+
CHECK_CALL(TVMBackendRegisterEnvCAPI(c_str("PyGILState_Release"), <void*>PyGILState_Release))
222+
CHECK_CALL(TVMBackendRegisterEnvCAPI(c_str("PyGILState_Release"), <void*>PyGILState_Release))
223+
CHECK_CALL(TVMBackendRegisterEnvCAPI(c_str("Py_IncRef"), <void*>Py_IncRef))
224+
CHECK_CALL(TVMBackendRegisterEnvCAPI(c_str("Py_DecRef"), <void*>Py_DecRef))
219225

220226
_init_env_api()

python/tvm/_ffi/_cython/packed_func.pxi

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -376,19 +376,3 @@ def _set_class_object_generic(object_generic_class, func_convert_to_object):
376376
global _FUNC_CONVERT_TO_OBJECT
377377
_CLASS_OBJECT_GENERIC = object_generic_class
378378
_FUNC_CONVERT_TO_OBJECT = func_convert_to_object
379-
380-
# Py_INCREF and Py_DECREF are C macros, not function objects.
381-
# Therefore, providing a wrapper function.
382-
cdef void _py_incref_wrapper(void* py_object):
383-
Py_INCREF(<object>py_object)
384-
cdef void _py_decref_wrapper(void* py_object):
385-
Py_DECREF(<object>py_object)
386-
387-
def _init_pythonapi_inc_def_ref():
388-
register_func = TVMBackendRegisterEnvCAPI
389-
register_func(c_str("Py_IncRef"), <void*>_py_incref_wrapper)
390-
register_func(c_str("Py_DecRef"), <void*>_py_decref_wrapper)
391-
register_func(c_str("PyGILState_Ensure"), <void*>PyGILState_Ensure)
392-
register_func(c_str("PyGILState_Release"), <void*>PyGILState_Release)
393-
394-
_init_pythonapi_inc_def_ref()

src/runtime/registry.cc

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,10 +183,14 @@ class EnvCAPIRegistry {
183183
// implementation of tvm::runtime::EnvCheckSignals
184184
void CheckSignals() {
185185
// check python signal to see if there are exception raised
186-
if (pyerr_check_signals != nullptr && (*pyerr_check_signals)() != 0) {
187-
// The error will let FFI know that the frontend environment
188-
// already set an error.
189-
throw EnvErrorAlreadySet("");
186+
if (pyerr_check_signals != nullptr) {
187+
// The C++ env comes without gil, so we need to grab gil here
188+
WithGIL context(this);
189+
if ((*pyerr_check_signals)() != 0) {
190+
// The error will let FFI know that the frontend environment
191+
// already set an error.
192+
throw EnvErrorAlreadySet("");
193+
}
190194
}
191195
}
192196

src/support/ffi_testing.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,14 @@ TVM_REGISTER_GLOBAL("testing.sleep_in_ffi").set_body_typed([](double timeout) {
178178
std::this_thread::sleep_for(duration);
179179
});
180180

181+
TVM_REGISTER_GLOBAL("testing.check_signals").set_body_typed([](double sleep_period) {
182+
while (true) {
183+
std::chrono::duration<int64_t, std::nano> duration(static_cast<int64_t>(sleep_period * 1e9));
184+
std::this_thread::sleep_for(duration);
185+
runtime::EnvCheckSignals();
186+
}
187+
});
188+
181189
TVM_REGISTER_GLOBAL("testing.ReturnsVariant").set_body_typed([](int x) -> Variant<String, IntImm> {
182190
if (x % 2 == 0) {
183191
return IntImm(DataType::Int(64), x / 2);

0 commit comments

Comments
 (0)