[Python-checkins] r75631 - in sandbox/trunk/newgil: Include/ceval.h Makefile.pre.in Modules/_io/fileio.c Modules/_multiprocessing/connection.h Modules/_multiprocessing/pipe_connection.c Modules/_multiprocessing/socket_connection.c Modules/_ssl.c Modules/bz2module.c Modules/posixmodule.c Modules/selectmodule.c Modules/socketmodule.c Objects/longobject.c Python/ceval.c Python/ceval_pthread.h Python/pystate.c
antoine.pitrou
python-checkins at python.org
Fri Oct 23 19:38:15 CEST 2009
Author: antoine.pitrou
Date: Fri Oct 23 19:38:14 2009
New Revision: 75631
Log:
Commit initial patch (from personal hg repo).
Only POSIX-compatible for now. I plan to try and make it working on Windows.
Added:
sandbox/trunk/newgil/Python/ceval_pthread.h (contents, props changed)
Modified:
sandbox/trunk/newgil/Include/ceval.h
sandbox/trunk/newgil/Makefile.pre.in
sandbox/trunk/newgil/Modules/_io/fileio.c
sandbox/trunk/newgil/Modules/_multiprocessing/connection.h
sandbox/trunk/newgil/Modules/_multiprocessing/pipe_connection.c
sandbox/trunk/newgil/Modules/_multiprocessing/socket_connection.c
sandbox/trunk/newgil/Modules/_ssl.c
sandbox/trunk/newgil/Modules/bz2module.c
sandbox/trunk/newgil/Modules/posixmodule.c
sandbox/trunk/newgil/Modules/selectmodule.c
sandbox/trunk/newgil/Modules/socketmodule.c
sandbox/trunk/newgil/Objects/longobject.c
sandbox/trunk/newgil/Python/ceval.c
sandbox/trunk/newgil/Python/pystate.c
Modified: sandbox/trunk/newgil/Include/ceval.h
==============================================================================
--- sandbox/trunk/newgil/Include/ceval.h (original)
+++ sandbox/trunk/newgil/Include/ceval.h Fri Oct 23 19:38:14 2009
@@ -113,7 +113,7 @@
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(volatile int) _Py_Ticker;
PyAPI_DATA(int) _Py_CheckInterval;
/* Interface for threads.
@@ -163,6 +163,7 @@
PyAPI_FUNC(PyThreadState *) PyEval_SaveThread(void);
PyAPI_FUNC(void) PyEval_RestoreThread(PyThreadState *);
+PyAPI_FUNC(void) PyEval_RestoreThreadPrio(PyThreadState *, int prio);
#ifdef WITH_THREAD
@@ -181,6 +182,8 @@
#define Py_UNBLOCK_THREADS _save = PyEval_SaveThread();
#define Py_END_ALLOW_THREADS PyEval_RestoreThread(_save); \
}
+#define Py_END_ALLOW_THREADS_PRIO(x) PyEval_RestoreThreadPrio(_save, (x)); \
+ }
#else /* !WITH_THREAD */
@@ -192,6 +195,7 @@
#endif /* !WITH_THREAD */
PyAPI_FUNC(int) _PyEval_SliceIndex(PyObject *, Py_ssize_t *);
+PyAPI_FUNC(void) _PyEval_SignalAsyncExc(void);
#ifdef __cplusplus
Modified: sandbox/trunk/newgil/Makefile.pre.in
==============================================================================
--- sandbox/trunk/newgil/Makefile.pre.in (original)
+++ sandbox/trunk/newgil/Makefile.pre.in Fri Oct 23 19:38:14 2009
@@ -596,7 +596,7 @@
$(OPCODETARGETS_H): $(OPCODETARGETGEN_FILES)
$(OPCODETARGETGEN) $(OPCODETARGETS_H)
-Python/ceval.o: $(OPCODETARGETS_H)
+Python/ceval.o: $(OPCODETARGETS_H) Python/ceval_pthread.h
Python/formatter_unicode.o: $(srcdir)/Python/formatter_unicode.c \
$(BYTESTR_DEPS) \
Modified: sandbox/trunk/newgil/Modules/_io/fileio.c
==============================================================================
--- sandbox/trunk/newgil/Modules/_io/fileio.c (original)
+++ sandbox/trunk/newgil/Modules/_io/fileio.c Fri Oct 23 19:38:14 2009
@@ -476,7 +476,7 @@
Py_BEGIN_ALLOW_THREADS
errno = 0;
n = read(self->fd, pbuf.buf, pbuf.len);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(n > 0)
} else
n = -1;
PyBuffer_Release(&pbuf);
@@ -559,7 +559,7 @@
n = read(self->fd,
PyBytes_AS_STRING(result) + total,
newsize - total);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(n > 0)
if (n == 0)
break;
if (n < 0) {
@@ -615,7 +615,7 @@
Py_BEGIN_ALLOW_THREADS
errno = 0;
n = read(self->fd, ptr, size);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(n > 0)
} else
n = -1;
Modified: sandbox/trunk/newgil/Modules/_multiprocessing/connection.h
==============================================================================
--- sandbox/trunk/newgil/Modules/_multiprocessing/connection.h (original)
+++ sandbox/trunk/newgil/Modules/_multiprocessing/connection.h Fri Oct 23 19:38:14 2009
@@ -367,7 +367,7 @@
Py_BEGIN_ALLOW_THREADS
res = conn_poll(self, timeout, _save);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(res == TRUE)
switch (res) {
case TRUE:
Modified: sandbox/trunk/newgil/Modules/_multiprocessing/pipe_connection.c
==============================================================================
--- sandbox/trunk/newgil/Modules/_multiprocessing/pipe_connection.c (original)
+++ sandbox/trunk/newgil/Modules/_multiprocessing/pipe_connection.c Fri Oct 23 19:38:14 2009
@@ -49,7 +49,7 @@
Py_BEGIN_ALLOW_THREADS
ret = ReadFile(conn->handle, buffer, MIN(buflength, maxlength),
&length, NULL);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(ret)
if (ret)
return length;
@@ -75,7 +75,7 @@
Py_BEGIN_ALLOW_THREADS
ret = ReadFile(conn->handle, *newbuffer+length, left, &length, NULL);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(ret)
if (ret) {
assert(length == left);
return full_length;
Modified: sandbox/trunk/newgil/Modules/_multiprocessing/socket_connection.c
==============================================================================
--- sandbox/trunk/newgil/Modules/_multiprocessing/socket_connection.c (original)
+++ sandbox/trunk/newgil/Modules/_multiprocessing/socket_connection.c Fri Oct 23 19:38:14 2009
@@ -124,7 +124,7 @@
Py_BEGIN_ALLOW_THREADS
res = _conn_recvall(conn->handle, (char*)&ulength, 4);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(res == MP_SUCCESS)
if (res < 0)
return res;
@@ -135,7 +135,7 @@
if (ulength <= buflength) {
Py_BEGIN_ALLOW_THREADS
res = _conn_recvall(conn->handle, buffer, (size_t)ulength);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(res == MP_SUCCESS)
return res < 0 ? res : ulength;
} else {
*newbuffer = PyMem_Malloc((size_t)ulength);
@@ -143,7 +143,7 @@
return MP_MEMORY_ERROR;
Py_BEGIN_ALLOW_THREADS
res = _conn_recvall(conn->handle, *newbuffer, (size_t)ulength);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(res == MP_SUCCESS)
return res < 0 ? (Py_ssize_t)res : (Py_ssize_t)ulength;
}
}
Modified: sandbox/trunk/newgil/Modules/_ssl.c
==============================================================================
--- sandbox/trunk/newgil/Modules/_ssl.c (original)
+++ sandbox/trunk/newgil/Modules/_ssl.c Fri Oct 23 19:38:14 2009
@@ -24,6 +24,9 @@
#define PySSL_UNBLOCK_THREADS if (_ssl_locks_count>0){_save = PyEval_SaveThread()};
#define PySSL_END_ALLOW_THREADS if (_ssl_locks_count>0){PyEval_RestoreThread(_save);} \
}
+#define PySSL_END_ALLOW_THREADS_PRIO(x) \
+ if (_ssl_locks_count>0){PyEval_RestoreThreadPrio(_save, (x));} \
+ }
#else /* no WITH_THREAD */
@@ -31,6 +34,7 @@
#define PySSL_BLOCK_THREADS
#define PySSL_UNBLOCK_THREADS
#define PySSL_END_ALLOW_THREADS
+#define PySSL_END_ALLOW_THREADS_PRIO(x)
#endif
@@ -1110,7 +1114,7 @@
timeout = (int)(s->sock_timeout * 1000 + 0.5);
PySSL_BEGIN_ALLOW_THREADS
rc = poll(&pollfd, 1, timeout);
- PySSL_END_ALLOW_THREADS
+ PySSL_END_ALLOW_THREADS_PRIO(rc > 0)
goto normal_return;
}
@@ -1134,7 +1138,7 @@
rc = select(s->sock_fd+1, NULL, &fds, NULL, &tv);
else
rc = select(s->sock_fd+1, &fds, NULL, NULL, &tv);
- PySSL_END_ALLOW_THREADS
+ PySSL_END_ALLOW_THREADS_PRIO(rc > 0)
#ifdef HAVE_POLL
normal_return:
@@ -1294,7 +1298,7 @@
/* first check if there are bytes ready to be read */
PySSL_BEGIN_ALLOW_THREADS
count = SSL_pending(self->ssl);
- PySSL_END_ALLOW_THREADS
+ PySSL_END_ALLOW_THREADS_PRIO(count > 0)
if (!count) {
sockstate = check_socket_and_wait_for_timeout(sock, 0);
@@ -1316,7 +1320,7 @@
PySSL_BEGIN_ALLOW_THREADS
count = SSL_read(self->ssl, mem, len);
err = SSL_get_error(self->ssl, count);
- PySSL_END_ALLOW_THREADS
+ PySSL_END_ALLOW_THREADS_PRIO(count > 0)
if (PyErr_CheckSignals())
goto error;
if (err == SSL_ERROR_WANT_READ) {
Modified: sandbox/trunk/newgil/Modules/bz2module.c
==============================================================================
--- sandbox/trunk/newgil/Modules/bz2module.c (original)
+++ sandbox/trunk/newgil/Modules/bz2module.c Fri Oct 23 19:38:14 2009
@@ -248,7 +248,8 @@
break;
*buf++ = c;
} while (bzerror == BZ_OK && c != '\n' && buf != end);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(bzerror == BZ_STREAM_END ||
+ bzerror == BZ_OK)
if (bzerror == BZ_STREAM_END) {
f->size = f->pos;
f->mode = MODE_READ_EOF;
@@ -322,7 +323,8 @@
}
Py_BEGIN_ALLOW_THREADS
chunksize = BZ2_bzRead(&bzerror, f->fp, f->f_buf, bufsize);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(bzerror == BZ_STREAM_END ||
+ bzerror == BZ_OK)
f->pos += chunksize;
if (bzerror == BZ_STREAM_END) {
f->size = f->pos;
@@ -443,7 +445,8 @@
BUF(ret)+bytesread,
buffersize-bytesread);
self->pos += chunksize;
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(bzerror == BZ_STREAM_END ||
+ bzerror == BZ_OK)
bytesread += chunksize;
if (bzerror == BZ_STREAM_END) {
self->size = self->pos;
@@ -576,7 +579,8 @@
nread = BZ2_bzRead(&bzerror, self->fp,
buffer+nfilled, buffersize-nfilled);
self->pos += nread;
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(bzerror == BZ_STREAM_END ||
+ bzerror == BZ_OK)
if (bzerror == BZ_STREAM_END) {
self->size = self->pos;
self->mode = MODE_READ_EOF;
@@ -933,7 +937,9 @@
chunksize = BZ2_bzRead(&bzerror, self->fp,
buffer, buffersize);
self->pos += chunksize;
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(
+ bzerror == BZ_STREAM_END ||
+ bzerror == BZ_OK)
bytesread += chunksize;
if (bzerror == BZ_STREAM_END) {
@@ -992,7 +998,8 @@
Py_BEGIN_ALLOW_THREADS
chunksize = BZ2_bzRead(&bzerror, self->fp, buffer, readsize);
self->pos += chunksize;
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(bzerror == BZ_STREAM_END ||
+ bzerror == BZ_OK)
bytesread += chunksize;
if (bzerror == BZ_STREAM_END) {
self->size = self->pos;
Modified: sandbox/trunk/newgil/Modules/posixmodule.c
==============================================================================
--- sandbox/trunk/newgil/Modules/posixmodule.c (original)
+++ sandbox/trunk/newgil/Modules/posixmodule.c Fri Oct 23 19:38:14 2009
@@ -2197,7 +2197,7 @@
}
Py_BEGIN_ALLOW_THREADS
result = FindNextFileW(hFindFile, &wFileData);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(result)
/* FindNextFile sets error to ERROR_NO_MORE_FILES if
it got to the end of the directory. */
if (!result && GetLastError() != ERROR_NO_MORE_FILES) {
@@ -2270,7 +2270,7 @@
}
Py_BEGIN_ALLOW_THREADS
result = FindNextFile(hFindFile, &FileData);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(result)
/* FindNextFile sets error to ERROR_NO_MORE_FILES if
it got to the end of the directory. */
if (!result && GetLastError() != ERROR_NO_MORE_FILES) {
@@ -2395,7 +2395,7 @@
errno = 0;
Py_BEGIN_ALLOW_THREADS
ep = readdir(dirp);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(ep || !errno)
if (ep == NULL) {
if (errno == 0) {
break;
@@ -4461,7 +4461,7 @@
Py_BEGIN_ALLOW_THREADS
pid = wait3(&status, options, &ru);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(pid > 0)
return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
}
@@ -4486,7 +4486,7 @@
Py_BEGIN_ALLOW_THREADS
pid = wait4(pid, &status, options, &ru);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(pid > 0)
return wait_helper(pid, WAIT_STATUS_INT(status), &ru);
}
@@ -4509,7 +4509,7 @@
return NULL;
Py_BEGIN_ALLOW_THREADS
pid = waitpid(pid, &status, options);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(pid > 0)
if (pid == -1)
return posix_error();
@@ -4533,7 +4533,7 @@
return NULL;
Py_BEGIN_ALLOW_THREADS
pid = _cwait(&status, pid, options);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(pid > 0)
if (pid == -1)
return posix_error();
@@ -4556,7 +4556,7 @@
Py_BEGIN_ALLOW_THREADS
pid = wait(&status);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(pid > 0)
if (pid == -1)
return posix_error();
@@ -5037,7 +5037,7 @@
return posix_error();
Py_BEGIN_ALLOW_THREADS
n = read(fd, PyBytes_AS_STRING(buffer), size);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(n > 0)
if (n < 0) {
Py_DECREF(buffer);
return posix_error();
Modified: sandbox/trunk/newgil/Modules/selectmodule.c
==============================================================================
--- sandbox/trunk/newgil/Modules/selectmodule.c (original)
+++ sandbox/trunk/newgil/Modules/selectmodule.c Fri Oct 23 19:38:14 2009
@@ -273,7 +273,7 @@
Py_BEGIN_ALLOW_THREADS
n = select(max, &ifdset, &ofdset, &efdset, tvp);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(n > 0)
#ifdef MS_WINDOWS
if (n == SOCKET_ERROR) {
@@ -532,7 +532,7 @@
/* call poll() */
Py_BEGIN_ALLOW_THREADS
poll_result = poll(self->ufds, self->ufd_len, timeout);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(poll_result > 0)
if (poll_result < 0) {
PyErr_SetFromErrno(SelectError);
@@ -1023,7 +1023,7 @@
Py_BEGIN_ALLOW_THREADS
nfds = epoll_wait(self->epfd, evs, maxevents, timeout);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(nfds > 0)
if (nfds < 0) {
PyErr_SetFromErrno(PyExc_IOError);
goto error;
@@ -1578,7 +1578,7 @@
Py_BEGIN_ALLOW_THREADS
gotevents = kevent(self->kqfd, chl, nchanges,
evl, nevents, ptimeoutspec);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(gotevents > 0)
if (gotevents == -1) {
PyErr_SetFromErrno(PyExc_OSError);
Modified: sandbox/trunk/newgil/Modules/socketmodule.c
==============================================================================
--- sandbox/trunk/newgil/Modules/socketmodule.c (original)
+++ sandbox/trunk/newgil/Modules/socketmodule.c Fri Oct 23 19:38:14 2009
@@ -1591,7 +1591,7 @@
timeout = internal_select(s, 0);
if (!timeout)
newfd = accept(s->sock_fd, SAS2SA(&addrbuf), &addrlen);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(!timeout)
if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out");
@@ -1967,7 +1967,7 @@
Py_BEGIN_ALLOW_THREADS
res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(res == 0)
if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out");
@@ -2001,7 +2001,7 @@
Py_BEGIN_ALLOW_THREADS
res = internal_connect(s, SAS2SA(&addrbuf), addrlen, &timeout);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(res == 0)
/* Signals are not errors (though they may raise exceptions). Adapted
from PyErr_SetFromErrnoWithFilenameObject(). */
@@ -2155,7 +2155,7 @@
timeout = internal_select(s, 0);
if (!timeout)
outlen = recv(s->sock_fd, cbuf, len, flags);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(outlen > 0)
if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out");
@@ -2186,7 +2186,7 @@
timeout = internal_select(s, 0);
if (!timeout)
nread = recv(s->sock_fd, read_buf, segment, flags);
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(nread > 0)
if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out");
@@ -2370,7 +2370,7 @@
SAS2SA(&addrbuf), &addrlen);
#endif
}
- Py_END_ALLOW_THREADS
+ Py_END_ALLOW_THREADS_PRIO(n > 0)
if (timeout == 1) {
PyErr_SetString(socket_timeout, "timed out");
Modified: sandbox/trunk/newgil/Objects/longobject.c
==============================================================================
--- sandbox/trunk/newgil/Objects/longobject.c (original)
+++ sandbox/trunk/newgil/Objects/longobject.c Fri Oct 23 19:38:14 2009
@@ -95,11 +95,16 @@
#define MAX(x, y) ((x) < (y) ? (y) : (x))
#define MIN(x, y) ((x) > (y) ? (y) : (x))
+#if 0
#define SIGCHECK(PyTryBlock) \
if (--_Py_Ticker < 0) { \
_Py_Ticker = _Py_CheckInterval; \
if (PyErr_CheckSignals()) PyTryBlock \
}
+#endif
+
+#define SIGCHECK(PyTryBlock) \
+ if (PyErr_CheckSignals()) PyTryBlock \
/* forward declaration */
static int bits_in_digit(digit d);
Modified: sandbox/trunk/newgil/Python/ceval.c
==============================================================================
--- sandbox/trunk/newgil/Python/ceval.c (original)
+++ sandbox/trunk/newgil/Python/ceval.c Fri Oct 23 19:38:14 2009
@@ -198,6 +198,28 @@
#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
@@ -205,36 +227,54 @@
#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;
+static volatile int pending_async_exc = 0;
+
+#include "ceval_pthread.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,
+ therefore we avoid PyThreadState_GET() which prints a fatal error
+ in debug mode.
+ */
+ drop_gil(_PyThreadState_Current);
}
void
@@ -243,8 +283,8 @@
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");
@@ -257,7 +297,7 @@
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
@@ -269,17 +309,17 @@
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.
@@ -299,7 +339,21 @@
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
@@ -312,27 +366,36 @@
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;
}
void
-PyEval_RestoreThread(PyThreadState *tstate)
+PyEval_RestoreThreadPrio(PyThreadState *tstate, int prio)
{
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);
+ if (prio)
+ take_gil_prio(tstate);
+ else
+ take_gil(tstate);
errno = err;
}
#endif
PyThreadState_Swap(tstate);
}
+void
+PyEval_RestoreThread(PyThreadState *tstate)
+{
+ PyEval_RestoreThreadPrio(tstate, 0);
+}
+
/* Mechanism whereby asynchronously executing callbacks (e.g. UNIX
signal handlers or Mac I/O completion routines) can schedule calls
@@ -372,7 +435,6 @@
} 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
@@ -411,8 +473,8 @@
pendinglast = j;
}
/* signal main loop */
- _Py_Ticker = 0;
- pendingcalls_to_do = 1;
+// _Py_Ticker = 0;
+ SIGNAL_PENDING_CALLS();
if (lock != NULL)
PyThread_release_lock(lock);
return result;
@@ -454,7 +516,11 @@
arg = pendingcalls[j].arg;
pendingfirst = (j + 1) % NPENDINGCALLS;
}
- pendingcalls_to_do = pendingfirst != pendinglast;
+ if (pendingfirst != pendinglast)
+ SIGNAL_PENDING_CALLS();
+ else
+ UNSIGNAL_PENDING_CALLS();
+// pendingcalls_to_do = pendingfirst != pendinglast;
PyThread_release_lock(pending_lock);
/* having released the lock, perform the callback */
if (func == NULL)
@@ -520,8 +586,7 @@
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;
@@ -534,7 +599,7 @@
if (busy)
return 0;
busy = 1;
- pendingcalls_to_do = 0;
+ UNSIGNAL_PENDING_CALLS();
for (;;) {
int i;
int (*func)(void *);
@@ -547,7 +612,7 @@
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;
}
}
@@ -642,8 +707,10 @@
/* 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 */
+int _Py_CheckInterval = 1000;
+// volatile int _Py_Ticker = 0; /* so that we hit a "tick" first thing */
+
+
PyObject *
PyEval_EvalCode(PyCodeObject *co, PyObject *globals, PyObject *locals)
@@ -773,10 +840,7 @@
#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; \
@@ -1150,13 +1214,14 @@
async I/O handler); see Py_AddPendingCall() and
Py_MakePendingCalls() above. */
- if (--_Py_Ticker < 0) {
+// 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;
+// _Py_Ticker = _Py_CheckInterval;
tstate->tick_counter++;
#ifdef WITH_TSC
ticked = 1;
@@ -1166,39 +1231,38 @@
why = WHY_EXCEPTION;
goto on_error;
}
- if (pendingcalls_to_do)
+// if (pendingcalls_to_do)
/* MakePendingCalls() didn't succeed.
Force early re-execution of this
"periodic" code, possibly after
a thread switch */
- _Py_Ticker = 0;
+// _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:
Added: sandbox/trunk/newgil/Python/ceval_pthread.h
==============================================================================
--- (empty file)
+++ sandbox/trunk/newgil/Python/ceval_pthread.h Fri Oct 23 19:38:14 2009
@@ -0,0 +1,258 @@
+/*
+ * Implementation of the Global Interpreter Lock (GIL) for POSIX pthreads.
+ */
+
+#include <stdlib.h>
+#include <errno.h>
+#include <pthread.h>
+
+/* 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 ADD_MILLISECONDS(tv, interval) \
+do { \
+ tv.tv_usec += interval * 1000; \
+ tv.tv_sec += tv.tv_usec / 1000000; \
+ tv.tv_usec %= 1000000; \
+} while (0)
+
+/* milliseconds */
+#define INTERVAL 5
+
+/* Enable if you want to force the switching of threads at least every INTERVAL */
+#undef FORCE_SWITCHING
+#define FORCE_SWITCHING
+
+
+#undef TRACE_PRIO
+// #define TRACE_PRIO
+
+#define YIELD_IF_PRIO_REQUEST() \
+do { \
+ if (prio_request) { \
+ if (pthread_mutex_lock(&prio_mutex)) \
+ Py_FatalError("pthread_mutex_lock(&prio_mutex) failed"); \
+ if (pthread_mutex_unlock(&prio_mutex)) \
+ Py_FatalError("pthread_mutex_unlock(&prio_mutex) failed"); \
+ } \
+} while (0)
+
+/* Whether the GIL is already taken (-1 if uninitialized) */
+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 */
+static PyThreadState *gil_last_holder = NULL;
+
+/* This condition variable allows to put threads to sleep.
+ In addition, the mutex also protects the above variables. */
+static pthread_cond_t gil_cond;
+static pthread_mutex_t gil_mutex;
+/* This mutex is taken when a priority request is made, and released when
+ it is finally honoured.
+ Other threads can sleep by trying to lock the mutex. */
+static pthread_mutex_t prio_mutex;
+/* The thread making the prio request, or NULL. */
+static volatile PyThreadState *prio_request = NULL;
+
+#ifdef FORCE_SWITCHING
+/* This condition variable forces the GIL-releasing thread to wait for
+ the scheduling of a GIL-awaiting thread, if any. */
+static pthread_cond_t switch_cond;
+static pthread_mutex_t switch_mutex;
+#endif
+
+static int gil_created(void)
+{
+ return gil_locked >= 0;
+}
+
+static void create_gil(void)
+{
+ if (pthread_mutex_init(&gil_mutex, NULL)
+ || pthread_mutex_init(&prio_mutex, NULL)
+#ifdef FORCE_SWITCHING
+ || pthread_mutex_init(&switch_mutex, NULL)
+#endif
+ )
+ Py_FatalError("create_gil: pthread_mutex_init() failed");
+ if (pthread_cond_init(&gil_cond, NULL)
+#ifdef FORCE_SWITCHING
+ || pthread_cond_init(&switch_cond, NULL)
+#endif
+ )
+ Py_FatalError("create_gil: pthread_cond_init() failed");
+ gil_locked = 0;
+ gil_last_holder = NULL;
+ prio_request = 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");
+
+ if (pthread_mutex_lock(&gil_mutex))
+ Py_FatalError("drop_gil: pthread_mutex_lock() failed");
+ gil_locked = 0;
+ if (pthread_cond_signal(&gil_cond))
+ Py_FatalError("drop_gil: pthread_cond_signal() failed");
+ if (pthread_mutex_unlock(&gil_mutex))
+ Py_FatalError("drop_gil: pthread_mutex_unlock() failed");
+
+#ifdef FORCE_SWITCHING
+ if (gil_drop_request) {
+ if (pthread_mutex_lock(&switch_mutex))
+ Py_FatalError("drop_gil: pthread_mutex_lock(&switch_mutex) failed");
+ /* Not switched yet => wait */
+ if (gil_last_holder == tstate &&
+ pthread_cond_wait(&switch_cond, &switch_mutex))
+ Py_FatalError("drop_gil: pthread_cond_wait(&switch_cond) failed");
+ if (pthread_mutex_unlock(&switch_mutex))
+ Py_FatalError("drop_gil: pthread_mutex_unlock(&switch_mutex) failed");
+ }
+#endif
+}
+
+static void _take_gil(PyThreadState *tstate, int prio)
+{
+ int err;
+ if (tstate == NULL)
+ Py_FatalError("take_gil: NULL tstate");
+
+ /* If another thread is requesting priority, give it a chance to run
+ before we take the mutex.
+ */
+ YIELD_IF_PRIO_REQUEST();
+
+ err = errno;
+ if (pthread_mutex_lock(&gil_mutex))
+ Py_FatalError("take_gil: pthread_mutex_lock() failed");
+
+ if (!gil_locked) {
+ prio = 0;
+ goto _ready;
+ }
+
+ if (prio) {
+#ifdef TRACE_PRIO
+ struct timeval tv;
+ GETTIMEOFDAY(&tv);
+ printf("trying to take gil with prio: %.3f <--\n",
+ tv.tv_sec + tv.tv_usec / 1000000.0);
+#endif
+ if (!prio_request) {
+ if (pthread_mutex_lock(&prio_mutex))
+ Py_FatalError("take_gil: pthread_mutex_lock(&prio_mutex) failed");
+ prio_request = tstate;
+ }
+ else
+ prio = 0;
+ }
+ while (gil_locked) {
+ int r;
+ unsigned long saved_switchnum;
+ struct timespec ts;
+ struct timeval now, deadline;
+
+ if (prio_request) {
+ /* Tell the eval loop the GIL must be dropped as soon as possible */
+ SET_GIL_DROP_REQUEST();
+ if (!prio) {
+ /* If another thread is making the prio_request, give it a
+ chance to run and take the mutex. */
+ pthread_mutex_unlock(&gil_mutex);
+ YIELD_IF_PRIO_REQUEST();
+ pthread_mutex_lock(&gil_mutex);
+ }
+ }
+
+ GETTIMEOFDAY(&now);
+ deadline = now;
+ ADD_MILLISECONDS(deadline, INTERVAL);
+ ts.tv_sec = deadline.tv_sec;
+ ts.tv_nsec = deadline.tv_usec * 1000;
+
+ saved_switchnum = gil_switch_number;
+ r = pthread_cond_timedwait(&gil_cond, &gil_mutex, &ts);
+
+ if (r == ETIMEDOUT) {
+ /* If no switch occurred in the meantime, it is time to ask
+ the GIL-holding thread to drop it. */
+ if (gil_locked && gil_switch_number == saved_switchnum) {
+ SET_GIL_DROP_REQUEST();
+ }
+ }
+ else if (r)
+ Py_FatalError("take_gil: pthread_cond_timedwait() failed");
+ else {
+ /* We were woken up for no reason, ignore. */
+ }
+ }
+_ready:
+#ifdef FORCE_SWITCHING
+ /* This mutex must be taken before modifying gil_last_holder (see drop_gil()). */
+ if (pthread_mutex_lock(&switch_mutex))
+ Py_FatalError("take_gil: pthread_mutex_lock() failed");
+#endif
+ /* We now hold the GIL */
+ gil_locked = 1;
+
+ if (tstate != gil_last_holder) {
+ gil_last_holder = tstate;
+ ++gil_switch_number;
+#ifdef TRACE_PRIO
+ if (prio) {
+ struct timeval tv;
+ GETTIMEOFDAY(&tv);
+ printf("gil taken with prio: %.3f\n",
+ tv.tv_sec + tv.tv_usec / 1000000.0);
+ }
+#endif
+ }
+#ifdef FORCE_SWITCHING
+ if (pthread_cond_signal(&switch_cond))
+ Py_FatalError("take_gil: pthread_cond_signal() failed");
+ if (pthread_mutex_unlock(&switch_mutex))
+ Py_FatalError("take_gil: pthread_mutex_unlock() failed");
+#endif
+ if (prio) {
+ /* The prio request was granted. */
+ prio_request = NULL;
+ if (pthread_mutex_unlock(&prio_mutex))
+ Py_FatalError("take_gil: pthread_mutex_unlock(&prio_mutex) failed");
+ }
+ if (gil_drop_request && !prio_request) {
+ /* No prio_request pending. */
+ RESET_GIL_DROP_REQUEST();
+ }
+ if (tstate->async_exc != NULL) {
+ _PyEval_SignalAsyncExc();
+ }
+
+ if (pthread_mutex_unlock(&gil_mutex))
+ Py_FatalError("take_gil: pthread_mutex_unlock() failed");
+ errno = err;
+}
+
+static void take_gil(PyThreadState *tstate)
+{
+ _take_gil(tstate, 0);
+}
+
+static void take_gil_prio(PyThreadState *tstate)
+{
+ _take_gil(tstate, 1);
+}
Modified: sandbox/trunk/newgil/Python/pystate.c
==============================================================================
--- sandbox/trunk/newgil/Python/pystate.c (original)
+++ sandbox/trunk/newgil/Python/pystate.c Fri Oct 23 19:38:14 2009
@@ -434,6 +434,7 @@
p->async_exc = exc;
HEAD_UNLOCK();
Py_XDECREF(old_exc);
+ _PyEval_SignalAsyncExc();
return 1;
}
}
More information about the Python-checkins
mailing list