1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 <sys/cdefs.h> 30 __FBSDID("$FreeBSD$"); 31 32 #include "namespace.h" 33 #include <errno.h> 34 #include <stdlib.h> 35 #include <pthread.h> 36 #include "un-namespace.h" 37 38 #include "thr_private.h" 39 40 _Static_assert(sizeof(struct pthread_barrier) <= THR_PAGE_SIZE_MIN, 41 "pthread_barrier is too large for off-page"); 42 43 __weak_reference(_pthread_barrier_init, pthread_barrier_init); 44 __weak_reference(_pthread_barrier_wait, pthread_barrier_wait); 45 __weak_reference(_pthread_barrier_destroy, pthread_barrier_destroy); 46 47 int 48 _pthread_barrier_destroy(pthread_barrier_t *barrier) 49 { 50 pthread_barrier_t bar; 51 struct pthread *curthread; 52 int pshared; 53 54 if (barrier == NULL || *barrier == NULL) 55 return (EINVAL); 56 57 if (*barrier == THR_PSHARED_PTR) { 58 bar = __thr_pshared_offpage(barrier, 0); 59 if (bar == NULL) { 60 *barrier = NULL; 61 return (0); 62 } 63 pshared = 1; 64 } else { 65 bar = *barrier; 66 pshared = 0; 67 } 68 curthread = _get_curthread(); 69 THR_UMUTEX_LOCK(curthread, &bar->b_lock); 70 if (bar->b_destroying) { 71 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); 72 return (EBUSY); 73 } 74 bar->b_destroying = 1; 75 do { 76 if (bar->b_waiters > 0) { 77 bar->b_destroying = 0; 78 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); 79 return (EBUSY); 80 } 81 if (bar->b_refcount != 0) { 82 _thr_ucond_wait(&bar->b_cv, &bar->b_lock, NULL, 0); 83 THR_UMUTEX_LOCK(curthread, &bar->b_lock); 84 } else 85 break; 86 } while (1); 87 bar->b_destroying = 0; 88 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); 89 90 *barrier = NULL; 91 if (pshared) 92 __thr_pshared_destroy(barrier); 93 else 94 free(bar); 95 return (0); 96 } 97 98 int 99 _pthread_barrier_init(pthread_barrier_t * __restrict barrier, 100 const pthread_barrierattr_t * __restrict attr, unsigned count) 101 { 102 pthread_barrier_t bar; 103 int pshared; 104 105 if (barrier == NULL || count == 0 || count > INT_MAX) 106 return (EINVAL); 107 108 if (attr == NULL || *attr == NULL || 109 (*attr)->pshared == PTHREAD_PROCESS_PRIVATE) { 110 bar = calloc(1, sizeof(struct pthread_barrier)); 111 if (bar == NULL) 112 return (ENOMEM); 113 *barrier = bar; 114 pshared = 0; 115 } else { 116 bar = __thr_pshared_offpage(barrier, 1); 117 if (bar == NULL) 118 return (EFAULT); 119 *barrier = THR_PSHARED_PTR; 120 pshared = 1; 121 } 122 123 _thr_umutex_init(&bar->b_lock); 124 _thr_ucond_init(&bar->b_cv); 125 if (pshared) { 126 bar->b_lock.m_flags |= USYNC_PROCESS_SHARED; 127 bar->b_cv.c_flags |= USYNC_PROCESS_SHARED; 128 } 129 bar->b_count = count; 130 return (0); 131 } 132 133 int 134 _pthread_barrier_wait(pthread_barrier_t *barrier) 135 { 136 struct pthread *curthread; 137 pthread_barrier_t bar; 138 int64_t cycle; 139 int ret; 140 141 if (barrier == NULL || *barrier == NULL) 142 return (EINVAL); 143 144 if (*barrier == THR_PSHARED_PTR) { 145 bar = __thr_pshared_offpage(barrier, 0); 146 if (bar == NULL) 147 return (EINVAL); 148 } else { 149 bar = *barrier; 150 } 151 curthread = _get_curthread(); 152 THR_UMUTEX_LOCK(curthread, &bar->b_lock); 153 if (++bar->b_waiters == bar->b_count) { 154 /* Current thread is lastest thread */ 155 bar->b_waiters = 0; 156 bar->b_cycle++; 157 _thr_ucond_broadcast(&bar->b_cv); 158 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); 159 ret = PTHREAD_BARRIER_SERIAL_THREAD; 160 } else { 161 cycle = bar->b_cycle; 162 bar->b_refcount++; 163 do { 164 _thr_ucond_wait(&bar->b_cv, &bar->b_lock, NULL, 0); 165 THR_UMUTEX_LOCK(curthread, &bar->b_lock); 166 /* test cycle to avoid bogus wakeup */ 167 } while (cycle == bar->b_cycle); 168 if (--bar->b_refcount == 0 && bar->b_destroying) 169 _thr_ucond_broadcast(&bar->b_cv); 170 THR_UMUTEX_UNLOCK(curthread, &bar->b_lock); 171 ret = 0; 172 } 173 return (ret); 174 } 175