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