Make sure we tally an interrupt when calling sched_ithd() from
[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
37 #include <errno.h>
38 #include <fcntl.h>
39 #include <pthread.h>
40 #include <stdlib.h>
41 #include <time.h>
42 #include "un-namespace.h"
43 #include "thr_private.h"
44
45 /*
46  * Semaphore definitions.
47  */
48 struct sem {
49 #define SEM_MAGIC       ((u_int32_t) 0x09fa4012)
50         u_int32_t               magic;
51         volatile umtx_t         count;
52         int                     semid;  /* kernel based semaphore id. */
53 };
54
55 static inline int
56 sem_check_validity(sem_t *sem)
57 {
58
59         if ((sem != NULL) && ((*sem)->magic == SEM_MAGIC))
60                 return (0);
61         else {
62                 errno = EINVAL;
63                 return (-1);
64         }
65 }
66
67 static sem_t
68 sem_alloc(unsigned int value, int semid)
69 {
70         sem_t sem;
71
72         if (value > SEM_VALUE_MAX) {
73                 errno = EINVAL;
74                 return (NULL);
75         }
76
77         sem = (sem_t)malloc(sizeof(struct sem));
78         if (sem == NULL) {
79                 errno = ENOSPC;
80                 return (NULL);
81         }
82         sem->magic = SEM_MAGIC;
83         sem->count = (u_int32_t)value;
84         sem->semid = semid;
85         return (sem);
86 }
87
88 int
89 _sem_init(sem_t *sem, int pshared, unsigned int value)
90 {
91         if (pshared != 0) {
92                 /*
93                  * We really can support pshared, but sem_t was
94                  * defined as a pointer, if it is a structure,
95                  * it will work between processes.
96                  */
97                 errno = EPERM;
98                 return (-1);
99         }
100
101         (*sem) = sem_alloc(value, -1);
102         if ((*sem) == NULL)
103                 return (-1);
104         return (0);
105 }
106
107 int
108 _sem_destroy(sem_t *sem)
109 {
110         if (sem_check_validity(sem) != 0)
111                 return (-1);
112
113         free(*sem);
114         return (0);
115 }
116
117 int
118 _sem_getvalue(sem_t * __restrict sem, int * __restrict sval)
119 {
120         if (sem_check_validity(sem) != 0)
121                 return (-1);
122
123         *sval = (*sem)->count;
124         return (0);
125 }
126
127 int
128 _sem_trywait(sem_t *sem)
129 {
130         int val;
131
132         if (sem_check_validity(sem) != 0)
133                 return (-1);
134
135         while ((val = (*sem)->count) > 0) {
136                 if (atomic_cmpset_int(&(*sem)->count, val, val - 1))
137                         return (0);
138         }
139         errno = EAGAIN;
140         return (-1);
141 }
142
143 int
144 _sem_wait(sem_t *sem)
145 {
146         struct pthread *curthread;
147         int val, oldcancel, retval;
148
149         if (sem_check_validity(sem) != 0)
150                 return (-1);
151
152         curthread = tls_get_curthread();
153         _pthread_testcancel();
154         do {
155                 while ((val = (*sem)->count) > 0) {
156                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
157                                 return (0);
158                 }
159                 oldcancel = _thr_cancel_enter(curthread);
160                 retval = _thr_umtx_wait(&(*sem)->count, 0, NULL, 0);
161                 _thr_cancel_leave(curthread, oldcancel);
162         } while (retval == 0);
163         errno = retval;
164         return (-1);
165 }
166
167 #if 0
168 int
169 _sem_timedwait(sem_t * __restrict sem, struct timespec * __restrict abstime)
170 {
171         struct timespec ts, ts2;
172         struct pthread *curthread;
173         int val, oldcancel, retval;
174
175         if (sem_check_validity(sem) != 0)
176                 return (-1);
177
178         curthread = tls_get_curthread();
179
180         /*
181          * The timeout argument is only supposed to
182          * be checked if the thread would have blocked.
183          */
184         _pthread_testcancel();
185         do {
186                 while ((val = (*sem)->count) > 0) {
187                         if (atomic_cmpset_acq_int(&(*sem)->count, val, val - 1))
188                                 return (0);
189                 }
190                 if (abstime == NULL) {
191                         errno = EINVAL;
192                         return (-1);
193                 }
194                 clock_gettime(CLOCK_REALTIME, &ts);
195                 TIMESPEC_SUB(&ts2, abstime, &ts);
196                 oldcancel = _thr_cancel_enter(curthread);
197                 retval = _thr_umtx_wait(&(*sem)->count, 0, &ts2,
198                                         CLOCK_REALTIME);
199                 _thr_cancel_leave(curthread, oldcancel);
200         } while (retval == 0);
201         errno = retval;
202         return (-1);
203 }
204 #endif
205
206 int
207 _sem_post(sem_t *sem)
208 {
209         int val;
210         
211         if (sem_check_validity(sem) != 0)
212                 return (-1);
213
214         /*
215          * sem_post() is required to be safe to call from within
216          * signal handlers, these code should work as that.
217          */
218         do {
219                 val = (*sem)->count;
220         } while (!atomic_cmpset_acq_int(&(*sem)->count, val, val + 1));
221         _thr_umtx_wake(&(*sem)->count, val + 1);
222         return (0);
223 }
224
225 __strong_reference(_sem_destroy, sem_destroy);
226 __strong_reference(_sem_getvalue, sem_getvalue);
227 __strong_reference(_sem_init, sem_init);
228 __strong_reference(_sem_trywait, sem_trywait);
229 __strong_reference(_sem_wait, sem_wait);
230 #if 0
231 __strong_reference(_sem_timedwait, sem_timedwait);
232 #endif
233 __strong_reference(_sem_post, sem_post);
234