Changed the spec definition of *s*printf from *str to ptr, since that
[wine.git] / scheduler / synchro.c
blob0add600e89de95c97852a4aa82ced345430e9c3b
1 /*
2 * Win32 process and thread synchronisation
4 * Copyright 1997 Alexandre Julliard
5 */
7 #include <assert.h>
8 #include <errno.h>
9 #include <signal.h>
10 #include <sys/time.h>
11 #include <sys/poll.h>
12 #include <unistd.h>
13 #include <string.h>
15 #include "file.h" /* for DOSFS_UnixTimeToFileTime */
16 #include "thread.h"
17 #include "winerror.h"
18 #include "server.h"
21 /***********************************************************************
22 * get_timeout
24 inline static void get_timeout( struct timeval *when, int timeout )
26 gettimeofday( when, 0 );
27 if (timeout)
29 long sec = timeout / 1000;
30 if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000)
32 when->tv_usec -= 1000000;
33 when->tv_sec++;
35 when->tv_sec += sec;
39 #define MAX_NUMBER_OF_FDS 20
41 static inline int time_before( struct timeval *t1, struct timeval *t2 )
43 return ((t1->tv_sec < t2->tv_sec) ||
44 ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec < t2->tv_usec)));
47 static void finish_async(async_private *ovp)
49 /* remove it from the active list */
50 if(ovp->prev)
51 ovp->prev->next = ovp->next;
52 else
53 NtCurrentTeb()->pending_list = ovp->next;
55 if(ovp->next)
56 ovp->next->prev = ovp->prev;
58 ovp->next=NULL;
59 ovp->prev=NULL;
61 close(ovp->fd);
62 NtSetEvent(ovp->lpOverlapped->hEvent,NULL);
63 HeapFree(GetProcessHeap(), 0, ovp);
66 /***********************************************************************
67 * check_async_list
69 * Create a list of fds for poll to check while waiting on the server
70 * FIXME: this loop is too large, cut into smaller functions
71 * perhaps we could share/steal some of the code in server/select.c?
73 static void check_async_list(void)
75 /* FIXME: should really malloc these two arrays */
76 struct pollfd fds[MAX_NUMBER_OF_FDS];
77 async_private *user[MAX_NUMBER_OF_FDS], *tmp;
78 int i, n, r, timeout;
79 async_private *ovp, *timeout_user;
80 struct timeval now;
82 while(1)
84 /* the first fd belongs to the server connection */
85 fds[0].events=POLLIN;
86 fds[0].revents=0;
87 fds[0].fd = NtCurrentTeb()->wait_fd[0];
89 ovp = NtCurrentTeb()->pending_list;
90 timeout = -1;
91 timeout_user = NULL;
92 gettimeofday(&now,NULL);
93 for(n=1; ovp && (n<MAX_NUMBER_OF_FDS); ovp = tmp)
95 tmp = ovp->next;
97 if(ovp->lpOverlapped->Internal!=STATUS_PENDING)
99 ovp->lpOverlapped->Internal=STATUS_UNSUCCESSFUL;
100 finish_async(ovp);
101 continue;
104 if(ovp->timeout && time_before(&ovp->tv,&now))
106 ovp->lpOverlapped->Internal=STATUS_TIMEOUT;
107 finish_async(ovp);
108 continue;
111 fds[n].fd=ovp->fd;
112 fds[n].events=ovp->event;
113 fds[n].revents=0;
114 user[n] = ovp;
116 if(ovp->timeout && ( (!timeout_user) || time_before(&ovp->tv,&timeout_user->tv)))
118 timeout = (ovp->tv.tv_sec - now.tv_sec) * 1000
119 + (ovp->tv.tv_usec - now.tv_usec) / 1000;
120 timeout_user = ovp;
123 n++;
126 /* if there aren't any active asyncs return */
127 if(n==1)
128 return;
130 r = poll(fds, n, timeout);
132 /* if there were any errors, return immediately */
133 if( (r<0) || (fds[0].revents==POLLNVAL) )
134 return;
136 if( r==0 )
138 timeout_user->lpOverlapped->Internal = STATUS_TIMEOUT;
139 finish_async(timeout_user);
140 continue;
143 /* search for async operations that are ready */
144 for( i=1; i<n; i++)
146 if (fds[i].revents)
147 user[i]->func(user[i],fds[i].revents);
149 if(user[i]->lpOverlapped->Internal!=STATUS_PENDING)
150 finish_async(user[i]);
153 if(fds[0].revents == POLLIN)
154 return;
159 /***********************************************************************
160 * wait_reply
162 * Wait for a reply on the waiting pipe of the current thread.
164 static int wait_reply( void *cookie )
166 int signaled;
167 struct wake_up_reply reply;
168 for (;;)
170 int ret;
171 if (NtCurrentTeb()->pending_list) check_async_list();
172 ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) );
173 if (ret == sizeof(reply))
175 if (!reply.cookie) break; /* thread got killed */
176 if (reply.cookie == cookie) return reply.signaled;
177 /* we stole another reply, wait for the real one */
178 signaled = wait_reply( cookie );
179 /* and now put the wrong one back in the pipe */
180 for (;;)
182 ret = write( NtCurrentTeb()->wait_fd[1], &reply, sizeof(reply) );
183 if (ret == sizeof(reply)) break;
184 if (ret >= 0) server_protocol_error( "partial wakeup write %d\n", ret );
185 if (errno == EINTR) continue;
186 server_protocol_perror("wakeup write");
188 return signaled;
190 if (ret >= 0) server_protocol_error( "partial wakeup read %d\n", ret );
191 if (errno == EINTR) continue;
192 server_protocol_perror("wakeup read");
194 /* the server closed the connection; time to die... */
195 SYSDEPS_ExitThread(0);
199 /***********************************************************************
200 * call_apcs
202 * Call outstanding APCs.
204 static void call_apcs( BOOL alertable )
206 FARPROC proc = NULL;
207 FILETIME ft;
208 void *args[4];
210 for (;;)
212 int type = APC_NONE;
213 SERVER_START_VAR_REQ( get_apc, sizeof(args) )
215 req->alertable = alertable;
216 if (!SERVER_CALL())
218 type = req->type;
219 proc = req->func;
220 memcpy( args, server_data_ptr(req), server_data_size(req) );
223 SERVER_END_VAR_REQ;
225 switch(type)
227 case APC_NONE:
228 return; /* no more APCs */
229 case APC_ASYNC:
230 break;
231 case APC_USER:
232 proc( args[0] );
233 break;
234 case APC_TIMER:
235 /* convert sec/usec to NT time */
236 DOSFS_UnixTimeToFileTime( (time_t)args[0], &ft, (DWORD)args[1] * 10 );
237 proc( args[2], ft.dwLowDateTime, ft.dwHighDateTime );
238 break;
239 default:
240 server_protocol_error( "get_apc_request: bad type %d\n", type );
241 break;
246 /***********************************************************************
247 * Sleep (KERNEL32.679)
249 VOID WINAPI Sleep( DWORD timeout )
251 WaitForMultipleObjectsEx( 0, NULL, FALSE, timeout, FALSE );
254 /******************************************************************************
255 * SleepEx (KERNEL32.680)
257 DWORD WINAPI SleepEx( DWORD timeout, BOOL alertable )
259 DWORD ret = WaitForMultipleObjectsEx( 0, NULL, FALSE, timeout, alertable );
260 if (ret != WAIT_IO_COMPLETION) ret = 0;
261 return ret;
265 /***********************************************************************
266 * WaitForSingleObject (KERNEL32.723)
268 DWORD WINAPI WaitForSingleObject( HANDLE handle, DWORD timeout )
270 return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, FALSE );
274 /***********************************************************************
275 * WaitForSingleObjectEx (KERNEL32.724)
277 DWORD WINAPI WaitForSingleObjectEx( HANDLE handle, DWORD timeout,
278 BOOL alertable )
280 return WaitForMultipleObjectsEx( 1, &handle, FALSE, timeout, alertable );
284 /***********************************************************************
285 * WaitForMultipleObjects (KERNEL32.721)
287 DWORD WINAPI WaitForMultipleObjects( DWORD count, const HANDLE *handles,
288 BOOL wait_all, DWORD timeout )
290 return WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
294 /***********************************************************************
295 * WaitForMultipleObjectsEx (KERNEL32.722)
297 DWORD WINAPI WaitForMultipleObjectsEx( DWORD count, const HANDLE *handles,
298 BOOL wait_all, DWORD timeout,
299 BOOL alertable )
301 int i, ret, cookie;
302 struct timeval tv;
304 if (count > MAXIMUM_WAIT_OBJECTS)
306 SetLastError( ERROR_INVALID_PARAMETER );
307 return WAIT_FAILED;
310 if (timeout == INFINITE) tv.tv_sec = tv.tv_usec = 0;
311 else get_timeout( &tv, timeout );
313 for (;;)
315 SERVER_START_VAR_REQ( select, count * sizeof(int) )
317 int *data = server_data_ptr( req );
319 req->flags = SELECT_INTERRUPTIBLE;
320 req->cookie = &cookie;
321 req->sec = tv.tv_sec;
322 req->usec = tv.tv_usec;
323 for (i = 0; i < count; i++) data[i] = handles[i];
325 if (wait_all) req->flags |= SELECT_ALL;
326 if (alertable) req->flags |= SELECT_ALERTABLE;
327 if (timeout != INFINITE) req->flags |= SELECT_TIMEOUT;
329 ret = SERVER_CALL();
331 SERVER_END_VAR_REQ;
332 if (ret == STATUS_PENDING) ret = wait_reply( &cookie );
333 if (ret != STATUS_USER_APC) break;
334 call_apcs( alertable );
335 if (alertable) break;
337 if (HIWORD(ret)) /* is it an error code? */
339 SetLastError( RtlNtStatusToDosError(ret) );
340 ret = WAIT_FAILED;
342 return ret;
346 /***********************************************************************
347 * WaitForSingleObject16 (KERNEL.460)
349 DWORD WINAPI WaitForSingleObject16( HANDLE handle, DWORD timeout )
351 DWORD retval, mutex_count;
353 ReleaseThunkLock( &mutex_count );
354 retval = WaitForSingleObject( handle, timeout );
355 RestoreThunkLock( mutex_count );
356 return retval;
359 /***********************************************************************
360 * WaitForMultipleObjects16 (KERNEL.461)
362 DWORD WINAPI WaitForMultipleObjects16( DWORD count, const HANDLE *handles,
363 BOOL wait_all, DWORD timeout )
365 DWORD retval, mutex_count;
367 ReleaseThunkLock( &mutex_count );
368 retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, FALSE );
369 RestoreThunkLock( mutex_count );
370 return retval;
373 /***********************************************************************
374 * WaitForMultipleObjectsEx16 (KERNEL.495)
376 DWORD WINAPI WaitForMultipleObjectsEx16( DWORD count, const HANDLE *handles,
377 BOOL wait_all, DWORD timeout, BOOL alertable )
379 DWORD retval, mutex_count;
381 ReleaseThunkLock( &mutex_count );
382 retval = WaitForMultipleObjectsEx( count, handles, wait_all, timeout, alertable );
383 RestoreThunkLock( mutex_count );
384 return retval;