[Python-Dev] PEP Draft: Simplified Global Interpreter Lock acquisition for extensions
Mark Hammond
mhammond@skippinet.com.au
Wed, 5 Feb 2003 14:33:52 +1100
This is a multi-part message in MIME format.
------=_NextPart_000_006D_01C2CD23.A1DD0080
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
Hi all,
Following the thread of a couple of months ago, I have prepared a first
draft of a PEP. There is still more detail to be filled in the
"implementation" area, but I would still like feedback on all other areas.
Thanks,
Mark.
------=_NextPart_000_006D_01C2CD23.A1DD0080
Content-Type: text/plain;
name="pep_gil.txt"
Content-Transfer-Encoding: quoted-printable
Content-Disposition: attachment;
filename="pep_gil.txt"
PEP: xxx
Title: Simple Global Interpreter Lock acquisition for extensions
Version: $Revision: $
Last-Modified: $Date: $
Author: Mark Hammond <mhammond@skippinet.com.au>
Status:=20
Type:=20
Content-Type: text/plain
Created: Feb-2003
Post-History:
Abstract
This PEP proposes a simplified API for access to the Global =
Interpreter
Lock (GIL) for Python extension modules. Specifically, it provides =
a=20
solution for authors of complex multi-threaded extensions, where the
current state of Python (i.e., the state of the GIL, or if Python is =
currently using the GIL, or indeed if Python has been initialized) =
is
unknown.
This PEP proposes a new, optional API to manage the Python thread=20
state. This API is almost certain to require a platform =
implementation of
Thread Local Storage (TLS), which will form an optional extension to =
the
existing "Python threading" implementation required for all=20
threading-enabled Python platforms. Thus, before this new API will
be available on a given platform, the TLS extensions to that =
platform's
Python threading API must be implemented.
=20
An initial implementation of this PEP will target the Windows =
platform,
and any platforms using a modern "pthreads" [1] implementation.
Rationale
The current Python interpreter state API is suitable for simple,=20
single-threaded extensions, but quickly becomes incredibly complex
for non-trivial, multi-threaded extensions.
=20
Currently Python provides two mechanisms for dealing with the GIL:
=20
- Py_BEGIN_ALLOW_THREADS and Py_END_ALLOW_THREADS macros.
These macros are provided primarily to allow a simple Python =
extension
that already owns the GIL to temporarily release it while making =
an
"external" (ie, non-Python), generally expensive, call. Any =
existing=20
Python threads that are blocked waiting for the GIL are then free =
to=20
run.
=20
- PyThreadState and PyInterpreterState APIs.
These API functions allow an extension/embedded application to
acquire the GIL, but suffer from a serious boot-strapping problem =
- they
require you to know the state of the Python interpreter and of the =
GIL=20
before they can be used. One particular problem is for extension=20
authors that need to deal with threads never before seen by =
Python, but
need to call Python from this thread. It is very difficult, =
delicate=20
and error prone to author an extension where these "new" threads =
always=20
know the exact state of the GIL, and therefore can reliably =
interact=20
with this API.
=20
For these reasons, the question of how such extensions should
interact with Python is quickly becoming a FAQ. The main impetus
for this PEP, a thread on python-dev [2], immediately identified the
following projects with this exact issue:
- The win32all extensions.
- Boost
- ctypes
- Python-GTK bindings
- Uno
Currently, there is no reasonable, portable solution to this =
problem,
forcing each extension author to implement their own hand-rolled=20
version. Further, the problem is complex, meaning many=20
implementations are likely to be incorrect, leading to a variety of=20
problems that will often manifest simply as "Python has hung."
=20
While the biggest problem in the existing thread-state API is the =
lack
of the ability to query the current state of the lock, it is felt =
that
a more complete, simplified solution should be offered to extension=20
authors. Such a solution should encourage authors to provide=20
error-free, complex extension modules that take full advantage of=20
Python's threading mechanisms.
Limitations and Exclusions
This proposal identifies a solution for extension authors with =
complex
multi-threaded requirements, but that only require a single=20
"PyInterpeterState". There is no attempt to cater for extensions
that require multiple interpreter states. As at time of writing, no
extension has been identified that requires multiple =
PyInterpreterStates,
and indeed it is not clear if that facility works correctly in =
Python=20
itself.
=20
It is intended that this API be all that is necessary to acquire the
Python GIL. Apart from the existing, standard =
Py_BEGIN_ALLOW_THREADS=20
and Py_END_ALLOW_THREADS macros, it is assumed that no additional =
thread
state API functions will be used by the extension. Extensions with
such complicated requirements are free to continue to use the =
existing
thread state API.
=20
Proposal
This proposal recommends a new, optional API be added to Python to
simplify the management of the GIL.
=20
The intent is that an extension author be able to use a small,=20
well-defined "prologue dance", at any time and on any thread,
and this dance will ensure Python is ready to be used on that =
thread.
After the extension has finished with Python, it must also perform =
an
"epilogue dance" to release any resources previously acquired. =
Ideally,
these dances will be able to be expressed in a single line.
Specifically, the following new APIs are proposed:
=20
/*
Ensure that the current thread is ready to call the Python
C API, regardless of the current state of Python, or of its
thread lock. This may be called as many times as desired
by a thread, so long as each call is matched with a call to
PyAutoThreadState_Release()
=20
When the function returns, the current thread will hold the GIL. =
Thus,
the GIL is held by the thread until PyAutoThreadState_Release() is =
called.
(Note that as happens now in Python, calling a Python API function =
may=20
indeed cause a thread-switch and therefore a GIL ownership change. =20
However, Python gurantees that when the API function returns, the =
GIL will
again be owned by the thread making the call)
Failure is a fatal error.
*/
void PyAutoThreadState_Ensure(void);
=20
/*
Release any resources previously acquired. After this call, =
Python's
state will again be indeterminate, so the API can not be used.
=20
Every call to PyAutoThreadState_Ensure must be matched by a
call to PyAutoThreadState_Release on the same thread.
*/
void PyAutoThreadState_Release(void);
=20
As this PEP develops, it may become obvious that certain =
optimizations
are possible over this simplified API, assuming extension authors =
are able=20
to provide some context. These will be described and implemented =
during
the development of this PEP.
Design and Implementation
The general operation of PyAutoThreadState_Ensure() will be:
- Ensure Python is initialized.
- If the current thread does not own the GIL, acquire it.
- Increment a counter for how many calls to PyAutoThreadState_Ensure
have been made on the current thread.
- return
=20
The general operation of PyAutoThreadState_Release() will be:
- assert our thread currently holds the lock.
- Decrement the PyAutoThreadState_Ensure counter for the thread.
- If counter =3D=3D 0:
- release the lock.
- release any additional resources consumed.
- return
The implementation of this functionality will require some =
additional
functionality from Python's hosting platform. These requirements =
are:
=20
[tbd]
References
[1] pthreads???
[2] =
http://mail.python.org/pipermail/python-dev/2002-December/031424.html
[3]=20
Copyright
This document has been placed in the public domain.
=0C
Local Variables:
mode: indented-text
indent-tabs-mode: nil
sentence-end-double-space: t
fill-column: 70
End:
------=_NextPart_000_006D_01C2CD23.A1DD0080--