Skip to content

Allow (compile-time) conditional "with nogil" blocks #2579

@scoder

Description

@scoder

Especially when using fused types, some code section may or may not require the GIL. Currently, the only way to release the GIL in these cases is to duplicate the code sections and wrap one in a with nogil block but not the other, e.g.

    if fusedtype is object:
        ...  # complicated code here
    else:
        with nogil:
            ... # complicated code repeated here

The normal reflex of moving the code into a cdef function also does not work out because the function then has the same declaration problem.

At least for cases where the distinction is known at compile time (which includes type checks for fused types), there should be a way to base the nogil case on a condition.

Constraints:

The solution should be as obvious from a Python perspective as possible. with nogil is currently special-cased in Cython, but that shouldn't impact its use. An obvious approach in Python would be, for example:

    cm = nogil if condition else dummy
    with cm:
        ...

but this is difficult to match with the need for special casing nogil in Cython at compile time.

Also, while an initial implementation could be restricted to compile time constant conditions, the same syntax should still be applicable when allowing the decision to be taken at runtime (by implicitly generating a duplicate code block in C).

nogil is not only a (special) context manager in Cython but also a decorator, so nogil(True) might not be easy to distinguish from @nogil, especially in pure Python code (Shadow.py). Still, also allowing @nogil(True) as a decorator would be nice.

Approach:

Given that directives like with boundscheck(True) already exist, with nogil(True) is probably the way to go. Thus, allow with nogil(flag) and detect the decorator case in pure Python mode by checking for callables in the dummy implementation in Shadow.py.

The InterpretCompilerDirectives transform in ParseTreeTransforms.py is the place where compiler directives are evaluated. For with nogil and with gil, a Nodes.GILStatNode is created. Note that this is run before transforming normal with-statements (with is not relevant for this special case) and constant folding (in the ConstantFolding tree transform, which determines the compile time value of the condition), and also long before type analysis (AnalyseExpressionsTransform) where fused types are evaluated (see the order in Pipeline.py), so the directive condition will not be finally known until constant folding and type analysis have run, and must therefore be stored as a syntax tree node instead of a boolean flag.

The type analysis step should be able to take the final decision about the condition value in Nodes.GILStatNode. However, it could well be that certain syntax tree nodes in the code block have already decided by then that they are in a nogil context. That might need to be revised to make sure they only take that decision after the compile time condition is evaluated and the final GIL state known.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions