Release 980503
[wine] / scheduler / synchro.c
1 /*
2  * Win32 process and thread synchronisation
3  *
4  * Copyright 1997 Alexandre Julliard
5  */
6
7 #include <assert.h>
8 #include <signal.h>
9 #include <stdio.h>
10 #include <sys/time.h>
11 #include <unistd.h>
12 #include "k32obj.h"
13 #include "heap.h"
14 #include "process.h"
15 #include "thread.h"
16 #include "winerror.h"
17 #include "debug.h"
18
19 /***********************************************************************
20  *           SYNC_BuildWaitStruct
21  */
22 static BOOL32 SYNC_BuildWaitStruct( DWORD count, const HANDLE32 *handles,
23                                     BOOL32 wait_all, WAIT_STRUCT *wait )
24 {
25     DWORD i;
26     K32OBJ **ptr;
27
28     wait->count    = count;
29     wait->signaled = WAIT_FAILED;
30     wait->wait_all = wait_all;
31     SYSTEM_LOCK();
32     for (i = 0, ptr = wait->objs; i < count; i++, ptr++)
33     {
34         if (!(*ptr = HANDLE_GetObjPtr( PROCESS_Current(), handles[i],
35                                        K32OBJ_UNKNOWN, SYNCHRONIZE )))
36             break;
37         if (!K32OBJ_OPS( *ptr )->signaled)
38         {
39             /* This object type cannot be waited upon */
40             K32OBJ_DecCount( *ptr );
41             break;
42         }
43
44     }
45     if (i != count)
46     {
47         /* There was an error */
48         while (i--) K32OBJ_DecCount( wait->objs[i] );
49     }
50     SYSTEM_UNLOCK();
51     return (i == count);
52 }
53
54
55 /***********************************************************************
56  *           SYNC_FreeWaitStruct
57  */
58 static void SYNC_FreeWaitStruct( WAIT_STRUCT *wait )
59 {
60     DWORD i;
61     K32OBJ **ptr;
62     SYSTEM_LOCK();
63     for (i = 0, ptr = wait->objs; i < wait->count; i++, ptr++)
64         K32OBJ_DecCount( *ptr );
65     SYSTEM_UNLOCK();
66 }
67
68
69 /***********************************************************************
70  *           SYNC_CheckCondition
71  */
72 static BOOL32 SYNC_CheckCondition( WAIT_STRUCT *wait, DWORD thread_id )
73 {
74     DWORD i;
75     K32OBJ **ptr;
76
77     SYSTEM_LOCK();
78     if (wait->wait_all)
79     {
80         for (i = 0, ptr = wait->objs; i < wait->count; i++, ptr++)
81         {
82             if (!K32OBJ_OPS( *ptr )->signaled( *ptr, thread_id ))
83             {
84                 SYSTEM_UNLOCK();
85                 return FALSE;
86             }
87         }
88         /* Wait satisfied: tell it to all objects */
89         wait->signaled = WAIT_OBJECT_0;
90         for (i = 0, ptr = wait->objs; i < wait->count; i++, ptr++)
91             if (K32OBJ_OPS( *ptr )->satisfied( *ptr, thread_id ))
92                 wait->signaled = WAIT_ABANDONED_0;
93         SYSTEM_UNLOCK();
94         return TRUE;
95     }
96     else
97     {
98         for (i = 0, ptr = wait->objs; i < wait->count; i++, ptr++)
99         {
100             if (K32OBJ_OPS( *ptr )->signaled( *ptr, thread_id ))
101             {
102                 /* Wait satisfied: tell it to the object */
103                 wait->signaled = WAIT_OBJECT_0 + i;
104                 if (K32OBJ_OPS( *ptr )->satisfied( *ptr, thread_id ))
105                     wait->signaled = WAIT_ABANDONED_0 + i;
106                 SYSTEM_UNLOCK();
107                 return TRUE;
108             }
109         }
110         SYSTEM_UNLOCK();
111         return FALSE;
112     }
113 }
114
115
116 /***********************************************************************
117  *           SYNC_WaitForCondition
118  */
119 void SYNC_WaitForCondition( WAIT_STRUCT *wait, DWORD timeout )
120 {
121     DWORD i, thread_id = GetCurrentThreadId();
122     LONG count;
123     K32OBJ **ptr;
124     sigset_t set;
125
126     SYSTEM_LOCK();
127     if (SYNC_CheckCondition( wait, thread_id ))
128         goto done;  /* Condition already satisfied */
129     if (!timeout)
130     {
131         /* No need to wait */
132         wait->signaled = WAIT_TIMEOUT;
133         goto done;
134     }
135
136     /* Add ourselves to the waiting list of all objects */
137
138     for (i = 0, ptr = wait->objs; i < wait->count; i++, ptr++)
139         K32OBJ_OPS( *ptr )->add_wait( *ptr, thread_id );
140
141     /* Release the system lock completely */
142
143     count = SYSTEM_LOCK_COUNT();
144     for (i = count; i > 0; i--) SYSTEM_UNLOCK();
145
146     /* Now wait for it */
147
148     TRACE(win32, "starting wait (%p %04x)\n",
149                  THREAD_Current(), THREAD_Current()->teb_sel );
150
151     sigprocmask( SIG_SETMASK, NULL, &set );
152     sigdelset( &set, SIGUSR1 );
153     sigdelset( &set, SIGALRM );
154     if (timeout != INFINITE32)
155     {
156         while (wait->signaled == WAIT_FAILED)
157         {
158             struct itimerval timer;
159             DWORD start_ticks, elapsed;
160             timer.it_interval.tv_sec = timer.it_interval.tv_usec = 0;
161             timer.it_value.tv_sec = timeout / 1000;
162             timer.it_value.tv_usec = (timeout % 1000) * 1000;
163             start_ticks = GetTickCount();
164             setitimer( ITIMER_REAL, &timer, NULL );
165             sigsuspend( &set );
166             if (wait->signaled != WAIT_FAILED) break;
167             /* Recompute the timer value */
168             elapsed = GetTickCount() - start_ticks;
169             if (elapsed >= timeout) wait->signaled = WAIT_TIMEOUT;
170             else timeout -= elapsed;
171         }
172     }
173     else
174     {
175         while (wait->signaled == WAIT_FAILED)
176         {
177             sigsuspend( &set );
178         }
179     }
180
181     /* Grab the system lock again */
182
183     while (count--) SYSTEM_LOCK();
184     TRACE(win32, "wait finished (%p %04x)\n",
185                  THREAD_Current(), THREAD_Current()->teb_sel );
186
187     /* Remove ourselves from the lists */
188
189     for (i = 0, ptr = wait->objs; i < wait->count; i++, ptr++)
190         K32OBJ_OPS( *ptr )->remove_wait( *ptr, thread_id );
191
192 done:
193     SYSTEM_UNLOCK();
194 }
195
196
197 /***********************************************************************
198  *           SYNC_DummySigHandler
199  *
200  * Dummy signal handler
201  */
202 static void SYNC_DummySigHandler(void)
203 {
204 }
205
206
207 /***********************************************************************
208  *           SYNC_SetupSignals
209  *
210  * Setup signal handlers for a new thread.
211  * FIXME: should merge with SIGNAL_Init.
212  */
213 void SYNC_SetupSignals(void)
214 {
215     sigset_t set;
216     SIGNAL_SetHandler( SIGUSR1, SYNC_DummySigHandler, 0 );
217     /* FIXME: conflicts with system timers */
218     SIGNAL_SetHandler( SIGALRM, SYNC_DummySigHandler, 0 );
219     sigemptyset( &set );
220     /* Make sure these are blocked by default */
221     sigaddset( &set, SIGUSR1 );
222     sigaddset( &set, SIGALRM );
223     sigprocmask( SIG_BLOCK , &set, NULL);
224 }
225
226
227 /***********************************************************************
228  *           SYNC_WakeUp
229  */
230 void SYNC_WakeUp( THREAD_QUEUE *wait_queue, DWORD max )
231 {
232     THREAD_ENTRY *entry;
233
234     if (!max) max = INFINITE32;
235     SYSTEM_LOCK();
236     if (!*wait_queue)
237     {
238         SYSTEM_UNLOCK();
239         return;
240     }
241     entry = (*wait_queue)->next;
242     for (;;)
243     {
244         THDB *thdb = entry->thread;
245         if (SYNC_CheckCondition( &thdb->wait_struct, THDB_TO_THREAD_ID(thdb) ))
246         {
247             TRACE(win32, "waking up %04x\n", thdb->teb_sel );
248             kill( thdb->unix_pid, SIGUSR1 );
249             if (!--max) break;
250         }
251         if (entry == *wait_queue) break;
252         entry = entry->next;
253     }
254     SYSTEM_UNLOCK();
255 }
256
257
258 /***********************************************************************
259  *           WaitForSingleObject   (KERNEL32.723)
260  */
261 DWORD WINAPI WaitForSingleObject( HANDLE32 handle, DWORD timeout )
262 {
263     return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, FALSE );
264 }
265
266
267 /***********************************************************************
268  *           WaitForSingleObjectEx   (KERNEL32.724)
269  */
270 DWORD WINAPI WaitForSingleObjectEx( HANDLE32 handle, DWORD timeout,
271                                     BOOL32 alertable )
272 {
273     return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, alertable );
274 }
275
276
277 /***********************************************************************
278  *           WaitForMultipleObjects   (KERNEL32.721)
279  */
280 DWORD WINAPI WaitForMultipleObjects( DWORD count, const HANDLE32 *handles,
281                                      BOOL32 wait_all, DWORD timeout )
282 {
283     return WaitForMultipleObjectsEx(count, handles, wait_all, timeout, FALSE);
284 }
285
286
287 /***********************************************************************
288  *           WaitForMultipleObjectsEx   (KERNEL32.722)
289  */
290 DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE32 *handles,
291                                        BOOL32 wait_all, DWORD timeout,
292                                        BOOL32 alertable )
293 {
294     WAIT_STRUCT *wait = &THREAD_Current()->wait_struct;
295
296     if (count > MAXIMUM_WAIT_OBJECTS)
297     {
298         SetLastError( ERROR_INVALID_PARAMETER );
299         return WAIT_FAILED;
300     }
301
302     if (alertable)
303         fprintf( stderr,
304                  "WaitForMultipleObjectEx: alertable not implemented\n" );
305
306     SYSTEM_LOCK();
307     if (!SYNC_BuildWaitStruct( count, handles, wait_all, wait ))
308         wait->signaled = WAIT_FAILED;
309     else
310     {
311         /* Now wait for it */
312         SYNC_WaitForCondition( wait, timeout );
313         SYNC_FreeWaitStruct( wait );
314     }
315     SYSTEM_UNLOCK();
316     return wait->signaled;
317 }