2 * Win32 process and thread synchronisation
4 * Copyright 1997 Alexandre Julliard
15 #include "file.h" /* for DOSFS_UnixTimeToFileTime */
21 /***********************************************************************
24 inline static void get_timeout( struct timeval
*when
, int timeout
)
26 gettimeofday( when
, 0 );
29 long sec
= timeout
/ 1000;
30 if ((when
->tv_usec
+= (timeout
- 1000*sec
) * 1000) >= 1000000)
32 when
->tv_usec
-= 1000000;
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 */
51 ovp
->prev
->next
= ovp
->next
;
53 NtCurrentTeb()->pending_list
= ovp
->next
;
56 ovp
->next
->prev
= ovp
->prev
;
62 NtSetEvent(ovp
->lpOverlapped
->hEvent
,NULL
);
63 HeapFree(GetProcessHeap(), 0, ovp
);
66 /***********************************************************************
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
;
79 async_private
*ovp
, *timeout_user
;
84 /* the first fd belongs to the server connection */
87 fds
[0].fd
= NtCurrentTeb()->wait_fd
[0];
89 ovp
= NtCurrentTeb()->pending_list
;
92 gettimeofday(&now
,NULL
);
93 for(n
=1; ovp
&& (n
<MAX_NUMBER_OF_FDS
); ovp
= tmp
)
97 if(ovp
->lpOverlapped
->Internal
!=STATUS_PENDING
)
99 ovp
->lpOverlapped
->Internal
=STATUS_UNSUCCESSFUL
;
104 if(ovp
->timeout
&& time_before(&ovp
->tv
,&now
))
106 ovp
->lpOverlapped
->Internal
=STATUS_TIMEOUT
;
112 fds
[n
].events
=ovp
->event
;
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;
126 /* if there aren't any active asyncs return */
130 r
= poll(fds
, n
, timeout
);
132 /* if there were any errors, return immediately */
133 if( (r
<0) || (fds
[0].revents
==POLLNVAL
) )
138 timeout_user
->lpOverlapped
->Internal
= STATUS_TIMEOUT
;
139 finish_async(timeout_user
);
143 /* search for async operations that are ready */
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
)
159 /***********************************************************************
162 * Wait for a reply on the waiting pipe of the current thread.
164 static int wait_reply( void *cookie
)
167 struct wake_up_reply reply
;
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 */
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");
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 /***********************************************************************
202 * Call outstanding APCs.
204 static void call_apcs( BOOL alertable
)
213 SERVER_START_VAR_REQ( get_apc
, sizeof(args
) )
215 req
->alertable
= alertable
;
220 memcpy( args
, server_data_ptr(req
), server_data_size(req
) );
228 return; /* no more APCs */
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
);
240 server_protocol_error( "get_apc_request: bad type %d\n", type
);
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;
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
,
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
,
304 if (count
> MAXIMUM_WAIT_OBJECTS
)
306 SetLastError( ERROR_INVALID_PARAMETER
);
310 if (timeout
== INFINITE
) tv
.tv_sec
= tv
.tv_usec
= 0;
311 else get_timeout( &tv
, timeout
);
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
;
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
) );
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
);
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
);
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
);