1 /* $NetBSD: pthread_barrier.c,v 1.17 2008/04/28 20:23:01 martin 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 __RCSID("$NetBSD: pthread_barrier.c,v 1.17 2008/04/28 20:23:01 martin Exp $"); 34 35 #include <errno.h> 36 37 #include "pthread.h" 38 #include "pthread_int.h" 39 40 int 41 pthread_barrier_init(pthread_barrier_t *barrier, 42 const pthread_barrierattr_t *attr, unsigned int count) 43 { 44 pthread_t self; 45 46 #ifdef ERRORCHECK 47 if ((barrier == NULL) || 48 (attr && (attr->ptba_magic != _PT_BARRIERATTR_MAGIC))) 49 return EINVAL; 50 #endif 51 52 if (count == 0) 53 return EINVAL; 54 55 if (barrier->ptb_magic == _PT_BARRIER_MAGIC) { 56 self = pthread__self(); 57 58 /* 59 * We're simply reinitializing the barrier to a 60 * new count. 61 */ 62 pthread__spinlock(self, &barrier->ptb_lock); 63 64 if (barrier->ptb_magic != _PT_BARRIER_MAGIC) { 65 pthread__spinunlock(self, &barrier->ptb_lock); 66 return EINVAL; 67 } 68 69 if (!PTQ_EMPTY(&barrier->ptb_waiters)) { 70 pthread__spinunlock(self, &barrier->ptb_lock); 71 return EBUSY; 72 } 73 74 barrier->ptb_initcount = count; 75 barrier->ptb_curcount = 0; 76 barrier->ptb_generation = 0; 77 78 pthread__spinunlock(self, &barrier->ptb_lock); 79 80 return 0; 81 } 82 83 barrier->ptb_magic = _PT_BARRIER_MAGIC; 84 pthread_lockinit(&barrier->ptb_lock); 85 PTQ_INIT(&barrier->ptb_waiters); 86 barrier->ptb_initcount = count; 87 barrier->ptb_curcount = 0; 88 barrier->ptb_generation = 0; 89 90 return 0; 91 } 92 93 94 int 95 pthread_barrier_destroy(pthread_barrier_t *barrier) 96 { 97 pthread_t self; 98 99 #ifdef ERRORCHECK 100 if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC)) 101 return EINVAL; 102 #endif 103 104 self = pthread__self(); 105 pthread__spinlock(self, &barrier->ptb_lock); 106 107 if (barrier->ptb_magic != _PT_BARRIER_MAGIC) { 108 pthread__spinunlock(self, &barrier->ptb_lock); 109 return EINVAL; 110 } 111 112 if (!PTQ_EMPTY(&barrier->ptb_waiters)) { 113 pthread__spinunlock(self, &barrier->ptb_lock); 114 return EBUSY; 115 } 116 117 barrier->ptb_magic = _PT_BARRIER_DEAD; 118 119 pthread__spinunlock(self, &barrier->ptb_lock); 120 121 return 0; 122 } 123 124 125 int 126 pthread_barrier_wait(pthread_barrier_t *barrier) 127 { 128 pthread_t self; 129 unsigned int gen; 130 131 #ifdef ERRORCHECK 132 if ((barrier == NULL) || (barrier->ptb_magic != _PT_BARRIER_MAGIC)) 133 return EINVAL; 134 #endif 135 self = pthread__self(); 136 137 pthread__spinlock(self, &barrier->ptb_lock); 138 139 /* 140 * A single arbitrary thread is supposed to return 141 * PTHREAD_BARRIER_SERIAL_THREAD, and everone else 142 * is supposed to return 0. Since pthread_barrier_wait() 143 * is not a cancellation point, this is trivial; we 144 * simply elect that the thread that causes the barrier 145 * to be satisfied gets the special return value. Note 146 * that this final thread does not actually need to block, 147 * but instead is responsible for waking everyone else up. 148 */ 149 if (barrier->ptb_curcount + 1 == barrier->ptb_initcount) { 150 barrier->ptb_generation++; 151 pthread__unpark_all(self, &barrier->ptb_lock, 152 &barrier->ptb_waiters); 153 return PTHREAD_BARRIER_SERIAL_THREAD; 154 } 155 156 barrier->ptb_curcount++; 157 gen = barrier->ptb_generation; 158 while (gen == barrier->ptb_generation) { 159 PTQ_INSERT_TAIL(&barrier->ptb_waiters, self, pt_sleep); 160 self->pt_sleeponq = 1; 161 self->pt_sleepobj = &barrier->ptb_waiters; 162 pthread__spinunlock(self, &barrier->ptb_lock); 163 (void)pthread__park(self, &barrier->ptb_lock, 164 &barrier->ptb_waiters, NULL, 0, 165 &barrier->ptb_waiters); 166 pthread__spinlock(self, &barrier->ptb_lock); 167 } 168 pthread__spinunlock(self, &barrier->ptb_lock); 169 170 return 0; 171 } 172 173 174 int 175 pthread_barrierattr_init(pthread_barrierattr_t *attr) 176 { 177 178 #ifdef ERRORCHECK 179 if (attr == NULL) 180 return EINVAL; 181 #endif 182 183 attr->ptba_magic = _PT_BARRIERATTR_MAGIC; 184 185 return 0; 186 } 187 188 189 int 190 pthread_barrierattr_destroy(pthread_barrierattr_t *attr) 191 { 192 193 #ifdef ERRORCHECK 194 if ((attr == NULL) || 195 (attr->ptba_magic != _PT_BARRIERATTR_MAGIC)) 196 return EINVAL; 197 #endif 198 199 attr->ptba_magic = _PT_BARRIERATTR_DEAD; 200 201 return 0; 202 } 203