1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2003 David Xu <davidxu@freebsd.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include "namespace.h"
30 #include <errno.h>
31 #include <stdlib.h>
32 #include <pthread.h>
33 #include "un-namespace.h"
34
35 #include "thr_private.h"
36
37 _Static_assert(sizeof(struct pthread_barrier) <= THR_PAGE_SIZE_MIN,
38 "pthread_barrier is too large for off-page");
39
40 __weak_reference(_pthread_barrier_init, pthread_barrier_init);
41 __weak_reference(_pthread_barrier_wait, pthread_barrier_wait);
42 __weak_reference(_pthread_barrier_destroy, pthread_barrier_destroy);
43
44 int
_pthread_barrier_destroy(pthread_barrier_t * barrier)45 _pthread_barrier_destroy(pthread_barrier_t *barrier)
46 {
47 pthread_barrier_t bar;
48 struct pthread *curthread;
49 int pshared;
50
51 if (barrier == NULL || *barrier == NULL)
52 return (EINVAL);
53
54 if (*barrier == THR_PSHARED_PTR) {
55 bar = __thr_pshared_offpage(barrier, 0);
56 if (bar == NULL) {
57 *barrier = NULL;
58 return (0);
59 }
60 pshared = 1;
61 } else {
62 bar = *barrier;
63 pshared = 0;
64 }
65 curthread = _get_curthread();
66 THR_UMUTEX_LOCK(curthread, &bar->b_lock);
67 if (bar->b_destroying) {
68 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock);
69 return (EBUSY);
70 }
71 bar->b_destroying = 1;
72 do {
73 if (bar->b_waiters > 0) {
74 bar->b_destroying = 0;
75 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock);
76 return (EBUSY);
77 }
78 if (bar->b_refcount != 0) {
79 _thr_ucond_wait(&bar->b_cv, &bar->b_lock, NULL, 0);
80 THR_UMUTEX_LOCK(curthread, &bar->b_lock);
81 } else
82 break;
83 } while (1);
84 bar->b_destroying = 0;
85 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock);
86
87 *barrier = NULL;
88 if (pshared)
89 __thr_pshared_destroy(barrier);
90 else
91 free(bar);
92 return (0);
93 }
94
95 int
_pthread_barrier_init(pthread_barrier_t * __restrict barrier,const pthread_barrierattr_t * __restrict attr,unsigned count)96 _pthread_barrier_init(pthread_barrier_t * __restrict barrier,
97 const pthread_barrierattr_t * __restrict attr, unsigned count)
98 {
99 pthread_barrier_t bar;
100 int pshared;
101
102 if (barrier == NULL || count == 0 || count > INT_MAX)
103 return (EINVAL);
104
105 if (attr == NULL || *attr == NULL ||
106 (*attr)->pshared == PTHREAD_PROCESS_PRIVATE) {
107 bar = calloc(1, sizeof(struct pthread_barrier));
108 if (bar == NULL)
109 return (ENOMEM);
110 *barrier = bar;
111 pshared = 0;
112 } else {
113 bar = __thr_pshared_offpage(barrier, 1);
114 if (bar == NULL)
115 return (EFAULT);
116 *barrier = THR_PSHARED_PTR;
117 pshared = 1;
118 }
119
120 _thr_umutex_init(&bar->b_lock);
121 _thr_ucond_init(&bar->b_cv);
122 if (pshared) {
123 bar->b_lock.m_flags |= USYNC_PROCESS_SHARED;
124 bar->b_cv.c_flags |= USYNC_PROCESS_SHARED;
125 }
126 bar->b_count = count;
127 return (0);
128 }
129
130 int
_pthread_barrier_wait(pthread_barrier_t * barrier)131 _pthread_barrier_wait(pthread_barrier_t *barrier)
132 {
133 struct pthread *curthread;
134 pthread_barrier_t bar;
135 int64_t cycle;
136 int ret;
137
138 if (barrier == NULL || *barrier == NULL)
139 return (EINVAL);
140
141 if (*barrier == THR_PSHARED_PTR) {
142 bar = __thr_pshared_offpage(barrier, 0);
143 if (bar == NULL)
144 return (EINVAL);
145 } else {
146 bar = *barrier;
147 }
148 curthread = _get_curthread();
149 THR_UMUTEX_LOCK(curthread, &bar->b_lock);
150 if (++bar->b_waiters == bar->b_count) {
151 /* Current thread is lastest thread */
152 bar->b_waiters = 0;
153 bar->b_cycle++;
154 _thr_ucond_broadcast(&bar->b_cv);
155 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock);
156 ret = PTHREAD_BARRIER_SERIAL_THREAD;
157 } else {
158 cycle = bar->b_cycle;
159 bar->b_refcount++;
160 do {
161 _thr_ucond_wait(&bar->b_cv, &bar->b_lock, NULL, 0);
162 THR_UMUTEX_LOCK(curthread, &bar->b_lock);
163 /* test cycle to avoid bogus wakeup */
164 } while (cycle == bar->b_cycle);
165 if (--bar->b_refcount == 0 && bar->b_destroying)
166 _thr_ucond_broadcast(&bar->b_cv);
167 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock);
168 ret = 0;
169 }
170 return (ret);
171 }
172