Open yssrku opened 1 year ago
Hello,
Thanks for reporting your observations, and sorry for the late reply.
Indeed, child threads which are forked is a side effect of monkey patching the stdlib.
I don't know if by design of eventlet/greenlet it is voluntary or not, or if we should consider that behavior as a bug.
In all case, I think that the code in your snippet will lead at some point to a race condition related to seq
increment which is not an atomic operation.
Besides, when I ran this snippet with a monkey patched stdlib, I can observe that at some point the code exit from the infinite loop created in the in_thread
function. When I ran it with a vanilla stdlib, the loop continue indefinitely.
Without much available design details, lets consider it as a bug for now.
Here is the strace for the parent process:
strace: Process 669 attached
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129850, tv_nsec=214844952}, NULL) = 0
getpid() = 669
write(1, "669: 6\n", 7) = 7
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129851, tv_nsec=217304793}, NULL) = 0
getpid() = 669
write(1, "669: 7\n", 7) = 7
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129852, tv_nsec=219498230}, NULL) = 0
getpid() = 669
write(1, "669: 8\n", 7) = 7
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129853, tv_nsec=222583495}, NULL) = 0
getpid() = 669
write(1, "669: 9\n", 7) = 7
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129854, tv_nsec=212697741}, NULL) = 0
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK, sa_restorer=0x7fefdf165050}, {sa_handler=0x7fefdf4b55f9, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK, sa_restorer=0x7fefdf165050}, 8) = 0
munmap(0x7fefddd67000, 417792) = 0
munmap(0x7fefdee79000, 16384) = 0
exit_group(0) = ?
+++ exited with 0 +++
And here is the strace for the forked process:
strace: Process 669 attached
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129850, tv_nsec=214844952}, NULL) = 0
getpid() = 669
write(1, "669: 6\n", 7) = 7
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129851, tv_nsec=217304793}, NULL) = 0
getpid() = 669
write(1, "669: 7\n", 7) = 7
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129852, tv_nsec=219498230}, NULL) = 0
getpid() = 669
write(1, "669: 8\n", 7) = 7
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129853, tv_nsec=222583495}, NULL) = 0
getpid() = 669
write(1, "669: 9\n", 7) = 7
clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, {tv_sec=1129854, tv_nsec=212697741}, NULL) = 0
rt_sigaction(SIGINT, {sa_handler=SIG_DFL, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK, sa_restorer=0x7fefdf165050}, {sa_handler=0x7fefdf4b55f9, sa_mask=[], sa_flags=SA_RESTORER|SA_ONSTACK, sa_restorer=0x7fefdf165050}, 8) = 0
munmap(0x7fefddd67000, 417792) = 0
munmap(0x7fefdee79000, 16384) = 0
exit_group(0) = ?
+++ exited with 0 +++
We can observe that both process exited alone without manually killing them.
Here is a code snippet where the pid is logged:
import eventlet
eventlet.monkey_patch() # behavior differs when monkey_patch() is called
import os
import time
import threading
def in_thread():
seq = 0
while True:
print(f"{os.getpid()}: {seq}")
seq += 1
time.sleep(1)
threading.Thread(target=in_thread).start()
os.fork()
time.sleep(10)
The sigaction()
system call is used to change the action taken by a process on receipt of a specific signal. Here the signal is the SIGINT
(signal interrupt). See the rt_sigaction call in the previous straces. https://linux.die.net/man/2/rt_sigaction.
Since fork() only duplicates the calling thread, seeing background threads being duplicated might be surprising. I know this is a side effect of green thread, but some code might not expect this.