2004-04-13 Zoltan Varga <vargaz@freemail.hu>
[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         
117         mono_once (&sem_ops_once, sem_ops_init);
118         
119         if(max<=0) {
120 #ifdef DEBUG
121                 g_message(G_GNUC_PRETTY_FUNCTION ": max <= 0");
122 #endif
123
124                 return(NULL);
125         }
126         
127         if(initial>max || initial<0) {
128 #ifdef DEBUG
129                 g_message(G_GNUC_PRETTY_FUNCTION ": initial>max or < 0");
130 #endif
131
132                 return(NULL);
133         }
134         
135         handle=_wapi_handle_new (WAPI_HANDLE_SEM);
136         if(handle==_WAPI_HANDLE_INVALID) {
137                 g_warning (G_GNUC_PRETTY_FUNCTION
138                            ": error creating semaphore handle");
139                 return(NULL);
140         }
141
142         _wapi_handle_lock_handle (handle);
143         
144         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
145                                 (gpointer *)&sem_handle, NULL);
146         if(ok==FALSE) {
147                 g_warning (G_GNUC_PRETTY_FUNCTION
148                            ": error lookup up semaphore handle %p", handle);
149                 _wapi_handle_unlock_handle (handle);
150                 return(NULL);
151         }
152
153         sem_handle->val=initial;
154         sem_handle->max=max;
155
156         if(initial!=0) {
157                 _wapi_handle_set_signal_state (handle, TRUE, FALSE);
158         }
159
160 #ifdef DEBUG
161         g_message(G_GNUC_PRETTY_FUNCTION
162                   ": Created semaphore handle %p initial %d max %d", handle,
163                   initial, max);
164 #endif
165
166         _wapi_handle_unlock_handle (handle);
167         
168         return(handle);
169 }
170
171 /**
172  * ReleaseSemaphore:
173  * @handle: The semaphore handle to release.
174  * @count: The amount by which the semaphore's count should be
175  * increased.
176  * @prevcount: Pointer to a location to store the previous count of
177  * the semaphore, or %NULL.
178  *
179  * Increases the count of semaphore @handle by @count.
180  *
181  * Return value: %TRUE on success, %FALSE otherwise.
182  */
183 gboolean ReleaseSemaphore(gpointer handle, gint32 count, gint32 *prevcount)
184 {
185         struct _WapiHandle_sem *sem_handle;
186         gboolean ok;
187         gboolean ret=FALSE;
188         
189         ok=_wapi_lookup_handle (handle, WAPI_HANDLE_SEM,
190                                 (gpointer *)&sem_handle, NULL);
191         if(ok==FALSE) {
192                 g_warning (G_GNUC_PRETTY_FUNCTION
193                            ": error looking up sem handle %p", handle);
194                 return(FALSE);
195         }
196
197         _wapi_handle_lock_handle (handle);
198
199 #ifdef DEBUG
200         g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val %d count %d",
201                   handle, sem_handle->val, count);
202 #endif
203         
204         /* Do this before checking for count overflow, because overflowing max
205          * is a listed technique for finding the current value
206          */
207         if(prevcount!=NULL) {
208                 *prevcount=sem_handle->val;
209         }
210         
211         /* No idea why max is signed, but thats the spec :-( */
212         if(sem_handle->val+count > (guint32)sem_handle->max) {
213 #ifdef DEBUG
214                 g_message(G_GNUC_PRETTY_FUNCTION ": sem %p max value would be exceeded: max %d current %d count %d",
215                           handle, sem_handle->max, sem_handle->val, count);
216 #endif
217
218                 goto end;
219         }
220         
221         sem_handle->val+=count;
222         _wapi_handle_set_signal_state (handle, TRUE, TRUE);
223         
224         ret=TRUE;
225
226 #ifdef DEBUG
227         g_message(G_GNUC_PRETTY_FUNCTION ": sem %p val now %d", handle,
228                   sem_handle->val);
229 #endif
230         
231 end:
232         _wapi_handle_unlock_handle (handle);
233
234         return(ret);
235 }