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