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
44int
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
95int
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
130int
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