[Python-checkins] python/dist/src/Python thread_nt.h,2.22,2.23
tim_one@users.sourceforge.net
tim_one@users.sourceforge.net
Thu, 03 Jul 2003 21:40:47 -0700
Update of /cvsroot/python/python/dist/src/Python
In directory sc8-pr-cvs1:/tmp/cvs-serv23219/Python
Modified Files:
thread_nt.h
Log Message:
An Anonymous Coward on c.l.py posted a little program with bizarre
behavior, creating many threads very quickly. A long debugging session
revealed that the Windows implementation of PyThread_start_new_thread()
was choked with "laziness" errors:
1. It checked MS _beginthread() for a failure return, but when that
happened it returned heap trash as the function result, instead of
an id of -1 (the proper error-return value).
2. It didn't consider that the Win32 CreateSemaphore() can fail.
3. When creating a great many threads very quickly, it's quite possible
that any particular bootstrap call can take virtually any amount of
time to return. But the code waited for a maximum of 5 seconds, and
didn't check to see whether the semaphore it was waiting for got
signaled. If it in fact timed out, the function could again return
heap trash as the function result. This is actually what confused
the test program, as the heap trash usually turned out to be 0, and
then multiple threads all got id 0 simultaneously, confusing the
hell out of threading.py's _active dict (mapping id to thread
object). A variety of baffling behaviors followed from that.
WRT #1 and #2, error returns are checked now, and "thread.error: can't
start new thread" gets raised now if a new thread (or new semaphore)
can't be created. WRT #3, we now wait for the semaphore without a
timeout.
Also removed useless local vrbls, folded long lines, and changed callobj
to a stack auto (it was going thru malloc/free instead, for no discernible
reason).
Bugfix candidate.
Index: thread_nt.h
===================================================================
RCS file: /cvsroot/python/python/dist/src/Python/thread_nt.h,v
retrieving revision 2.22
retrieving revision 2.23
diff -C2 -d -r2.22 -r2.23
*** thread_nt.h 28 Feb 2002 21:34:34 -0000 2.22
--- thread_nt.h 4 Jul 2003 04:40:45 -0000 2.23
***************
*** 149,153 ****
typedef struct {
void (*func)(void*);
! void *arg;
long id;
HANDLE done;
--- 149,153 ----
typedef struct {
void (*func)(void*);
! void *arg;
long id;
HANDLE done;
***************
*** 168,200 ****
}
! long PyThread_start_new_thread(void (*func)(void *), void *arg)
{
unsigned long rv;
! int success = 0;
! callobj *obj;
! int id;
! dprintf(("%ld: PyThread_start_new_thread called\n", PyThread_get_thread_ident()));
if (!initialized)
PyThread_init_thread();
! obj = malloc(sizeof(callobj));
! obj->func = func;
! obj->arg = arg;
! obj->done = CreateSemaphore(NULL, 0, 1, NULL);
! rv = _beginthread(bootstrap, 0, obj); /* use default stack size */
!
! if (rv != (unsigned long)-1) {
! success = 1;
! dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n", PyThread_get_thread_ident(), rv));
}
!
! /* wait for thread to initialize and retrieve id */
! WaitForSingleObject(obj->done, 5000); /* maybe INFINITE instead of 5000? */
! CloseHandle((HANDLE)obj->done);
! id = obj->id;
! free(obj);
! return id;
}
--- 168,207 ----
}
! long
! PyThread_start_new_thread(void (*func)(void *), void *arg)
{
unsigned long rv;
! callobj obj;
! dprintf(("%ld: PyThread_start_new_thread called\n",
! PyThread_get_thread_ident()));
if (!initialized)
PyThread_init_thread();
! obj.id = -1; /* guilty until proved innocent */
! obj.func = func;
! obj.arg = arg;
! obj.done = CreateSemaphore(NULL, 0, 1, NULL);
! if (obj.done == NULL)
! return -1;
! rv = _beginthread(bootstrap, 0, &obj); /* use default stack size */
! if (rv == (unsigned long)-1) {
! /* I've seen errno == EAGAIN here, which means "there are
! * too many threads".
! */
! dprintf(("%ld: PyThread_start_new_thread failed: %p errno %d\n",
! PyThread_get_thread_ident(), rv, errno));
! obj.id = -1;
}
! else {
! dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n",
! PyThread_get_thread_ident(), rv));
! /* wait for thread to initialize, so we can get its id */
! WaitForSingleObject(obj.done, INFINITE);
! assert(obj.id != -1);
! }
! CloseHandle((HANDLE)obj.done);
! return obj.id;
}