83768a1d00e44944006a7f5b740b5cd98e1afd49
[dragonfly.git] / lib / libthread_xu / thread / thr_sem.c
1 /*
2  * Copyright (C) 2005 David Xu <davidxu@freebsd.org>.
3  * Copyright (C) 2000 Jason Evans <jasone@freebsd.org>.
4  * All rights reserved.
5  * 
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice(s), this list of conditions and the following disclaimer as
11  *    the first lines of this file unmodified other than the possible
12  *    addition of one or more copyright notices.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice(s), this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
21  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30
31 #include "namespace.h"
32 #include <machine/tls.h>
33 #include <sys/semaphore.h>
34 #include <sys/mman.h>
35
36 #include <errno.h>
37 #include <fcntl.h>
38 #include <pthread.h>
39 #include <stdlib.h>
40 #include <time.h>
41 #include "un-namespace.h"
42 #include "thr_private.h"
43
44 /*
45  * Semaphore definitions.
46  */
47 struct sem {
48         u_int32_t               magic;
49         volatile umtx_t         count;
50         int                     semid;
51         int                     unused; /* pad */
52 };
53
54 #define SEM_MAGIC       ((u_int32_t) 0x09fa4012)
55
56 #define SEMID_LWP       0
57 #define SEMID_FORK      1
58
59 static inline int
60 sem_check_validity(sem_t *sem)
61 {
62
63         if ((sem != NULL) && (*sem != NULL) && ((*sem)->magic == SEM_MAGIC)) {
64                 return (0);
65         } else {
66                 errno = EINVAL;
67                 return (-1);
68         }
69 }
70
71 static sem_t
72 sem_alloc(unsigned int value, int pshared)
73 {
74         sem_t sem;
75         int semid;
76
77         if (value > SEM_VALUE_MAX) {
78                 errno = EINVAL;
79                 return (NULL);
80         }
81         if (pshared) {
82                 static __thread sem_t sem_base;
83                 static __thread int sem_count;
84
85                 if (sem_base == NULL) {
86                         sem_base = mmap(NULL, getpagesize(),
87                                         PROT_READ | PROT_WRITE,
88                                         MAP_ANON | MAP_SHARED,
89                                         -1, 0);
90                         sem_count = getpagesize() / sizeof(*sem);
91                 }
92                 sem = sem_base++;
93                 if (--sem_count == 0)
94                         sem_base = NULL;
95                 semid = SEMID_FORK;
96         } else {
97                 sem = malloc(sizeof(struct sem));
98                 semid = SEMID_LWP;
99         }
100         if (sem == NULL) {
101                 errno = ENOSPC;
102                 return (NULL);
103         }
104         sem->magic = SEM_MAGIC;
105         sem->count = (u_int32_t)value;
106         sem->semid = semid;
107         return (sem);
108 }
109
110 int
111 _sem_init(sem_t *sem, int pshared, unsigned int value)
112 {
113         (*sem) = sem_alloc(value, pshared);
114         if ((*sem) == NULL)
115                 return (-1);
116         return (0);
117 }
118
119 int
120 _sem_destroy(sem_t *sem)
121 {
122         if (sem_check_validity(sem) != 0)
123                 return (-1);
124
125         (*sem)->magic = 0;
126
127         switch ((*sem)->semid) {
128         case SEMID_LWP:
129                 free(*sem);
130                 break;
131         case SEMID_FORK:
132                 /* memory is left intact */
133                 break;
134         }
135         return (0);
136 }
137
138 int
139 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
140 {
141         if (sem_check_validity(sem) != 0)
142                 return (-1);
143
144         *sval = (*sem)->count;
145         return (0);
146 }
147
148 int
149 _sem_trywait(sem_t *sem)
150 {
151         int val;
152
153         if (sem_check_validity(sem) != 0)
154                 return (-1);
155
156         while ((val = (*sem)->count) > 0) {
157                 if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
158                         return (0);
159         }
160         errno = EAGAIN;
161         return (-1);
162 }
163
164 int
165 _sem_wait(sem_t *sem)
166 {
167         struct pthread *curthread;
168         int val, oldcancel, retval;
169
170         if (sem_check_validity(sem) != 0)
171                 return (-1);
172
173         curthread = tls_get_curthread();
174         _pthread_testcancel();
175         do {
176                 while ((val = (*sem)->count) > 0) {
177                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
178                                 return (0);
179                 }
180                 oldcancel = _thr_cancel_enter(curthread);
181                 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0);
182                 _thr_cancel_leave(curthread, oldcancel);
183         } while (retval == 0);
184         errno = retval;
185         return (-1);
186 }
187
188 #if 0
189 int
190 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime)
191 {
192         struct timespec ts, ts2;
193         struct pthread *curthread;
194         int val, oldcancel, retval;
195
196         if (sem_check_validity(sem) != 0)
197                 return (-1);
198
199         curthread = tls_get_curthread();
200
201         /*
202          * The timeout argument is only supposed to
203          * be checked if the thread would have blocked.
204          */
205         _pthread_testcancel();
206         do {
207                 while ((val = (*sem)->count) > 0) {
208                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
209                                 return (0);
210                 }
211                 if (abstime == NULL) {
212                         errno = EINVAL;
213                         return (-1);
214                 }
215                 clock_gettime(CLOCK_REALTIME, &ts);
216                 TIMESPEC_SUB(&ts2, abstime, &ts);
217                 oldcancel = _thr_cancel_enter(curthread);
218                 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
219                                         CLOCK_REALTIME);
220                 _thr_cancel_leave(curthread, oldcancel);
221         } while (retval == 0);
222         errno = retval;
223         return (-1);
224 }
225 #endif
226
227 int
228 _sem_post(sem_t *sem)
229 {
230         int val;
231         
232         if (sem_check_validity(sem) != 0)
233                 return (-1);
234
235         /*
236          * sem_post() is required to be safe to call from within
237          * signal handlers, these code should work as that.
238          */
239         do {
240                 val = (*sem)->count;
241         } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
242         _thr_umtx_wake(&(*sem)->count, val + 1);
243         return (0);
244 }
245
246 sem_t *
247 _sem_open(__unused const char *name, __unused int oflag, ...)
248 {
249         errno = ENOSYS;
250         return (SEM_FAILED);
251 }
252
253 int
254 _sem_close(__unused sem_t *sem)
255 {
256         errno = ENOSYS;
257         return (-1);
258 }
259
260 int
261 _sem_unlink(__unused const char *name)
262 {
263         errno = ENOSYS;
264         return (-1);
265 }
266
267 __strong_reference(_sem_destroy, sem_destroy);
268 __strong_reference(_sem_getvalue, sem_getvalue);
269 __strong_reference(_sem_init, sem_init);
270 __strong_reference(_sem_trywait, sem_trywait);
271 __strong_reference(_sem_wait, sem_wait);
272 #if 0
273 __strong_reference(_sem_timedwait, sem_timedwait);
274 #endif
275 __strong_reference(_sem_post, sem_post);
276 __strong_reference(_sem_open, sem_open);
277 __strong_reference(_sem_close, sem_close);
278 __strong_reference(_sem_unlink, sem_unlink);
279