Commit ede2621d authored by Jason Madden's avatar Jason Madden

Documentation for hub.reinit. Avoid printing an error on shutdown if...

Documentation for hub.reinit. Avoid printing an error on shutdown if gevent.threading is just imported but not monkey patched.
parent 87e57a0f
......@@ -165,23 +165,41 @@ class signal(object):
def reinit():
# An internal, undocumented function. Called by gevent.os.fork
# in the child process. The loop reinit function in turn calls
# libev's ev_loop_fork function.
"""
Prepare the gevent hub to run in a new process.
This should be called *immediately* after :func:`os.fork` in the
child process. This is done automatically by
:func:`gevent.os.fork` or if the :mod:`os` module has been
monkey-patched. If this function is not called in a forked
process, symptoms may include hanging of functions like
:func:`socket.getaddrinfo`, and the hub's threadpool is unlikely
to work.
.. note:: Registered fork watchers may or may not run before
this function (and thus ``gevent.os.fork``) return. If they have
not run, they will run "soon", after an iteration of the event loop.
You can force this by inserting a few small (but non-zero) calls to :func:`sleep`.
"""
# The loop reinit function in turn calls libev's ev_loop_fork
# function.
hub = _get_hub()
if hub is not None:
# Note that we reinit the existing loop, not destroy it.
# See https://github.com/gevent/gevent/issues/200.
hub.loop.reinit()
# libev's fork watchers are slow to fire because the only fire
# at the beginning of a loop; due to our use of callbacks that
# run at the end of the loop, that may be too late. The
# threadpool and resolvers depend on the fork handlers being
# run ( specifically, the threadpool will fail in the forked
# run (specifically, the threadpool will fail in the forked
# child if there were any threads in it, which there will be
# if the resolver_thread was in use (the default) before the
# fork.)
#
# If the forked process wants to use the threadpool or
# resolver immediately, it would hang.
# resolver immediately (in a queued callback), it would hang.
#
# The below is a workaround. Fortunately, both of these
# methods are idempotent and can be called multiple times
......@@ -194,6 +212,15 @@ def reinit():
if hasattr(hub.resolver, '_on_fork'):
hub.resolver._on_fork()
# TODO: We'd like to sleep for a non-zero amount of time to force the loop to make a
# pass around before returning to this greenlet. That will allow any
# user-provided fork watchers to run. (Two calls are necessary.) HOWEVER, if
# we do this, certain tests that heavily mix threads and forking,
# like 2.7/test_threading:test_reinit_tls_after_fork, fail. It's not immediately clear
# why.
#sleep(0.00001)
#sleep(0.00001)
def get_hub_class():
"""Return the type of hub to use for the current thread.
......
......@@ -169,14 +169,17 @@ if hasattr(os, 'fork'):
"""
Fork a child process and start a child watcher for it in the parent process.
This call cooperates with the :func:`waitpid`` function defined in this module.
This call cooperates with the :func:`gevent.os.waitpid` to enable cooperatively waiting
for children to finish.
:keyword callback: If given, a callable that will be called with the child watcher
when the child finishes.
:keyword loop: The loop to start the watcher in. Defaults to the
current loop.
loop of the current hub.
:keyword fork: The fork function. Defaults to the one defined in this
module.
module (which automatically calls :func:`gevent.hub.reinit`).
Pass the builtin :func:`os.fork` function if you do not need to
initialize gevent in the child process.
.. versionadded: 1.1a3
"""
......
......@@ -19,7 +19,7 @@ Lock = _allocate_lock
def _cleanup(g):
__threading__._active.pop(id(g))
__threading__._active.pop(id(g), None)
class _DummyThread(_DummyThread_):
......@@ -44,8 +44,22 @@ class _DummyThread(_DummyThread_):
if _get_ident() not in __threading__._active and len(__threading__._active) == 1:
k, v = next(iter(__threading__._active.items()))
del __threading__._active[k]
v._Thread__ident = _get_ident()
__threading__._active[_get_ident()] = v
# Avoid printing an error on shutdown trying to remove the thread entry
# we just replaced if we're not fully monkey patched in
_MAIN_THREAD = __threading__._get_ident() if hasattr(__threading__, '_get_ident') else __threading__.get_ident()
class _active(dict):
def __delitem__(self, k):
if k == _MAIN_THREAD and k not in self:
return
dict.__delitem__(self, k)
__threading__._active = _active(__threading__._active)
import sys
if sys.version_info[:2] >= (3, 4):
# XXX: Issue 18808 breaks us on Python 3.4.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment