-
-
Notifications
You must be signed in to change notification settings - Fork 32k
Not able to start an instantiated Thread in a child process through multiprocessing.Process in python 3.13 #134381
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
Comments
Hi, I am a beginner contributor to the CPython project, I scan your issue and I believe this related to following function,
void
_PyThread_AfterFork(struct _pythread_runtime_state *state)
{
// gh-115035: We mark ThreadHandles as not joinable early in the child's
// after-fork handler. We do this before calling any Python code to ensure
// that it happens before any ThreadHandles are deallocated, such as by a
// GC cycle.
PyThread_ident_t current = PyThread_get_thread_ident_ex();
struct llist_node *node;
llist_for_each_safe(node, &state->handles) {
ThreadHandle *handle = llist_data(node, ThreadHandle, node);
if (handle->ident == current) {
continue;
}
// Mark all threads as done. Any attempts to join or detach the
// underlying OS thread (if any) could crash. We are the only thread;
// it's safe to set this non-atomically.
/* Issue Here, it will set `handle->state` to THREAD_HANDLE_DONE */
// handle->state = THREAD_HANDLE_DONE;
handle->once = (_PyOnceFlag){_Py_ONCE_INITIALIZED};
handle->mutex = (PyMutex){_Py_UNLOCKED};
_PyEvent_Notify(&handle->thread_is_exiting);
llist_remove(node);
remove_from_shutdown_handles(handle);
}
} There is a related PR to this topic: gh-114271 If I've made any mistakes, please point them out and I'll continue to explore the issue. |
The problem is that cpython/Modules/_threadmodule.c Line 302 in e6dde10
We need to ignore threads that haven't started. That said, doing this sort of thing is incredibly weird. What's the use-case for creating a |
In my particular scenario, I'm running tests in client/server project. Test code for the client executes server code in a Process instance, as a precondition. Some parts of the server are implemented as Thread subclasses. |
Hi everyone, I was thinking about this today. Based on an idea from @ZeroIntensity. (It's probably a very basic implement), since this is a thread that hasn't been started yet, I can't think of any harm in keeping it. (The only trouble could be we will need another GC cycle to cleanup the dead thread) void
_PyThread_AfterFork(struct _pythread_runtime_state *state)
{
// gh-115035: We mark ThreadHandles as not joinable early in the child's
// after-fork handler. We do this before calling any Python code to ensure
// that it happens before any ThreadHandles are deallocated, such as by a
// GC cycle.
PyThread_ident_t current = PyThread_get_thread_ident_ex();
struct llist_node *node;
llist_for_each_safe(node, &state->handles) {
ThreadHandle *handle = llist_data(node, ThreadHandle, node);
if (handle->ident == current) {
continue;
}
+ // keep not_start handles – they are safe to start in the child
+ if (handle->state == THREAD_HANDLE_NOT_STARTED){
+ continue;
+ }
// Mark all threads as done. Any attempts to join or detach the
// underlying OS thread (if any) could crash. We are the only thread;
// it's safe to set this non-atomically.
handle->state = THREAD_HANDLE_DONE;
handle->once = (_PyOnceFlag){_Py_ONCE_INITIALIZED};
handle->mutex = (PyMutex){_Py_UNLOCKED};
_PyEvent_Notify(&handle->thread_is_exiting);
llist_remove(node);
remove_from_shutdown_handles(handle);
}
} |
Sounds reasonable to me. |
Hi there, I wish to start draft a PR for this issue. Should I target to branch This only fix the issue on 3.13 now, but I will also keep observe more detail about the new issue on Thanks for your help @ZeroIntensity @anadrianmanrique |
Put up a PR for the main branch. If we decide to fix this for older versions, we can automatically backport the fix. |
Done, thanks so much :) |
Thank you all for the quick response/fix |
This pattern will only work with "fork", not "spawn" because you can't pickle a |
@colesbury Thanks a bunch! I was also wondering why this crashed. I learned something new, thanks! |
…d after fork (pythongh-134514) (cherry picked from commit 9a2346d) Co-authored-by: Jiucheng(Oliver) <[email protected]>
…d after fork (pythongh-134514) (cherry picked from commit 9a2346d) Co-authored-by: Jiucheng(Oliver) <[email protected]>
…ad after fork (gh-134514) (gh-134597) (cherry picked from commit 9a2346d) Co-authored-by: Jiucheng(Oliver) <[email protected]>
…ad after fork (gh-134514) (gh-134596) (cherry picked from commit 9a2346d) Co-authored-by: Jiucheng(Oliver) <[email protected]>
* Update build_and_test.yml added python 3.13 removed python 3.8 * workaround delying thread initialization to avoid python3.13 bug * workaround delaying thread initialization to avoid python3.13 bug * avoid running Process instances beacuse of a bug in python3.13 python/cpython#134381 * removed hack * make helper servers run as daemon in order to make testecases not to get stuck
Uh oh!
There was an error while loading. Please reload this page.
Bug report
Bug description:
I'm experiencing an issue specifically in python 3.13 regarding Threads in the context of multiprocessing.
The following code is working in python < 3.13
After executing the above in python 3.13 I get this output:
I managed to workaround this by delaying the Thread instance initialization, moving the Thread.init() call to start() method in the derived class, so everything gets executed in the context of the child process.
CPython versions tested on:
3.13
Operating systems tested on:
Linux
Linked PRs
The text was updated successfully, but these errors were encountered: