1 /* $NetBSD: pthread_barrier.c,v 1.6 2003/03/08 08:03:35 lukem 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 <sys/cdefs.h> 40 __RCSID("$NetBSD: pthread_barrier.c,v 1.6 2003/03/08 08:03:35 lukem Exp $"); 41 42 #include <errno.h> 43 #include <sys/cdefs.h> 44 45 #include "pthread.h" 46 #include "pthread_int.h" 47 48 #undef PTHREAD_BARRIER_DEBUG 49 50 #ifdef PTHREAD_BARRIER_DEBUG 51 #define SDPRINTF(x) DPRINTF(x) 52 #else 53 #define SDPRINTF(x) 54 #endif 55 56 int 57 pthread_barrier_init(pthread_barrier_t *barrier, 58 const pthread_barrierattr_t *attr, unsigned int count) 59 { 60 pthread_t self; 61 62 #ifdef ERRORCHECK 63 if ((barrier == NULL) || 64 (attr && (attr->ptba_magic != _PT_BARRIERATTR_MAGIC))) 65 return EINVAL; 66 #endif 67 68 if (count == 0) 69 return EINVAL; 70 71 self = pthread__self(); 72 73 if (barrier->ptb_magic == _PT_BARRIER_MAGIC) { 74 /* 75 * We're simply reinitializing the barrier to a 76 * new count. 77 */ 78 pthread_spinlock(self, &barrier->ptb_lock); 79 80 if (barrier->ptb_magic != _PT_BARRIER_MAGIC) { 81 pthread_spinunlock(self, &barrier->ptb_lock); 82 return EINVAL; 83 } 84 85 if (!PTQ_EMPTY(&barrier->ptb_waiters)) { 86 pthread_spinunlock(self, &barrier->ptb_lock); 87 return EBUSY; 88 } 89 90 barrier->ptb_initcount = count; 91 barrier->ptb_curcount = 0; 92 barrier->ptb_generation = 0; 93 94 pthread_spinunlock(self, &barrier->ptb_lock); 95 96 return 0; 97 } 98 99 barrier->ptb_magic = _PT_BARRIER_MAGIC; 100 pthread_lockinit(&barrier->ptb_lock); 101 PTQ_INIT(&barrier->ptb_waiters); 102 barrier->ptb_initcount = count; 103 barrier->ptb_curcount = 0; 104 barrier->ptb_generation = 0; 105 106 return 0; 107 } 108 109 110 int 111 pthread_barrier_destroy(pthread_barrier_t *barrier) 112 { 113 pthread_t self; 114 115 #ifdef ERRORCHECK 116 if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC)) 117 return EINVAL; 118 #endif 119 120 self = pthread__self(); 121 122 pthread_spinlock(self, &barrier->ptb_lock); 123 124 if (barrier->ptb_magic != _PT_BARRIER_MAGIC) { 125 pthread_spinunlock(self, &barrier->ptb_lock); 126 return EINVAL; 127 } 128 129 if (!PTQ_EMPTY(&barrier->ptb_waiters)) { 130 pthread_spinunlock(self, &barrier->ptb_lock); 131 return EBUSY; 132 } 133 134 barrier->ptb_magic = _PT_BARRIER_DEAD; 135 136 pthread_spinunlock(self, &barrier->ptb_lock); 137 138 return 0; 139 } 140 141 142 int 143 pthread_barrier_wait(pthread_barrier_t *barrier) 144 { 145 pthread_t self; 146 unsigned int gen; 147 148 #ifdef ERRORCHECK 149 if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC)) 150 return EINVAL; 151 #endif 152 self = pthread__self(); 153 154 pthread_spinlock(self, &barrier->ptb_lock); 155 156 /* 157 * A single arbitrary thread is supposed to return 158 * PTHREAD_BARRIER_SERIAL_THREAD, and everone else 159 * is supposed to return 0. Since pthread_barrier_wait() 160 * is not a cancellation point, this is trivial; we 161 * simply elect that the thread that causes the barrier 162 * to be satisfied gets the special return value. Note 163 * that this final thread does not actually need to block, 164 * but instead is responsible for waking everyone else up. 165 */ 166 if (barrier->ptb_curcount + 1 == barrier->ptb_initcount) { 167 struct pthread_queue_t blockedq; 168 169 SDPRINTF(("(barrier wait %p) Satisfied %p\n", 170 self, barrier)); 171 172 blockedq = barrier->ptb_waiters; 173 PTQ_INIT(&barrier->ptb_waiters); 174 barrier->ptb_curcount = 0; 175 barrier->ptb_generation++; 176 177 pthread__sched_sleepers(self, &blockedq); 178 179 pthread_spinunlock(self, &barrier->ptb_lock); 180 181 return PTHREAD_BARRIER_SERIAL_THREAD; 182 } 183 184 barrier->ptb_curcount++; 185 gen = barrier->ptb_generation; 186 while (gen == barrier->ptb_generation) { 187 SDPRINTF(("(barrier wait %p) Waiting on %p\n", 188 self, barrier)); 189 190 pthread_spinlock(self, &self->pt_statelock); 191 192 self->pt_state = PT_STATE_BLOCKED_QUEUE; 193 self->pt_sleepobj = barrier; 194 self->pt_sleepq = &barrier->ptb_waiters; 195 self->pt_sleeplock = &barrier->ptb_lock; 196 197 pthread_spinunlock(self, &self->pt_statelock); 198 199 PTQ_INSERT_TAIL(&barrier->ptb_waiters, self, pt_sleep); 200 201 pthread__block(self, &barrier->ptb_lock); 202 SDPRINTF(("(barrier wait %p) Woke up on %p\n", 203 self, barrier)); 204 /* Spinlock is unlocked on return */ 205 pthread_spinlock(self, &barrier->ptb_lock); 206 } 207 pthread_spinunlock(self, &barrier->ptb_lock); 208 209 return 0; 210 } 211 212 213 int 214 pthread_barrierattr_init(pthread_barrierattr_t *attr) 215 { 216 217 #ifdef ERRORCHECK 218 if (attr == NULL) 219 return EINVAL; 220 #endif 221 222 attr->ptba_magic = _PT_BARRIERATTR_MAGIC; 223 224 return 0; 225 } 226 227 228 int 229 pthread_barrierattr_destroy(pthread_barrierattr_t *attr) 230 { 231 232 #ifdef ERRORCHECK 233 if ((attr == NULL) || 234 (attr->ptba_magic != _PT_BARRIERATTR_MAGIC)) 235 return EINVAL; 236 #endif 237 238 attr->ptba_magic = _PT_BARRIERATTR_DEAD; 239 240 return 0; 241 } 242