1 /* $OpenBSD: rthread_sem.c,v 1.7 2012/03/03 11:09:19 guenther Exp $ */ 2 /* 3 * Copyright (c) 2004,2005 Ted Unangst <tedu@openbsd.org> 4 * All Rights Reserved. 5 * 6 * Permission to use, copy, modify, and distribute this software for any 7 * purpose with or without fee is hereby granted, provided that the above 8 * copyright notice and this permission notice appear in all copies. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 */ 18 19 #include <stdlib.h> 20 #include <unistd.h> 21 #include <errno.h> 22 23 #include <pthread.h> 24 25 #include "rthread.h" 26 27 /* 28 * Internal implementation of semaphores 29 */ 30 int 31 _sem_wait(sem_t sem, int tryonly, const struct timespec *abstime, 32 int *delayed_cancel) 33 { 34 int r; 35 36 _spinlock(&sem->lock); 37 if (sem->value) { 38 sem->value--; 39 r = 0; 40 } else if (tryonly) { 41 r = EAGAIN; 42 } else { 43 sem->waitcount++; 44 do { 45 r = __thrsleep(&sem->waitcount, CLOCK_REALTIME, 46 abstime, &sem->lock, delayed_cancel); 47 _spinlock(&sem->lock); 48 /* ignore interruptions other than cancelation */ 49 if (r == EINTR && (delayed_cancel == NULL || 50 *delayed_cancel == 0)) 51 r = 0; 52 } while (r == 0 && sem->value == 0); 53 sem->waitcount--; 54 if (r == 0) 55 sem->value--; 56 } 57 _spinunlock(&sem->lock); 58 return (r); 59 } 60 61 /* always increment count */ 62 int 63 _sem_post(sem_t sem) 64 { 65 int rv = 0; 66 67 _spinlock(&sem->lock); 68 sem->value++; 69 if (sem->waitcount) { 70 __thrwakeup(&sem->waitcount, 1); 71 rv = 1; 72 } 73 _spinunlock(&sem->lock); 74 return (rv); 75 } 76 77 /* 78 * exported semaphores 79 */ 80 int 81 sem_init(sem_t *semp, int pshared, unsigned int value) 82 { 83 sem_t sem; 84 85 if (pshared) { 86 errno = EPERM; 87 return (-1); 88 } 89 90 if (value > SEM_VALUE_MAX) { 91 errno = EINVAL; 92 return (-1); 93 } 94 95 sem = calloc(1, sizeof(*sem)); 96 if (!sem) { 97 errno = ENOSPC; 98 return (-1); 99 } 100 sem->lock = _SPINLOCK_UNLOCKED; 101 sem->value = value; 102 *semp = sem; 103 104 return (0); 105 } 106 107 int 108 sem_destroy(sem_t *semp) 109 { 110 if (!semp || !*semp) { 111 errno = EINVAL; 112 return (-1); 113 } 114 115 if ((*semp)->waitcount) { 116 #define MSG "sem_destroy on semaphore with waiters!\n" 117 write(2, MSG, sizeof(MSG) - 1); 118 #undef MSG 119 errno = EBUSY; 120 return (-1); 121 } 122 free(*semp); 123 *semp = NULL; 124 125 return (0); 126 } 127 128 int 129 sem_getvalue(sem_t *semp, int *sval) 130 { 131 sem_t sem = *semp; 132 133 if (!semp || !*semp) { 134 errno = EINVAL; 135 return (-1); 136 } 137 138 _spinlock(&sem->lock); 139 *sval = sem->value; 140 _spinunlock(&sem->lock); 141 142 return (0); 143 } 144 145 int 146 sem_post(sem_t *semp) 147 { 148 sem_t sem = *semp; 149 150 if (!semp || !*semp) { 151 errno = EINVAL; 152 return (-1); 153 } 154 155 _sem_post(sem); 156 157 return (0); 158 } 159 160 int 161 sem_wait(sem_t *semp) 162 { 163 sem_t sem = *semp; 164 pthread_t self = pthread_self(); 165 int r; 166 167 if (!semp || !*semp) { 168 errno = EINVAL; 169 return (-1); 170 } 171 172 _enter_delayed_cancel(self); 173 r = _sem_wait(sem, 0, NULL, &self->delayed_cancel); 174 _leave_delayed_cancel(self, r); 175 176 if (r) { 177 errno = r; 178 return (-1); 179 } 180 181 return (0); 182 } 183 184 int 185 sem_timedwait(sem_t *semp, const struct timespec *abstime) 186 { 187 sem_t sem = *semp; 188 pthread_t self = pthread_self(); 189 int r; 190 191 if (!semp || !*semp) { 192 errno = EINVAL; 193 return (-1); 194 } 195 196 _enter_delayed_cancel(self); 197 r = _sem_wait(sem, 0, abstime, &self->delayed_cancel); 198 _leave_delayed_cancel(self, r); 199 200 if (r) { 201 errno = r == EWOULDBLOCK ? ETIMEDOUT : r; 202 return (-1); 203 } 204 205 return (0); 206 } 207 208 int 209 sem_trywait(sem_t *semp) 210 { 211 sem_t sem = *semp; 212 int r; 213 214 if (!semp || !*semp) { 215 errno = EINVAL; 216 return (-1); 217 } 218 219 r = _sem_wait(sem, 1, NULL, NULL); 220 221 if (r) { 222 errno = r; 223 return (-1); 224 } 225 226 return (0); 227 } 228 229 /* ARGSUSED */ 230 sem_t * 231 sem_open(const char *name __unused, int oflag __unused, ...) 232 { 233 errno = ENOSYS; 234 return (SEM_FAILED); 235 } 236 237 /* ARGSUSED */ 238 int 239 sem_close(sem_t *sem __unused) 240 { 241 errno = ENOSYS; 242 return (-1); 243 } 244 245 /* ARGSUSED */ 246 int 247 sem_unlink(const char *name __unused) 248 { 249 errno = ENOSYS; 250 return (-1); 251 } 252 253