From 56b74ecf45f63abefa5b8dddcd70ec78033ee082 Mon Sep 17 00:00:00 2001 From: Tomasz Kalinowski Date: Wed, 7 Aug 2024 15:38:54 -0400 Subject: [PATCH] add `py_allow_threads()` function --- R/RcppExports.R | 4 ++++ R/threads.R | 16 ++++++++++++++++ src/RcppExports.cpp | 12 ++++++++++++ src/python.cpp | 16 +++++++++++++++- src/reticulate_types.h | 5 ----- 5 files changed, 47 insertions(+), 6 deletions(-) create mode 100644 R/threads.R diff --git a/R/RcppExports.R b/R/RcppExports.R index e6eb3dbaf..c5047e3af 100644 --- a/R/RcppExports.R +++ b/R/RcppExports.R @@ -365,6 +365,10 @@ py_iterate <- function(x, f, simplify = TRUE) { .Call(`_reticulate_py_iterate`, x, f, simplify) } +py_allow_threads_impl <- function(allow) { + .Call(`_reticulate_py_allow_threads_impl`, allow) +} + readline <- function(prompt) { .Call(`_reticulate_readline`, prompt) } diff --git a/R/threads.R b/R/threads.R new file mode 100644 index 000000000..c3ee58bee --- /dev/null +++ b/R/threads.R @@ -0,0 +1,16 @@ + + +py_allow_threads <- function(allow = TRUE) { + if (allow) { + reticulate_ns <- environment(sys.function()) + for (f in sys.frames()) { + if (identical(parent.env(f), reticulate_ns) && + !identical(f, environment())) + # Can't release the gil as unlocked while we're holding it + # elsewhere on the callstack. + stop("Unblocking python threads only allowed from as a top-level reticulate call") + } + } + invisible(py_allow_threads_impl(allow)) +} + diff --git a/src/RcppExports.cpp b/src/RcppExports.cpp index 3750cd3c1..7c1affef1 100644 --- a/src/RcppExports.cpp +++ b/src/RcppExports.cpp @@ -812,6 +812,17 @@ BEGIN_RCPP return rcpp_result_gen; END_RCPP } +// py_allow_threads_impl +bool py_allow_threads_impl(bool allow); +RcppExport SEXP _reticulate_py_allow_threads_impl(SEXP allowSEXP) { +BEGIN_RCPP + Rcpp::RObject rcpp_result_gen; + Rcpp::RNGScope rcpp_rngScope_gen; + Rcpp::traits::input_parameter< bool >::type allow(allowSEXP); + rcpp_result_gen = Rcpp::wrap(py_allow_threads_impl(allow)); + return rcpp_result_gen; +END_RCPP +} // readline SEXP readline(const std::string& prompt); RcppExport SEXP _reticulate_readline(SEXP promptSEXP) { @@ -895,6 +906,7 @@ static const R_CallMethodDef CallEntries[] = { {"_reticulate_as_iterator", (DL_FUNC) &_reticulate_as_iterator, 1}, {"_reticulate_py_iter_next", (DL_FUNC) &_reticulate_py_iter_next, 2}, {"_reticulate_py_iterate", (DL_FUNC) &_reticulate_py_iterate, 3}, + {"_reticulate_py_allow_threads_impl", (DL_FUNC) &_reticulate_py_allow_threads_impl, 1}, {"_reticulate_readline", (DL_FUNC) &_reticulate_readline, 1}, {NULL, NULL, 0} }; diff --git a/src/python.cpp b/src/python.cpp index a5a673a41..85a652d76 100644 --- a/src/python.cpp +++ b/src/python.cpp @@ -2868,7 +2868,7 @@ void py_initialize(const std::string& python, s_main_thread = tthread::this_thread::get_id(); s_is_python_initialized = true; - GILScope scope(/*restore=*/ PyGILState_UNLOCKED); + GILScope _gil; // initialize type objects initialize_type_objects(is_python3()); @@ -4477,3 +4477,17 @@ SEXP py_exception_as_condition(PyObject* object, SEXP refenv) { UNPROTECT(1); return out; } + + +// [[Rcpp::export]] +bool py_allow_threads_impl(bool allow) { + PyGILState_STATE gstate = PyGILState_Ensure(); + if (allow) { + PyGILState_Release(PyGILState_UNLOCKED); + } else { + PyGILState_Release(PyGILState_LOCKED); + } + return gstate == PyGILState_UNLOCKED; +} + + diff --git a/src/reticulate_types.h b/src/reticulate_types.h index 277bd993d..3ae1c7dfe 100644 --- a/src/reticulate_types.h +++ b/src/reticulate_types.h @@ -164,11 +164,6 @@ class GILScope { gstate = PyGILState_Ensure(); } - GILScope(PyGILState_STATE restore_state) { - PyGILState_Ensure(); - gstate = restore_state; - } - ~GILScope() { PyGILState_Release(gstate); }