Merge branch 'vendor/OPENSSL'
[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  * $DragonFly: src/lib/libthread_xu/thread/thr_sem.c,v 1.6 2007/06/26 23:30:05 josepht Exp $
31  */
32
33 #include "namespace.h"
34 #include <machine/tls.h>
35 #include <sys/semaphore.h>
36 #include <sys/mman.h>
37
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <pthread.h>
41 #include <stdlib.h>
42 #include <time.h>
43 #include "un-namespace.h"
44 #include "thr_private.h"
45
46 /*
47  * Semaphore definitions.
48  */
49 struct sem {
50         u_int32_t               magic;
51         volatile umtx_t         count;
52         int                     semid;
53         int                     unused; /* pad */
54 };
55
56 #define SEM_MAGIC       ((u_int32_t) 0x09fa4012)
57
58 #define SEMID_LWP       0
59 #define SEMID_FORK      1
60
61 static inline int
62 sem_check_validity(sem_t *sem)
63 {
64
65         if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC)) {
66                 return (0);
67         } else {
68                 errno = EINVAL;
69                 return (-1);
70         }
71 }
72
73 static sem_t
74 sem_alloc(unsigned int value, int pshared)
75 {
76         sem_t sem;
77         int semid;
78
79         if (value > SEM_VALUE_MAX) {
80                 errno = EINVAL;
81                 return (NULL);
82         }
83         if (pshared) {
84                 static __thread sem_t sem_base;
85                 static __thread int sem_count;
86
87                 if (sem_base == NULL) {
88                         sem_base = mmap(NULL, getpagesize(),
89                                         PROT_READ | PROT_WRITE,
90                                         MAP_ANON | MAP_SHARED,
91                                         -1, 0);
92                         sem_count = getpagesize() / sizeof(*sem);
93                 }
94                 sem = sem_base++;
95                 if (--sem_count == 0)
96                         sem_base = NULL;
97                 semid = SEMID_FORK;
98         } else {
99                 sem = malloc(sizeof(struct sem));
100                 semid = SEMID_LWP;
101         }
102         if (sem == NULL) {
103                 errno = ENOSPC;
104                 return (NULL);
105         }
106         sem->magic = SEM_MAGIC;
107         sem->count = (u_int32_t)value;
108         sem->semid = semid;
109         return (sem);
110 }
111
112 int
113 _sem_init(sem_t *sem, int pshared, unsigned int value)
114 {
115         (*sem) = sem_alloc(value, pshared);
116         if ((*sem) == NULL)
117                 return (-1);
118         return (0);
119 }
120
121 int
122 _sem_destroy(sem_t *sem)
123 {
124         if (sem_check_validity(sem) != 0)
125                 return (-1);
126
127         (*sem)->magic = 0;
128
129         switch ((*sem)->semid) {
130         case SEMID_LWP:
131                 free(*sem);
132                 break;
133         case SEMID_FORK:
134                 /* memory is left intact */
135                 break;
136         }
137         return (0);
138 }
139
140 int
141 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
142 {
143         if (sem_check_validity(sem) != 0)
144                 return (-1);
145
146         *sval = (*sem)->count;
147         return (0);
148 }
149
150 int
151 _sem_trywait(sem_t *sem)
152 {
153         int val;
154
155         if (sem_check_validity(sem) != 0)
156                 return (-1);
157
158         while ((val = (*sem)->count) > 0) {
159                 if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
160                         return (0);
161         }
162         errno = EAGAIN;
163         return (-1);
164 }
165
166 int
167 _sem_wait(sem_t *sem)
168 {
169         struct pthread *curthread;
170         int val, oldcancel, retval;
171
172         if (sem_check_validity(sem) != 0)
173                 return (-1);
174
175         curthread = tls_get_curthread();
176         _pthread_testcancel();
177         do {
178                 while ((val = (*sem)->count) > 0) {
179                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
180                                 return (0);
181                 }
182                 oldcancel = _thr_cancel_enter(curthread);
183                 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0);
184                 _thr_cancel_leave(curthread, oldcancel);
185         } while (retval == 0);
186         errno = retval;
187         return (-1);
188 }
189
190 #if 0
191 int
192 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime)
193 {
194         struct timespec ts, ts2;
195         struct pthread *curthread;
196         int val, oldcancel, retval;
197
198         if (sem_check_validity(sem) != 0)
199                 return (-1);
200
201         curthread = tls_get_curthread();
202
203         /*
204          * The timeout argument is only supposed to
205          * be checked if the thread would have blocked.
206          */
207         _pthread_testcancel();
208         do {
209                 while ((val = (*sem)->count) > 0) {
210                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
211                                 return (0);
212                 }
213                 if (abstime == NULL) {
214                         errno = EINVAL;
215                         return (-1);
216                 }
217                 clock_gettime(CLOCK_REALTIME, &ts);
218                 TIMESPEC_SUB(&ts2, abstime, &ts);
219                 oldcancel = _thr_cancel_enter(curthread);
220                 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
221                                         CLOCK_REALTIME);
222                 _thr_cancel_leave(curthread, oldcancel);
223         } while (retval == 0);
224         errno = retval;
225         return (-1);
226 }
227 #endif
228
229 int
230 _sem_post(sem_t *sem)
231 {
232         int val;
233         
234         if (sem_check_validity(sem) != 0)
235                 return (-1);
236
237         /*
238          * sem_post() is required to be safe to call from within
239          * signal handlers, these code should work as that.
240          */
241         do {
242                 val = (*sem)->count;
243         } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
244         _thr_umtx_wake(&(*sem)->count, val + 1);
245         return (0);
246 }
247
248 __strong_reference(_sem_destroy, sem_destroy);
249 __strong_reference(_sem_getvalue, sem_getvalue);
250 __strong_reference(_sem_init, sem_init);
251 __strong_reference(_sem_trywait, sem_trywait);
252 __strong_reference(_sem_wait, sem_wait);
253 #if 0
254 __strong_reference(_sem_timedwait, sem_timedwait);
255 #endif
256 __strong_reference(_sem_post, sem_post);
257