Skip to content

Commit 21e5b07

Browse files
committed
[WIP] [218] Implement early/temporary GIL releasing/unlocking
1 parent e48aeb5 commit 21e5b07

File tree

1 file changed

+88
-1
lines changed

1 file changed

+88
-1
lines changed

src/main.cpp

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// include this first to fix macro redef warnings
22
#include <pyconfig.h>
33

4+
#include <cassert>
45
#include <cstdint>
56
#include <cstring>
67
#include <ctime>
@@ -27,6 +28,7 @@
2728
#include <irods/rodsErrorTable.h>
2829
#include <irods/irods_default_paths.hpp>
2930
#include <irods/irods_error.hpp>
31+
#include <irods/irods_exception.hpp>
3032
#include <irods/irods_logger.hpp>
3133
#include <irods/irods_re_plugin.hpp>
3234
#include <irods/irods_re_structs.hpp>
@@ -254,18 +256,97 @@ namespace
254256

255257
~python_gil_lock()
256258
{
257-
PyGILState_Release(_previous_gil_state);
259+
if (!_released) {
260+
PyGILState_Release(_previous_gil_state);
261+
}
258262
}
259263

260264
python_gil_lock(const python_gil_lock&) = delete;
261265

262266
python_gil_lock& operator=(const python_gil_lock&) = delete;
263267

268+
inline bool released()
269+
{
270+
return _released;
271+
}
272+
273+
// restores python state to prior to last PyGILState_Ensure call
274+
// returns active PyThreadState
275+
// doesn't necessarily release the GIL, only this instance's hold on it.
276+
// if GIL was acquired further up the stack, it will still be locked.
277+
inline PyThreadState* release()
278+
{
279+
if (_released) {
280+
THROW(RULE_ENGINE_ERROR, "release called on already-released python_gil_lock");
281+
}
282+
283+
PyThreadState* current_thread_state = PyThreadState_Get();
284+
if (current_thread_state->gilstate_counter <= 1) {
285+
current_thread_state = nullptr;
286+
}
287+
288+
PyGILState_Release(_previous_gil_state);
289+
290+
_released = true;
291+
return current_thread_state;
292+
}
293+
294+
// reacquires GIL after a call to release
295+
inline void reacquire()
296+
{
297+
if (_released) {
298+
THROW(RULE_ENGINE_ERROR, "reacquire called on non-released python_gil_lock");
299+
}
300+
301+
_previous_gil_state = PyGILState_Ensure();
302+
_released = false;
303+
}
304+
264305
private:
265306
PyGILState_STATE _previous_gil_state; // GIL state prior to calling PyGILState_Ensure
307+
bool _released = false; // whether or not PyGILState_Release has already been called
266308

267309
}; //class python_gil_lock
268310

311+
// Helper class that unlocks GIL while in scope
312+
class python_gil_unlock
313+
{
314+
public:
315+
316+
python_gil_unlock()
317+
: _previous_thread_state(PyEval_SaveThread())
318+
{ }
319+
320+
python_gil_lock(const python_gil_lock const* gil_lock, bool should_reacquire)
321+
: _gil_lock(gil_lock), _should_reacquire(should_reacquire)
322+
{
323+
if (_gil_lock.release() != nullptr) {
324+
_previous_thread_state = PyEval_SaveThread();
325+
}
326+
}
327+
328+
~python_gil_unlock()
329+
{
330+
if (_previous_thread_state != nullptr) {
331+
PyEval_RestoreThread(_previous_thread_state);
332+
}
333+
if (should_reacquire) {
334+
assert(_gil_lock != null);
335+
_gil_lock.reacquire();
336+
}
337+
}
338+
339+
python_gil_unlock(const python_gil_unlock&) = delete;
340+
341+
python_gil_unlock& operator=(const python_gil_unlock&) = delete;
342+
343+
private:
344+
PyThreadState* _previous_thread_state = nullptr;
345+
const python_gil_lock const* _gil_lock = nullptr;
346+
bool _should_reacquire = false;
347+
348+
}; //class python_gil_unlock
349+
269350
struct RuleCallWrapper
270351
{
271352
RuleCallWrapper(irods::callback& effect_handler, std::string rule_name)
@@ -538,6 +619,7 @@ static irods::error rule_exists(const irods::default_re_ctx&, const std::string&
538619
}
539620
catch (const bp::error_already_set&) {
540621
const std::string formatted_python_exception = extract_python_exception();
622+
python_gil_unlock gil_release(&gil_lock, false);
541623
// clang-format off
542624
log_re::error({
543625
{"rule_engine_plugin", rule_engine_name},
@@ -583,6 +665,7 @@ static irods::error list_rules(const irods::default_re_ctx&, std::vector<std::st
583665
}
584666
catch (const bp::error_already_set&) {
585667
const std::string formatted_python_exception = extract_python_exception();
668+
python_gil_unlock gil_release(&gil_lock, false);
586669
// clang-format off
587670
log_re::error({
588671
{"rule_engine_plugin", rule_engine_name},
@@ -660,6 +743,7 @@ static irods::error exec_rule(const irods::default_re_ctx&,
660743
catch (const bp::error_already_set&) {
661744
const std::string formatted_python_exception = extract_python_exception();
662745
// clang-format off
746+
python_gil_unlock gil_release(&gil_lock, false);
663747
log_re::error({
664748
{"rule_engine_plugin", rule_engine_name},
665749
{"log_message", "caught python exception"},
@@ -849,6 +933,7 @@ static irods::error exec_rule_text(const irods::default_re_ctx&,
849933
rule_function(rule_arguments_python, CallbackWrapper{effect_handler}, rei));
850934
}
851935
else {
936+
python_gil_unlock gil_release(&gil_lock, false);
852937
// clang-format off
853938
log_re::error({
854939
{"rule_engine_plugin", rule_engine_name},
@@ -860,6 +945,7 @@ static irods::error exec_rule_text(const irods::default_re_ctx&,
860945
}
861946
catch (const bp::error_already_set&) {
862947
const std::string formatted_python_exception = extract_python_exception();
948+
python_gil_unlock gil_release(&gil_lock, false);
863949
// clang-format off
864950
log_re::error({
865951
{"rule_engine_plugin", rule_engine_name},
@@ -971,6 +1057,7 @@ static irods::error exec_rule_expression(irods::default_re_ctx&,
9711057
}
9721058
catch (const bp::error_already_set&) {
9731059
const std::string formatted_python_exception = extract_python_exception();
1060+
python_gil_unlock gil_release(&gil_lock, false);
9741061
// clang-format off
9751062
log_re::error({
9761063
{"rule_engine_plugin", rule_engine_name},

0 commit comments

Comments
 (0)