📄 readme.cv
字号:
/* * Wait for all the awakened threads to acquire their part of * the counting semaphore */ if (WaitForSingleObject (cv->waitersDone, INFINITE) == WAIT_OBJECT_0) { result = 0; } else { result = EINVAL; } } return (result);}BTW, on my system (2 CPUs) I can manage to getthe program stalled even without any source codemodification if I run the tennisb program manytimes in different shell sessions.===================pthread-win32 patch===================struct pthread_cond_t_ { long nWaitersBlocked; /* Number of threads blocked*/ long nWaitersUnblocked; /* Number of threads unblocked*/ long nWaitersToUnblock; /* Number of threads to unblock*/ sem_t semBlockQueue; /* Queue up threads waiting for the*/ /* condition to become signalled*/ sem_t semBlockLock; /* Semaphore that guards access to*/ /* | waiters blocked count/block queue*/ /* +-> Mandatory Sync.LEVEL-1*/ pthread_mutex_t mtxUnblockLock; /* Mutex that guards access to*/ /* | waiters (to)unblock(ed) counts*/ /* +-> Optional* Sync.LEVEL-2*/}; /* Opt*) for _timedwait andcancellation*/intpthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) int result = EAGAIN; pthread_cond_t cv = NULL; if (cond == NULL) {(cond return EINVAL; } if ((attr != NULL && *attr != NULL) && ((*attr)->pshared == PTHREAD_PROCESS_SHARED)) { /* * Creating condition variable that can be shared between * processes. */ result = ENOSYS; goto FAIL0; } cv = (pthread_cond_t) calloc (1, sizeof (*cv)); if (cv == NULL) {(cv result = ENOMEM; goto FAIL0; } cv->nWaitersBlocked = 0; cv->nWaitersUnblocked = 0; cv->nWaitersToUnblock = 0; if (sem_init (&(cv->semBlockLock), 0, 1) != 0) {(sem_init goto FAIL0; } if (sem_init (&(cv->semBlockQueue), 0, 0) != 0) {(sem_init goto FAIL1; } if (pthread_mutex_init (&(cv->mtxUnblockLock), 0) != 0) {(pthread_mutex_init goto FAIL2; } result = 0; goto DONE; /* * ------------- * Failed... * ------------- */FAIL2: (void) sem_destroy (&(cv->semBlockQueue));FAIL1: (void) sem_destroy (&(cv->semBlockLock));FAIL0:DONE: *cond = cv; return (result);} /* pthread_cond_init */intpthread_cond_destroy (pthread_cond_t * cond){ int result = 0; pthread_cond_t cv; /* * Assuming any race condition here is harmless. */ if (cond == NULL || *cond == NULL) { return EINVAL; } if (*cond != (pthread_cond_t) PTW32_OBJECT_AUTO_INIT) {(*cond cv = *cond; /* * Synchronize access to waiters blocked count (LEVEL-1) */ if (sem_wait(&(cv->semBlockLock)) != 0) {(sem_wait(&(cv->semBlockLock)) return errno; } /* * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2) */ if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0) {((result (void) sem_post(&(cv->semBlockLock)); return result; } /* * Check whether cv is still busy (still has waiters blocked) */ if (cv->nWaitersBlocked - cv->nWaitersUnblocked > 0) {(cv->nWaitersBlocked (void) sem_post(&(cv->semBlockLock)); (void) pthread_mutex_unlock(&(cv->mtxUnblockLock)); return EBUSY; } /* * Now it is safe to destroy */ (void) sem_destroy (&(cv->semBlockLock)); (void) sem_destroy (&(cv->semBlockQueue)); (void) pthread_mutex_unlock (&(cv->mtxUnblockLock)); (void) pthread_mutex_destroy (&(cv->mtxUnblockLock)); free(cv); *cond = NULL; } else { /* * See notes in ptw32_cond_check_need_init() above also. */ EnterCriticalSection(&ptw32_cond_test_init_lock); /* * Check again. */ if (*cond == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT) {(*cond /* * This is all we need to do to destroy a statically * initialised cond that has not yet been used (initialised). * If we get to here, another thread * waiting to initialise this cond will get an EINVAL. */ *cond = NULL; } else { /* * The cv has been initialised while we were waiting * so assume it's in use. */ result = EBUSY; } LeaveCriticalSection(&ptw32_cond_test_init_lock); } return (result);}/* * Arguments for cond_wait_cleanup, since we can only pass a * single void * to it. */typedef struct { pthread_mutex_t * mutexPtr; pthread_cond_t cv; int * resultPtr;} ptw32_cond_wait_cleanup_args_t;static voidptw32_cond_wait_cleanup(void * args){ ptw32_cond_wait_cleanup_args_t * cleanup_args =(ptw32_cond_wait_cleanup_args_t *) args; pthread_cond_t cv = cleanup_args->cv; int * resultPtr = cleanup_args->resultPtr; int eLastSignal; /* enum: 1=yes 0=no -1=cancelled/timedout w/o signal(s)*/ int result; /* * Whether we got here as a result of signal/broadcast or because of * timeout on wait or thread cancellation we indicate that we are no * longer waiting. The waiter is responsible for adjusting waiters * (to)unblock(ed) counts (protected by unblock lock). * Unblock lock/Sync.LEVEL-2 supports _timedwait and cancellation. */ if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0) {((result *resultPtr = result; return; } cv->nWaitersUnblocked++; eLastSignal = (cv->nWaitersToUnblock == 0) ? -1 : (--cv->nWaitersToUnblock == 0); /* * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed */ if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0) {((result *resultPtr = result; return; } /* * If last signal... */ if (eLastSignal == 1) {(eLastSignal /* * ...it means that we have end of 'atomic' signal/broadcast */ if (sem_post(&(cv->semBlockLock)) != 0) {(sem_post(&(cv->semBlockLock)) *resultPtr = errno; return; } } /* * If not last signal and not timed out/cancelled wait w/o signal... */ else if (eLastSignal == 0) { /* * ...it means that next waiter can go through semaphore */ if (sem_post(&(cv->semBlockQueue)) != 0) {(sem_post(&(cv->semBlockQueue)) *resultPtr = errno; return; } } /* * XSH: Upon successful return, the mutex has been locked and is owned * by the calling thread */ if ((result = pthread_mutex_lock(cleanup_args->mutexPtr)) != 0) {((result *resultPtr = result; }} /* ptw32_cond_wait_cleanup */static intptw32_cond_timedwait (pthread_cond_t * cond, pthread_mutex_t * mutex, const struct timespec *abstime){ int result = 0; pthread_cond_t cv; ptw32_cond_wait_cleanup_args_t cleanup_args; if (cond == NULL || *cond == NULL) {(cond return EINVAL; } /* * We do a quick check to see if we need to do more work * to initialise a static condition variable. We check * again inside the guarded section of ptw32_cond_check_need_init() * to avoid race conditions. */ if (*cond == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT) {(*cond result = ptw32_cond_check_need_init(cond); } if (result != 0 && result != EBUSY) {(result return result; } cv = *cond; /* * Synchronize access to waiters blocked count (LEVEL-1) */ if (sem_wait(&(cv->semBlockLock)) != 0) {(sem_wait(&(cv->semBlockLock)) return errno; } cv->nWaitersBlocked++; /* * Thats it. Counted means waiting, no more access needed */ if (sem_post(&(cv->semBlockLock)) != 0) {(sem_post(&(cv->semBlockLock)) return errno; } /* * Setup this waiter cleanup handler */ cleanup_args.mutexPtr = mutex; cleanup_args.cv = cv; cleanup_args.resultPtr = &result; pthread_cleanup_push (ptw32_cond_wait_cleanup, (void *) &cleanup_args); /* * Now we can release 'mutex' and... */ if ((result = pthread_mutex_unlock (mutex)) == 0) {((result /* * ...wait to be awakened by * pthread_cond_signal, or * pthread_cond_broadcast, or * timeout, or * thread cancellation * * Note: * * ptw32_sem_timedwait is a cancellation point, * hence providing the mechanism for making * pthread_cond_wait a cancellation point. * We use the cleanup mechanism to ensure we * re-lock the mutex and adjust (to)unblock(ed) waiters * counts if we are cancelled, timed out or signalled. */ if (ptw32_sem_timedwait (&(cv->semBlockQueue), abstime) != 0) {(ptw32_sem_timedwait result = errno; } } /* * Always cleanup */ pthread_cleanup_pop (1); /* * "result" can be modified by the cleanup handler. */ return (result);} /* ptw32_cond_timedwait */static intptw32_cond_unblock (pthread_cond_t * cond, int unblockAll){ int result; pthread_cond_t cv; if (cond == NULL || *cond == NULL) {(cond return EINVAL; } cv = *cond; /* * No-op if the CV is static and hasn't been initialised yet. * Assuming that any race condition is harmless. */ if (cv == (pthread_cond_t) PTW32_OBJECT_AUTO_INIT) {(cv return 0; } /* * Synchronize access to waiters blocked count (LEVEL-1) */ if (sem_wait(&(cv->semBlockLock)) != 0) {(sem_wait(&(cv->semBlockLock)) return errno; } /* * Synchronize access to waiters (to)unblock(ed) counts (LEVEL-2) * This sync.level supports _timedwait and cancellation */ if ((result = pthread_mutex_lock(&(cv->mtxUnblockLock))) != 0) {((result return result; } /* * Adjust waiters blocked and unblocked counts (collect garbage) */ if (cv->nWaitersUnblocked != 0) {(cv->nWaitersUnblocked cv->nWaitersBlocked -= cv->nWaitersUnblocked; cv->nWaitersUnblocked = 0; } /* * If (after adjustment) there are still some waiters blocked counted... */ if ( cv->nWaitersBlocked > 0) {( /* * We will unblock first waiter and leave semBlockLock/LEVEL-1 locked * LEVEL-1 access is left disabled until last signal/unblockcompletes */ cv->nWaitersToUnblock = (unblockAll) ? cv->nWaitersBlocked : 1; /* * No more LEVEL-2 access to waiters (to)unblock(ed) counts needed * This sync.level supports _timedwait and cancellation */ if ((result = pthread_mutex_unlock(&(cv->mtxUnblockLock))) != 0) {((result return result; } /* * Now, with LEVEL-2 lock released let first waiter go throughsemaphore */ if (sem_post(&(cv->semBlockQueue)) != 0) {(sem_post(&(cv->semBlockQueue)) return errno; } } /* * No waiter blocked - no more LEVEL-1 access to blocked count needed... */ else if (sem_post(&(cv->semBlockLock)) != 0) { return errno; } /* * ...and no more LEVEL-2 access to waiters (to)unblock(ed) counts needed
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -