mirror of
https://github.com/HDFGroup/hdf5.git
synced 2025-02-11 16:01:00 +08:00
Merge pull request #2411 in HDFFV/hdf5 from ~DYOUNG/werror:darwin-barriers to develop
* commit '803d805c74466a9d736455930b17de2d9f5cb02d': Complete the comment on thread_main(), explaining why the barrier is used. The first implementation seemed to allow for the possibility that a thread could block at the barrier, wake and exit the barrier, re-acquire the barrier lock and increase `nentered` before the other blocked threads woke and checked `nentered % count == 0`. Then the other blocked threads would check `nentered % count == 0` and, finding it false, go back to sleep in the barrier. This new implementation waits for a looser condition to obtain so that threads don't go back to sleep in the barrier. Test the right condition for the EBUSY return in pthread_barrier_destroy(). s/exit_failure/EXIT_FAILURE/g Implement pthread_barrier(3) for Darwin using a counter, condition variable, and mutex. Untested.
This commit is contained in:
commit
d7eec7d6ec
140
test/thread_id.c
140
test/thread_id.c
@ -42,6 +42,136 @@ my_errx(int code, const char *fmt, ...)
|
||||
|
||||
#if defined(H5_HAVE_THREADSAFE) && !defined(H5_HAVE_WIN_THREADS)
|
||||
|
||||
#if defined(H5_HAVE_DARWIN)
|
||||
|
||||
typedef struct _pthread_barrierattr {
|
||||
uint8_t unused;
|
||||
} pthread_barrierattr_t;
|
||||
|
||||
typedef struct _pthread_barrier {
|
||||
uint32_t magic;
|
||||
unsigned int count;
|
||||
uint64_t nentered;
|
||||
pthread_cond_t cv;
|
||||
pthread_mutex_t mtx;
|
||||
} pthread_barrier_t;
|
||||
|
||||
int pthread_barrier_init(pthread_barrier_t *, const pthread_barrierattr_t *,
|
||||
unsigned int);
|
||||
int pthread_barrier_wait(pthread_barrier_t *);
|
||||
int pthread_barrier_destroy(pthread_barrier_t *);
|
||||
|
||||
static const uint32_t barrier_magic = 0xf00dd00f;
|
||||
|
||||
int
|
||||
pthread_barrier_init(pthread_barrier_t *barrier,
|
||||
const pthread_barrierattr_t *attr, unsigned int count)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (count == 0)
|
||||
return EINVAL;
|
||||
|
||||
if (attr != NULL)
|
||||
return EINVAL;
|
||||
|
||||
memset(barrier, 0, sizeof(*barrier));
|
||||
|
||||
barrier->count = count;
|
||||
|
||||
if ((rc = pthread_cond_init(&barrier->cv, NULL)) != 0)
|
||||
return rc;
|
||||
|
||||
if ((rc = pthread_mutex_init(&barrier->mtx, NULL)) != 0) {
|
||||
(void)pthread_cond_destroy(&barrier->cv);
|
||||
return rc;
|
||||
}
|
||||
|
||||
barrier->magic = barrier_magic;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
barrier_lock(pthread_barrier_t *barrier)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = pthread_mutex_lock(&barrier->mtx)) != 0) {
|
||||
my_errx(EXIT_FAILURE, "%s: pthread_mutex_lock: %s", __func__,
|
||||
strerror(rc));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
barrier_unlock(pthread_barrier_t *barrier)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if ((rc = pthread_mutex_unlock(&barrier->mtx)) != 0) {
|
||||
my_errx(EXIT_FAILURE, "%s: pthread_mutex_unlock: %s", __func__,
|
||||
strerror(rc));
|
||||
}
|
||||
}
|
||||
|
||||
int
|
||||
pthread_barrier_destroy(pthread_barrier_t *barrier)
|
||||
{
|
||||
int rc;
|
||||
|
||||
barrier_lock(barrier);
|
||||
if (barrier->magic != barrier_magic)
|
||||
rc = EINVAL;
|
||||
else if (barrier->nentered % barrier->count != 0)
|
||||
rc = EBUSY;
|
||||
else {
|
||||
rc = 0;
|
||||
barrier->magic = ~barrier->magic;
|
||||
}
|
||||
barrier_unlock(barrier);
|
||||
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
(void)pthread_cond_destroy(&barrier->cv);
|
||||
(void)pthread_mutex_destroy(&barrier->mtx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
pthread_barrier_wait(pthread_barrier_t *barrier)
|
||||
{
|
||||
int rc;
|
||||
uint64_t threshold;
|
||||
|
||||
if (barrier == NULL)
|
||||
return EINVAL;
|
||||
|
||||
barrier_lock(barrier);
|
||||
if (barrier->magic != barrier_magic) {
|
||||
rc = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
/* Compute the release `threshold`. All threads entering with count = 5
|
||||
* and `nentered` in [0, 4] should be released once `nentered` reaches 5:
|
||||
* call 5 the release `threshold`. All threads entering with count = 5
|
||||
* and `nentered` in [5, 9] should be released once `nentered` reaches 10.
|
||||
*/
|
||||
threshold = (barrier->nentered / barrier->count + 1) * barrier->count;
|
||||
barrier->nentered++;
|
||||
while (barrier->nentered < threshold) {
|
||||
if ((rc = pthread_cond_wait(&barrier->cv, &barrier->mtx)) != 0)
|
||||
goto out;
|
||||
}
|
||||
rc = pthread_cond_broadcast(&barrier->cv);
|
||||
out:
|
||||
barrier_unlock(barrier);
|
||||
return rc;
|
||||
}
|
||||
|
||||
#endif /* H5_HAVE_DARWIN */
|
||||
|
||||
static void my_err(int, const char *, ...) H5_ATTR_FORMAT(printf, 2, 3);
|
||||
|
||||
static void
|
||||
@ -96,7 +226,15 @@ atomic_printf(const char *fmt, ...)
|
||||
/* Each thread runs this routine. The routine fetches the current
|
||||
* thread's ID, makes sure that it is in the expected range, makes
|
||||
* sure that in this round of testing, no two threads shared the
|
||||
* same ID,
|
||||
* same ID, and checks that each thread's ID is constant over its lifetime.
|
||||
*
|
||||
* main() checks that every ID in [1, NTHREADS] is used in each round
|
||||
* of testing. All NTHREADS threads synchronize on a barrier after each
|
||||
* has fetched its ID. The barrier guarantees that all threads' lifetimes
|
||||
* overlap at least momentarily, so the IDs will be unique, and there
|
||||
* will be NTHREADS of them. Further, since thread IDs are assigned
|
||||
* starting with 1, and the number of threads with IDs alive never exceeds
|
||||
* NTHREADS, the least ID has to be 1 and the greatest, NTHREADS.
|
||||
*/
|
||||
static void *
|
||||
thread_main(void H5_ATTR_UNUSED *arg)
|
||||
|
Loading…
Reference in New Issue
Block a user