xref: /openbsd-src/lib/librthread/rthread_barrier.c (revision ae3cb403620ab940fbaabb3055fac045a63d56b7)
1 /*	$OpenBSD: rthread_barrier.c,v 1.3 2016/04/15 17:54:17 tedu Exp $	*/
2 /*
3  * Copyright (c) 2012 Paul Irofti <pirofti@openbsd.org>
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 (attr != NULL) {
35 		if (*attr == NULL)
36 			return (EINVAL);
37 
38 		if ((*attr)->pshared != PTHREAD_PROCESS_PRIVATE)
39 			return (ENOTSUP);
40 	}
41 
42 	b = calloc(1, sizeof *b);
43 	if (b == NULL)
44 		return (ENOMEM);
45 
46 	if ((rc = pthread_mutex_init(&b->mutex, NULL)))
47 		goto err;
48 	if ((rc = pthread_cond_init(&b->cond, NULL)))
49 		goto err;
50 
51 	b->threshold = count;
52 
53 	*barrier = b;
54 
55 	return (0);
56 
57 err:
58 	if (b) {
59 		if (b->mutex)
60 			pthread_mutex_destroy(&b->mutex);
61 		if (b->cond)
62 			pthread_cond_destroy(&b->cond);
63 		free(b);
64 	}
65 
66 	return (rc);
67 }
68 
69 int
70 pthread_barrier_destroy(pthread_barrier_t *barrier)
71 {
72 	int rc;
73 	pthread_barrier_t b;
74 
75 	if (barrier == NULL || *barrier == NULL)
76 		return (EINVAL);
77 
78 	if ((rc = pthread_mutex_lock(&(*barrier)->mutex)))
79 		return (rc);
80 
81 	b = *barrier;
82 
83 	if (b->out > 0 || b->in > 0) {
84 		pthread_mutex_unlock(&b->mutex);
85 		return (EBUSY);
86 	}
87 
88 	*barrier = NULL;
89 	pthread_mutex_unlock(&b->mutex);
90 	pthread_mutex_destroy(&b->mutex);
91 	pthread_cond_destroy(&b->cond);
92 	free(b);
93 	return (0);
94 }
95 
96 int
97 pthread_barrier_wait(pthread_barrier_t *barrier)
98 {
99 	pthread_barrier_t b;
100 	int rc, old_state, gen;
101 	int done = 0;
102 
103 	if (barrier == NULL || *barrier == NULL)
104 		return (EINVAL);
105 
106 	if ((rc = pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state)))
107 		return (rc);
108 
109 	b = *barrier;
110 	if ((rc = pthread_mutex_lock(&b->mutex)))
111 		goto cancel;
112 
113 	_rthread_debug(6, "in: %d, threshold: %d\n", b->in, b->threshold);
114 	if (++b->in == b->threshold) {
115 		b->out = b->in - 1;
116 		b->in = 0;
117 		b->generation++;
118 		if ((rc = pthread_cond_signal(&b->cond)))
119 			goto err;
120 		done = 1;
121 		_rthread_debug(6, "threshold reached\n");
122 	} else {
123 		gen = b->generation;
124 		_rthread_debug(6, "waiting on condition\n");
125 		do {
126 			if ((rc = pthread_cond_wait(&b->cond, &b->mutex)))
127 				goto err;
128 		} while (gen == b->generation);
129 		b->out--; /* mark thread exit */
130 		if ((rc = pthread_cond_signal(&b->cond)))
131 			goto err;
132 	}
133 
134 err:
135 	if ((rc = pthread_mutex_unlock(&b->mutex)))
136 		return (rc);
137 cancel:
138 	rc = pthread_setcancelstate(old_state, NULL);
139 	if (rc == 0 && done)
140 		rc = PTHREAD_BARRIER_SERIAL_THREAD;
141 
142 	return (rc);
143 }
144