|  | 
 | /* This code implemented by Dag.Gruneau@elsa.preseco.comm.se */ | 
 | /* Fast NonRecursiveMutex support by Yakov Markovitch, markovitch@iso.ru */ | 
 | /* Eliminated some memory leaks, gsw@agere.com */ | 
 |  | 
 | #include <windows.h> | 
 | #include <limits.h> | 
 | #ifdef HAVE_PROCESS_H | 
 | #include <process.h> | 
 | #endif | 
 |  | 
 | typedef struct NRMUTEX { | 
 | 	LONG   owned ; | 
 | 	DWORD  thread_id ; | 
 | 	HANDLE hevent ; | 
 | } NRMUTEX, *PNRMUTEX ; | 
 |  | 
 |  | 
 | BOOL | 
 | InitializeNonRecursiveMutex(PNRMUTEX mutex) | 
 | { | 
 | 	mutex->owned = -1 ;  /* No threads have entered NonRecursiveMutex */ | 
 | 	mutex->thread_id = 0 ; | 
 | 	mutex->hevent = CreateEvent(NULL, FALSE, FALSE, NULL) ; | 
 | 	return mutex->hevent != NULL ;	/* TRUE if the mutex is created */ | 
 | } | 
 |  | 
 | VOID | 
 | DeleteNonRecursiveMutex(PNRMUTEX mutex) | 
 | { | 
 | 	/* No in-use check */ | 
 | 	CloseHandle(mutex->hevent) ; | 
 | 	mutex->hevent = NULL ; /* Just in case */ | 
 | } | 
 |  | 
 | DWORD | 
 | EnterNonRecursiveMutex(PNRMUTEX mutex, BOOL wait) | 
 | { | 
 | 	/* Assume that the thread waits successfully */ | 
 | 	DWORD ret ; | 
 |  | 
 | 	/* InterlockedIncrement(&mutex->owned) == 0 means that no thread currently owns the mutex */ | 
 | 	if (!wait) | 
 | 	{ | 
 | 		if (InterlockedCompareExchange(&mutex->owned, 0, -1) != -1) | 
 | 			return WAIT_TIMEOUT ; | 
 | 		ret = WAIT_OBJECT_0 ; | 
 | 	} | 
 | 	else | 
 | 		ret = InterlockedIncrement(&mutex->owned) ? | 
 | 			/* Some thread owns the mutex, let's wait... */ | 
 | 			WaitForSingleObject(mutex->hevent, INFINITE) : WAIT_OBJECT_0 ; | 
 |  | 
 | 	mutex->thread_id = GetCurrentThreadId() ; /* We own it */ | 
 | 	return ret ; | 
 | } | 
 |  | 
 | BOOL | 
 | LeaveNonRecursiveMutex(PNRMUTEX mutex) | 
 | { | 
 | 	/* We don't own the mutex */ | 
 | 	mutex->thread_id = 0 ; | 
 | 	return | 
 | 		InterlockedDecrement(&mutex->owned) < 0 || | 
 | 		SetEvent(mutex->hevent) ; /* Other threads are waiting, wake one on them up */ | 
 | } | 
 |  | 
 | PNRMUTEX | 
 | AllocNonRecursiveMutex(void) | 
 | { | 
 | 	PNRMUTEX mutex = (PNRMUTEX)malloc(sizeof(NRMUTEX)) ; | 
 | 	if (mutex && !InitializeNonRecursiveMutex(mutex)) | 
 | 	{ | 
 | 		free(mutex) ; | 
 | 		mutex = NULL ; | 
 | 	} | 
 | 	return mutex ; | 
 | } | 
 |  | 
 | void | 
 | FreeNonRecursiveMutex(PNRMUTEX mutex) | 
 | { | 
 | 	if (mutex) | 
 | 	{ | 
 | 		DeleteNonRecursiveMutex(mutex) ; | 
 | 		free(mutex) ; | 
 | 	} | 
 | } | 
 |  | 
 | long PyThread_get_thread_ident(void); | 
 |  | 
 | /* | 
 |  * Initialization of the C package, should not be needed. | 
 |  */ | 
 | static void | 
 | PyThread__init_thread(void) | 
 | { | 
 | } | 
 |  | 
 | /* | 
 |  * Thread support. | 
 |  */ | 
 |  | 
 | typedef struct { | 
 | 	void (*func)(void*); | 
 | 	void *arg; | 
 | 	long id; | 
 | 	HANDLE done; | 
 | } callobj; | 
 |  | 
 | static int | 
 | bootstrap(void *call) | 
 | { | 
 | 	callobj *obj = (callobj*)call; | 
 | 	/* copy callobj since other thread might free it before we're done */ | 
 | 	void (*func)(void*) = obj->func; | 
 | 	void *arg = obj->arg; | 
 |  | 
 | 	obj->id = PyThread_get_thread_ident(); | 
 | 	ReleaseSemaphore(obj->done, 1, NULL); | 
 | 	func(arg); | 
 | 	return 0; | 
 | } | 
 |  | 
 | long | 
 | PyThread_start_new_thread(void (*func)(void *), void *arg) | 
 | { | 
 | 	Py_uintptr_t 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, | 
 | 			  Py_SAFE_DOWNCAST(_pythread_stacksize, | 
 | 					   Py_ssize_t, int), | 
 | 			  &obj); | 
 | 	if (rv == (Py_uintptr_t)-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(), (void*)rv, errno)); | 
 | 		obj.id = -1; | 
 | 	} | 
 | 	else { | 
 | 		dprintf(("%ld: PyThread_start_new_thread succeeded: %p\n", | 
 | 		         PyThread_get_thread_ident(), (void*)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; | 
 | } | 
 |  | 
 | /* | 
 |  * Return the thread Id instead of an handle. The Id is said to uniquely identify the | 
 |  * thread in the system | 
 |  */ | 
 | long | 
 | PyThread_get_thread_ident(void) | 
 | { | 
 | 	if (!initialized) | 
 | 		PyThread_init_thread(); | 
 |  | 
 | 	return GetCurrentThreadId(); | 
 | } | 
 |  | 
 | static void | 
 | do_PyThread_exit_thread(int no_cleanup) | 
 | { | 
 | 	dprintf(("%ld: PyThread_exit_thread called\n", PyThread_get_thread_ident())); | 
 | 	if (!initialized) | 
 | 		if (no_cleanup) | 
 | 			_exit(0); | 
 | 		else | 
 | 			exit(0); | 
 | 	_endthread(); | 
 | } | 
 |  | 
 | void | 
 | PyThread_exit_thread(void) | 
 | { | 
 | 	do_PyThread_exit_thread(0); | 
 | } | 
 |  | 
 | void | 
 | PyThread__exit_thread(void) | 
 | { | 
 | 	do_PyThread_exit_thread(1); | 
 | } | 
 |  | 
 | #ifndef NO_EXIT_PROG | 
 | static void | 
 | do_PyThread_exit_prog(int status, int no_cleanup) | 
 | { | 
 | 	dprintf(("PyThread_exit_prog(%d) called\n", status)); | 
 | 	if (!initialized) | 
 | 		if (no_cleanup) | 
 | 			_exit(status); | 
 | 		else | 
 | 			exit(status); | 
 | } | 
 |  | 
 | void | 
 | PyThread_exit_prog(int status) | 
 | { | 
 | 	do_PyThread_exit_prog(status, 0); | 
 | } | 
 |  | 
 | void | 
 | PyThread__exit_prog(int status) | 
 | { | 
 | 	do_PyThread_exit_prog(status, 1); | 
 | } | 
 | #endif /* NO_EXIT_PROG */ | 
 |  | 
 | /* | 
 |  * Lock support. It has too be implemented as semaphores. | 
 |  * I [Dag] tried to implement it with mutex but I could find a way to | 
 |  * tell whether a thread already own the lock or not. | 
 |  */ | 
 | PyThread_type_lock | 
 | PyThread_allocate_lock(void) | 
 | { | 
 | 	PNRMUTEX aLock; | 
 |  | 
 | 	dprintf(("PyThread_allocate_lock called\n")); | 
 | 	if (!initialized) | 
 | 		PyThread_init_thread(); | 
 |  | 
 | 	aLock = AllocNonRecursiveMutex() ; | 
 |  | 
 | 	dprintf(("%ld: PyThread_allocate_lock() -> %p\n", PyThread_get_thread_ident(), aLock)); | 
 |  | 
 | 	return (PyThread_type_lock) aLock; | 
 | } | 
 |  | 
 | void | 
 | PyThread_free_lock(PyThread_type_lock aLock) | 
 | { | 
 | 	dprintf(("%ld: PyThread_free_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); | 
 |  | 
 | 	FreeNonRecursiveMutex(aLock) ; | 
 | } | 
 |  | 
 | /* | 
 |  * Return 1 on success if the lock was acquired | 
 |  * | 
 |  * and 0 if the lock was not acquired. This means a 0 is returned | 
 |  * if the lock has already been acquired by this thread! | 
 |  */ | 
 | int | 
 | PyThread_acquire_lock(PyThread_type_lock aLock, int waitflag) | 
 | { | 
 | 	int success ; | 
 |  | 
 | 	dprintf(("%ld: PyThread_acquire_lock(%p, %d) called\n", PyThread_get_thread_ident(),aLock, waitflag)); | 
 |  | 
 | 	success = aLock && EnterNonRecursiveMutex((PNRMUTEX) aLock, (waitflag ? INFINITE : 0)) == WAIT_OBJECT_0 ; | 
 |  | 
 | 	dprintf(("%ld: PyThread_acquire_lock(%p, %d) -> %d\n", PyThread_get_thread_ident(),aLock, waitflag, success)); | 
 |  | 
 | 	return success; | 
 | } | 
 |  | 
 | void | 
 | PyThread_release_lock(PyThread_type_lock aLock) | 
 | { | 
 | 	dprintf(("%ld: PyThread_release_lock(%p) called\n", PyThread_get_thread_ident(),aLock)); | 
 |  | 
 | 	if (!(aLock && LeaveNonRecursiveMutex((PNRMUTEX) aLock))) | 
 | 		dprintf(("%ld: Could not PyThread_release_lock(%p) error: %ld\n", PyThread_get_thread_ident(), aLock, GetLastError())); | 
 | } | 
 |  | 
 | /* minimum/maximum thread stack sizes supported */ | 
 | #define THREAD_MIN_STACKSIZE	0x8000		/* 32kB */ | 
 | #define THREAD_MAX_STACKSIZE	0x10000000	/* 256MB */ | 
 |  | 
 | /* set the thread stack size. | 
 |  * Return 0 if size is valid, -1 otherwise. | 
 |  */ | 
 | static int | 
 | _pythread_nt_set_stacksize(size_t size) | 
 | { | 
 | 	/* set to default */ | 
 | 	if (size == 0) { | 
 | 		_pythread_stacksize = 0; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	/* valid range? */ | 
 | 	if (size >= THREAD_MIN_STACKSIZE && size < THREAD_MAX_STACKSIZE) { | 
 | 		_pythread_stacksize = size; | 
 | 		return 0; | 
 | 	} | 
 |  | 
 | 	return -1; | 
 | } | 
 |  | 
 | #define THREAD_SET_STACKSIZE(x)	_pythread_nt_set_stacksize(x) |