Skip to content

Add support for global interpreter lock #10

@glopesdev

Description

@glopesdev

The current library assumes implicit locking of interpreter operations such as Exec, Eval, Get, Set, etc. While this may be convenient from a workflow developer's perspective, there are both performance and long-term maintenance implications of this choice which we should consider.

  • Because each operation acquires and releases the GIL implicitly, there is a performance cost to chaining a sequence of operations, even if they are guaranteed to be synchronous. For example, it is exceedingly common to use a combination of Set > Eval > Get as a simple idiomatic wrapper around a function call.
  • Starting from Python 3.12, it is possible to create sub-interpreters with their own independent per-interpreter GIL. While this feature is not supported yet in Python.NET, reading both the C API documentation and related discussions indicates that this feature is fundamentally incompatible with using the GIL for interop.

This suggests we might want to review our existing approach to implicitly acquire the GIL inside these operators, and instead consider introducing our own GIL operator which can be used to explicitly acquire the lock so we can work with current versions of Python.NET.

The current proposal is to implement a nested operator with close semantics to Defer (accepting zero or one arguments), to ensure the following:

  • The nested workflow is subscribed to only after the Python runtime has initialized, with subscription execution itself surrounded with a call to Py.GIL.
  • If an input is present, all notifications raised into the nested workflow are to be surrounded with a call to Py.GIL. Asynchronous operations inside the nested workflow will not be protected.

An outstanding question is re. naming of this operator, with three options currently under consideration:

  • PythonInterpreterLock
  • GlobalInterpreterLock
  • EnsureGIL

With the indications pointing that there will actually only ever be one GIL, I am currently leaning towards the latter two, since PythonInterpreterLock might leave the wrong impression that this operator would later be extended to work with individual sub-interpreter GILs. Instead, it looks like sub-interpreters will need to be accessed from within the thread that first created them, and this synchronization mechanism might be enough.

Between GlobalInterpreterLock and EnsureGIL, the former might be preferable, since EnsureGIL might also give the wrong impression that the entirety of the nested workflow runs under the lock, which is not the current design.

We are hoping to make this transition very soon with a draft implementation, but other naming suggestions are welcome.

Metadata

Metadata

Assignees

No one assigned

    Labels

    criticalThis is a blocking issue affecting all usersfeatureNew planned feature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions