src/lib/librthread/rthread_barrier.c

147 lines
3.2 KiB
C

/* $OpenBSD: rthread_barrier.c,v 1.5 2020/04/06 00:01:08 pirofti Exp $ */
/*
* Copyright (c) 2012 Paul Irofti <paul@irofti.net>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#include <errno.h>
#include <stdlib.h>
#include <pthread.h>
#include "rthread.h"
int
pthread_barrier_init(pthread_barrier_t *barrier, pthread_barrierattr_t *attr,
unsigned int count) {
int rc = 0;
pthread_barrier_t b = NULL;
if (barrier == NULL)
return (EINVAL);
if (count == 0)
return (EINVAL);
if (attr != NULL) {
if (*attr == NULL)
return (EINVAL);
if ((*attr)->pshared != PTHREAD_PROCESS_PRIVATE)
return (ENOTSUP);
}
b = calloc(1, sizeof *b);
if (b == NULL)
return (ENOMEM);
if ((rc = pthread_mutex_init(&b->mutex, NULL)))
goto err;
if ((rc = pthread_cond_init(&b->cond, NULL)))
goto err;
b->threshold = count;
*barrier = b;
return (0);
err:
if (b) {
if (b->mutex)
pthread_mutex_destroy(&b->mutex);
if (b->cond)
pthread_cond_destroy(&b->cond);
free(b);
}
return (rc);
}
int
pthread_barrier_destroy(pthread_barrier_t *barrier)
{
int rc;
pthread_barrier_t b;
if (barrier == NULL || *barrier == NULL)
return (EINVAL);
if ((rc = pthread_mutex_lock(&(*barrier)->mutex)))
return (rc);
b = *barrier;
if (b->out > 0 || b->in > 0) {
pthread_mutex_unlock(&b->mutex);
return (EBUSY);
}
*barrier = NULL;
pthread_mutex_unlock(&b->mutex);
pthread_mutex_destroy(&b->mutex);
pthread_cond_destroy(&b->cond);
free(b);
return (0);
}
int
pthread_barrier_wait(pthread_barrier_t *barrier)
{
pthread_barrier_t b;
int rc, old_state, gen;
int done = 0;
if (barrier == NULL || *barrier == NULL)
return (EINVAL);
if ((rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state)))
return (rc);
b = *barrier;
if ((rc = pthread_mutex_lock(&b->mutex)))
goto cancel;
_rthread_debug(6, "in: %d, threshold: %d\n", b->in, b->threshold);
if (++b->in == b->threshold) {
b->out = b->in - 1;
b->in = 0;
b->generation++;
if ((rc = pthread_cond_signal(&b->cond)))
goto err;
done = 1;
_rthread_debug(6, "threshold reached\n");
} else {
gen = b->generation;
_rthread_debug(6, "waiting on condition\n");
do {
if ((rc = pthread_cond_wait(&b->cond, &b->mutex)))
goto err;
} while (gen == b->generation);
b->out--; /* mark thread exit */
if ((rc = pthread_cond_signal(&b->cond)))
goto err;
}
err:
if ((rc = pthread_mutex_unlock(&b->mutex)))
return (rc);
cancel:
rc = pthread_setcancelstate(old_state, NULL);
if (rc == 0 && done)
rc = PTHREAD_BARRIER_SERIAL_THREAD;
return (rc);
}