📄 readme.cv
字号:
#ifdef NEED_SEM
result = (ptw32_increase_semaphore( &cv->sema, cv->waiters )
? 0
: EINVAL);
#else /* NEED_SEM */
result = (ReleaseSemaphore( cv->sema, cv->waiters, NULL )
? 0
: EINVAL);
#endif /* NEED_SEM */
}
(void) pthread_mutex_unlock(&(cv->waitersLock));
if (wereWaiters && result == 0)
{(wereWaiters
/*
* 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 get
the program stalled even without any source code
modification if I run the tennisb program many
times 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 and
cancellation*/
int
pthread_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 */
int
pthread_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 void
ptw32_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 int
ptw32_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 int
ptw32_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/unblock
completes
*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -