Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
074e5ed9
Commit
074e5ed9
authored
Nov 10, 2009
by
Antoine Pitrou
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Merge in the new GIL.
parent
434736a1
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
522 additions
and
73 deletions
+522
-73
Include/ceval.h
Include/ceval.h
+4
-4
Include/pystate.h
Include/pystate.h
+2
-0
Include/sysmodule.h
Include/sysmodule.h
+0
-1
Lib/test/test_sys.py
Lib/test/test_sys.py
+15
-0
Makefile.pre.in
Makefile.pre.in
+1
-1
Objects/longobject.c
Objects/longobject.c
+1
-4
Python/ceval.c
Python/ceval.c
+101
-61
Python/ceval_gil.h
Python/ceval_gil.h
+335
-0
Python/pystate.c
Python/pystate.c
+1
-0
Python/sysmodule.c
Python/sysmodule.c
+62
-2
No files found.
Include/ceval.h
View file @
074e5ed9
...
...
@@ -112,10 +112,6 @@ PyAPI_FUNC(PyObject *) PyEval_GetCallStats(PyObject *);
PyAPI_FUNC
(
PyObject
*
)
PyEval_EvalFrame
(
struct
_frame
*
);
PyAPI_FUNC
(
PyObject
*
)
PyEval_EvalFrameEx
(
struct
_frame
*
f
,
int
exc
);
/* this used to be handled on a per-thread basis - now just two globals */
PyAPI_DATA
(
volatile
int
)
_Py_Ticker
;
PyAPI_DATA
(
int
)
_Py_CheckInterval
;
/* Interface for threads.
A module that plans to do a blocking system call (or something else
...
...
@@ -174,6 +170,9 @@ PyAPI_FUNC(void) PyEval_AcquireThread(PyThreadState *tstate);
PyAPI_FUNC
(
void
)
PyEval_ReleaseThread
(
PyThreadState
*
tstate
);
PyAPI_FUNC
(
void
)
PyEval_ReInitThreads
(
void
);
PyAPI_FUNC
(
void
)
_PyEval_SetSwitchInterval
(
unsigned
long
microseconds
);
PyAPI_FUNC
(
unsigned
long
)
_PyEval_GetSwitchInterval
(
void
);
#define Py_BEGIN_ALLOW_THREADS { \
PyThreadState *_save; \
_save = PyEval_SaveThread();
...
...
@@ -192,6 +191,7 @@ PyAPI_FUNC(void) PyEval_ReInitThreads(void);
#endif
/* !WITH_THREAD */
PyAPI_FUNC
(
int
)
_PyEval_SliceIndex
(
PyObject
*
,
Py_ssize_t
*
);
PyAPI_FUNC
(
void
)
_PyEval_SignalAsyncExc
(
void
);
#ifdef __cplusplus
...
...
Include/pystate.h
View file @
074e5ed9
...
...
@@ -88,6 +88,8 @@ typedef struct _ts {
PyObject
*
dict
;
/* Stores per-thread state */
/* XXX doesn't mean anything anymore (the comment below is obsolete)
=> deprecate or remove? */
/* tick_counter is incremented whenever the check_interval ticker
* reaches zero. The purpose is to give a useful measure of the number
* of interpreted bytecode instructions in a given thread. This
...
...
Include/sysmodule.h
View file @
074e5ed9
...
...
@@ -18,7 +18,6 @@ PyAPI_FUNC(void) PySys_WriteStderr(const char *format, ...)
Py_GCC_ATTRIBUTE
((
format
(
printf
,
1
,
2
)));
PyAPI_DATA
(
PyObject
*
)
_PySys_TraceFunc
,
*
_PySys_ProfileFunc
;
PyAPI_DATA
(
int
)
_PySys_CheckInterval
;
PyAPI_FUNC
(
void
)
PySys_ResetWarnOptions
(
void
);
PyAPI_FUNC
(
void
)
PySys_AddWarnOption
(
const
wchar_t
*
);
...
...
Lib/test/test_sys.py
View file @
074e5ed9
...
...
@@ -154,6 +154,21 @@ class SysModuleTest(unittest.TestCase):
sys
.
setcheckinterval
(
n
)
self
.
assertEquals
(
sys
.
getcheckinterval
(),
n
)
def
test_switchinterval
(
self
):
self
.
assertRaises
(
TypeError
,
sys
.
setswitchinterval
)
self
.
assertRaises
(
TypeError
,
sys
.
setswitchinterval
,
"a"
)
self
.
assertRaises
(
ValueError
,
sys
.
setswitchinterval
,
-
1.0
)
self
.
assertRaises
(
ValueError
,
sys
.
setswitchinterval
,
0.0
)
orig
=
sys
.
getswitchinterval
()
# sanity check
self
.
assertTrue
(
orig
<
0.5
,
orig
)
try
:
for
n
in
0.00001
,
0.05
,
3.0
,
orig
:
sys
.
setswitchinterval
(
n
)
self
.
assertAlmostEquals
(
sys
.
getswitchinterval
(),
n
)
finally
:
sys
.
setswitchinterval
(
orig
)
def
test_recursionlimit
(
self
):
self
.
assertRaises
(
TypeError
,
sys
.
getrecursionlimit
,
42
)
oldlimit
=
sys
.
getrecursionlimit
()
...
...
Makefile.pre.in
View file @
074e5ed9
...
...
@@ -596,7 +596,7 @@ Objects/unicodeobject.o: $(srcdir)/Objects/unicodeobject.c \
$(OPCODETARGETS_H)
:
$(OPCODETARGETGEN_FILES)
$(OPCODETARGETGEN)
$(OPCODETARGETS_H)
Python/ceval.o
:
$(OPCODETARGETS_H)
Python/ceval.o
:
$(OPCODETARGETS_H)
Python/ceval_gil.h
Python/formatter_unicode.o
:
$(srcdir)/Python/formatter_unicode.c
\
$(BYTESTR_DEPS)
\
...
...
Objects/longobject.c
View file @
074e5ed9
...
...
@@ -96,10 +96,7 @@ maybe_small_long(PyLongObject *v)
#define MIN(x, y) ((x) > (y) ? (y) : (x))
#define SIGCHECK(PyTryBlock) \
if (--_Py_Ticker < 0) { \
_Py_Ticker = _Py_CheckInterval; \
if (PyErr_CheckSignals()) PyTryBlock \
}
if (PyErr_CheckSignals()) PyTryBlock \
/* forward declaration */
static
int
bits_in_digit
(
digit
d
);
...
...
Python/ceval.c
View file @
074e5ed9
...
...
@@ -216,6 +216,28 @@ PyEval_GetCallStats(PyObject *self)
#endif
#define COMPUTE_EVAL_BREAKER() \
(eval_breaker = gil_drop_request | pendingcalls_to_do | pending_async_exc)
#define SET_GIL_DROP_REQUEST() \
do { gil_drop_request = 1; eval_breaker = 1; } while (0)
#define RESET_GIL_DROP_REQUEST() \
do { gil_drop_request = 0; COMPUTE_EVAL_BREAKER(); } while (0)
#define SIGNAL_PENDING_CALLS() \
do { pendingcalls_to_do = 1; eval_breaker = 1; } while (0)
#define UNSIGNAL_PENDING_CALLS() \
do { pendingcalls_to_do = 0; COMPUTE_EVAL_BREAKER(); } while (0)
#define SIGNAL_ASYNC_EXC() \
do { pending_async_exc = 1; eval_breaker = 1; } while (0)
#define UNSIGNAL_ASYNC_EXC() \
do { pending_async_exc = 0; COMPUTE_EVAL_BREAKER(); } while (0)
#ifdef WITH_THREAD
#ifdef HAVE_ERRNO_H
...
...
@@ -223,36 +245,55 @@ PyEval_GetCallStats(PyObject *self)
#endif
#include "pythread.h"
static
PyThread_type_lock
interpreter_lock
=
0
;
/* This is the GIL */
static
PyThread_type_lock
pending_lock
=
0
;
/* for pending calls */
static
long
main_thread
=
0
;
/* This single variable consolidates all requests to break out of the fast path
in the eval loop. */
static
volatile
int
eval_breaker
=
0
;
/* Request for droppping the GIL */
static
volatile
int
gil_drop_request
=
0
;
/* Request for running pending calls */
static
volatile
int
pendingcalls_to_do
=
0
;
/* Request for looking at the `async_exc` field of the current thread state */
static
volatile
int
pending_async_exc
=
0
;
#include "ceval_gil.h"
int
PyEval_ThreadsInitialized
(
void
)
{
return
interpreter_lock
!=
0
;
return
gil_created
()
;
}
void
PyEval_InitThreads
(
void
)
{
if
(
interpreter_lock
)
if
(
gil_created
()
)
return
;
interpreter_lock
=
PyThread_allocate_lock
();
PyThread_acquire_lock
(
interpreter_lock
,
1
);
create_gil
();
take_gil
(
PyThreadState_GET
()
);
main_thread
=
PyThread_get_thread_ident
();
if
(
!
pending_lock
)
pending_lock
=
PyThread_allocate_lock
();
}
void
PyEval_AcquireLock
(
void
)
{
PyThread_acquire_lock
(
interpreter_lock
,
1
);
PyThreadState
*
tstate
=
PyThreadState_GET
();
if
(
tstate
==
NULL
)
Py_FatalError
(
"PyEval_AcquireLock: current thread state is NULL"
);
take_gil
(
tstate
);
}
void
PyEval_ReleaseLock
(
void
)
{
PyThread_release_lock
(
interpreter_lock
);
/* This function must succeed when the current thread state is NULL.
We therefore avoid PyThreadState_GET() which dumps a fatal error
in debug mode.
*/
drop_gil
(
_PyThreadState_Current
);
}
void
...
...
@@ -261,8 +302,8 @@ PyEval_AcquireThread(PyThreadState *tstate)
if
(
tstate
==
NULL
)
Py_FatalError
(
"PyEval_AcquireThread: NULL new thread state"
);
/* Check someone has called PyEval_InitThreads() to create the lock */
assert
(
interpreter_lock
);
PyThread_acquire_lock
(
interpreter_lock
,
1
);
assert
(
gil_created
()
);
take_gil
(
tstate
);
if
(
PyThreadState_Swap
(
tstate
)
!=
NULL
)
Py_FatalError
(
"PyEval_AcquireThread: non-NULL old thread state"
);
...
...
@@ -275,7 +316,7 @@ PyEval_ReleaseThread(PyThreadState *tstate)
Py_FatalError
(
"PyEval_ReleaseThread: NULL thread state"
);
if
(
PyThreadState_Swap
(
NULL
)
!=
tstate
)
Py_FatalError
(
"PyEval_ReleaseThread: wrong thread state"
);
PyThread_release_lock
(
interpreter_lock
);
drop_gil
(
tstate
);
}
/* This function is called from PyOS_AfterFork to ensure that newly
...
...
@@ -287,17 +328,17 @@ void
PyEval_ReInitThreads
(
void
)
{
PyObject
*
threading
,
*
result
;
PyThreadState
*
tstate
;
PyThreadState
*
tstate
=
PyThreadState_GET
()
;
if
(
!
interpreter_lock
)
if
(
!
gil_created
()
)
return
;
/*XXX Can't use PyThread_free_lock here because it does too
much error-checking. Doing this cleanly would require
adding a new function to each thread_*.h. Instead, just
create a new lock and waste a little bit of memory */
interpreter_lock
=
PyThread_allocate_lock
();
recreate_gil
();
pending_lock
=
PyThread_allocate_lock
();
PyThread_acquire_lock
(
interpreter_lock
,
1
);
take_gil
(
tstate
);
main_thread
=
PyThread_get_thread_ident
();
/* Update the threading module with the new state.
...
...
@@ -317,7 +358,21 @@ PyEval_ReInitThreads(void)
Py_DECREF
(
result
);
Py_DECREF
(
threading
);
}
#endif
#else
static
int
eval_breaker
=
0
;
static
int
gil_drop_request
=
0
;
static
int
pending_async_exc
=
0
;
#endif
/* WITH_THREAD */
/* This function is used to signal that async exceptions are waiting to be
raised, therefore it is also useful in non-threaded builds. */
void
_PyEval_SignalAsyncExc
(
void
)
{
SIGNAL_ASYNC_EXC
();
}
/* Functions save_thread and restore_thread are always defined so
dynamically loaded modules needn't be compiled separately for use
...
...
@@ -330,8 +385,8 @@ PyEval_SaveThread(void)
if
(
tstate
==
NULL
)
Py_FatalError
(
"PyEval_SaveThread: NULL tstate"
);
#ifdef WITH_THREAD
if
(
interpreter_lock
)
PyThread_release_lock
(
interpreter_lock
);
if
(
gil_created
()
)
drop_gil
(
tstate
);
#endif
return
tstate
;
}
...
...
@@ -342,9 +397,9 @@ PyEval_RestoreThread(PyThreadState *tstate)
if
(
tstate
==
NULL
)
Py_FatalError
(
"PyEval_RestoreThread: NULL tstate"
);
#ifdef WITH_THREAD
if
(
interpreter_lock
)
{
if
(
gil_created
()
)
{
int
err
=
errno
;
PyThread_acquire_lock
(
interpreter_lock
,
1
);
take_gil
(
tstate
);
errno
=
err
;
}
#endif
...
...
@@ -390,7 +445,6 @@ static struct {
}
pendingcalls
[
NPENDINGCALLS
];
static
int
pendingfirst
=
0
;
static
int
pendinglast
=
0
;
static
volatile
int
pendingcalls_to_do
=
1
;
/* trigger initialization of lock */
static
char
pendingbusy
=
0
;
int
...
...
@@ -429,8 +483,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
pendinglast
=
j
;
}
/* signal main loop */
_Py_Ticker
=
0
;
pendingcalls_to_do
=
1
;
SIGNAL_PENDING_CALLS
();
if
(
lock
!=
NULL
)
PyThread_release_lock
(
lock
);
return
result
;
...
...
@@ -472,7 +525,10 @@ Py_MakePendingCalls(void)
arg
=
pendingcalls
[
j
].
arg
;
pendingfirst
=
(
j
+
1
)
%
NPENDINGCALLS
;
}
pendingcalls_to_do
=
pendingfirst
!=
pendinglast
;
if
(
pendingfirst
!=
pendinglast
)
SIGNAL_PENDING_CALLS
();
else
UNSIGNAL_PENDING_CALLS
();
PyThread_release_lock
(
pending_lock
);
/* having released the lock, perform the callback */
if
(
func
==
NULL
)
...
...
@@ -538,8 +594,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
pendingcalls
[
i
].
arg
=
arg
;
pendinglast
=
j
;
_Py_Ticker
=
0
;
pendingcalls_to_do
=
1
;
/* Signal main loop */
SIGNAL_PENDING_CALLS
();
busy
=
0
;
/* XXX End critical section */
return
0
;
...
...
@@ -552,7 +607,7 @@ Py_MakePendingCalls(void)
if
(
busy
)
return
0
;
busy
=
1
;
pendingcalls_to_do
=
0
;
UNSIGNAL_PENDING_CALLS
()
;
for
(;;)
{
int
i
;
int
(
*
func
)(
void
*
);
...
...
@@ -565,7 +620,7 @@ Py_MakePendingCalls(void)
pendingfirst
=
(
i
+
1
)
%
NPENDINGCALLS
;
if
(
func
(
arg
)
<
0
)
{
busy
=
0
;
pendingcalls_to_do
=
1
;
/* We're not done yet */
SIGNAL_PENDING_CALLS
()
;
/* We're not done yet */
return
-
1
;
}
}
...
...
@@ -658,10 +713,7 @@ static int unpack_iterable(PyObject *, int, int, PyObject **);
fast_next_opcode*/
static
int
_Py_TracingPossible
=
0
;
/* for manipulating the thread switch and periodic "stuff" - used to be
per thread, now just a pair o' globals */
int
_Py_CheckInterval
=
100
;
volatile
int
_Py_Ticker
=
0
;
/* so that we hit a "tick" first thing */
PyObject
*
PyEval_EvalCode
(
PyCodeObject
*
co
,
PyObject
*
globals
,
PyObject
*
locals
)
...
...
@@ -791,10 +843,7 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
#define DISPATCH() \
{ \
/* Avoid multiple loads from _Py_Ticker despite `volatile` */
\
int _tick = _Py_Ticker - 1; \
_Py_Ticker = _tick; \
if (_tick >= 0) { \
if (!eval_breaker) { \
FAST_DISPATCH(); \
} \
continue; \
...
...
@@ -1168,13 +1217,12 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
async I/O handler); see Py_AddPendingCall() and
Py_MakePendingCalls() above. */
if
(
--
_Py_Ticker
<
0
)
{
if
(
eval_breaker
)
{
if
(
*
next_instr
==
SETUP_FINALLY
)
{
/* Make the last opcode before
a try: finally: block uninterruptable. */
goto
fast_next_opcode
;
}
_Py_Ticker
=
_Py_CheckInterval
;
tstate
->
tick_counter
++
;
#ifdef WITH_TSC
ticked
=
1
;
...
...
@@ -1184,39 +1232,31 @@ PyEval_EvalFrameEx(PyFrameObject *f, int throwflag)
why
=
WHY_EXCEPTION
;
goto
on_error
;
}
if
(
pendingcalls_to_do
)
/* MakePendingCalls() didn't succeed.
Force early re-execution of this
"periodic" code, possibly after
a thread switch */
_Py_Ticker
=
0
;
}
if
(
gil_drop_request
)
{
#ifdef WITH_THREAD
if
(
interpreter_lock
)
{
/* Give another thread a chance */
if
(
PyThreadState_Swap
(
NULL
)
!=
tstate
)
Py_FatalError
(
"ceval: tstate mix-up"
);
PyThread_release_lock
(
interpreter_lock
);
drop_gil
(
tstate
);
/* Other threads may run now */
PyThread_acquire_lock
(
interpreter_lock
,
1
);
take_gil
(
tstate
);
if
(
PyThreadState_Swap
(
tstate
)
!=
NULL
)
Py_FatalError
(
"ceval: orphan tstate"
);
/* Check for thread interrupts */
if
(
tstate
->
async_exc
!=
NULL
)
{
x
=
tstate
->
async_exc
;
tstate
->
async_exc
=
NULL
;
PyErr_SetNone
(
x
);
Py_DECREF
(
x
);
why
=
WHY_EXCEPTION
;
goto
on_error
;
}
}
#endif
}
/* Check for asynchronous exceptions. */
if
(
tstate
->
async_exc
!=
NULL
)
{
x
=
tstate
->
async_exc
;
tstate
->
async_exc
=
NULL
;
UNSIGNAL_ASYNC_EXC
();
PyErr_SetNone
(
x
);
Py_DECREF
(
x
);
why
=
WHY_EXCEPTION
;
goto
on_error
;
}
}
fast_next_opcode:
...
...
Python/ceval_gil.h
0 → 100644
View file @
074e5ed9
/*
* Implementation of the Global Interpreter Lock (GIL).
*/
#include <stdlib.h>
#include <errno.h>
/* First some general settings */
/* microseconds (the Python API uses seconds, though) */
#define DEFAULT_INTERVAL 5000
static
unsigned
long
gil_interval
=
DEFAULT_INTERVAL
;
#define INTERVAL (gil_interval >= 1 ? gil_interval : 1)
/* Enable if you want to force the switching of threads at least every `gil_interval` */
#undef FORCE_SWITCHING
#define FORCE_SWITCHING
/*
Notes about the implementation:
- The GIL is just a boolean variable (gil_locked) whose access is protected
by a mutex (gil_mutex), and whose changes are signalled by a condition
variable (gil_cond). gil_mutex is taken for short periods of time,
and therefore mostly uncontended.
- In the GIL-holding thread, the main loop (PyEval_EvalFrameEx) must be
able to release the GIL on demand by another thread. A volatile boolean
variable (gil_drop_request) is used for that purpose, which is checked
at every turn of the eval loop. That variable is set after a wait of
`interval` microseconds on `gil_cond` has timed out.
[Actually, another volatile boolean variable (eval_breaker) is used
which ORs several conditions into one. Volatile booleans are
sufficient as inter-thread signalling means since Python is run
on cache-coherent architectures only.]
- A thread wanting to take the GIL will first let pass a given amount of
time (`interval` microseconds) before setting gil_drop_request. This
encourages a defined switching period, but doesn't enforce it since
opcodes can take an arbitrary time to execute.
The `interval` value is available for the user to read and modify
using the Python API `sys.{get,set}switchinterval()`.
- When a thread releases the GIL and gil_drop_request is set, that thread
ensures that another GIL-awaiting thread gets scheduled.
It does so by waiting on a condition variable (switch_cond) until
the value of gil_last_holder is changed to something else than its
own thread state pointer, indicating that another thread was able to
take the GIL.
This is meant to prohibit the latency-adverse behaviour on multi-core
machines where one thread would speculatively release the GIL, but still
run and end up being the first to re-acquire it, making the "timeslices"
much longer than expected.
(Note: this mechanism is enabled with FORCE_SWITCHING above)
*/
#ifndef _POSIX_THREADS
/* This means pthreads are not implemented in libc headers, hence the macro
not present in unistd.h. But they still can be implemented as an external
library (e.g. gnu pth in pthread emulation) */
# ifdef HAVE_PTHREAD_H
# include <pthread.h>
/* _POSIX_THREADS */
# endif
#endif
#ifdef _POSIX_THREADS
/*
* POSIX support
*/
#include <pthread.h>
#define ADD_MICROSECONDS(tv, interval) \
do { \
tv.tv_usec += (long) interval; \
tv.tv_sec += tv.tv_usec / 1000000; \
tv.tv_usec %= 1000000; \
} while (0)
/* We assume all modern POSIX systems have gettimeofday() */
#ifdef GETTIMEOFDAY_NO_TZ
#define GETTIMEOFDAY(ptv) gettimeofday(ptv)
#else
#define GETTIMEOFDAY(ptv) gettimeofday(ptv, (struct timezone *)NULL)
#endif
#define MUTEX_T pthread_mutex_t
#define MUTEX_INIT(mut) \
if (pthread_mutex_init(&mut, NULL)) { \
Py_FatalError("pthread_mutex_init(" #mut ") failed"); };
#define MUTEX_LOCK(mut) \
if (pthread_mutex_lock(&mut)) { \
Py_FatalError("pthread_mutex_lock(" #mut ") failed"); };
#define MUTEX_UNLOCK(mut) \
if (pthread_mutex_unlock(&mut)) { \
Py_FatalError("pthread_mutex_unlock(" #mut ") failed"); };
#define COND_T pthread_cond_t
#define COND_INIT(cond) \
if (pthread_cond_init(&cond, NULL)) { \
Py_FatalError("pthread_cond_init(" #cond ") failed"); };
#define COND_PREPARE(cond)
#define COND_SIGNAL(cond) \
if (pthread_cond_signal(&cond)) { \
Py_FatalError("pthread_cond_signal(" #cond ") failed"); };
#define COND_WAIT(cond, mut) \
if (pthread_cond_wait(&cond, &mut)) { \
Py_FatalError("pthread_cond_wait(" #cond ") failed"); };
#define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
{ \
int r; \
struct timespec ts; \
struct timeval deadline; \
\
GETTIMEOFDAY(&deadline); \
ADD_MICROSECONDS(deadline, microseconds); \
ts.tv_sec = deadline.tv_sec; \
ts.tv_nsec = deadline.tv_usec * 1000; \
\
r = pthread_cond_timedwait(&cond, &mut, &ts); \
if (r == ETIMEDOUT) \
timeout_result = 1; \
else if (r) \
Py_FatalError("pthread_cond_timedwait(" #cond ") failed"); \
else \
timeout_result = 0; \
} \
#elif defined(NT_THREADS)
/*
* Windows (2000 and later, as well as (hopefully) CE) support
*/
#include <windows.h>
#define MUTEX_T HANDLE
#define MUTEX_INIT(mut) \
if (!(mut = CreateMutex(NULL, FALSE, NULL))) { \
Py_FatalError("CreateMutex(" #mut ") failed"); };
#define MUTEX_LOCK(mut) \
if (WaitForSingleObject(mut, INFINITE) != WAIT_OBJECT_0) { \
Py_FatalError("WaitForSingleObject(" #mut ") failed"); };
#define MUTEX_UNLOCK(mut) \
if (!ReleaseMutex(mut)) { \
Py_FatalError("ReleaseMutex(" #mut ") failed"); };
/* We emulate condition variables with events. It is sufficient here.
(WaitForMultipleObjects() allows the event to be caught and the mutex
to be taken atomically) */
#define COND_T HANDLE
#define COND_INIT(cond) \
/* auto-reset, non-signalled */
\
if (!(cond = CreateEvent(NULL, FALSE, FALSE, NULL))) { \
Py_FatalError("CreateMutex(" #cond ") failed"); };
#define COND_PREPARE(cond) \
if (!ResetEvent(cond)) { \
Py_FatalError("ResetEvent(" #cond ") failed"); };
#define COND_SIGNAL(cond) \
if (!SetEvent(cond)) { \
Py_FatalError("SetEvent(" #cond ") failed"); };
#define COND_WAIT(cond, mut) \
{ \
DWORD r; \
HANDLE objects[2] = { cond, mut }; \
MUTEX_UNLOCK(mut); \
r = WaitForMultipleObjects(2, objects, TRUE, INFINITE); \
if (r != WAIT_OBJECT_0) \
Py_FatalError("WaitForSingleObject(" #cond ") failed"); \
}
#define COND_TIMED_WAIT(cond, mut, microseconds, timeout_result) \
{ \
DWORD r; \
HANDLE objects[2] = { cond, mut }; \
MUTEX_UNLOCK(mut); \
r = WaitForMultipleObjects(2, objects, TRUE, microseconds / 1000); \
if (r == WAIT_TIMEOUT) { \
MUTEX_LOCK(mut); \
timeout_result = 1; \
} \
else if (r != WAIT_OBJECT_0) \
Py_FatalError("WaitForSingleObject(" #cond ") failed"); \
else \
timeout_result = 0; \
}
#else
#error You need either a POSIX-compatible or a Windows system!
#endif
/* _POSIX_THREADS, NT_THREADS */
/* Whether the GIL is already taken (-1 if uninitialized). This is volatile
because it can be read without any lock taken in ceval.c. */
static
volatile
int
gil_locked
=
-
1
;
/* Number of GIL switches since the beginning. */
static
unsigned
long
gil_switch_number
=
0
;
/* Last thread holding / having held the GIL. This helps us know whether
anyone else was scheduled after we dropped the GIL. */
static
PyThreadState
*
gil_last_holder
=
NULL
;
/* This condition variable allows one or several threads to wait until
the GIL is released. In addition, the mutex also protects the above
variables. */
static
COND_T
gil_cond
;
static
MUTEX_T
gil_mutex
;
#ifdef FORCE_SWITCHING
/* This condition variable helps the GIL-releasing thread wait for
a GIL-awaiting thread to be scheduled and take the GIL. */
static
COND_T
switch_cond
;
static
MUTEX_T
switch_mutex
;
#endif
static
int
gil_created
(
void
)
{
return
gil_locked
>=
0
;
}
static
void
create_gil
(
void
)
{
MUTEX_INIT
(
gil_mutex
);
#ifdef FORCE_SWITCHING
MUTEX_INIT
(
switch_mutex
);
#endif
COND_INIT
(
gil_cond
);
#ifdef FORCE_SWITCHING
COND_INIT
(
switch_cond
);
#endif
gil_locked
=
0
;
gil_last_holder
=
NULL
;
}
static
void
recreate_gil
(
void
)
{
create_gil
();
}
static
void
drop_gil
(
PyThreadState
*
tstate
)
{
/* NOTE: tstate is allowed to be NULL. */
if
(
!
gil_locked
)
Py_FatalError
(
"drop_gil: GIL is not locked"
);
if
(
tstate
!=
NULL
&&
tstate
!=
gil_last_holder
)
Py_FatalError
(
"drop_gil: wrong thread state"
);
MUTEX_LOCK
(
gil_mutex
);
gil_locked
=
0
;
COND_SIGNAL
(
gil_cond
);
#ifdef FORCE_SWITCHING
COND_PREPARE
(
switch_cond
);
#endif
MUTEX_UNLOCK
(
gil_mutex
);
#ifdef FORCE_SWITCHING
if
(
gil_drop_request
)
{
MUTEX_LOCK
(
switch_mutex
);
/* Not switched yet => wait */
if
(
gil_last_holder
==
tstate
)
COND_WAIT
(
switch_cond
,
switch_mutex
);
MUTEX_UNLOCK
(
switch_mutex
);
}
#endif
}
static
void
take_gil
(
PyThreadState
*
tstate
)
{
int
err
;
if
(
tstate
==
NULL
)
Py_FatalError
(
"take_gil: NULL tstate"
);
err
=
errno
;
MUTEX_LOCK
(
gil_mutex
);
if
(
!
gil_locked
)
goto
_ready
;
COND_PREPARE
(
gil_cond
);
while
(
gil_locked
)
{
int
timed_out
=
0
;
unsigned
long
saved_switchnum
;
saved_switchnum
=
gil_switch_number
;
COND_TIMED_WAIT
(
gil_cond
,
gil_mutex
,
INTERVAL
,
timed_out
);
/* If we timed out and no switch occurred in the meantime, it is time
to ask the GIL-holding thread to drop it. */
if
(
timed_out
&&
gil_locked
&&
gil_switch_number
==
saved_switchnum
)
{
SET_GIL_DROP_REQUEST
();
}
}
_ready:
#ifdef FORCE_SWITCHING
/* This mutex must be taken before modifying gil_last_holder (see drop_gil()). */
MUTEX_LOCK
(
switch_mutex
);
#endif
/* We now hold the GIL */
gil_locked
=
1
;
if
(
tstate
!=
gil_last_holder
)
{
gil_last_holder
=
tstate
;
++
gil_switch_number
;
}
#ifdef FORCE_SWITCHING
COND_SIGNAL
(
switch_cond
);
MUTEX_UNLOCK
(
switch_mutex
);
#endif
if
(
gil_drop_request
)
{
RESET_GIL_DROP_REQUEST
();
}
if
(
tstate
->
async_exc
!=
NULL
)
{
_PyEval_SignalAsyncExc
();
}
MUTEX_UNLOCK
(
gil_mutex
);
errno
=
err
;
}
void
_PyEval_SetSwitchInterval
(
unsigned
long
microseconds
)
{
gil_interval
=
microseconds
;
}
unsigned
long
_PyEval_GetSwitchInterval
()
{
return
gil_interval
;
}
Python/pystate.c
View file @
074e5ed9
...
...
@@ -434,6 +434,7 @@ PyThreadState_SetAsyncExc(long id, PyObject *exc) {
p
->
async_exc
=
exc
;
HEAD_UNLOCK
();
Py_XDECREF
(
old_exc
);
_PyEval_SignalAsyncExc
();
return
1
;
}
}
...
...
Python/sysmodule.c
View file @
074e5ed9
...
...
@@ -448,10 +448,18 @@ Return the profiling function set with sys.setprofile.\n\
See the profiler chapter in the library manual."
);
/* TODO: deprecate */
static
int
_check_interval
=
100
;
static
PyObject
*
sys_setcheckinterval
(
PyObject
*
self
,
PyObject
*
args
)
{
if
(
!
PyArg_ParseTuple
(
args
,
"i:setcheckinterval"
,
&
_Py_CheckInterval
))
if
(
PyErr_WarnEx
(
PyExc_DeprecationWarning
,
"sys.getcheckinterval() and sys.setcheckinterval() "
"are deprecated. Use sys.setswitchinterval() "
"instead."
,
1
)
<
0
)
return
NULL
;
if
(
!
PyArg_ParseTuple
(
args
,
"i:setcheckinterval"
,
&
_check_interval
))
return
NULL
;
Py_INCREF
(
Py_None
);
return
Py_None
;
...
...
@@ -467,13 +475,59 @@ n instructions. This also affects how often thread switches occur."
static
PyObject
*
sys_getcheckinterval
(
PyObject
*
self
,
PyObject
*
args
)
{
return
PyLong_FromLong
(
_Py_CheckInterval
);
if
(
PyErr_WarnEx
(
PyExc_DeprecationWarning
,
"sys.getcheckinterval() and sys.setcheckinterval() "
"are deprecated. Use sys.getswitchinterval() "
"instead."
,
1
)
<
0
)
return
NULL
;
return
PyLong_FromLong
(
_check_interval
);
}
PyDoc_STRVAR
(
getcheckinterval_doc
,
"getcheckinterval() -> current check interval; see setcheckinterval()."
);
#ifdef WITH_THREAD
static
PyObject
*
sys_setswitchinterval
(
PyObject
*
self
,
PyObject
*
args
)
{
double
d
;
if
(
!
PyArg_ParseTuple
(
args
,
"d:setswitchinterval"
,
&
d
))
return
NULL
;
if
(
d
<=
0
.
0
)
{
PyErr_SetString
(
PyExc_ValueError
,
"switch interval must be strictly positive"
);
return
NULL
;
}
_PyEval_SetSwitchInterval
((
unsigned
long
)
(
1e6
*
d
));
Py_INCREF
(
Py_None
);
return
Py_None
;
}
PyDoc_STRVAR
(
setswitchinterval_doc
,
"setswitchinterval(n)
\n
\
\n
\
Set the ideal thread switching delay inside the Python interpreter
\n
\
The actual frequency of switching threads can be lower if the
\n
\
interpreter executes long sequences of uninterruptible code
\n
\
(this is implementation-specific and workload-dependent).
\n
\
\n
\
The parameter must represent the desired switching delay in seconds
\n
\
A typical value is 0.005 (5 milliseconds)."
);
static
PyObject
*
sys_getswitchinterval
(
PyObject
*
self
,
PyObject
*
args
)
{
return
PyFloat_FromDouble
(
1e-6
*
_PyEval_GetSwitchInterval
());
}
PyDoc_STRVAR
(
getswitchinterval_doc
,
"getswitchinterval() -> current thread switch interval; see setswitchinterval()."
);
#endif
/* WITH_THREAD */
#ifdef WITH_TSC
static
PyObject
*
sys_settscdump
(
PyObject
*
self
,
PyObject
*
args
)
...
...
@@ -895,6 +949,12 @@ static PyMethodDef sys_methods[] = {
setcheckinterval_doc
},
{
"getcheckinterval"
,
sys_getcheckinterval
,
METH_NOARGS
,
getcheckinterval_doc
},
#ifdef WITH_THREAD
{
"setswitchinterval"
,
sys_setswitchinterval
,
METH_VARARGS
,
setswitchinterval_doc
},
{
"getswitchinterval"
,
sys_getswitchinterval
,
METH_NOARGS
,
getswitchinterval_doc
},
#endif
#ifdef HAVE_DLOPEN
{
"setdlopenflags"
,
sys_setdlopenflags
,
METH_VARARGS
,
setdlopenflags_doc
},
...
...
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