-
Notifications
You must be signed in to change notification settings - Fork 691
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
Fix #1978: Support Python 3.7's new pre- and post-fork handlers #1995
base: master
Are you sure you want to change the base?
Fix #1978: Support Python 3.7's new pre- and post-fork handlers #1995
Conversation
[bump] Could I get some 👀 on this? Thanks! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The changes looks fine to me but there's a bit that puzzles me. We are adding a new callback for the plugins to set when we have a uwsgi hook just one line above below which would be very cool to reuse if possible.
I'm not sure I understand what bit puzzles you. Could you elaborate more? It sounds like you're saying that it would be very cool to re-use the new |
What I mean is that it would be cool to reuse, if possible, the hook at master.c:643 https://github.com/unbit/uwsgi/pull/1995/files#diff-d752b2bf332e435717f74037eb981f1fL642 instead of adding the master_start callback in the uwsgi_plugin struct. |
Oh, so you're talking about this line, which is already there?
I thought about that at first, but I couldn't figure out how to detect |
@xrmx: Per my most recent comment about two weeks ago, could you advise on how I can change this to work as you suggest? I can't figure out how to make what you suggest work. |
@nickwilliams-eventbrite AFAIK there's no interface for internal callers to use the hook system. Maybe @unbit can chime in to sort out if it's a good idea or not? |
|
||
#ifdef REQUIRES_PyOS_BeforeAndAfterFork_ParentAndChild | ||
if (getpid() == masterpid) { | ||
uwsgi_log("*** WARNING: post_fork called for masterpid (pid: %d)! ***\n", uwsgi.mypid); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
without master this will print the warning, we should probably check uwsgi.master_process
spawned uWSGI worker 1 (and the only) (pid: 16127, cores: 1)
*** WARNING: post_fork called for masterpid (pid: 16127)! ***
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So do you think this is correct?
if (getpid() == masterpid) {
if (uwsgi.master_process) {
uwsgi_log("*** WARNING: post_fork called for masterpid (pid: %d)! ***\n", uwsgi.mypid);
}
} else {
// debug log if enabled
PyOS_AfterFork_Child();
}
Or should I do this (doesn't seem right to me, but just checking)?
if (getpid() == masterpid && uwsgi.master_process) {
uwsgi_log("*** WARNING: post_fork called for masterpid (pid: %d)! ***\n", uwsgi.mypid);
} else {
// debug log if enabled
PyOS_AfterFork_Child();
}
@@ -194,7 +195,7 @@ struct uwsgi_option uwsgi_python_options[] = { | |||
{"py-sharedarea", required_argument, 0, "create a sharedarea from a python bytearray object of the specified size", uwsgi_opt_add_string_list, &up.sharedarea, 0}, | |||
#endif | |||
|
|||
{"py-call-osafterfork", no_argument, 0, "enable child processes running cpython to trap OS signals", uwsgi_opt_true, &up.call_osafterfork, 0}, | |||
{"py-call-osafterfork", no_argument, 0, "enable child processes running cpython to trap OS signals (ignored in Python 3.7+, because PyOS_BeforeFork and PyOS_AfterFork_* are always called)", uwsgi_opt_true, &up.call_osafterfork, 0}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd add just the (ignored in Python 3.7)
bits. Eventually we can add a more detailed explanation if it has been called with --py-call-osafterfork
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I can remove the extra verbiage.
Hi there! Thanks for this code, it really helped our issue. I have a few quick questions if you don't mind my asking:
Thanks again for all this work :D |
Hey @nickwilliams-eventbrite , thanks for taking a stab at that issue. I've looked through your changes and attempted to run them with the test app from #1978 - some thoughts as follows.
Almost certainly 3) and 2) somehow lead to 1). Hmm, hmm. I stared at it for quite a while and dabbled around a bit - see branch below. The only real solution I came up with involved adding two plugin hooks pre_fork() and post_fork_parent(). It does survive the simple and respawned test-case, though and maybe it's possible to fold https://github.com/awelzel/uwsgi/tree/before-after-fork Thoughts? @xrmx - happy to open that as a separate request if that could be useful? |
This commit works around a requirement introduced in Python 3.7 that all calls to fork() be preceeded by calls to PyOS_BeforeFork() in the parent and PyOS_AfterFork_Parent() and PyOS_AfterFork_Child() in the respective processes. Also, Python 3.7 always initialize threads, but if the option is not set on uWSGI the thread-local data is not set and it causes random crashes, therefore it is now a fatal error if that option is not set. A plethora of new hooks were added because Python specifies that after fork the first calls must be to PyOS_AfterFork_* and unfortunately uWSGI was doing a bunch of stuff before calling the existing post_fork() hook. We decided to modify the function uwsgi_fork() itself because it would make it easier to handle all use-cases: mules, spoolers, re-forking of workers, lazy apps, etc. Reference: https://docs.python.org/3.7/c-api/sys.html#c.PyOS_BeforeFork https://docs.python.org/3.7/c-api/init.html#c.PyEval_InitThreads unbit#1995 unbit#1978 https://github.com/awelzel/uwsgi/tree/before-after-fork
Remove the py-call-osafterfork setting from uwsgi.ini. This is about to be deprecated, and is buggy with Python 3.7. References: - mitodl/micromasters#4569 - unbit/uwsgi#1978 - unbit/uwsgi#1995
Remove the py-call-osafterfork setting from uwsgi.ini. This is about to be deprecated, and is buggy with Python 3.7. References: - mitodl/micromasters#4569 - unbit/uwsgi#1978 - unbit/uwsgi#1995
This commit works around a requirement introduced in Python 3.7 that all calls to fork() be preceeded by calls to PyOS_BeforeFork() in the parent and PyOS_AfterFork_Parent() and PyOS_AfterFork_Child() in the respective processes. Also, Python 3.7 always initialize threads, but if the option is not set on uWSGI the thread-local data is not set and it causes random crashes, therefore it is now a fatal error if that option is not set. A plethora of new hooks were added because Python specifies that after fork the first calls must be to PyOS_AfterFork_* and unfortunately uWSGI was doing a bunch of stuff before calling the existing post_fork() hook. We decided to modify the function uwsgi_fork() itself because it would make it easier to handle all use-cases: mules, spoolers, re-forking of workers, lazy apps, etc. Reference: https://docs.python.org/3.7/c-api/sys.html#c.PyOS_BeforeFork https://docs.python.org/3.7/c-api/init.html#c.PyEval_InitThreads unbit#1995 unbit#1978 https://github.com/awelzel/uwsgi/tree/before-after-fork
Is there any update on this PR getting merged and released? Has anyone found a solution that fixes the |
@zaheerabbas-prodigal They already did something similar in d80f9d9 My company does not use this fix because we found that it is not fully working for mules that fork, but we haven't got the time to port this to Python 3.11 so we are still running this branch under Python 3.8. |
@edufelipe - thanks for a quick response. Do you use this branch |
@zaheerabbas-prodigal we actually use our own branch based on uWSGI 2.0.20: https://github.com/onsigntv/uwsgi/tree/lts |
This change addresses #1978 by ensuring that, in Python 3.7 and newer versions, the following always happens:
PyOS_BeforeFork()
is called before the parent process callsfork()
PyOS_AfterFork_Parent()
is called in the master process after the parent process callsfork()
PyOS_AfterFork_Child()
is called in all worker processes after the parent process callsfork()
This new behavior ignores the value of the
py-call-osafterfork
setting (only in Python 3.7 and newer), because not calling the before- and after-fork hooks is invalid behavior for CPython extensions in Python 3.7 and newer.I tested this by compiling, installing, and using in our new Python 3.7 systems:
This also adds a Python 3.7 build to the Travis build (I won't know if that works until posting this PR).