Add identity preserving pattern to critical_sections - #2453
Conversation
|
this is annoying, but at least the library and probably kernel attributes should be idempotent. But critical sections *can and will be* released (similar to the GIL although not sure what is more likely). The important thing to note here is that the final attribute setting section is self-contained and holds the lock (even if another thread may have already set the attribute or still be executing the code above).
38345fa to
1e52ae3
Compare
kkraus14
left a comment
There was a problem hiding this comment.
One concurrency concern in the lazy module loader.
| if not _h_library: | ||
| HANDLE_RETURN(get_last_error()) | ||
|
|
||
| if not self._h_library: |
There was a problem hiding this comment.
critical_section is only a publication guard here, not an exactly-once initializer. create_library_handle_from_*() releases the Python thread state via GILReleaseGuard, so two callers can both reach cuLibraryLoad*() before either publishes _h_library. This second check preserves one handle identity, but the losing LibraryHandle is discarded while its deleter currently does not call cuLibraryUnload, leaving an extra CUDA library loaded for the process. Could we use a true single-flight primitive for _lazy_load_module (for example Cython's py_safe_call_once/py_safe_once_flag, or an equivalent persistent lock) and add a free-threaded concurrent-first-load regression test?
Split from gh-2194 just to shrink it a bit.
These commits are straight-forward and an attempt to guarantee identity results (not a single creation of the internal object). One of these ran into an issue in the tests. It probably doesn't matter for most, but it also doesn't hurt.
To explain what is going on: Critical sections are like the GIL. When we call arbitrary Python code they can be suspended (they definitely will be suspended in
with nogil:, i.e. when the Python thread-state is detached!).So, the callbacks can run in multiple threads, but after that we double-check and
if self.attr is None: self.attr = objis all pure C code, so only one thread can hold the critical section lock for that chunk of code.