Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
M
mitogen
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Labels
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Commits
Open sidebar
nexedi
mitogen
Commits
4a6d55ce
Commit
4a6d55ce
authored
Feb 12, 2018
by
David Wilson
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
docs: vastly simplify importer concurrency docs
parent
984b3918
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
59 additions
and
71 deletions
+59
-71
docs/howitworks.rst
docs/howitworks.rst
+59
-71
No files found.
docs/howitworks.rst
View file @
4a6d55ce
...
@@ -682,93 +682,81 @@ Package children are enumerated using :py:func:`pkgutil.iter_modules`.
...
@@ -682,93 +682,81 @@ Package children are enumerated using :py:func:`pkgutil.iter_modules`.
Concurrency
Concurrency
###########
###########
The importer must ensure duplicate requests are never issued to the parent,
Duplicate requests must never be issued to the parent, either due to a local
either due to an import originating on a local thread, or many
import or any :py:data:`GET_MODULE` originating from a child. This lets parents
:py:data:`GET_MODULE` requests originating from children. This allows parents
assume a module requested once by a downstream connection need never be
to assume that when a module has been requested once by a downstream
re-sent, for example, if it appears as a preloading dependency in a subsequent
connection, it need never be re-sent, for example, if it appears as a
:py:data:`GET_MODULE`, or had been requested immediately after being sent as a
preloading dependency in a subsequent module request, or it was just requested
preloading dependency for an unrelated request by a descendent.
immediately after being sent as a preloading dependency for a module request by
some indirect descendent.
Therefore each tree layer must deduplicate :py:data:`GET_MODULE` requests, and
synchronize their descendents and local threads on corresponding
Since requests from children are serviced on the IO multiplexer thread
:py:data:`LOAD_MODULE` responses from the parent.
concurrent to local thread requests, care is required to ensure deadlock cannot
occur.
In each context, pending requests are serialized by a
In each context, pending requests are serialized by a
:py:class:`threading.Lock` within :py:class:`mitogen.core.Importer`, which may
:py:class:`threading.Lock` within :py:class:`mitogen.core.Importer`, which may
only be held for operations that cannot block, since :py:class:`ModuleForwarder
only be held for operations that cannot block, since :py:class:`ModuleForwarder
<mitogen.master.ModuleForwarder>` must acquire it while servicing
<mitogen.master.ModuleForwarder>` must acquire it while synchronizing
:py:data:`GET_MODULE` requests on the IO multiplexer thread.
:py:data:`GET_MODULE` requests from children on the IO multiplexer thread.
Requests From Local Threads
~~~~~~~~~~~~~~~~~~~~~~~~~~~
When Mitogen begins satisfying an import, it is known the module has never been
imported in the local process. :py:class:`Importer <mitogen.core.Importer>`
executes under the runtime importer lock, ensuring :py:keyword:`import`
statements executing in local threads are serialized.
.. note::
In Python 2, :py:exc:`ImportError` is raised when :py:keyword:`import` is
attempted while the runtime import lock is held by another thread,
therefore imports must be serialized by only attempting them from the main
(:py:data:`CALL_FUNCTION`) thread.
The design must also take into account complications in Python 2.x'
s
import
The problem is most likely to manifest in third party libraries that lazily
locking
semantics
,
where
a
global
lock
exists
to
protect
import optional dependencies at runtime from a non-main thread. The
:
py
:
data
:`
sys
.
modules
`,
in
addition
to
a
per
-
module
lock
that
protects
the
workaround is to explicitly import those dependencies from the main thread
module
object
itself
,
so
that
a
module
being
initialized
on
one
thread
cannot
before initializing the third party library.
be
observed
in
a
partially
initialized
state
from
another
thread
.
Import
locking
changed
significantly
in
Python
3.5
,
but
this
design
is
not
yet
This was fixed in Python 3.5, but Python 3.x is not yet supported. See
verified
to
work
correctly
with
3.
x
.
See
`
Python
Issue
#
9260
`
_
.
`Python Issue #9260`_.
.. _Python Issue #9260: https://bugs.python.org/issue9260
.. _Python Issue #9260: https://bugs.python.org/issue9260
While holding its own lock, :py:class:`Importer <mitogen.core.Importer>`
checks if the source is not yet cached, determines if an in-flight
:py:data:`GET_MODULE` exists for it, starting one if none exists, adds itself
to a list of callbacks fired when a corresponding :py:data:`LOAD_MODULE`
arrives from the parent, then sleeps waiting for the callback.
Local
Thread
Requests
When the source becomes available, the module is constructed on the calling
~~~~~~~~~~~~~~~~~~~~~
thread using the best practice documented in `PEP 302`_.
In
Python
2.
x
,
by
the
time
:
py
:
class
:`
mitogen
.
core
.
Importer
`
is
invoked
on
a
local
thread
,
the
Python
importer
lock
has
already
been
acquired
by
the
import
machinery
.
``
ImportError
``
will
be
raised
unconditionally
by
Python
if
another
thread
attempts
an
import
while
this
lock
is
held
,
therefore
imports
should
always
be
serialized
by
only
attempting
them
from
the
main
(:
py
:
data
:`
CALL_FUNCTION
`)
thread
.
By
the
time
Mitogen
begins
satisfying
a
local
thread
request
,
it
is
known
that
the
module
has
never
previously
been
imported
in
the
local
process
.
A
local
thread
request
:
1.
Takes
the
Mitogen
importer
lock
.
2.
Checks
if
the
module
is
already
cached
.
3.
If
the
module
source
is
not
yet
cached
,
1.
If
no
in
-
flight
request
for
the
exists
module
,
a
.
a
:
py
:
class
:`
threading
.
Event
`
is
created
that
fires
when
the
module
source
becomes
available
,
b
.
the
Event
's :py:meth:`set <threading.Event.set>` method is added to a
list of callbacks fired when a :py:data:`LOAD_MODULE` arrives from the
parent containing the module source.
2. If a request is in-flight, the existing Event is reused by step 7 below.
4. Releases the importer lock.
5. If the module source was already cached, skip to step 8.
6. If this thread was responsible for creating the :py:class:`threading.Event`,
it issues a :py:data:`GET_MODULE` request to the parent context.
7. Sleeps waiting for the event to be set.
8. Instantiates the module using the best practice documented in `PEP 302`_.
.. _PEP 302: https://www.python.org/dev/peps/pep-0302/
.. _PEP 302: https://www.python.org/dev/peps/pep-0302/
Child Context Requests
Requests From Children
~~~~~~~~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~~~~~~~~~
When :py:class:`ModuleForwarder <mitogen.master.ModuleForwarder>` receives a
As with local imports, when :py:data:`GET_MODULE` is received from a child,
:py:data:`GET_MODULE` request from a child context, it:
while holding the :py:class:`Importer <mitogen.core.Importer>` lock,
:py:class:`ModuleForwarder <mitogen.master.ModuleForwarder>` checks if the
1. Takes the Mitogen importer lock.
source is not yet cached, determines if an in-flight :py:data:`GET_MODULE`
2. Checks if the module is already cached.
toward the parent exists for it, starting one if none exists, then adds a
3. If the module source is not yet cached,
completion handler to the list of callbacks fired when a corresponding
1. If this is the first request for the module,
:py:data:`LOAD_MODULE` arrives from the parent.
a. a :py:class:`threading.Event` is created that fires when the module
source becomes available,
When the source becomes available, the completion handler issues corresponding
b. the Event'
s
:
py
:
meth
:`
set
<
threading
.
Event
.
set
>`
method
is
added
to
a
:py:data:`LOAD_MODULE` messages toward the child for the requested module after
list
of
callbacks
fired
when
a
:
py
:
data
:`
LOAD_MODULE
`
arrives
from
the
any required for dependencies known to be absent from the child.
parent
containing
the
module
source
.
2.
If
a
request
is
in
-
flight
,
the
existing
Event
is
reused
by
step
7
below
.
Since intermediaries do not know a module'
s
dependencies
until
the
module
's
4.
Releases
the
importer
lock
.
source arrives, it is not possible to preemptively issue :py:data:`LOAD_MODULE`
5.
If
the
module
source
was
already
cached
,
skip
to
step
8.
for those dependencies toward a requesting child as they become available from
6.
If
this
thread
was
responsible
for
creating
the
:
py
:
class
:`
threading
.
Event
`,
the parent at the intermediary. This creates needless network serialization and
it
issues
a
:
py
:
data
:`
GET_MODULE
`
request
to
the
parent
context
.
latency that should be addressed in a future design.
7.
Sleeps
waiting
for
the
event
to
be
set
.
8.
Instantiates
the
module
using
the
best
practice
documented
in
`
PEP
302
`
_
.
Use Of Threads
Use Of Threads
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment