1114902Sscottl/*	$OpenBSD: rthread_barrier.c,v 1.5 2020/04/06 00:01:08 pirofti Exp $	*/
2114902Sscottl/*
3114902Sscottl * Copyright (c) 2012 Paul Irofti <paul@irofti.net>
4114902Sscottl *
5114902Sscottl * Permission to use, copy, modify, and/or distribute this software for any
6114902Sscottl * purpose with or without fee is hereby granted, provided that the above
7114902Sscottl * copyright notice and this permission notice appear in all copies.
8114902Sscottl *
9114902Sscottl * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10114902Sscottl * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11114902Sscottl * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12114902Sscottl * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13114902Sscottl * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14114902Sscottl * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15114902Sscottl * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16114902Sscottl */
17114902Sscottl
18114902Sscottl#include <errno.h>
19114902Sscottl#include <stdlib.h>
20114902Sscottl
21114902Sscottl#include <pthread.h>
22114902Sscottl
23114902Sscottl#include "rthread.h"
24114902Sscottl
25114902Sscottlint
26114902Sscottlpthread_barrier_init(pthread_barrier_t *barrier, pthread_barrierattr_t *attr,
27114902Sscottl    unsigned int count) {
28114902Sscottl	int rc = 0;
29114902Sscottl	pthread_barrier_t b = NULL;
30114902Sscottl
31114902Sscottl	if (barrier == NULL)
32114902Sscottl		return (EINVAL);
33114902Sscottl
34114902Sscottl	if (count == 0)
35114902Sscottl		return (EINVAL);
36114902Sscottl
37114902Sscottl	if (attr != NULL) {
38114902Sscottl		if (*attr == NULL)
39114902Sscottl			return (EINVAL);
40114902Sscottl
41114902Sscottl		if ((*attr)->pshared != PTHREAD_PROCESS_PRIVATE)
42114902Sscottl			return (ENOTSUP);
43114902Sscottl	}
44114902Sscottl
45114902Sscottl	b = calloc(1, sizeof *b);
46114902Sscottl	if (b == NULL)
47114902Sscottl		return (ENOMEM);
48119280Simp
49119280Simp	if ((rc = pthread_mutex_init(&b->mutex, NULL)))
50114902Sscottl		goto err;
51114902Sscottl	if ((rc = pthread_cond_init(&b->cond, NULL)))
52114902Sscottl		goto err;
53114902Sscottl
54114902Sscottl	b->threshold = count;
55114902Sscottl
56114902Sscottl	*barrier = b;
57114902Sscottl
58114902Sscottl	return (0);
59114902Sscottl
60114902Sscottlerr:
61114902Sscottl	if (b) {
62114902Sscottl		if (b->mutex)
63125975Sphk			pthread_mutex_destroy(&b->mutex);
64114902Sscottl		if (b->cond)
65114902Sscottl			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