Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid thread unsafe borrowed references under free-threading #6178

Open
wants to merge 16 commits into
base: master
Choose a base branch
from

Conversation

lysnikolaou
Copy link
Contributor

No description provided.

Copy link
Contributor

@da-woods da-woods left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Linking #6164 because it's essentially this. I think I'm fine with the new macro rather than using two levels of the existing one though.

When the details of this are decided, can you add it to the documentation (https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html#c-macro-defines) since we've now tried to document these macros.

I wonder if a bunch of duplication could be avoided by defining in the module setup code:

#if CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS && PY_VERSION_HEX >= 0x030D0000  // adjust version hex here!
#define __Pyx_PyList_GetItemRef(o, i) Py_GetItemRef(o, i)
#else
#define __Pyx_PyList_GetItemRef(o, i) __Pyx_NewRef(PyList_GET_ITEM(o, i))
#endif

your current version has a lot of duplicated code which could be compacted quite a bit.

Cython/Compiler/ExprNodes.py Outdated Show resolved Hide resolved
Cython/Compiler/ExprNodes.py Outdated Show resolved Hide resolved
Cython/Utility/ObjectHandling.c Outdated Show resolved Hide resolved
Cython/Utility/ObjectHandling.c Outdated Show resolved Hide resolved
@lysnikolaou
Copy link
Contributor Author

I wonder if a bunch of duplication could be avoided by defining in the module setup code:

That's a good idea! Thanks @da-woods for the pointer. It also takes care of the version guard @scoder mentioned in his review comments.

@scoder
Copy link
Contributor

scoder commented May 6, 2024 via email

@lysnikolaou
Copy link
Contributor Author

I've pushed a commit with the changes suggested. My feeling is a more serious cleanup of all of this is needed, which I can do in a follow-up PR.

@lysnikolaou lysnikolaou force-pushed the nogil-unsafe-borrowed-references branch from 433d51b to feb7769 Compare May 7, 2024 11:15
Cython/Compiler/ExprNodes.py Outdated Show resolved Hide resolved
Cython/Utility/CythonFunction.c Outdated Show resolved Hide resolved
Cython/Utility/ModuleSetupCode.c Outdated Show resolved Hide resolved
Cython/Utility/ModuleSetupCode.c Outdated Show resolved Hide resolved
Cython/Utility/ModuleSetupCode.c Outdated Show resolved Hide resolved
Cython/Utility/CythonFunction.c Outdated Show resolved Hide resolved
Cython/Utility/TypeConversion.c Outdated Show resolved Hide resolved
Copy link
Contributor

@da-woods da-woods left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few comments but I think this mostly looks reasonable to me.

I'm going to leave this and wait for @scoder to look at it I think

Comment on lines 454 to 456
if ((!boundscheck) || likely(__Pyx_is_valid_index(wrapped_i, PyList_GET_SIZE(o)))) {
return __Pyx_PyList_GetItemRef(o, wrapped_i);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've created an issue about this #6191

It seems to me that users might want to be able to opt back in to the unsafe optimized version. Just using the safe version is probably OK for this PR though

Cython/Utility/ObjectHandling.c Outdated Show resolved Hide resolved
Cython/Utility/ObjectHandling.c Outdated Show resolved Hide resolved
Cython/Utility/ObjectHandling.c Outdated Show resolved Hide resolved
static CYTHON_INLINE PyObject *__Pyx_GetItemInt_List_Fast(PyObject *o, Py_ssize_t i,
CYTHON_NCP_UNUSED int wraparound,
CYTHON_NCP_UNUSED int boundscheck) {
#if CYTHON_ASSUME_SAFE_MACROS && CYTHON_ASSUME_SAFE_SIZE && !CYTHON_AVOID_BORROWED_REFS && !CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
Copy link
Contributor

@ngoldbaum ngoldbaum May 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you avoid the duplication by keeping the templating that was here before and conditionally insert && !CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS based on the value of type? I'm no tempita expert though so take that worth a grain of salt.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I also don't know enough about tempita to know whether this is possible. I'll look into it.

docs/src/userguide/source_files_and_compilation.rst Outdated Show resolved Hide resolved
Copy link
Contributor

@scoder scoder left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haven't finished my review yet, but here are a few comments.

Comment on lines 1060 to 1063
#define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i)
static CYTHON_INLINE int __Pyx_PyDict_GetItemRef(PyObject *dict, PyObject *key, PyObject **result) {
return PyDict_GetItemRef(dict, key, result);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like these could both be macros.

Suggested change
#define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i)
static CYTHON_INLINE int __Pyx_PyDict_GetItemRef(PyObject *dict, PyObject *key, PyObject **result) {
return PyDict_GetItemRef(dict, key, result);
}
#define __Pyx_PyList_GetItemRef(o, i) PyList_GetItemRef(o, i)
#define __Pyx_PyDict_GetItemRef(dict, key, result) PyDict_GetItemRef(dict, key, result)

return PyDict_GetItemRef(dict, key, result);
}
#else
#define __Pyx_PyList_GetItemRef(o, i) __Pyx_NewRef(PyList_GET_ITEM(o, i))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we're not doing any error checking here, this should depend on CYTHON_ASSUME_SAFE_MACROS and actually also !CYTHON_AVOID_BORROWED_REFS since we're preferring a borrowed reference over a strong returned reference here.

Comment on lines 2203 to 2243
#if CYTHON_AVOID_BORROWED_REFS
#if CYTHON_AVOID_BORROWED_REFS && !CYTHON_AVOID_THREAD_UNSAFE_BORROWED_REFS
Py_INCREF(varnames_tuple_dedup);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct since we've applied a version check above. In older Pythons, the reference will not be increfed.

I'd move the error check + incref into the #else block above (and duplicate the error check in the #if block).

@lysnikolaou
Copy link
Contributor Author

Is this good to go? Any more feedback before it's okay to be merged?

Also, on another note, could we maybe do an "intermediate" release since Cython currently does not build with the free-threaded build and people seem to need it? If not, is there a plan for an upcoming release?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants