5 #include "mono/io-layer/wapi.h"
6 #include "wait-private.h"
7 #include "timed-thread.h"
8 #include "handles-private.h"
9 #include "wapi-private.h"
13 static pthread_once_t wait_once=PTHREAD_ONCE_INIT;
15 static GPtrArray *WaitQueue=NULL;
17 static pthread_t wait_monitor_thread_id;
18 static gboolean wait_monitor_thread_running=FALSE;
19 static pthread_mutex_t wait_monitor_mutex=PTHREAD_MUTEX_INITIALIZER;
20 static sem_t wait_monitor_sem;
22 static void launch_tidy(guint32 exitcode G_GNUC_UNUSED, gpointer user)
24 WaitQueueItem *item=(WaitQueueItem *)user;
27 g_message(G_GNUC_PRETTY_FUNCTION ": Informing monitor thread");
30 /* Update queue item */
31 pthread_mutex_lock(&item->mutex);
33 pthread_mutex_unlock(&item->mutex);
36 sem_post(&wait_monitor_sem);
39 /* This function is called by the monitor thread to launch handle-type
40 * specific threads to block in particular ways.
42 * The item mutex is held by the monitor thread when this function is
45 static void launch_blocker_threads(WaitQueueItem *item)
50 g_message(G_GNUC_PRETTY_FUNCTION ": Launching blocker threads");
53 for(i=0; i<WAPI_HANDLE_COUNT; i++) {
54 if(item->handles[i]->len>0) {
57 handle=g_ptr_array_index(item->handles[i], 0);
58 g_assert(handle!=NULL);
59 g_assert(handle->ops->wait_multiple!=NULL);
62 g_message("Handle type %d active", i);
64 item->waited[i]=FALSE;
66 ret=_wapi_timed_thread_create(
67 &item->thread[i], NULL,
68 handle->ops->wait_multiple, launch_tidy, item,
71 g_warning(G_GNUC_PRETTY_FUNCTION
72 ": Thread create error: %s",
77 /* Pretend to have already waited for the
78 * thread; it makes life easier for the
86 static gboolean launch_threads_done(WaitQueueItem *item)
90 for(i=0; i<WAPI_HANDLE_COUNT; i++) {
91 if(item->waited[i]==FALSE) {
99 /* This is the main loop for the monitor thread. It waits for a
100 * signal to check the wait queue, then takes any action necessary on
101 * any queue items that have indicated readiness.
103 static void *wait_monitor_thread(void *unused G_GNUC_UNUSED)
107 /* Signal that the monitor thread is ready */
108 wait_monitor_thread_running=TRUE;
111 /* Use a semaphore rather than a cond so we dont miss
114 sem_wait(&wait_monitor_sem);
117 g_message(G_GNUC_PRETTY_FUNCTION
118 ": Blocking thread doing stuff");
121 /* We've been signalled, so scan the wait queue for
124 pthread_mutex_lock(&wait_monitor_mutex);
125 for(i=0; i<WaitQueue->len; i++) {
126 WaitQueueItem *item=g_ptr_array_index(WaitQueue, i);
128 if(item->update > item->ack) {
129 /* Something changed */
130 pthread_mutex_lock(&item->mutex);
131 item->ack=item->update;
133 switch(item->state) {
135 /* Launch a new thread for each type of
136 * handle to be waited for here.
138 launch_blocker_threads(item);
140 item->state=WQ_WAITING;
144 /* See if we have waited for
145 * the last blocker thread.
147 if(launch_threads_done(item)) {
159 item->state=WQ_SIGNALLED;
160 sem_post(&item->wait_sem);
165 /* This shouldn't happen. Prod
166 * the blocking thread again
169 g_warning(G_GNUC_PRETTY_FUNCTION
170 ": Prodding blocker again");
171 sem_post(&item->wait_sem);
175 pthread_mutex_unlock(&item->mutex);
179 pthread_mutex_unlock(&wait_monitor_mutex);
185 static void wait_init(void)
190 g_message(G_GNUC_PRETTY_FUNCTION ": Starting monitor thread");
193 WaitQueue=g_ptr_array_new();
195 sem_init(&wait_monitor_sem, 0, 0);
197 /* Launch a thread which manages the wait queue, and deals
198 * with waiting for handles of various types.
200 ret=pthread_create(&wait_monitor_thread_id, NULL,
201 wait_monitor_thread, NULL);
203 g_warning(G_GNUC_PRETTY_FUNCTION
204 ": Couldn't start handle monitor thread: %s",
208 /* Wait for the monitor thread to get going */
209 while(wait_monitor_thread_running==FALSE) {
214 static WaitQueueItem *wait_item_new(guint32 timeout, gboolean waitall)
219 new=g_new0(WaitQueueItem, 1);
221 pthread_mutex_init(&new->mutex, NULL);
222 sem_init(&new->wait_sem, 0, 0);
224 new->update=1; /* As soon as this item is queued it
225 * will need attention.
228 new->timeout=timeout;
229 new->waitall=waitall;
230 new->lowest_signal=MAXIMUM_WAIT_OBJECTS;
232 for(i=0; i<WAPI_HANDLE_COUNT; i++) {
233 new->handles[i]=g_ptr_array_new();
234 new->waitindex[i]=g_array_new(FALSE, FALSE, sizeof(guint32));
240 /* Adds our queue item to the work queue, and blocks until the monitor
241 * thread thinks it's done the work. Returns TRUE for done, FALSE for
242 * timed out. Sets lowest to the index of the first signalled handle
245 static gboolean wait_for_item(WaitQueueItem *item, guint32 *lowest)
250 /* Add the wait item to the monitor queue, and signal the
252 pthread_mutex_lock(&wait_monitor_mutex);
253 g_ptr_array_add(WaitQueue, item);
254 sem_post(&wait_monitor_sem);
255 pthread_mutex_unlock(&wait_monitor_mutex);
257 /* Wait for the item to become ready */
258 sem_wait(&item->wait_sem);
260 pthread_mutex_lock(&item->mutex);
262 /* If waitall is TRUE, then the number signalled in each handle type
263 * must be the length of the handle type array for the wait to be
264 * successful. Otherwise, any signalled handle is good enough
266 if(item->waitall==TRUE) {
268 for(i=0; i<WAPI_HANDLE_COUNT; i++) {
269 if(item->waitcount[i]!=item->handles[i]->len) {
276 for(i=0; i<WAPI_HANDLE_COUNT; i++) {
277 if(item->waitcount[i]>0) {
284 *lowest=item->lowest_signal;
286 pthread_mutex_unlock(&item->mutex);
291 static gboolean wait_dequeue_item(WaitQueueItem *item)
295 g_assert(WaitQueue!=NULL);
297 pthread_mutex_lock(&wait_monitor_mutex);
298 ret=g_ptr_array_remove_fast(WaitQueue, item);
299 pthread_mutex_unlock(&wait_monitor_mutex);
304 static void wait_item_destroy(WaitQueueItem *item)
308 pthread_mutex_destroy(&item->mutex);
309 sem_destroy(&item->wait_sem);
311 for(i=0; i<WAPI_HANDLE_COUNT; i++) {
312 if(item->thread[i]!=NULL) {
313 g_free(item->thread[i]);
315 g_ptr_array_free(item->handles[i], FALSE);
316 g_array_free(item->waitindex[i], FALSE);
322 * WaitForSingleObject:
323 * @handle: an object to wait for
324 * @timeout: the maximum time in milliseconds to wait for
326 * This function returns when either @handle is signalled, or @timeout
327 * ms elapses. If @timeout is zero, the object's state is tested and
328 * the function returns immediately. If @timeout is %INFINITE, the
329 * function waits forever.
331 * Return value: %WAIT_ABANDONED - @handle is a mutex that was not
332 * released by the owning thread when it exited. Ownership of the
333 * mutex object is granted to the calling thread and the mutex is set
334 * to nonsignalled. %WAIT_OBJECT_0 - The state of @handle is
335 * signalled. %WAIT_TIMEOUT - The @timeout interval elapsed and
336 * @handle's state is still not signalled. %WAIT_FAILED - an error
339 guint32 WaitForSingleObject(WapiHandle *handle, guint32 timeout)
343 if(handle->ops->wait==NULL) {
348 /* Just poll the object */
350 g_message(G_GNUC_PRETTY_FUNCTION ": Polling");
353 if(handle->signalled==TRUE) {
354 return(WAIT_OBJECT_0);
356 return(WAIT_TIMEOUT);
360 wait=handle->ops->wait(handle, timeout);
362 /* Object signalled before timeout expired */
364 g_message(G_GNUC_PRETTY_FUNCTION ": Object %p signalled",
367 return(WAIT_OBJECT_0);
370 g_message(G_GNUC_PRETTY_FUNCTION ": Object %p wait timed out",
373 return(WAIT_TIMEOUT);
378 * WaitForMultipleObjects:
379 * @numobjects: The number of objects in @handles. The maximum allowed
380 * is %MAXIMUM_WAIT_OBJECTS.
381 * @handles: An array of object handles. Duplicates are not allowed.
382 * @waitall: If %TRUE, this function waits until all of the handles
383 * are signalled. If %FALSE, this function returns when any object is
385 * @timeout: The maximum time in milliseconds to wait for.
387 * This function returns when either one or more of @handles is
388 * signalled, or @timeout ms elapses. If @timeout is zero, the state
389 * of each item of @handles is tested and the function returns
390 * immediately. If @timeout is %INFINITE, the function waits forever.
392 * Return value: %WAIT_OBJECT_0 to %WAIT_OBJECT_0 + @numobjects - 1 -
393 * if @waitall is %TRUE, indicates that all objects are signalled. If
394 * @waitall is %FALSE, the return value minus %WAIT_OBJECT_0 indicates
395 * the first index into @handles of the objects that are signalled.
396 * %WAIT_ABANDONED_0 to %WAIT_ABANDONED_0 + @numobjects - 1 - if
397 * @waitall is %TRUE, indicates that all objects are signalled, and at
398 * least one object is an abandoned mutex object (See
399 * WaitForSingleObject() for a description of abandoned mutexes.) If
400 * @waitall is %FALSE, the return value minus %WAIT_ABANDONED_0
401 * indicates the first index into @handles of an abandoned mutex.
402 * %WAIT_TIMEOUT - The @timeout interval elapsed and no objects in
403 * @handles are signalled. %WAIT_FAILED - an error occurred.
405 guint32 WaitForMultipleObjects(guint32 numobjects, WapiHandle **handles,
406 gboolean waitall, guint32 timeout)
410 gboolean duplicate=FALSE, bogustype=FALSE;
415 pthread_once(&wait_once, wait_init);
417 if(numobjects>MAXIMUM_WAIT_OBJECTS) {
419 g_message(G_GNUC_PRETTY_FUNCTION ": Too many handles: %d",
426 /* Check for duplicates */
427 dups=g_hash_table_new(g_direct_hash, g_direct_equal);
428 for(i=0; i<numobjects; i++) {
429 gpointer exists=g_hash_table_lookup(dups, handles[i]);
432 g_message(G_GNUC_PRETTY_FUNCTION
433 ": Handle %p duplicated", handles[i]);
440 if(handles[i]->ops->wait_multiple==NULL) {
442 g_message(G_GNUC_PRETTY_FUNCTION
443 ": Handle %p can't be waited for (type %d)",
444 handles[i], handles[i]->type);
450 g_hash_table_insert(dups, handles[i], handles[i]);
452 g_hash_table_destroy(dups);
454 if(duplicate==TRUE) {
456 g_message(G_GNUC_PRETTY_FUNCTION
457 ": Returning due to duplicates");
463 if(bogustype==TRUE) {
465 g_message(G_GNUC_PRETTY_FUNCTION
466 ": Returning due to bogus type");
472 item=wait_item_new(timeout, waitall);
474 /* Sort the handles by type */
475 for(i=0; i<numobjects; i++) {
476 g_ptr_array_add(item->handles[handles[i]->type], handles[i]);
477 g_array_append_val(item->waitindex[handles[i]->type], i);
480 wait=wait_for_item(item, &lowest);
481 wait_dequeue_item(item);
482 wait_item_destroy(item);
486 return(WAIT_TIMEOUT);
489 return(WAIT_OBJECT_0+lowest);