Hi everyone,
We just started using gevent (1.0rc3 right now), and ran into an issue when trying to shut down cleanly. We're making a modular server, where each module is pluggable, and is allowed to start greenlets on its own. From the docs, the greenlet.kill() function claims to throw an exception into the specified greenlet, so that seemed perfect - just loop through each greenlet, throw a GreenletExit (or derived) exception. Most of the developers writing modules wouldn't even need to do anything, they'd just let the exception fall through and exit normally.
However, it turns out kill() doesn't work as advertised. It doesn't throw the exception immediately, it appears to add it to the greenlet's queue. Easy to demonstrate:
def child_func():
while True:
print 'Child running'
gevent.sleep()
child = gevent.spawn(child_func)
gevent.sleep() # allow child to run
gevent.kill(child)
child.join()
The output shows "Child running" twice, and using greenlet.settrace() confirms the child runs through the loop one additional time after the kill call. Changing gevent.sleep() in the child_func to gevent.sleep(1) results in the child exiting immediately after the kill call, as expected. So it appears that gevent is not throwing the exception immediately, it's just placing the exception into the child's event queue, which always has the wakeup event from sleep in it. At best, this delays shutdown as the child does extra work that is not necessary.
So what's the proper way to shut things down? Is there a way to force the exception to be thrown immediately into each greenlet? Tsing child.throw() makes the child exit immediately, but also causes the main thread to due claiming the throw() call would block forever.
Or is there a better way to accomplish this?
Thanks!