1 /* $NetBSD: pthread_barrier.c,v 1.4 2003/01/31 04:59:40 nathanw Exp $ */ 2 3 /*- 4 * Copyright (c) 2001, 2003 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Nathan J. Williams, and by Jason R. Thorpe. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 #include <assert.h> 40 #include <errno.h> 41 #include <sys/cdefs.h> 42 43 #include "pthread.h" 44 #include "pthread_int.h" 45 46 #undef PTHREAD_BARRIER_DEBUG 47 48 #ifdef PTHREAD_BARRIER_DEBUG 49 #define SDPRINTF(x) DPRINTF(x) 50 #else 51 #define SDPRINTF(x) 52 #endif 53 54 int 55 pthread_barrier_init(pthread_barrier_t *barrier, 56 const pthread_barrierattr_t *attr, unsigned int count) 57 { 58 pthread_t self; 59 60 #ifdef ERRORCHECK 61 if ((barrier == NULL) || 62 (attr && (attr->ptba_magic != _PT_BARRIERATTR_MAGIC))) 63 return EINVAL; 64 #endif 65 66 if (count == 0) 67 return EINVAL; 68 69 self = pthread__self(); 70 71 if (barrier->ptb_magic == _PT_BARRIER_MAGIC) { 72 /* 73 * We're simply reinitializing the barrier to a 74 * new count. 75 */ 76 pthread_spinlock(self, &barrier->ptb_lock); 77 78 if (barrier->ptb_magic != _PT_BARRIER_MAGIC) { 79 pthread_spinunlock(self, &barrier->ptb_lock); 80 return EINVAL; 81 } 82 83 if (!PTQ_EMPTY(&barrier->ptb_waiters)) { 84 pthread_spinunlock(self, &barrier->ptb_lock); 85 return EBUSY; 86 } 87 88 barrier->ptb_initcount = count; 89 barrier->ptb_curcount = 0; 90 barrier->ptb_generation = 0; 91 92 pthread_spinunlock(self, &barrier->ptb_lock); 93 94 return 0; 95 } 96 97 barrier->ptb_magic = _PT_BARRIER_MAGIC; 98 pthread_lockinit(&barrier->ptb_lock); 99 PTQ_INIT(&barrier->ptb_waiters); 100 barrier->ptb_initcount = count; 101 barrier->ptb_curcount = 0; 102 barrier->ptb_generation = 0; 103 104 return 0; 105 } 106 107 108 int 109 pthread_barrier_destroy(pthread_barrier_t *barrier) 110 { 111 pthread_t self; 112 113 #ifdef ERRORCHECK 114 if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC)) 115 return EINVAL; 116 #endif 117 118 self = pthread__self(); 119 120 pthread_spinlock(self, &barrier->ptb_lock); 121 122 if (barrier->ptb_magic != _PT_BARRIER_MAGIC) { 123 pthread_spinunlock(self, &barrier->ptb_lock); 124 return EINVAL; 125 } 126 127 if (!PTQ_EMPTY(&barrier->ptb_waiters)) { 128 pthread_spinunlock(self, &barrier->ptb_lock); 129 return EBUSY; 130 } 131 132 barrier->ptb_magic = _PT_BARRIER_DEAD; 133 134 pthread_spinunlock(self, &barrier->ptb_lock); 135 136 return 0; 137 } 138 139 140 int 141 pthread_barrier_wait(pthread_barrier_t *barrier) 142 { 143 pthread_t self; 144 unsigned int gen; 145 146 #ifdef ERRORCHECK 147 if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC)) 148 return EINVAL; 149 #endif 150 self = pthread__self(); 151 152 pthread_spinlock(self, &barrier->ptb_lock); 153 154 /* 155 * A single arbitrary thread is supposed to return 156 * PTHREAD_BARRIER_SERIAL_THREAD, and everone else 157 * is supposed to return 0. Since pthread_barrier_wait() 158 * is not a cancellation point, this is trivial; we 159 * simply elect that the thread that causes the barrier 160 * to be satisfied gets the special return value. Note 161 * that this final thread does not actually need to block, 162 * but instead is responsible for waking everyone else up. 163 */ 164 if (barrier->ptb_curcount + 1 == barrier->ptb_initcount) { 165 struct pthread_queue_t blockedq; 166 167 SDPRINTF(("(barrier wait %p) Satisfied %p\n", 168 self, barrier)); 169 170 blockedq = barrier->ptb_waiters; 171 PTQ_INIT(&barrier->ptb_waiters); 172 barrier->ptb_curcount = 0; 173 barrier->ptb_generation++; 174 175 pthread__sched_sleepers(self, &blockedq); 176 177 pthread_spinunlock(self, &barrier->ptb_lock); 178 179 return PTHREAD_BARRIER_SERIAL_THREAD; 180 } 181 182 barrier->ptb_curcount++; 183 gen = barrier->ptb_generation; 184 while (gen == barrier->ptb_generation) { 185 SDPRINTF(("(barrier wait %p) Waiting on %p\n", 186 self, barrier)); 187 188 pthread_spinlock(self, &self->pt_statelock); 189 190 self->pt_state = PT_STATE_BLOCKED_QUEUE; 191 self->pt_sleepobj = barrier; 192 self->pt_sleepq = &barrier->ptb_waiters; 193 self->pt_sleeplock = &barrier->ptb_lock; 194 195 pthread_spinunlock(self, &self->pt_statelock); 196 197 PTQ_INSERT_TAIL(&barrier->ptb_waiters, self, pt_sleep); 198 199 pthread__block(self, &barrier->ptb_lock); 200 SDPRINTF(("(barrier wait %p) Woke up on %p\n", 201 self, barrier)); 202 /* Spinlock is unlocked on return */ 203 pthread_spinlock(self, &barrier->ptb_lock); 204 } 205 pthread_spinunlock(self, &barrier->ptb_lock); 206 207 return 0; 208 } 209 210 211 int 212 pthread_barrierattr_init(pthread_barrierattr_t *attr) 213 { 214 215 #ifdef ERRORCHECK 216 if (attr == NULL) 217 return EINVAL; 218 #endif 219 220 attr->ptba_magic = _PT_BARRIERATTR_MAGIC; 221 222 return 0; 223 } 224 225 226 int 227 pthread_barrierattr_destroy(pthread_barrierattr_t *attr) 228 { 229 230 #ifdef ERRORCHECK 231 if ((attr == NULL) || 232 (attr->ptba_magic != _PT_BARRIERATTR_MAGIC)) 233 return EINVAL; 234 #endif 235 236 attr->ptba_magic = _PT_BARRIERATTR_DEAD; 237 238 return 0; 239 } 240