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