2006-05-31 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / metadata / monitor.c
1 /*
2  * monitor.c:  Monitor locking functions
3  *
4  * Author:
5  *      Dick Porter (dick@ximian.com)
6  *
7  * (C) 2003 Ximian, Inc.
8  */
9
10 #include <config.h>
11 #include <glib.h>
12
13 #include <mono/metadata/monitor.h>
14 #include <mono/metadata/threads-types.h>
15 #include <mono/metadata/exception.h>
16 #include <mono/metadata/threads.h>
17 #include <mono/io-layer/io-layer.h>
18 #include <mono/metadata/object-internals.h>
19 #include <mono/metadata/gc-internal.h>
20
21 /*#define LOCK_DEBUG(a) do { a; } while (0)*/
22 #define LOCK_DEBUG(a)
23
24 /*
25  * The monitor implementation here is based on
26  * http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf and
27  * http://www.research.ibm.com/people/d/dfb/papers/Bacon98Thin.ps
28  *
29  * The Dice paper describes a technique for saving lock record space
30  * by returning records to a free list when they become unused.  That
31  * sounds like unnecessary complexity to me, though if it becomes
32  * clear that unused lock records are taking up lots of space or we
33  * need to shave more time off by avoiding a malloc then we can always
34  * implement the free list idea later.  The timeout parameter to
35  * try_enter voids some of the assumptions about the reference count
36  * field in Dice's implementation too.  In his version, the thread
37  * attempting to lock a contended object will block until it succeeds,
38  * so the reference count will never be decremented while an object is
39  * locked.
40  *
41  * Bacon's thin locks have a fast path that doesn't need a lock record
42  * for the common case of locking an unlocked or shallow-nested
43  * object, but the technique relies on encoding the thread ID in 15
44  * bits (to avoid too much per-object space overhead.)  Unfortunately
45  * I don't think it's possible to reliably encode a pthread_t into 15
46  * bits. (The JVM implementation used seems to have a 15-bit
47  * per-thread identifier available.)
48  *
49  * This implementation then combines Dice's basic lock model with
50  * Bacon's simplification of keeping a lock record for the lifetime of
51  * an object.
52  */
53
54 struct _MonoThreadsSync
55 {
56         gsize owner;                    /* thread ID */
57         guint32 nest;
58 #ifdef HAVE_MOVING_COLLECTOR
59         gint32 hash_code;
60 #endif
61         volatile guint32 entry_count;
62         HANDLE entry_sem;
63         GSList *wait_list;
64         void *data;
65 };
66
67 typedef struct _MonitorArray MonitorArray;
68
69 struct _MonitorArray {
70         MonitorArray *next;
71         int num_monitors;
72         MonoThreadsSync monitors [MONO_ZERO_LEN_ARRAY];
73 };
74
75 #define mono_monitor_allocator_lock() EnterCriticalSection (&monitor_mutex)
76 #define mono_monitor_allocator_unlock() LeaveCriticalSection (&monitor_mutex)
77 static CRITICAL_SECTION monitor_mutex;
78 static MonoThreadsSync *monitor_freelist;
79 static MonitorArray *monitor_allocated;
80 static int array_size = 16;
81
82 void
83 mono_monitor_init (void)
84 {
85         InitializeCriticalSection (&monitor_mutex);
86 }
87
88 /* LOCKING: this is called with monitor_mutex held */
89 static void 
90 mon_finalize (MonoThreadsSync *mon)
91 {
92         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync %p", mon));
93
94         if (mon->entry_sem != NULL) {
95                 CloseHandle (mon->entry_sem);
96                 mon->entry_sem = NULL;
97         }
98         /* If this isn't empty then something is seriously broken - it
99          * means a thread is still waiting on the object that owned
100          * this lock, but the object has been finalized.
101          */
102         g_assert (mon->wait_list == NULL);
103
104         mon->entry_count = 0;
105         /* owner and nest are set in mon_new, no need to zero them out */
106
107         mon->data = monitor_freelist;
108         monitor_freelist = mon;
109 }
110
111 /* LOCKING: this is called with monitor_mutex held */
112 static MonoThreadsSync *
113 mon_new (gsize id)
114 {
115         MonoThreadsSync *new;
116
117         if (!monitor_freelist) {
118                 MonitorArray *marray;
119                 int i;
120                 /* see if any sync block has been collected */
121                 new = NULL;
122                 for (marray = monitor_allocated; marray; marray = marray->next) {
123                         for (i = 0; i < marray->num_monitors; ++i) {
124                                 if (marray->monitors [i].data == NULL) {
125                                         new = &marray->monitors [i];
126                                         new->data = monitor_freelist;
127                                         monitor_freelist = new;
128                                 }
129                         }
130                         /* small perf tweak to avoid scanning all the blocks */
131                         if (new)
132                                 break;
133                 }
134                 /* need to allocate a new array of monitors */
135                 if (!monitor_freelist) {
136                         MonitorArray *last;
137                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": allocating more monitors: %d", array_size));
138                         marray = g_malloc0 (sizeof (MonoArray) + array_size * sizeof (MonoThreadsSync));
139                         marray->num_monitors = array_size;
140                         array_size *= 2;
141                         /* link into the freelist */
142                         for (i = 0; i < marray->num_monitors - 1; ++i) {
143                                 marray->monitors [i].data = &marray->monitors [i + 1];
144                         }
145                         marray->monitors [i].data = NULL; /* the last one */
146                         monitor_freelist = &marray->monitors [0];
147                         /* we happend the marray instead of prepending so that
148                          * the collecting loop above will need to scan smaller arrays first
149                          */
150                         if (!monitor_allocated) {
151                                 monitor_allocated = marray;
152                         } else {
153                                 last = monitor_allocated;
154                                 while (last->next)
155                                         last = last->next;
156                                 last->next = marray;
157                         }
158                 }
159         }
160
161         new = monitor_freelist;
162         monitor_freelist = new->data;
163
164         new->owner = id;
165         new->nest = 1;
166         
167         return new;
168 }
169
170 /*
171  * Format of the lock word:
172  * thinhash | fathash | data
173  *
174  * thinhash is the lower bit: if set data is the shifted hashcode of the object.
175  * fathash is another bit: if set the hash code is stored in the MonoThreadsSync
176  *   struct pointed to by data
177  * if neither bit is set and data is non-NULL, data is a MonoThreadsSync
178  */
179 typedef union {
180         gsize lock_word;
181         MonoThreadsSync *sync;
182 } LockWord;
183
184 enum {
185         LOCK_WORD_THIN_HASH = 1,
186         LOCK_WORD_FAT_HASH = 1 << 1,
187         LOCK_WORD_BITS_MASK = 0x3,
188         LOCK_WORD_HASH_SHIFT = 2
189 };
190
191 #define MONO_OBJECT_ALIGNMENT_SHIFT     3
192
193 /*
194  * mono_object_hash:
195  * @obj: an object
196  *
197  * Calculate a hash code for @obj that is constant while @obj is alive.
198  */
199 int
200 mono_object_hash (MonoObject* obj)
201 {
202 #ifdef HAVE_MOVING_COLLECTOR
203         LockWord lw;
204         unsigned int hash;
205         if (!obj)
206                 return 0;
207         lw.sync = obj->synchronisation;
208         if (lw.lock_word & LOCK_WORD_THIN_HASH) {
209                 /*g_print ("fast thin hash %d for obj %p store\n", (unsigned int)lw.lock_word >> LOCK_WORD_HASH_SHIFT, obj);*/
210                 return (unsigned int)lw.lock_word >> LOCK_WORD_HASH_SHIFT;
211         }
212         if (lw.lock_word & LOCK_WORD_FAT_HASH) {
213                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
214                 /*g_print ("fast fat hash %d for obj %p store\n", lw.sync->hash_code, obj);*/
215                 return lw.sync->hash_code;
216         }
217         /*
218          * while we are inside this function, the GC will keep this object pinned,
219          * since we are in the unamanged stack. Thanks to this and to the hash
220          * function that depends only on the address, we can ignore the races if
221          * another thread computes the hash at the same time, because it'll end up
222          * with the same value.
223          */
224         hash = (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u;
225         /* clear the top bits as they can be discarded */
226         hash &= ~(LOCK_WORD_BITS_MASK << 30);
227         /* no hash flags were set, so it must be a MonoThreadsSync pointer if not NULL */
228         if (lw.sync) {
229                 lw.sync->hash_code = hash;
230                 /*g_print ("storing hash code %d for obj %p in sync %p\n", hash, obj, lw.sync);*/
231                 lw.lock_word |= LOCK_WORD_FAT_HASH;
232                 /* this is safe since we don't deflate locks */
233                 obj->synchronisation = lw.sync;
234         } else {
235                 /*g_print ("storing thin hash code %d for obj %p\n", hash, obj);*/
236                 lw.lock_word = LOCK_WORD_THIN_HASH | (hash << LOCK_WORD_HASH_SHIFT);
237                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, NULL) == NULL)
238                         return hash;
239                 /*g_print ("failed store\n");*/
240                 /* someone set the hash flag or someone inflated the object */
241                 lw.sync = obj->synchronisation;
242                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
243                         return hash;
244                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
245                 lw.sync->hash_code = hash;
246                 lw.lock_word |= LOCK_WORD_FAT_HASH;
247                 /* this is safe since we don't deflate locks */
248                 obj->synchronisation = lw.sync;
249         }
250         return hash;
251 #else
252 /*
253  * Wang's address-based hash function:
254  *   http://www.concentric.net/~Ttwang/tech/addrhash.htm
255  */
256         return (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u;
257 #endif
258 }
259
260 /* If allow_interruption==TRUE, the method will be interrumped if abort or suspend
261  * is requested. In this case it returns -1.
262  */ 
263 static gint32 
264 mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_interruption)
265 {
266         MonoThreadsSync *mon;
267         gsize id = GetCurrentThreadId ();
268         HANDLE sem;
269         guint32 then = 0, now, delta;
270         guint32 waitms;
271         guint32 ret;
272         
273         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
274                   ": (%d) Trying to lock object %p (%d ms)", id, obj, ms));
275
276 retry:
277         mon = obj->synchronisation;
278
279         /* If the object has never been locked... */
280         if (mon == NULL) {
281                 mono_monitor_allocator_lock ();
282                 mon = mon_new (id);
283                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL) == NULL) {
284                         mono_gc_weak_link_add (&mon->data, obj);
285                         mono_monitor_allocator_unlock ();
286                         /* Successfully locked */
287                         return 1;
288                 } else {
289 #ifdef HAVE_MOVING_COLLECTOR
290                         LockWord lw;
291                         lw.sync = obj->synchronisation;
292                         if (lw.lock_word & LOCK_WORD_THIN_HASH) {
293                                 MonoThreadsSync *oldlw = lw.sync;
294                                 /* move the already calculated hash */
295                                 mon->hash_code = lw.lock_word >> LOCK_WORD_HASH_SHIFT;
296                                 lw.sync = mon;
297                                 lw.lock_word |= LOCK_WORD_FAT_HASH;
298                                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) {
299                                         mono_gc_weak_link_add (&mon->data, obj);
300                                         mono_monitor_allocator_unlock ();
301                                         /* Successfully locked */
302                                         return 1;
303                                 } else {
304                                         mon_finalize (mon);
305                                         mono_monitor_allocator_unlock ();
306                                         goto retry;
307                                 }
308                         } else if (lw.lock_word & LOCK_WORD_FAT_HASH) {
309                                 mon_finalize (mon);
310                                 mono_monitor_allocator_unlock ();
311                                 /* get the old lock without the fat hash bit */
312                                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
313                                 mon = lw.sync;
314                         } else {
315                                 mon_finalize (mon);
316                                 mono_monitor_allocator_unlock ();
317                                 mon = obj->synchronisation;
318                         }
319 #else
320                         mon_finalize (mon);
321                         mono_monitor_allocator_unlock ();
322                         mon = obj->synchronisation;
323 #endif
324                 }
325         } else {
326 #ifdef HAVE_MOVING_COLLECTOR
327                 LockWord lw;
328                 lw.sync = mon;
329                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
330                         MonoThreadsSync *oldlw = lw.sync;
331                         mono_monitor_allocator_lock ();
332                         mon = mon_new (id);
333                         /* move the already calculated hash */
334                         mon->hash_code = lw.lock_word >> LOCK_WORD_HASH_SHIFT;
335                         lw.sync = mon;
336                         lw.lock_word |= LOCK_WORD_FAT_HASH;
337                         if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) {
338                                 mono_gc_weak_link_add (&mon->data, obj);
339                                 mono_monitor_allocator_unlock ();
340                                 /* Successfully locked */
341                                 return 1;
342                         } else {
343                                 mon_finalize (mon);
344                                 mono_monitor_allocator_unlock ();
345                                 goto retry;
346                         }
347                 }
348 #endif
349         }
350
351 #ifdef HAVE_MOVING_COLLECTOR
352         {
353                 LockWord lw;
354                 lw.sync = mon;
355                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
356                 mon = lw.sync;
357         }
358 #endif
359
360         /* If the object is currently locked by this thread... */
361         if (mon->owner == id) {
362                 mon->nest++;
363                 return 1;
364         }
365
366         /* If the object has previously been locked but isn't now... */
367
368         /* This case differs from Dice's case 3 because we don't
369          * deflate locks or cache unused lock records
370          */
371         if (mon->owner == 0) {
372                 /* Try to install our ID in the owner field, nest
373                  * should have been left at 1 by the previous unlock
374                  * operation
375                  */
376                 if (InterlockedCompareExchangePointer ((gpointer *)&mon->owner, (gpointer)id, 0) == 0) {
377                         /* Success */
378                         g_assert (mon->nest == 1);
379                         return 1;
380                 } else {
381                         /* Trumped again! */
382                         goto retry;
383                 }
384         }
385
386         /* The object must be locked by someone else... */
387
388         /* If ms is 0 we don't block, but just fail straight away */
389         if (ms == 0) {
390                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out, returning FALSE", id));
391                 return 0;
392         }
393
394         /* The slow path begins here.  We need to make sure theres a
395          * semaphore handle (creating it if necessary), and block on
396          * it
397          */
398         if (mon->entry_sem == NULL) {
399                 /* Create the semaphore */
400                 sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
401                 if (InterlockedCompareExchangePointer ((gpointer*)&mon->entry_sem, sem, NULL) != NULL) {
402                         /* Someone else just put a handle here */
403                         CloseHandle (sem);
404                 }
405         }
406
407         /* If we need to time out, record a timestamp and adjust ms,
408          * because WaitForSingleObject doesn't tell us how long it
409          * waited for.
410          *
411          * Don't block forever here, because theres a chance the owner
412          * thread released the lock while we were creating the
413          * semaphore: we would not get the wakeup.  Using the event
414          * handle technique from pulse/wait would involve locking the
415          * lock struct and therefore slowing down the fast path.
416          */
417         if (ms != INFINITE) {
418                 then = GetTickCount ();
419                 if (ms < 100) {
420                         waitms = ms;
421                 } else {
422                         waitms = 100;
423                 }
424         } else {
425                 waitms = 100;
426         }
427         
428         InterlockedIncrement (&mon->entry_count);
429         ret = WaitForSingleObjectEx (mon->entry_sem, waitms, allow_interruption);
430         InterlockedDecrement (&mon->entry_count);
431
432         if (ms != INFINITE) {
433                 now = GetTickCount ();
434                 
435                 if (now < then) {
436                         /* The counter must have wrapped around */
437                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
438                                    ": wrapped around! now=0x%x then=0x%x", now, then));
439                         
440                         now += (0xffffffff - then);
441                         then = 0;
442
443                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": wrap rejig: now=0x%x then=0x%x delta=0x%x", now, then, now-then));
444                 }
445                 
446                 delta = now - then;
447                 if (delta >= ms) {
448                         ms = 0;
449                 } else {
450                         ms -= delta;
451                 }
452
453                 if ((ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) && ms > 0) {
454                         /* More time left */
455                         goto retry;
456                 }
457         } else {
458                 if (ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) {
459                         /* Infinite wait, so just try again */
460                         goto retry;
461                 }
462         }
463         
464         if (ret == WAIT_OBJECT_0) {
465                 /* retry from the top */
466                 goto retry;
467         }
468         
469         /* We must have timed out */
470         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out waiting, returning FALSE", id));
471
472         if (ret == WAIT_IO_COMPLETION)
473                 return -1;
474         else 
475                 return 0;
476 }
477
478 gboolean 
479 mono_monitor_enter (MonoObject *obj)
480 {
481         return mono_monitor_try_enter_internal (obj, INFINITE, FALSE) == 1;
482 }
483
484 gboolean 
485 mono_monitor_try_enter (MonoObject *obj, guint32 ms)
486 {
487         return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1;
488 }
489
490 void 
491 mono_monitor_exit (MonoObject *obj)
492 {
493         MonoThreadsSync *mon;
494         guint32 nest;
495         
496         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p", GetCurrentThreadId (), obj));
497
498         mon = obj->synchronisation;
499
500 #ifdef HAVE_MOVING_COLLECTOR
501         {
502                 LockWord lw;
503                 lw.sync = mon;
504                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
505                         return;
506                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
507                 mon = lw.sync;
508         }
509 #endif
510         if (mon == NULL) {
511                 /* No one ever used Enter. Just ignore the Exit request as MS does */
512                 return;
513         }
514         if (mon->owner != GetCurrentThreadId ()) {
515                 return;
516         }
517         
518         nest = mon->nest - 1;
519         if (nest == 0) {
520                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
521                           ": (%d) Object %p is now unlocked", GetCurrentThreadId (), obj));
522         
523                 /* object is now unlocked, leave nest==1 so we don't
524                  * need to set it when the lock is reacquired
525                  */
526                 mon->owner = 0;
527
528                 /* Do the wakeup stuff.  It's possible that the last
529                  * blocking thread gave up waiting just before we
530                  * release the semaphore resulting in a futile wakeup
531                  * next time there's contention for this object, but
532                  * it means we don't have to waste time locking the
533                  * struct.
534                  */
535                 if (mon->entry_count > 0) {
536                         ReleaseSemaphore (mon->entry_sem, 1, NULL);
537                 }
538         } else {
539                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
540                           ": (%d) Object %p is now locked %d times", GetCurrentThreadId (), obj, nest));
541                 mon->nest = nest;
542         }
543 }
544
545 gboolean 
546 ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 ms)
547 {
548         gint32 res;
549
550         do {
551                 res = mono_monitor_try_enter_internal (obj, ms, TRUE);
552                 if (res == -1)
553                         mono_thread_interruption_checkpoint ();
554         } while (res == -1);
555         
556         return res == 1;
557 }
558
559 void 
560 ves_icall_System_Threading_Monitor_Monitor_exit (MonoObject *obj)
561 {
562         mono_monitor_exit (obj);
563 }
564
565 gboolean 
566 ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj)
567 {
568         MonoThreadsSync *mon;
569         
570         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
571                   ": Testing if %p is owned by thread %d", obj, GetCurrentThreadId()));
572
573         mon = obj->synchronisation;
574 #ifdef HAVE_MOVING_COLLECTOR
575         {
576                 LockWord lw;
577                 lw.sync = mon;
578                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
579                         return FALSE;
580                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
581                 mon = lw.sync;
582         }
583 #endif
584         if (mon == NULL) {
585                 return FALSE;
586         }
587         
588         if(mon->owner==GetCurrentThreadId ()) {
589                 return(TRUE);
590         }
591         
592         return(FALSE);
593 }
594
595 gboolean 
596 ves_icall_System_Threading_Monitor_Monitor_test_synchronised (MonoObject *obj)
597 {
598         MonoThreadsSync *mon;
599
600         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
601                   ": (%d) Testing if %p is owned by any thread", GetCurrentThreadId (), obj));
602         
603         mon = obj->synchronisation;
604 #ifdef HAVE_MOVING_COLLECTOR
605         {
606                 LockWord lw;
607                 lw.sync = mon;
608                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
609                         return FALSE;
610                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
611                 mon = lw.sync;
612         }
613 #endif
614         if (mon == NULL) {
615                 return FALSE;
616         }
617         
618         if (mon->owner != 0) {
619                 return TRUE;
620         }
621         
622         return FALSE;
623 }
624
625 /* All wait list manipulation in the pulse, pulseall and wait
626  * functions happens while the monitor lock is held, so we don't need
627  * any extra struct locking
628  */
629
630 void
631 ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj)
632 {
633         MonoThreadsSync *mon;
634         
635         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing %p", 
636                 GetCurrentThreadId (), obj));
637         
638         mon = obj->synchronisation;
639 #ifdef HAVE_MOVING_COLLECTOR
640         {
641                 LockWord lw;
642                 lw.sync = mon;
643                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
644                         mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
645                         return;
646                 }
647                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
648                 mon = lw.sync;
649         }
650 #endif
651         if (mon == NULL) {
652                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
653                 return;
654         }
655         if (mon->owner != GetCurrentThreadId ()) {
656                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
657                 return;
658         }
659
660         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting",
661                   GetCurrentThreadId (), g_slist_length (mon->wait_list)));
662         
663         if (mon->wait_list != NULL) {
664                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
665                           ": (%d) signalling and dequeuing handle %p",
666                           GetCurrentThreadId (), mon->wait_list->data));
667         
668                 SetEvent (mon->wait_list->data);
669                 mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
670         }
671 }
672
673 void
674 ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj)
675 {
676         MonoThreadsSync *mon;
677         
678         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing all %p",
679                   GetCurrentThreadId (), obj));
680
681         mon = obj->synchronisation;
682 #ifdef HAVE_MOVING_COLLECTOR
683         {
684                 LockWord lw;
685                 lw.sync = mon;
686                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
687                         mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
688                         return;
689                 }
690                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
691                 mon = lw.sync;
692         }
693 #endif
694         if (mon == NULL) {
695                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
696                 return;
697         }
698         if (mon->owner != GetCurrentThreadId ()) {
699                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
700                 return;
701         }
702
703         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting",
704                   GetCurrentThreadId (), g_slist_length (mon->wait_list)));
705
706         while (mon->wait_list != NULL) {
707                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
708                           ": (%d) signalling and dequeuing handle %p",
709                           GetCurrentThreadId (), mon->wait_list->data));
710         
711                 SetEvent (mon->wait_list->data);
712                 mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
713         }
714 }
715
716 gboolean
717 ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms)
718 {
719         MonoThreadsSync *mon;
720         HANDLE event;
721         guint32 nest;
722         guint32 ret;
723         gboolean success = FALSE;
724         gint32 regain;
725         MonoThread *thread = mono_thread_current ();
726
727         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
728                   ": (%d) Trying to wait for %p with timeout %dms",
729                   GetCurrentThreadId (), obj, ms));
730         
731         mon = obj->synchronisation;
732 #ifdef HAVE_MOVING_COLLECTOR
733         {
734                 LockWord lw;
735                 lw.sync = mon;
736                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
737                         mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
738                         return FALSE;
739                 }
740                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
741                 mon = lw.sync;
742         }
743 #endif
744         if (mon == NULL) {
745                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
746                 return FALSE;
747         }
748         if (mon->owner != GetCurrentThreadId ()) {
749                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
750                 return FALSE;
751         }
752         
753         event = CreateEvent (NULL, FALSE, FALSE, NULL);
754         if (event == NULL) {
755                 mono_raise_exception (mono_get_exception_synchronization_lock ("Failed to set up wait event"));
756                 return FALSE;
757         }
758         
759         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) queuing handle %p",
760                   GetCurrentThreadId (), event));
761
762         mono_monitor_enter (thread->synch_lock);
763         thread->state |= ThreadState_WaitSleepJoin;
764         mono_monitor_exit (thread->synch_lock);
765
766         mon->wait_list = g_slist_append (mon->wait_list, event);
767         
768         /* Save the nest count, and release the lock */
769         nest = mon->nest;
770         mon->nest = 1;
771         mono_monitor_exit (obj);
772
773         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocked %p lock %p",
774                   GetCurrentThreadId (), obj, mon));
775
776         /* There's no race between unlocking mon and waiting for the
777          * event, because auto reset events are sticky, and this event
778          * is private to this thread.  Therefore even if the event was
779          * signalled before we wait, we still succeed.
780          */
781         ret = WaitForSingleObjectEx (event, ms, TRUE);
782
783         /* Reset the thread state fairly early, so we don't have to worry
784          * about the monitor error checking
785          */
786         mono_monitor_enter (thread->synch_lock);
787         thread->state &= ~ThreadState_WaitSleepJoin;
788         mono_monitor_exit (thread->synch_lock);
789         
790         if (mono_thread_interruption_requested ()) {
791                 CloseHandle (event);
792                 return FALSE;
793         }
794
795         /* Regain the lock with the previous nest count */
796         do {
797                 regain = mono_monitor_try_enter_internal (obj, INFINITE, TRUE);
798                 if (regain == -1) 
799                         mono_thread_interruption_checkpoint ();
800         } while (regain == -1);
801
802         if (regain == 0) {
803                 /* Something went wrong, so throw a
804                  * SynchronizationLockException
805                  */
806                 CloseHandle (event);
807                 mono_raise_exception (mono_get_exception_synchronization_lock ("Failed to regain lock"));
808                 return FALSE;
809         }
810
811         mon->nest = nest;
812
813         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Regained %p lock %p",
814                   GetCurrentThreadId (), obj, mon));
815
816         if (ret == WAIT_TIMEOUT) {
817                 /* Poll the event again, just in case it was signalled
818                  * while we were trying to regain the monitor lock
819                  */
820                 ret = WaitForSingleObjectEx (event, 0, FALSE);
821         }
822
823         /* Pulse will have popped our event from the queue if it signalled
824          * us, so we only do it here if the wait timed out.
825          *
826          * This avoids a race condition where the thread holding the
827          * lock can Pulse several times before the WaitForSingleObject
828          * returns.  If we popped the queue here then this event might
829          * be signalled more than once, thereby starving another
830          * thread.
831          */
832         
833         if (ret == WAIT_OBJECT_0) {
834                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Success",
835                           GetCurrentThreadId ()));
836                 success = TRUE;
837         } else {
838                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed, dequeuing handle %p",
839                           GetCurrentThreadId (), event));
840                 /* No pulse, so we have to remove ourself from the
841                  * wait queue
842                  */
843                 mon->wait_list = g_slist_remove (mon->wait_list, event);
844         }
845         CloseHandle (event);
846         
847         return success;
848 }
849