some nice little micro-benchmarks
[mono.git] / mono / io-layer / semaphores.c
1 /*
2  * semaphores.c:  Semaphore handles
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2002 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12 #include <pthread.h>
13 #ifdef HAVE_SEMAPHORE_H
14 #include <semaphore.h>
15 #endif
16 #include <errno.h>
17 #include <string.h>
18 #include <sys/time.h>
19
20 #include <mono/io-layer/wapi.h>
21 #include <mono/io-layer/wapi-private.h>
22 #include <mono/io-layer/misc-private.h>
23 #include <mono/io-layer/handles-private.h>
24 #include <mono/io-layer/mono-mutex.h>
25 #include <mono/io-layer/semaphore-private.h>
26
27 #undef DEBUG
28
29 static void sema_close_shared (gpointer handle);
30 static void sema_signal(gpointer handle);
31 static void sema_own (gpointer handle);
32
33 struct _WapiHandleOps _wapi_sem_ops = {
34         sema_close_shared,      /* close_shared */
35         NULL,                   /* close_private */
36         sema_signal,            /* signal */
37         sema_own,               /* own */
38         NULL,                   /* is_owned */
39 };
40
41 static mono_once_t sem_ops_once=MONO_ONCE_INIT;
42
43 static void sem_ops_init (void)
44 {
45         _wapi_handle_register_capabilities (WAPI_HANDLE_SEM,
46                                             WAPI_HANDLE_CAP_WAIT |
47                                             WAPI_HANDLE_CAP_SIGNAL);
48 }
49
50 static void sema_close_shared (gpointer handle G_GNUC_UNUSED)
51 {
52         /* Not really much to do here */
53 #ifdef DEBUG
54         g_message(G_GNUC_PRETTY_FUNCTION ": closing sem handle %p", handle);
55 #endif
56 }
57
58 static void sema_signal(gpointer handle)
59 {
60         ReleaseSemaphore(handle, 1, NULL);
61 }
62
63 static void sema_own (gpointer handle)
64 {
65         struct _WapiHandle_sem *sem_handle;
66         gboolean ok;
67         
68         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
69                                 (gpointer *)&sem_handle, NULL);
70         if(ok==FALSE) {
71                 g_warning (G_GNUC_PRETTY_FUNCTION
72                            ": error looking up sem handle %p", handle);
73                 return;
74         }
75         
76 #ifdef DEBUG
77         g_message(G_GNUC_PRETTY_FUNCTION ": owning sem handle %p", handle);
78 #endif
79
80         sem_handle->val--;
81         
82 #ifdef DEBUG
83         g_message (G_GNUC_PRETTY_FUNCTION ": sem %p val now %d", handle,
84                    sem_handle->val);
85 #endif
86
87         if(sem_handle->val==0) {
88                 _wapi_handle_set_signal_state (handle, FALSE, FALSE);
89         }
90 }
91
92
93 /**
94  * CreateSemaphore:
95  * @security: Ignored for now.
96  * @initial: The initial count for the semaphore.  The value must be
97  * greater than or equal to zero, and less than or equal to @max.
98  * @max: The maximum count for this semaphore.  The value must be
99  * greater than zero.
100  * @name: Pointer to a string specifying the name of this semaphore,
101  * or %NULL.  Currently ignored.
102  *
103  * Creates a new semaphore handle.  A semaphore is signalled when its
104  * count is greater than zero, and unsignalled otherwise.  The count
105  * is decreased by one whenever a wait function releases a thread that
106  * was waiting for the semaphore.  The count is increased by calling
107  * ReleaseSemaphore().
108  *
109  * Return value: a new handle, or NULL
110  */
111 gpointer CreateSemaphore(WapiSecurityAttributes *security G_GNUC_UNUSED, gint32 initial, gint32 max, const gunichar2 *name G_GNUC_UNUSED)
112 {
113         struct _WapiHandle_sem *sem_handle;
114         gpointer handle;
115         gboolean ok;
116         int thr_ret;
117         gpointer ret = NULL;
118         
119         mono_once (&sem_ops_once, sem_ops_init);
120         
121         if(max<=0) {
122 #ifdef DEBUG
123                 g_message(G_GNUC_PRETTY_FUNCTION ": max <= 0");
124 #endif
125
126                 return(NULL);
127         }
128         
129         if(initial>max || initial<0) {
130 #ifdef DEBUG
131                 g_message(G_GNUC_PRETTY_FUNCTION ": initial>max or < 0");
132 #endif
133
134                 return(NULL);
135         }
136         
137         handle=_wapi_handle_new (WAPI_HANDLE_SEM);
138         if(handle==_WAPI_HANDLE_INVALID) {
139                 g_warning (G_GNUC_PRETTY_FUNCTION
140                            ": error creating semaphore handle");
141                 return(NULL);
142         }
143
144         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
145                               handle);
146         thr_ret = _wapi_handle_lock_handle (handle);
147         g_assert (thr_ret == 0);
148         
149         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
150                                 (gpointer *)&sem_handle, NULL);
151         if(ok==FALSE) {
152                 g_warning (G_GNUC_PRETTY_FUNCTION
153                            ": error lookup up semaphore handle %p", handle);
154                 goto cleanup;
155         }
156         ret = handle;
157         
158         sem_handle->val=initial;
159         sem_handle->max=max;
160
161         if(initial!=0) {
162                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
163         }
164
165 #ifdef DEBUG
166         g_message(G_GNUC_PRETTY_FUNCTION
167                   ": Created semaphore handle %p initial %d max %d", handle,
168                   initial, max);
169 #endif
170
171 cleanup:
172         thr_ret = _wapi_handle_unlock_handle (handle);
173         g_assert (thr_ret == 0);
174         pthread_cleanup_pop (0);
175         
176         return(ret);
177 }
178
179 /**
180  * ReleaseSemaphore:
181  * @handle: The semaphore handle to release.
182  * @count: The amount by which the semaphore's count should be
183  * increased.
184  * @prevcount: Pointer to a location to store the previous count of
185  * the semaphore, or %NULL.
186  *
187  * Increases the count of semaphore @handle by @count.
188  *
189  * Return value: %TRUE on success, %FALSE otherwise.
190  */
191 gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount)
192 {
193         struct _WapiHandle_sem *sem_handle;
194         gboolean ok;
195         gboolean ret=FALSE;
196         int thr_ret;
197         
198         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
199                                 (gpointer *)&sem_handle, NULL);
200         if(ok==FALSE) {
201                 g_warning (G_GNUC_PRETTY_FUNCTION
202                            ": error looking up sem handle %p", handle);
203                 return(FALSE);
204         }
205
206         pthread_cleanup_push ((void(*)(void *))_wapi_handle_unlock_handle,
207                               handle);
208         thr_ret = _wapi_handle_lock_handle (handle);
209         g_assert (thr_ret == 0);
210
211 #ifdef DEBUG
212         g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val %d count %d",
213                   handle, sem_handle->val, count);
214 #endif
215         
216         /* Do this before checking for count overflow, because overflowing max
217          * is a listed technique for finding the current value
218          */
219         if(prevcount!=NULL) {
220                 *prevcount=sem_handle->val;
221         }
222         
223         /* No idea why max is signed, but thats the spec :-( */
224         if(sem_handle->val+count > (guint32)sem_handle->max) {
225 #ifdef DEBUG
226                 g_message(G_GNUC_PRETTY_FUNCTION ": sem %p max value would be exceeded: max %d current %d count %d",
227                           handle, sem_handle->max, sem_handle->val, count);
228 #endif
229
230                 goto end;
231         }
232         
233         sem_handle->val+=count;
234         _wapi_handle_set_signal_state (handle, TRUE, TRUE);
235         
236         ret=TRUE;
237
238 #ifdef DEBUG
239         g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val now %d", handle,
240                   sem_handle->val);
241 #endif
242         
243 end:
244         thr_ret = _wapi_handle_unlock_handle (handle);
245         g_assert (thr_ret == 0);
246         pthread_cleanup_pop (0);
247
248         return(ret);
249 }