1/*	$OpenBSD: rthread_barrier.c,v 1.5 2020/04/06 00:01:08 pirofti Exp $	*/
2/*
3 * Copyright (c) 2012 Paul Irofti <paul@irofti.net>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <errno.h>
19#include <stdlib.h>
20
21#include <pthread.h>
22
23#include "rthread.h"
24
25int
26pthread_barrier_init(pthread_barrier_t *barrier, pthread_barrierattr_t *attr,
27    unsigned int count) {
28	int rc = 0;
29	pthread_barrier_t b = NULL;
30
31	if (barrier == NULL)
32		return (EINVAL);
33
34	if (count == 0)
35		return (EINVAL);
36
37	if (attr != NULL) {
38		if (*attr == NULL)
39			return (EINVAL);
40
41		if ((*attr)->pshared != PTHREAD_PROCESS_PRIVATE)
42			return (ENOTSUP);
43	}
44
45	b = calloc(1, sizeof *b);
46	if (b == NULL)
47		return (ENOMEM);
48
49	if ((rc = pthread_mutex_init(&b->mutex, NULL)))
50		goto err;
51	if ((rc = pthread_cond_init(&b->cond, NULL)))
52		goto err;
53
54	b->threshold = count;
55
56	*barrier = b;
57
58	return (0);
59
60err:
61	if (b) {
62		if (b->mutex)
63			pthread_mutex_destroy(&b->mutex);
64		if (b->cond)
65			pthread_cond_destroy(&b->cond);
66		free(b);
67	}
68
69	return (rc);
70}
71
72int
73pthread_barrier_destroy(pthread_barrier_t *barrier)
74{
75	int rc;
76	pthread_barrier_t b;
77
78	if (barrier == NULL || *barrier == NULL)
79		return (EINVAL);
80
81	if ((rc = pthread_mutex_lock(&(*barrier)->mutex)))
82		return (rc);
83
84	b = *barrier;
85
86	if (b->out > 0 || b->in > 0) {
87		pthread_mutex_unlock(&b->mutex);
88		return (EBUSY);
89	}
90
91	*barrier = NULL;
92	pthread_mutex_unlock(&b->mutex);
93	pthread_mutex_destroy(&b->mutex);
94	pthread_cond_destroy(&b->cond);
95	free(b);
96	return (0);
97}
98
99int
100pthread_barrier_wait(pthread_barrier_t *barrier)
101{
102	pthread_barrier_t b;
103	int rc, old_state, gen;
104	int done = 0;
105
106	if (barrier == NULL || *barrier == NULL)
107		return (EINVAL);
108
109	if ((rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state)))
110		return (rc);
111
112	b = *barrier;
113	if ((rc = pthread_mutex_lock(&b->mutex)))
114		goto cancel;
115
116	_rthread_debug(6, "in: %d, threshold: %d\n", b->in, b->threshold);
117	if (++b->in == b->threshold) {
118		b->out = b->in - 1;
119		b->in = 0;
120		b->generation++;
121		if ((rc = pthread_cond_signal(&b->cond)))
122			goto err;
123		done = 1;
124		_rthread_debug(6, "threshold reached\n");
125	} else {
126		gen = b->generation;
127		_rthread_debug(6, "waiting on condition\n");
128		do {
129			if ((rc = pthread_cond_wait(&b->cond, &b->mutex)))
130				goto err;
131		} while (gen == b->generation);
132		b->out--; /* mark thread exit */
133		if ((rc = pthread_cond_signal(&b->cond)))
134			goto err;
135	}
136
137err:
138	if ((rc = pthread_mutex_unlock(&b->mutex)))
139		return (rc);
140cancel:
141	rc = pthread_setcancelstate(old_state, NULL);
142	if (rc == 0 && done)
143		rc = PTHREAD_BARRIER_SERIAL_THREAD;
144
145	return (rc);
146}
147