xref: /openbsd/lib/librthread/rthread_barrier.c (revision 73471bf0)
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 
25 int
26 pthread_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 
60 err:
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 
72 int
73 pthread_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 
99 int
100 pthread_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 
137 err:
138 	if ((rc = pthread_mutex_unlock(&b->mutex)))
139 		return (rc);
140 cancel:
141 	rc = pthread_setcancelstate(old_state, NULL);
142 	if (rc == 0 && done)
143 		rc = PTHREAD_BARRIER_SERIAL_THREAD;
144 
145 	return (rc);
146 }
147