-
Notifications
You must be signed in to change notification settings - Fork 565
Fix - freeze when reading stdout from external process #268
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
Conversation
Should also fix the issue of #118 |
If you are willing to provide some test case I can look at it. Probably in separate PR. |
/* Auxiliary function to call global fork handler if defined. | ||
|
||
Note: Fork handler needs to be in C (not cython) otherwise it would require | ||
GIL to be present, but some forks can exec non-python processes. |
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'm not sure I follow... The old handler had a nogil
decoration, i.e. cdef void __atfork_child() nogil:
. How's this indirection makes that different?
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.
There is small but significant difference in case you compile it with cython and nogil traces enabled (-DCYTHON_TRACE=1 -DCYTHON_TRACE_NOGIL=1).
In this mode cython will insert trace calls during compilation and resulting compiled __atfork_child function will contain __Pyx_TraceCall
. It is not a problem when fork contains python runtime, but it will freeze at this trace call in case forking child does not contains python runtime.
Traces are enabled in make debug
, so I decide to write it in pure C where no such traces are inserted by cython.
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.
Just so that I understand the scope: this PR is only needed to unbreak make debug
?
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.
No, on the contrary, this PR should fix freezing uvloop (under some conditions) and freezing occurs even when compiled with pure make (without debug
), but for a different reason.
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.
Alright, I'll review the PR soon.
In the meantime, could you please squash all commits into 1 and write a commit message that explains the bug/problem in a detailed way? It would help me / future maintainers a lot.
Huge props for coming up with a functional test case.
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.
Thanks, I squash it. Give me a note if there is something not clear enough.
734c569
to
4aef64b
Compare
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 don't fully understand why this change would fix the GIL issue. You are still calling the original __get_fork_handler
which still grabs the GIL. The fact that you've wrapped the call into a C function shouldn't change that. What would make sense is to avoid installing __atfork_child
globally, and only for the period when the loop is doing the fork.
The fact that I've wrapped the call into a C function is the crucial part because decision if In current implementation this decision is located inside cython function Now you may assume that solution can be as simple as mark I hope my description is now more clear why this implementation should avoid using any cython function as a fork handler and why C function is much better choice.
I totally agree but currently |
…ork and execute non-python process Problem: Uvloop for each loop register `atFork` handler that is called after fork is executed by forked child. This handler works fine when fork was invoked by uvloop. In case fork is invoked by something else (such as external library) uvloop freeze in this handler because: - GIL is acquired inside `atFork` handler -> in case forked child does not contain python runtime `atFork` handler freeze at obtaining GIL - when compiled in debug mode (`make debug`) cython trace calls are inserted inside compiled `atFork` handler -> in case forked child does not contain python runtime `atFork` handler freeze at providing trace call Solution: This fix solve described problems by implementing `atFork` handler as C function so that forked child can call it safely whether or not contains python runtime.
4aef64b
to
060907e
Compare
@grungy-ado I'm trying to run the unit test you added with unmodified uvloop on Linux and macOS and it doesn't fail. Any advice on how to make sure that the unit test actually works? |
@1st1 That's strange because it fails for me on my ubuntu 18.04LTS machine. It even failed in travis when I push only the test case #283 : https://travis-ci.org/MagicStack/uvloop/builds/601677156?utm_source=github_status&utm_medium=notification . Though it seems like linux issue only. There is no fail for OSX. |
We had this issue also only on linux |
You're right, there seems to be no (portable) way to do this.
Yes, I see it now. I misread the diff initially. This makes sense, although it strikes me that those
I can reproduce on Ubuntu 18.04 and Debian Buster, but not on other distros like Gentoo or Alpine or even Ubuntu 19.10, so this definitely also depends on the version of libc. |
It's possible if you run ffmpeg and also only random on Alpine. |
Thanks so much for your patience and help, @grungy-ado. Merged. I'll do some cosmetic changes & refactorings but your approach is good. |
Great, thanks all |
In one project I encounter with random freezing behavior when reading stdout from spawned external process. I start to suspect uvloop because using pure asyncio loop does not cause this problem.
Read from stdout was provided by external library that spawn new process using low level popen() call that seems to create new process by forking parent process. Debugger reveals that forked child process is hanged by trying to acquire GIL (that was strange because external process has nothing to do with python). Uvloop seems to register
__atfork_child
interceptor that add some logic in forking behavior that require GIL to be acquired. That make sense when fork is provided by uvloop, but not so much when fork is provided by anybody else.I manage to replicate the problem in test case and hopefully fix it.