Skip to content

Commit fbeb500

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

File tree

1 file changed

+91
-1
lines changed

1 file changed

+91
-1
lines changed

src/main.cpp

Lines changed: 91 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,100 @@ 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+
// Saves the current thread state, releasing the GIL
316+
// Does not otherwise alter thread state in any way
317+
python_gil_unlock()
318+
: _previous_thread_state(PyEval_SaveThread())
319+
{ }
320+
321+
// Calls release on gil_lock and, if necessary, saves the current thread state
322+
// if should_reacquire is true, reacquire is called on gil_lock during destruction
323+
python_gil_unlock(python_gil_lock* const gil_lock, bool should_reacquire)
324+
: _gil_lock(gil_lock), _should_reacquire(should_reacquire)
325+
{
326+
if (_gil_lock->release() != nullptr) {
327+
_previous_thread_state = PyEval_SaveThread();
328+
}
329+
}
330+
331+
~python_gil_unlock()
332+
{
333+
if (_previous_thread_state != nullptr) {
334+
PyEval_RestoreThread(_previous_thread_state);
335+
}
336+
if (_should_reacquire) {
337+
assert(_gil_lock != nullptr);
338+
_gil_lock->reacquire();
339+
}
340+
}
341+
342+
python_gil_unlock(const python_gil_unlock&) = delete;
343+
344+
python_gil_unlock& operator=(const python_gil_unlock&) = delete;
345+
346+
private:
347+
PyThreadState* _previous_thread_state = nullptr;
348+
python_gil_lock* const _gil_lock = nullptr;
349+
const bool _should_reacquire = false;
350+
351+
}; //class python_gil_unlock
352+
269353
struct RuleCallWrapper
270354
{
271355
RuleCallWrapper(irods::callback& effect_handler, std::string rule_name)
@@ -538,6 +622,7 @@ static irods::error rule_exists(const irods::default_re_ctx&, const std::string&
538622
}
539623
catch (const bp::error_already_set&) {
540624
const std::string formatted_python_exception = extract_python_exception();
625+
python_gil_unlock gil_release(&gil_lock, false);
541626
// clang-format off
542627
log_re::error({
543628
{"rule_engine_plugin", rule_engine_name},
@@ -583,6 +668,7 @@ static irods::error list_rules(const irods::default_re_ctx&, std::vector<std::st
583668
}
584669
catch (const bp::error_already_set&) {
585670
const std::string formatted_python_exception = extract_python_exception();
671+
python_gil_unlock gil_release(&gil_lock, false);
586672
// clang-format off
587673
log_re::error({
588674
{"rule_engine_plugin", rule_engine_name},
@@ -660,6 +746,7 @@ static irods::error exec_rule(const irods::default_re_ctx&,
660746
catch (const bp::error_already_set&) {
661747
const std::string formatted_python_exception = extract_python_exception();
662748
// clang-format off
749+
python_gil_unlock gil_release(&gil_lock, false);
663750
log_re::error({
664751
{"rule_engine_plugin", rule_engine_name},
665752
{"log_message", "caught python exception"},
@@ -849,6 +936,7 @@ static irods::error exec_rule_text(const irods::default_re_ctx&,
849936
rule_function(rule_arguments_python, CallbackWrapper{effect_handler}, rei));
850937
}
851938
else {
939+
python_gil_unlock gil_release(&gil_lock, false);
852940
// clang-format off
853941
log_re::error({
854942
{"rule_engine_plugin", rule_engine_name},
@@ -860,6 +948,7 @@ static irods::error exec_rule_text(const irods::default_re_ctx&,
860948
}
861949
catch (const bp::error_already_set&) {
862950
const std::string formatted_python_exception = extract_python_exception();
951+
python_gil_unlock gil_release(&gil_lock, false);
863952
// clang-format off
864953
log_re::error({
865954
{"rule_engine_plugin", rule_engine_name},
@@ -971,6 +1060,7 @@ static irods::error exec_rule_expression(irods::default_re_ctx&,
9711060
}
9721061
catch (const bp::error_already_set&) {
9731062
const std::string formatted_python_exception = extract_python_exception();
1063+
python_gil_unlock gil_release(&gil_lock, false);
9741064
// clang-format off
9751065
log_re::error({
9761066
{"rule_engine_plugin", rule_engine_name},

0 commit comments

Comments
 (0)