> I built something very similar for my company last year, and it’s been running > flawlessly in production at a few customer sites since, with avg. CPU usage > ~50% > around the clock. I even posted about it on the Python mailing list [1] where > there was almost no resonance at that time. I never posted code, though -- > nobody seemed to be too interested.
I've never bothered to make this tidy and nice, especially the function naming (PySpecial_*) leaves some things to be desired. It's not too bad, though; it just doesn't have commit-ready quality. I don't worry about this anymore, so I just post what I have. Maybe someone can make use of it.
--- Python-2.5.2/Include/pythread.h.scheduling 2006-06-13 17:04:24.000000000 +0200 +++ Python-2.5.2/Include/pythread.h 2008-10-16 14:46:07.000000000 +0200 @@ -40,6 +40,20 @@ PyAPI_FUNC(void *) PyThread_get_key_value(int); PyAPI_FUNC(void) PyThread_delete_key_value(int key); +#ifndef _POSIX_THREADS +#error Requires POSIX threads +#endif + +PyAPI_FUNC(void *) PyThread_mutex_alloc(void); +PyAPI_FUNC(void) PyThread_mutex_free(void *); +PyAPI_FUNC(void) PyThread_mutex_lock(void *); +PyAPI_FUNC(void) PyThread_mutex_unlock(void *); + +PyAPI_FUNC(void *) PyThread_cond_alloc(void); +PyAPI_FUNC(void) PyThread_cond_free(void *); +PyAPI_FUNC(void) PyThread_cond_wait(void *, void *); +PyAPI_FUNC(void) PyThread_cond_signal(void *); + #ifdef __cplusplus } #endif --- Python-2.5.2/Python/thread.c.scheduling 2006-07-21 09:59:47.000000000 +0200 +++ Python-2.5.2/Python/thread.c 2008-10-16 14:46:07.000000000 +0200 @@ -155,6 +155,56 @@ #endif */ +void *PyThread_mutex_alloc(void) +{ + pthread_mutex_t *m = malloc(sizeof(pthread_mutex_t)); + if (pthread_mutex_init(m, NULL)) + Py_FatalError("PyThread_mutex_alloc: pthread_mutex_init failed"); + return m; +} + +void PyThread_mutex_free(void *m) +{ + if (pthread_mutex_destroy(m)) + Py_FatalError("PyThread_mutex_free: pthread_mutex_destroy failed"); + free(m); +} + +void PyThread_mutex_lock(void *m) +{ + pthread_mutex_lock(m); +} + +void PyThread_mutex_unlock(void *m) +{ + pthread_mutex_unlock(m); +} + +void *PyThread_cond_alloc(void) +{ + pthread_cond_t *c = malloc(sizeof(pthread_cond_t)); + if (pthread_cond_init(c, NULL)) + Py_FatalError("PyThread_cond_alloc: pthread_cond_init failed"); + return c; +} + +void PyThread_cond_free(void *c) +{ + if (pthread_cond_destroy(c)) + Py_FatalError("PyThread_cond_free: pthread_cond_destroy failed"); + free(c); +} + +void PyThread_cond_wait(void *c, void *m) +{ + pthread_cond_wait(c, m); +} + +void PyThread_cond_signal(void *c) +{ + pthread_cond_signal(c); +} + /* return the current thread stack size */ size_t PyThread_get_stacksize(void) --- Python-2.5.2/Python/ceval.c.scheduling 2008-01-23 21:09:39.000000000 +0100 +++ Python-2.5.2/Python/ceval.c 2008-10-16 14:47:07.000000000 +0200 @@ -210,7 +210,31 @@ #endif #include "pythread.h" -static PyThread_type_lock interpreter_lock = 0; /* This is the GIL */ +typedef void *PySpecial_cond_type; + +struct special_linkstruct { + PySpecial_cond_type wait; + struct special_linkstruct *queue_next, *free_next; + int in_use; +}; + +typedef void *PySpecial_lock_type; + +typedef struct { + PySpecial_lock_type the_lock; + struct special_linkstruct *wait_queue, *wait_last, *free_queue; +} PySpecialSemaphore; + +void +PySpecial_init(PySpecialSemaphore *s) +{ + s->the_lock = PyThread_mutex_alloc(); + s->wait_queue = NULL; + s->wait_last = NULL; + s->free_queue = NULL; +} + +static PySpecialSemaphore *interpreter_lock = NULL; /* This is the GIL */ static long main_thread = 0; int @@ -219,26 +243,100 @@ return interpreter_lock != 0; } +static PySpecialSemaphore *allocate_special(void) +{ + PySpecialSemaphore *s = malloc(sizeof(PySpecialSemaphore)); + PySpecial_init(s); + return s; +} + +static struct special_linkstruct *allocate_special_linkstruct(void) +{ + struct special_linkstruct *ls = malloc(sizeof(struct special_linkstruct)); + ls->wait = PyThread_cond_alloc(); + ls->queue_next = NULL; + ls->free_next = NULL; + ls->in_use = 0; + return ls; +} + +static void PySpecial_Lock(PySpecialSemaphore *s) +{ + struct special_linkstruct *ls; + + PyThread_mutex_lock(s->the_lock); + + if (!s->free_queue) + s->free_queue = allocate_special_linkstruct(); + + ls = s->free_queue; + s->free_queue = ls->free_next; + + if (!s->wait_queue) + { + ls->in_use = 1; + s->wait_queue = ls; + s->wait_last = ls; + PyThread_mutex_unlock(s->the_lock); + return; + } + + assert(s->wait_queue != ls); + assert(s->wait_last != ls); + assert(s->wait_last->queue_next == NULL); + assert(!ls->in_use); + s->wait_last->queue_next = ls; + s->wait_last = ls; + ls->in_use = 1; + + while (s->wait_queue != ls) + PyThread_cond_wait(ls->wait, s->the_lock); + + PyThread_mutex_unlock(s->the_lock); +} + +static void PySpecial_Unlock(PySpecialSemaphore *s) +{ + struct special_linkstruct *ls; + + PyThread_mutex_lock(s->the_lock); + ls = s->wait_queue; + assert(ls->in_use); + + s->wait_queue = ls->queue_next; + if (s->wait_queue) + { + ls->queue_next = NULL; + PyThread_cond_signal(s->wait_queue->wait); + } + ls->in_use = 0; + + ls->free_next = s->free_queue; + s->free_queue = ls; + + PyThread_mutex_unlock(s->the_lock); +} + void PyEval_InitThreads(void) { if (interpreter_lock) return; - interpreter_lock = PyThread_allocate_lock(); - PyThread_acquire_lock(interpreter_lock, 1); + interpreter_lock = allocate_special(); + PySpecial_Lock(interpreter_lock); main_thread = PyThread_get_thread_ident(); } void PyEval_AcquireLock(void) { - PyThread_acquire_lock(interpreter_lock, 1); + PySpecial_Lock(interpreter_lock); } void PyEval_ReleaseLock(void) { - PyThread_release_lock(interpreter_lock); + PySpecial_Unlock(interpreter_lock); } void @@ -248,7 +346,7 @@ 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); + PySpecial_Lock(interpreter_lock); if (PyThreadState_Swap(tstate) != NULL) Py_FatalError( "PyEval_AcquireThread: non-NULL old thread state"); @@ -261,7 +359,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); + PySpecial_Unlock(interpreter_lock); } /* This function is called from PyOS_AfterFork to ensure that newly @@ -278,8 +376,8 @@ 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(); - PyThread_acquire_lock(interpreter_lock, 1); + interpreter_lock = allocate_special(); + PySpecial_Lock(interpreter_lock); main_thread = PyThread_get_thread_ident(); } #endif @@ -296,7 +394,7 @@ Py_FatalError("PyEval_SaveThread: NULL tstate"); #ifdef WITH_THREAD if (interpreter_lock) - PyThread_release_lock(interpreter_lock); + PySpecial_Unlock(interpreter_lock); #endif return tstate; } @@ -309,7 +407,7 @@ #ifdef WITH_THREAD if (interpreter_lock) { int err = errno; - PyThread_acquire_lock(interpreter_lock, 1); + PySpecial_Lock(interpreter_lock); errno = err; } #endif @@ -818,21 +916,23 @@ _Py_Ticker = 0; } #ifdef WITH_THREAD - if (interpreter_lock) { + if (interpreter_lock && interpreter_lock->wait_queue) { /* Give another thread a chance */ if (PyThreadState_Swap(NULL) != tstate) Py_FatalError("ceval: tstate mix-up"); - PyThread_release_lock(interpreter_lock); + PySpecial_Unlock(interpreter_lock); /* Other threads may run now */ - PyThread_acquire_lock(interpreter_lock, 1); + PySpecial_Lock(interpreter_lock); if (PyThreadState_Swap(tstate) != NULL) Py_FatalError("ceval: orphan tstate"); - /* Check for thread interrupts */ + } + if (interpreter_lock) { + /* Check for thread interrupts */ if (tstate->async_exc != NULL) { x = tstate->async_exc; tstate->async_exc = NULL;
_______________________________________________ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com