197 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			197 lines
		
	
	
		
			5.3 KiB
		
	
	
	
		
			C
		
	
	
| /*
 | |
|  * Copyright (C) 2009 Andrzej K. Haczewski <ahaczewski@gmail.com>
 | |
|  *
 | |
|  * DISCLAIMER: The implementation is Git-specific, it is subset of original
 | |
|  * Pthreads API, without lots of other features that Git doesn't use.
 | |
|  * Git also makes sure that the passed arguments are valid, so there's
 | |
|  * no need for double-checking.
 | |
|  */
 | |
| 
 | |
| #include "../../git-compat-util.h"
 | |
| #include "pthread.h"
 | |
| 
 | |
| #include <errno.h>
 | |
| #include <limits.h>
 | |
| 
 | |
| static unsigned __stdcall win32_start_routine(void *arg)
 | |
| {
 | |
| 	pthread_t *thread = arg;
 | |
| 	thread->tid = GetCurrentThreadId();
 | |
| 	thread->arg = thread->start_routine(thread->arg);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int pthread_create(pthread_t *thread, const void *unused,
 | |
| 		   void *(*start_routine)(void*), void *arg)
 | |
| {
 | |
| 	thread->arg = arg;
 | |
| 	thread->start_routine = start_routine;
 | |
| 	thread->handle = (HANDLE)
 | |
| 		_beginthreadex(NULL, 0, win32_start_routine, thread, 0, NULL);
 | |
| 
 | |
| 	if (!thread->handle)
 | |
| 		return errno;
 | |
| 	else
 | |
| 		return 0;
 | |
| }
 | |
| 
 | |
| int win32_pthread_join(pthread_t *thread, void **value_ptr)
 | |
| {
 | |
| 	DWORD result = WaitForSingleObject(thread->handle, INFINITE);
 | |
| 	switch (result) {
 | |
| 		case WAIT_OBJECT_0:
 | |
| 			if (value_ptr)
 | |
| 				*value_ptr = thread->arg;
 | |
| 			return 0;
 | |
| 		case WAIT_ABANDONED:
 | |
| 			return EINVAL;
 | |
| 		default:
 | |
| 			return err_win_to_posix(GetLastError());
 | |
| 	}
 | |
| }
 | |
| 
 | |
| pthread_t pthread_self(void)
 | |
| {
 | |
| 	pthread_t t = { NULL };
 | |
| 	t.tid = GetCurrentThreadId();
 | |
| 	return t;
 | |
| }
 | |
| 
 | |
| int pthread_cond_init(pthread_cond_t *cond, const void *unused)
 | |
| {
 | |
| 	cond->waiters = 0;
 | |
| 	cond->was_broadcast = 0;
 | |
| 	InitializeCriticalSection(&cond->waiters_lock);
 | |
| 
 | |
| 	cond->sema = CreateSemaphore(NULL, 0, LONG_MAX, NULL);
 | |
| 	if (!cond->sema)
 | |
| 		die("CreateSemaphore() failed");
 | |
| 
 | |
| 	cond->continue_broadcast = CreateEvent(NULL,	/* security */
 | |
| 				FALSE,			/* auto-reset */
 | |
| 				FALSE,			/* not signaled */
 | |
| 				NULL);			/* name */
 | |
| 	if (!cond->continue_broadcast)
 | |
| 		die("CreateEvent() failed");
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int pthread_cond_destroy(pthread_cond_t *cond)
 | |
| {
 | |
| 	CloseHandle(cond->sema);
 | |
| 	CloseHandle(cond->continue_broadcast);
 | |
| 	DeleteCriticalSection(&cond->waiters_lock);
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int pthread_cond_wait(pthread_cond_t *cond, CRITICAL_SECTION *mutex)
 | |
| {
 | |
| 	int last_waiter;
 | |
| 
 | |
| 	EnterCriticalSection(&cond->waiters_lock);
 | |
| 	cond->waiters++;
 | |
| 	LeaveCriticalSection(&cond->waiters_lock);
 | |
| 
 | |
| 	/*
 | |
| 	 * Unlock external mutex and wait for signal.
 | |
| 	 * NOTE: we've held mutex locked long enough to increment
 | |
| 	 * waiters count above, so there's no problem with
 | |
| 	 * leaving mutex unlocked before we wait on semaphore.
 | |
| 	 */
 | |
| 	LeaveCriticalSection(mutex);
 | |
| 
 | |
| 	/* let's wait - ignore return value */
 | |
| 	WaitForSingleObject(cond->sema, INFINITE);
 | |
| 
 | |
| 	/*
 | |
| 	 * Decrease waiters count. If we are the last waiter, then we must
 | |
| 	 * notify the broadcasting thread that it can continue.
 | |
| 	 * But if we continued due to cond_signal, we do not have to do that
 | |
| 	 * because the signaling thread knows that only one waiter continued.
 | |
| 	 */
 | |
| 	EnterCriticalSection(&cond->waiters_lock);
 | |
| 	cond->waiters--;
 | |
| 	last_waiter = cond->was_broadcast && cond->waiters == 0;
 | |
| 	LeaveCriticalSection(&cond->waiters_lock);
 | |
| 
 | |
| 	if (last_waiter) {
 | |
| 		/*
 | |
| 		 * cond_broadcast was issued while mutex was held. This means
 | |
| 		 * that all other waiters have continued, but are contending
 | |
| 		 * for the mutex at the end of this function because the
 | |
| 		 * broadcasting thread did not leave cond_broadcast, yet.
 | |
| 		 * (This is so that it can be sure that each waiter has
 | |
| 		 * consumed exactly one slice of the semaphor.)
 | |
| 		 * The last waiter must tell the broadcasting thread that it
 | |
| 		 * can go on.
 | |
| 		 */
 | |
| 		SetEvent(cond->continue_broadcast);
 | |
| 		/*
 | |
| 		 * Now we go on to contend with all other waiters for
 | |
| 		 * the mutex. Auf in den Kampf!
 | |
| 		 */
 | |
| 	}
 | |
| 	/* lock external mutex again */
 | |
| 	EnterCriticalSection(mutex);
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * IMPORTANT: This implementation requires that pthread_cond_signal
 | |
|  * is called while the mutex is held that is used in the corresponding
 | |
|  * pthread_cond_wait calls!
 | |
|  */
 | |
| int pthread_cond_signal(pthread_cond_t *cond)
 | |
| {
 | |
| 	int have_waiters;
 | |
| 
 | |
| 	EnterCriticalSection(&cond->waiters_lock);
 | |
| 	have_waiters = cond->waiters > 0;
 | |
| 	LeaveCriticalSection(&cond->waiters_lock);
 | |
| 
 | |
| 	/*
 | |
| 	 * Signal only when there are waiters
 | |
| 	 */
 | |
| 	if (have_waiters)
 | |
| 		return ReleaseSemaphore(cond->sema, 1, NULL) ?
 | |
| 			0 : err_win_to_posix(GetLastError());
 | |
| 	else
 | |
| 		return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * DOUBLY IMPORTANT: This implementation requires that pthread_cond_broadcast
 | |
|  * is called while the mutex is held that is used in the corresponding
 | |
|  * pthread_cond_wait calls!
 | |
|  */
 | |
| int pthread_cond_broadcast(pthread_cond_t *cond)
 | |
| {
 | |
| 	EnterCriticalSection(&cond->waiters_lock);
 | |
| 
 | |
| 	if ((cond->was_broadcast = cond->waiters > 0)) {
 | |
| 		/* wake up all waiters */
 | |
| 		ReleaseSemaphore(cond->sema, cond->waiters, NULL);
 | |
| 		LeaveCriticalSection(&cond->waiters_lock);
 | |
| 		/*
 | |
| 		 * At this point all waiters continue. Each one takes its
 | |
| 		 * slice of the semaphor. Now it's our turn to wait: Since
 | |
| 		 * the external mutex is held, no thread can leave cond_wait,
 | |
| 		 * yet. For this reason, we can be sure that no thread gets
 | |
| 		 * a chance to eat *more* than one slice. OTOH, it means
 | |
| 		 * that the last waiter must send us a wake-up.
 | |
| 		 */
 | |
| 		WaitForSingleObject(cond->continue_broadcast, INFINITE);
 | |
| 		/*
 | |
| 		 * Since the external mutex is held, no thread can enter
 | |
| 		 * cond_wait, and, hence, it is safe to reset this flag
 | |
| 		 * without cond->waiters_lock held.
 | |
| 		 */
 | |
| 		cond->was_broadcast = 0;
 | |
| 	} else {
 | |
| 		LeaveCriticalSection(&cond->waiters_lock);
 | |
| 	}
 | |
| 	return 0;
 | |
| }
 |