2009-02-19 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  * Copyright 2003 Ximian, Inc (http://www.ximian.com)
8  * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
9  */
10
11 #include <config.h>
12 #include <glib.h>
13 #include <string.h>
14
15 #include <mono/metadata/monitor.h>
16 #include <mono/metadata/threads-types.h>
17 #include <mono/metadata/exception.h>
18 #include <mono/metadata/threads.h>
19 #include <mono/io-layer/io-layer.h>
20 #include <mono/metadata/object-internals.h>
21 #include <mono/metadata/class-internals.h>
22 #include <mono/metadata/gc-internal.h>
23 #include <mono/metadata/method-builder.h>
24 #include <mono/metadata/debug-helpers.h>
25 #include <mono/metadata/tabledefs.h>
26 #include <mono/metadata/marshal.h>
27 #include <mono/utils/mono-time.h>
28
29 /*
30  * Pull the list of opcodes
31  */
32 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
33         a = i,
34
35 enum {
36 #include "mono/cil/opcode.def"
37         LAST = 0xff
38 };
39 #undef OPDEF
40
41 /*#define LOCK_DEBUG(a) do { a; } while (0)*/
42 #define LOCK_DEBUG(a)
43
44 /*
45  * The monitor implementation here is based on
46  * http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf and
47  * http://www.research.ibm.com/people/d/dfb/papers/Bacon98Thin.ps
48  *
49  * The Dice paper describes a technique for saving lock record space
50  * by returning records to a free list when they become unused.  That
51  * sounds like unnecessary complexity to me, though if it becomes
52  * clear that unused lock records are taking up lots of space or we
53  * need to shave more time off by avoiding a malloc then we can always
54  * implement the free list idea later.  The timeout parameter to
55  * try_enter voids some of the assumptions about the reference count
56  * field in Dice's implementation too.  In his version, the thread
57  * attempting to lock a contended object will block until it succeeds,
58  * so the reference count will never be decremented while an object is
59  * locked.
60  *
61  * Bacon's thin locks have a fast path that doesn't need a lock record
62  * for the common case of locking an unlocked or shallow-nested
63  * object, but the technique relies on encoding the thread ID in 15
64  * bits (to avoid too much per-object space overhead.)  Unfortunately
65  * I don't think it's possible to reliably encode a pthread_t into 15
66  * bits. (The JVM implementation used seems to have a 15-bit
67  * per-thread identifier available.)
68  *
69  * This implementation then combines Dice's basic lock model with
70  * Bacon's simplification of keeping a lock record for the lifetime of
71  * an object.
72  */
73
74 struct _MonoThreadsSync
75 {
76         gsize owner;                    /* thread ID */
77         guint32 nest;
78 #ifdef HAVE_MOVING_COLLECTOR
79         gint32 hash_code;
80 #endif
81         volatile gint32 entry_count;
82         HANDLE entry_sem;
83         GSList *wait_list;
84         void *data;
85 };
86
87 typedef struct _MonitorArray MonitorArray;
88
89 struct _MonitorArray {
90         MonitorArray *next;
91         int num_monitors;
92         MonoThreadsSync monitors [MONO_ZERO_LEN_ARRAY];
93 };
94
95 #define mono_monitor_allocator_lock() EnterCriticalSection (&monitor_mutex)
96 #define mono_monitor_allocator_unlock() LeaveCriticalSection (&monitor_mutex)
97 static CRITICAL_SECTION monitor_mutex;
98 static MonoThreadsSync *monitor_freelist;
99 static MonitorArray *monitor_allocated;
100 static int array_size = 16;
101
102 #ifdef HAVE_KW_THREAD
103 static __thread gsize tls_pthread_self MONO_TLS_FAST;
104 #endif
105
106 #ifndef PLATFORM_WIN32
107 #ifdef HAVE_KW_THREAD
108 #define GetCurrentThreadId() tls_pthread_self
109 #else
110 /* 
111  * The usual problem: we can't replace GetCurrentThreadId () with a macro because
112  * it is in a public header.
113  */
114 #define GetCurrentThreadId() ((gsize)pthread_self ())
115 #endif
116 #endif
117
118 void
119 mono_monitor_init (void)
120 {
121         InitializeCriticalSection (&monitor_mutex);
122 }
123  
124 void
125 mono_monitor_cleanup (void)
126 {
127         /*DeleteCriticalSection (&monitor_mutex);*/
128 }
129
130 /*
131  * mono_monitor_init_tls:
132  *
133  *   Setup TLS variables used by the monitor code for the current thread.
134  */
135 void
136 mono_monitor_init_tls (void)
137 {
138 #if !defined(PLATFORM_WIN32) && defined(HAVE_KW_THREAD)
139         tls_pthread_self = pthread_self ();
140 #endif
141 }
142
143 static int
144 monitor_is_on_freelist (MonoThreadsSync *mon)
145 {
146         MonitorArray *marray;
147         for (marray = monitor_allocated; marray; marray = marray->next) {
148                 if (mon >= marray->monitors && mon < &marray->monitors [marray->num_monitors])
149                         return TRUE;
150         }
151         return FALSE;
152 }
153
154 /**
155  * mono_locks_dump:
156  * @include_untaken:
157  *
158  * Print a report on stdout of the managed locks currently held by
159  * threads. If @include_untaken is specified, list also inflated locks
160  * which are unheld.
161  * This is supposed to be used in debuggers like gdb.
162  */
163 void
164 mono_locks_dump (gboolean include_untaken)
165 {
166         int i;
167         int used = 0, on_freelist = 0, to_recycle = 0, total = 0, num_arrays = 0;
168         MonoThreadsSync *mon;
169         MonitorArray *marray;
170         for (mon = monitor_freelist; mon; mon = mon->data)
171                 on_freelist++;
172         for (marray = monitor_allocated; marray; marray = marray->next) {
173                 total += marray->num_monitors;
174                 num_arrays++;
175                 for (i = 0; i < marray->num_monitors; ++i) {
176                         mon = &marray->monitors [i];
177                         if (mon->data == NULL) {
178                                 if (i < marray->num_monitors - 1)
179                                         to_recycle++;
180                         } else {
181                                 if (!monitor_is_on_freelist (mon->data)) {
182                                         MonoObject *holder = mono_gc_weak_link_get (&mon->data);
183                                         if (mon->owner) {
184                                                 g_print ("Lock %p in object %p held by thread %p, nest level: %d\n",
185                                                         mon, holder, (void*)mon->owner, mon->nest);
186                                                 if (mon->entry_sem)
187                                                         g_print ("\tWaiting on semaphore %p: %d\n", mon->entry_sem, mon->entry_count);
188                                         } else if (include_untaken) {
189                                                 g_print ("Lock %p in object %p untaken\n", mon, holder);
190                                         }
191                                         used++;
192                                 }
193                         }
194                 }
195         }
196         g_print ("Total locks (in %d array(s)): %d, used: %d, on freelist: %d, to recycle: %d\n",
197                 num_arrays, total, used, on_freelist, to_recycle);
198 }
199
200 /* LOCKING: this is called with monitor_mutex held */
201 static void 
202 mon_finalize (MonoThreadsSync *mon)
203 {
204         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync %p", mon));
205
206         if (mon->entry_sem != NULL) {
207                 CloseHandle (mon->entry_sem);
208                 mon->entry_sem = NULL;
209         }
210         /* If this isn't empty then something is seriously broken - it
211          * means a thread is still waiting on the object that owned
212          * this lock, but the object has been finalized.
213          */
214         g_assert (mon->wait_list == NULL);
215
216         mon->entry_count = 0;
217         /* owner and nest are set in mon_new, no need to zero them out */
218
219         mon->data = monitor_freelist;
220         monitor_freelist = mon;
221         mono_perfcounters->gc_sync_blocks--;
222 }
223
224 /* LOCKING: this is called with monitor_mutex held */
225 static MonoThreadsSync *
226 mon_new (gsize id)
227 {
228         MonoThreadsSync *new;
229
230         if (!monitor_freelist) {
231                 MonitorArray *marray;
232                 int i;
233                 /* see if any sync block has been collected */
234                 new = NULL;
235                 for (marray = monitor_allocated; marray; marray = marray->next) {
236                         for (i = 0; i < marray->num_monitors; ++i) {
237                                 if (marray->monitors [i].data == NULL) {
238                                         new = &marray->monitors [i];
239                                         new->data = monitor_freelist;
240                                         monitor_freelist = new;
241                                 }
242                         }
243                         /* small perf tweak to avoid scanning all the blocks */
244                         if (new)
245                                 break;
246                 }
247                 /* need to allocate a new array of monitors */
248                 if (!monitor_freelist) {
249                         MonitorArray *last;
250                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": allocating more monitors: %d", array_size));
251                         marray = g_malloc0 (sizeof (MonoArray) + array_size * sizeof (MonoThreadsSync));
252                         marray->num_monitors = array_size;
253                         array_size *= 2;
254                         /* link into the freelist */
255                         for (i = 0; i < marray->num_monitors - 1; ++i) {
256                                 marray->monitors [i].data = &marray->monitors [i + 1];
257                         }
258                         marray->monitors [i].data = NULL; /* the last one */
259                         monitor_freelist = &marray->monitors [0];
260                         /* we happend the marray instead of prepending so that
261                          * the collecting loop above will need to scan smaller arrays first
262                          */
263                         if (!monitor_allocated) {
264                                 monitor_allocated = marray;
265                         } else {
266                                 last = monitor_allocated;
267                                 while (last->next)
268                                         last = last->next;
269                                 last->next = marray;
270                         }
271                 }
272         }
273
274         new = monitor_freelist;
275         monitor_freelist = new->data;
276
277         new->owner = id;
278         new->nest = 1;
279         
280         mono_perfcounters->gc_sync_blocks++;
281         return new;
282 }
283
284 /*
285  * Format of the lock word:
286  * thinhash | fathash | data
287  *
288  * thinhash is the lower bit: if set data is the shifted hashcode of the object.
289  * fathash is another bit: if set the hash code is stored in the MonoThreadsSync
290  *   struct pointed to by data
291  * if neither bit is set and data is non-NULL, data is a MonoThreadsSync
292  */
293 typedef union {
294         gsize lock_word;
295         MonoThreadsSync *sync;
296 } LockWord;
297
298 enum {
299         LOCK_WORD_THIN_HASH = 1,
300         LOCK_WORD_FAT_HASH = 1 << 1,
301         LOCK_WORD_BITS_MASK = 0x3,
302         LOCK_WORD_HASH_SHIFT = 2
303 };
304
305 #define MONO_OBJECT_ALIGNMENT_SHIFT     3
306
307 /*
308  * mono_object_hash:
309  * @obj: an object
310  *
311  * Calculate a hash code for @obj that is constant while @obj is alive.
312  */
313 int
314 mono_object_hash (MonoObject* obj)
315 {
316 #ifdef HAVE_MOVING_COLLECTOR
317         LockWord lw;
318         unsigned int hash;
319         if (!obj)
320                 return 0;
321         lw.sync = obj->synchronisation;
322         if (lw.lock_word & LOCK_WORD_THIN_HASH) {
323                 /*g_print ("fast thin hash %d for obj %p store\n", (unsigned int)lw.lock_word >> LOCK_WORD_HASH_SHIFT, obj);*/
324                 return (unsigned int)lw.lock_word >> LOCK_WORD_HASH_SHIFT;
325         }
326         if (lw.lock_word & LOCK_WORD_FAT_HASH) {
327                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
328                 /*g_print ("fast fat hash %d for obj %p store\n", lw.sync->hash_code, obj);*/
329                 return lw.sync->hash_code;
330         }
331         /*
332          * while we are inside this function, the GC will keep this object pinned,
333          * since we are in the unmanaged stack. Thanks to this and to the hash
334          * function that depends only on the address, we can ignore the races if
335          * another thread computes the hash at the same time, because it'll end up
336          * with the same value.
337          */
338         hash = (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u;
339         /* clear the top bits as they can be discarded */
340         hash &= ~(LOCK_WORD_BITS_MASK << 30);
341         /* no hash flags were set, so it must be a MonoThreadsSync pointer if not NULL */
342         if (lw.sync) {
343                 lw.sync->hash_code = hash;
344                 /*g_print ("storing hash code %d for obj %p in sync %p\n", hash, obj, lw.sync);*/
345                 lw.lock_word |= LOCK_WORD_FAT_HASH;
346                 /* this is safe since we don't deflate locks */
347                 obj->synchronisation = lw.sync;
348         } else {
349                 /*g_print ("storing thin hash code %d for obj %p\n", hash, obj);*/
350                 lw.lock_word = LOCK_WORD_THIN_HASH | (hash << LOCK_WORD_HASH_SHIFT);
351                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, NULL) == NULL)
352                         return hash;
353                 /*g_print ("failed store\n");*/
354                 /* someone set the hash flag or someone inflated the object */
355                 lw.sync = obj->synchronisation;
356                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
357                         return hash;
358                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
359                 lw.sync->hash_code = hash;
360                 lw.lock_word |= LOCK_WORD_FAT_HASH;
361                 /* this is safe since we don't deflate locks */
362                 obj->synchronisation = lw.sync;
363         }
364         return hash;
365 #else
366 /*
367  * Wang's address-based hash function:
368  *   http://www.concentric.net/~Ttwang/tech/addrhash.htm
369  */
370         return (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u;
371 #endif
372 }
373
374 /* If allow_interruption==TRUE, the method will be interrumped if abort or suspend
375  * is requested. In this case it returns -1.
376  */ 
377 static inline gint32 
378 mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_interruption)
379 {
380         MonoThreadsSync *mon;
381         gsize id = GetCurrentThreadId ();
382         HANDLE sem;
383         guint32 then = 0, now, delta;
384         guint32 waitms;
385         guint32 ret;
386         MonoThread *thread;
387
388         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
389                   ": (%d) Trying to lock object %p (%d ms)", id, obj, ms));
390
391         if (G_UNLIKELY (!obj)) {
392                 mono_raise_exception (mono_get_exception_argument_null ("obj"));
393                 return FALSE;
394         }
395
396 retry:
397         mon = obj->synchronisation;
398
399         /* If the object has never been locked... */
400         if (G_UNLIKELY (mon == NULL)) {
401                 mono_monitor_allocator_lock ();
402                 mon = mon_new (id);
403                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL) == NULL) {
404                         mono_gc_weak_link_add (&mon->data, obj);
405                         mono_monitor_allocator_unlock ();
406                         /* Successfully locked */
407                         return 1;
408                 } else {
409 #ifdef HAVE_MOVING_COLLECTOR
410                         LockWord lw;
411                         lw.sync = obj->synchronisation;
412                         if (lw.lock_word & LOCK_WORD_THIN_HASH) {
413                                 MonoThreadsSync *oldlw = lw.sync;
414                                 /* move the already calculated hash */
415                                 mon->hash_code = lw.lock_word >> LOCK_WORD_HASH_SHIFT;
416                                 lw.sync = mon;
417                                 lw.lock_word |= LOCK_WORD_FAT_HASH;
418                                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) {
419                                         mono_gc_weak_link_add (&mon->data, obj);
420                                         mono_monitor_allocator_unlock ();
421                                         /* Successfully locked */
422                                         return 1;
423                                 } else {
424                                         mon_finalize (mon);
425                                         mono_monitor_allocator_unlock ();
426                                         goto retry;
427                                 }
428                         } else if (lw.lock_word & LOCK_WORD_FAT_HASH) {
429                                 mon_finalize (mon);
430                                 mono_monitor_allocator_unlock ();
431                                 /* get the old lock without the fat hash bit */
432                                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
433                                 mon = lw.sync;
434                         } else {
435                                 mon_finalize (mon);
436                                 mono_monitor_allocator_unlock ();
437                                 mon = obj->synchronisation;
438                         }
439 #else
440                         mon_finalize (mon);
441                         mono_monitor_allocator_unlock ();
442                         mon = obj->synchronisation;
443 #endif
444                 }
445         } else {
446 #ifdef HAVE_MOVING_COLLECTOR
447                 LockWord lw;
448                 lw.sync = mon;
449                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
450                         MonoThreadsSync *oldlw = lw.sync;
451                         mono_monitor_allocator_lock ();
452                         mon = mon_new (id);
453                         /* move the already calculated hash */
454                         mon->hash_code = lw.lock_word >> LOCK_WORD_HASH_SHIFT;
455                         lw.sync = mon;
456                         lw.lock_word |= LOCK_WORD_FAT_HASH;
457                         if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) {
458                                 mono_gc_weak_link_add (&mon->data, obj);
459                                 mono_monitor_allocator_unlock ();
460                                 /* Successfully locked */
461                                 return 1;
462                         } else {
463                                 mon_finalize (mon);
464                                 mono_monitor_allocator_unlock ();
465                                 goto retry;
466                         }
467                 }
468 #endif
469         }
470
471 #ifdef HAVE_MOVING_COLLECTOR
472         {
473                 LockWord lw;
474                 lw.sync = mon;
475                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
476                 mon = lw.sync;
477         }
478 #endif
479
480         /* If the object has previously been locked but isn't now... */
481
482         /* This case differs from Dice's case 3 because we don't
483          * deflate locks or cache unused lock records
484          */
485         if (G_LIKELY (mon->owner == 0)) {
486                 /* Try to install our ID in the owner field, nest
487                  * should have been left at 1 by the previous unlock
488                  * operation
489                  */
490                 if (G_LIKELY (InterlockedCompareExchangePointer ((gpointer *)&mon->owner, (gpointer)id, 0) == 0)) {
491                         /* Success */
492                         g_assert (mon->nest == 1);
493                         return 1;
494                 } else {
495                         /* Trumped again! */
496                         goto retry;
497                 }
498         }
499
500         /* If the object is currently locked by this thread... */
501         if (mon->owner == id) {
502                 mon->nest++;
503                 return 1;
504         }
505
506         /* The object must be locked by someone else... */
507         mono_perfcounters->thread_contentions++;
508
509         /* If ms is 0 we don't block, but just fail straight away */
510         if (ms == 0) {
511                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out, returning FALSE", id));
512                 return 0;
513         }
514
515         /* The slow path begins here.  We need to make sure theres a
516          * semaphore handle (creating it if necessary), and block on
517          * it
518          */
519         if (mon->entry_sem == NULL) {
520                 /* Create the semaphore */
521                 sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
522                 g_assert (sem != NULL);
523                 if (InterlockedCompareExchangePointer ((gpointer*)&mon->entry_sem, sem, NULL) != NULL) {
524                         /* Someone else just put a handle here */
525                         CloseHandle (sem);
526                 }
527         }
528         
529         /* If we need to time out, record a timestamp and adjust ms,
530          * because WaitForSingleObject doesn't tell us how long it
531          * waited for.
532          *
533          * Don't block forever here, because theres a chance the owner
534          * thread released the lock while we were creating the
535          * semaphore: we would not get the wakeup.  Using the event
536          * handle technique from pulse/wait would involve locking the
537          * lock struct and therefore slowing down the fast path.
538          */
539         if (ms != INFINITE) {
540                 then = mono_msec_ticks ();
541                 if (ms < 100) {
542                         waitms = ms;
543                 } else {
544                         waitms = 100;
545                 }
546         } else {
547                 waitms = 100;
548         }
549         
550         InterlockedIncrement (&mon->entry_count);
551
552         mono_perfcounters->thread_queue_len++;
553         mono_perfcounters->thread_queue_max++;
554         thread = mono_thread_current ();
555
556         mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
557
558         /*
559          * We pass TRUE instead of allow_interruption since we have to check for the
560          * StopRequested case below.
561          */
562         ret = WaitForSingleObjectEx (mon->entry_sem, waitms, TRUE);
563
564         mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
565         
566         InterlockedDecrement (&mon->entry_count);
567         mono_perfcounters->thread_queue_len--;
568
569         if (ms != INFINITE) {
570                 now = mono_msec_ticks ();
571                 
572                 if (now < then) {
573                         /* The counter must have wrapped around */
574                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
575                                    ": wrapped around! now=0x%x then=0x%x", now, then));
576                         
577                         now += (0xffffffff - then);
578                         then = 0;
579
580                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": wrap rejig: now=0x%x then=0x%x delta=0x%x", now, then, now-then));
581                 }
582                 
583                 delta = now - then;
584                 if (delta >= ms) {
585                         ms = 0;
586                 } else {
587                         ms -= delta;
588                 }
589
590                 if ((ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) && ms > 0) {
591                         /* More time left */
592                         goto retry;
593                 }
594         } else {
595                 if (ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) {
596                         if (ret == WAIT_IO_COMPLETION && mono_thread_test_state (mono_thread_current (), ThreadState_StopRequested)) {
597                                 /* 
598                                  * We have to obey a stop request even if allow_interruption is
599                                  * FALSE to avoid hangs at shutdown.
600                                  */
601                                 return -1;
602                         }
603                         /* Infinite wait, so just try again */
604                         goto retry;
605                 }
606         }
607         
608         if (ret == WAIT_OBJECT_0) {
609                 /* retry from the top */
610                 goto retry;
611         }
612         
613         /* We must have timed out */
614         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out waiting, returning FALSE", id));
615
616         if (ret == WAIT_IO_COMPLETION)
617                 return -1;
618         else 
619                 return 0;
620 }
621
622 gboolean 
623 mono_monitor_enter (MonoObject *obj)
624 {
625         return mono_monitor_try_enter_internal (obj, INFINITE, FALSE) == 1;
626 }
627
628 gboolean 
629 mono_monitor_try_enter (MonoObject *obj, guint32 ms)
630 {
631         return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1;
632 }
633
634 void
635 mono_monitor_exit (MonoObject *obj)
636 {
637         MonoThreadsSync *mon;
638         guint32 nest;
639         
640         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p", GetCurrentThreadId (), obj));
641
642         if (G_UNLIKELY (!obj)) {
643                 mono_raise_exception (mono_get_exception_argument_null ("obj"));
644                 return;
645         }
646
647         mon = obj->synchronisation;
648
649 #ifdef HAVE_MOVING_COLLECTOR
650         {
651                 LockWord lw;
652                 lw.sync = mon;
653                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
654                         return;
655                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
656                 mon = lw.sync;
657         }
658 #endif
659         if (G_UNLIKELY (mon == NULL)) {
660                 /* No one ever used Enter. Just ignore the Exit request as MS does */
661                 return;
662         }
663         if (G_UNLIKELY (mon->owner != GetCurrentThreadId ())) {
664                 return;
665         }
666         
667         nest = mon->nest - 1;
668         if (nest == 0) {
669                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
670                           ": (%d) Object %p is now unlocked", GetCurrentThreadId (), obj));
671         
672                 /* object is now unlocked, leave nest==1 so we don't
673                  * need to set it when the lock is reacquired
674                  */
675                 mon->owner = 0;
676
677                 /* Do the wakeup stuff.  It's possible that the last
678                  * blocking thread gave up waiting just before we
679                  * release the semaphore resulting in a futile wakeup
680                  * next time there's contention for this object, but
681                  * it means we don't have to waste time locking the
682                  * struct.
683                  */
684                 if (mon->entry_count > 0) {
685                         ReleaseSemaphore (mon->entry_sem, 1, NULL);
686                 }
687         } else {
688                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
689                           ": (%d) Object %p is now locked %d times", GetCurrentThreadId (), obj, nest));
690                 mon->nest = nest;
691         }
692 }
693
694 static void
695 emit_obj_syncp_check (MonoMethodBuilder *mb, int syncp_loc, int *obj_null_branch, int *syncp_true_false_branch,
696         gboolean branch_on_true)
697 {
698         /*
699           ldarg         0                                                       obj
700           brfalse.s     obj_null
701         */
702
703         mono_mb_emit_byte (mb, CEE_LDARG_0);
704         *obj_null_branch = mono_mb_emit_short_branch (mb, CEE_BRFALSE_S);
705
706         /*
707           ldarg         0                                                       obj
708           conv.i                                                                objp
709           ldc.i4        G_STRUCT_OFFSET(MonoObject, synchronisation)            objp off
710           add                                                                   &syncp
711           ldind.i                                                               syncp
712           stloc         syncp
713           ldloc         syncp                                                   syncp
714           brtrue/false.s        syncp_true_false
715         */
716
717         mono_mb_emit_byte (mb, CEE_LDARG_0);
718         mono_mb_emit_byte (mb, CEE_CONV_I);
719         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoObject, synchronisation));
720         mono_mb_emit_byte (mb, CEE_ADD);
721         mono_mb_emit_byte (mb, CEE_LDIND_I);
722         mono_mb_emit_stloc (mb, syncp_loc);
723         mono_mb_emit_ldloc (mb, syncp_loc);
724         *syncp_true_false_branch = mono_mb_emit_short_branch (mb, branch_on_true ? CEE_BRTRUE_S : CEE_BRFALSE_S);
725 }
726
727 static MonoMethod*
728 mono_monitor_get_fast_enter_method (MonoMethod *monitor_enter_method)
729 {
730         static MonoMethod *fast_monitor_enter;
731         static MonoMethod *compare_exchange_method;
732
733         MonoMethodBuilder *mb;
734         int obj_null_branch, syncp_null_branch, has_owner_branch, other_owner_branch, tid_branch;
735         int tid_loc, syncp_loc, owner_loc;
736         int thread_tls_offset;
737
738 #ifdef HAVE_MOVING_COLLECTOR
739         return NULL;
740 #endif
741
742         thread_tls_offset = mono_thread_get_tls_offset ();
743         if (thread_tls_offset == -1)
744                 return NULL;
745
746         if (fast_monitor_enter)
747                 return fast_monitor_enter;
748
749         if (!compare_exchange_method) {
750                 MonoMethodDesc *desc;
751                 MonoClass *class;
752
753                 desc = mono_method_desc_new ("Interlocked:CompareExchange(intptr&,intptr,intptr)", FALSE);
754                 class = mono_class_from_name (mono_defaults.corlib, "System.Threading", "Interlocked");
755                 compare_exchange_method = mono_method_desc_search_in_class (desc, class);
756                 mono_method_desc_free (desc);
757
758                 if (!compare_exchange_method)
759                         return NULL;
760         }
761
762         mb = mono_mb_new (mono_defaults.monitor_class, "FastMonitorEnter", MONO_WRAPPER_UNKNOWN);
763
764         mb->method->slot = -1;
765         mb->method->flags = METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_STATIC |
766                 METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_FINAL;
767
768         tid_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
769         syncp_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
770         owner_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
771
772         emit_obj_syncp_check (mb, syncp_loc, &obj_null_branch, &syncp_null_branch, FALSE);
773
774         /*
775           mono. tls     thread_tls_offset                                       threadp
776           ldc.i4        G_STRUCT_OFFSET(MonoThread, tid)                        threadp off
777           add                                                                   &tid
778           ldind.i                                                               tid
779           stloc         tid
780           ldloc         syncp                                                   syncp
781           ldc.i4        G_STRUCT_OFFSET(MonoThreadsSync, owner)                 syncp off
782           add                                                                   &owner
783           ldind.i                                                               owner
784           stloc         owner
785           ldloc         owner                                                   owner
786           brtrue.s      tid
787         */
788
789         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
790         mono_mb_emit_byte (mb, CEE_MONO_TLS);
791         mono_mb_emit_i4 (mb, thread_tls_offset);
792         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThread, tid));
793         mono_mb_emit_byte (mb, CEE_ADD);
794         mono_mb_emit_byte (mb, CEE_LDIND_I);
795         mono_mb_emit_stloc (mb, tid_loc);
796         mono_mb_emit_ldloc (mb, syncp_loc);
797         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
798         mono_mb_emit_byte (mb, CEE_ADD);
799         mono_mb_emit_byte (mb, CEE_LDIND_I);
800         mono_mb_emit_stloc (mb, owner_loc);
801         mono_mb_emit_ldloc (mb, owner_loc);
802         tid_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
803
804         /*
805           ldloc         syncp                                                   syncp
806           ldc.i4        G_STRUCT_OFFSET(MonoThreadsSync, owner)                 syncp off
807           add                                                                   &owner
808           ldloc         tid                                                     &owner tid
809           ldc.i4        0                                                       &owner tid 0
810           call          System.Threading.Interlocked.CompareExchange            oldowner
811           brtrue.s      has_owner
812           ret
813         */
814
815         mono_mb_emit_ldloc (mb, syncp_loc);
816         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
817         mono_mb_emit_byte (mb, CEE_ADD);
818         mono_mb_emit_ldloc (mb, tid_loc);
819         mono_mb_emit_byte (mb, CEE_LDC_I4_0);
820         mono_mb_emit_managed_call (mb, compare_exchange_method, NULL);
821         has_owner_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
822         mono_mb_emit_byte (mb, CEE_RET);
823
824         /*
825          tid:
826           ldloc         owner                                                   owner
827           ldloc         tid                                                     owner tid
828           brne.s        other_owner
829           ldloc         syncp                                                   syncp
830           ldc.i4        G_STRUCT_OFFSET(MonoThreadsSync, nest)                  syncp off
831           add                                                                   &nest
832           dup                                                                   &nest &nest
833           ldind.i4                                                              &nest nest
834           ldc.i4        1                                                       &nest nest 1
835           add                                                                   &nest nest+
836           stind.i4
837           ret
838         */
839
840         mono_mb_patch_short_branch (mb, tid_branch);
841         mono_mb_emit_ldloc (mb, owner_loc);
842         mono_mb_emit_ldloc (mb, tid_loc);
843         other_owner_branch = mono_mb_emit_short_branch (mb, CEE_BNE_UN_S);
844         mono_mb_emit_ldloc (mb, syncp_loc);
845         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, nest));
846         mono_mb_emit_byte (mb, CEE_ADD);
847         mono_mb_emit_byte (mb, CEE_DUP);
848         mono_mb_emit_byte (mb, CEE_LDIND_I4);
849         mono_mb_emit_byte (mb, CEE_LDC_I4_1);
850         mono_mb_emit_byte (mb, CEE_ADD);
851         mono_mb_emit_byte (mb, CEE_STIND_I4);
852         mono_mb_emit_byte (mb, CEE_RET);
853
854         /*
855          obj_null, syncp_null, has_owner, other_owner:
856           ldarg         0                                                       obj
857           call          System.Threading.Monitor.Enter
858           ret
859         */
860
861         mono_mb_patch_short_branch (mb, obj_null_branch);
862         mono_mb_patch_short_branch (mb, syncp_null_branch);
863         mono_mb_patch_short_branch (mb, has_owner_branch);
864         mono_mb_patch_short_branch (mb, other_owner_branch);
865         mono_mb_emit_byte (mb, CEE_LDARG_0);
866         mono_mb_emit_managed_call (mb, monitor_enter_method, NULL);
867         mono_mb_emit_byte (mb, CEE_RET);
868
869         fast_monitor_enter = mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_enter_method), 5);
870         mono_mb_free (mb);
871
872         return fast_monitor_enter;
873 }
874
875 static MonoMethod*
876 mono_monitor_get_fast_exit_method (MonoMethod *monitor_exit_method)
877 {
878         static MonoMethod *fast_monitor_exit;
879
880         MonoMethodBuilder *mb;
881         int obj_null_branch, has_waiting_branch, has_syncp_branch, owned_branch, nested_branch;
882         int thread_tls_offset;
883         int syncp_loc;
884
885 #ifdef HAVE_MOVING_COLLECTOR
886         return NULL;
887 #endif
888
889         thread_tls_offset = mono_thread_get_tls_offset ();
890         if (thread_tls_offset == -1)
891                 return NULL;
892
893         if (fast_monitor_exit)
894                 return fast_monitor_exit;
895
896         mb = mono_mb_new (mono_defaults.monitor_class, "FastMonitorExit", MONO_WRAPPER_UNKNOWN);
897
898         mb->method->slot = -1;
899         mb->method->flags = METHOD_ATTRIBUTE_PUBLIC | METHOD_ATTRIBUTE_STATIC |
900                 METHOD_ATTRIBUTE_HIDE_BY_SIG | METHOD_ATTRIBUTE_FINAL;
901
902         syncp_loc = mono_mb_add_local (mb, &mono_defaults.int_class->byval_arg);
903
904         emit_obj_syncp_check (mb, syncp_loc, &obj_null_branch, &has_syncp_branch, TRUE);
905
906         /*
907           ret
908         */
909
910         mono_mb_emit_byte (mb, CEE_RET);
911
912         /*
913          has_syncp:
914           ldloc         syncp                                                   syncp
915           ldc.i4        G_STRUCT_OFFSET(MonoThreadsSync, owner)                 syncp off
916           add                                                                   &owner
917           ldind.i                                                               owner
918           mono. tls     thread_tls_offset                                       owner threadp
919           ldc.i4        G_STRUCT_OFFSET(MonoThread, tid)                        owner threadp off
920           add                                                                   owner &tid
921           ldind.i                                                               owner tid
922           beq.s         owned
923         */
924
925         mono_mb_patch_short_branch (mb, has_syncp_branch);
926         mono_mb_emit_ldloc (mb, syncp_loc);
927         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
928         mono_mb_emit_byte (mb, CEE_ADD);
929         mono_mb_emit_byte (mb, CEE_LDIND_I);
930         mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
931         mono_mb_emit_byte (mb, CEE_MONO_TLS);
932         mono_mb_emit_i4 (mb, thread_tls_offset);
933         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThread, tid));
934         mono_mb_emit_byte (mb, CEE_ADD);
935         mono_mb_emit_byte (mb, CEE_LDIND_I);
936         owned_branch = mono_mb_emit_short_branch (mb, CEE_BEQ_S);
937
938         /*
939           ret
940         */
941
942         mono_mb_emit_byte (mb, CEE_RET);
943
944         /*
945          owned:
946           ldloc         syncp                                                   syncp
947           ldc.i4        G_STRUCT_OFFSET(MonoThreadsSync, nest)                  syncp off
948           add                                                                   &nest
949           dup                                                                   &nest &nest
950           ldind.i4                                                              &nest nest
951           dup                                                                   &nest nest nest
952           ldc.i4        1                                                       &nest nest nest 1
953           bgt.un.s      nested                                                  &nest nest
954         */
955
956         mono_mb_patch_short_branch (mb, owned_branch);
957         mono_mb_emit_ldloc (mb, syncp_loc);
958         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, nest));
959         mono_mb_emit_byte (mb, CEE_ADD);
960         mono_mb_emit_byte (mb, CEE_DUP);
961         mono_mb_emit_byte (mb, CEE_LDIND_I4);
962         mono_mb_emit_byte (mb, CEE_DUP);
963         mono_mb_emit_byte (mb, CEE_LDC_I4_1);
964         nested_branch = mono_mb_emit_short_branch (mb, CEE_BGT_UN_S);
965
966         /*
967           pop                                                                   &nest
968           pop
969           ldloc         syncp                                                   syncp
970           ldc.i4        G_STRUCT_OFFSET(MonoThreadsSync, entry_count)           syncp off
971           add                                                                   &count
972           ldind.i4                                                              count
973           brtrue.s      has_waiting
974         */
975
976         mono_mb_emit_byte (mb, CEE_POP);
977         mono_mb_emit_byte (mb, CEE_POP);
978         mono_mb_emit_ldloc (mb, syncp_loc);
979         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, entry_count));
980         mono_mb_emit_byte (mb, CEE_ADD);
981         mono_mb_emit_byte (mb, CEE_LDIND_I4);
982         has_waiting_branch = mono_mb_emit_short_branch (mb, CEE_BRTRUE_S);
983
984         /*
985           ldloc         syncp                                                   syncp
986           ldc.i4        G_STRUCT_OFFSET(MonoThreadsSync, owner)                 syncp off
987           add                                                                   &owner
988           ldnull                                                                &owner 0
989           stind.i
990           ret
991         */
992
993         mono_mb_emit_ldloc (mb, syncp_loc);
994         mono_mb_emit_icon (mb, G_STRUCT_OFFSET (MonoThreadsSync, owner));
995         mono_mb_emit_byte (mb, CEE_ADD);
996         mono_mb_emit_byte (mb, CEE_LDNULL);
997         mono_mb_emit_byte (mb, CEE_STIND_I);
998         mono_mb_emit_byte (mb, CEE_RET);
999
1000         /*
1001          nested:
1002           ldc.i4        1                                                       &nest nest 1
1003           sub                                                                   &nest nest-
1004           stind.i4
1005           ret
1006         */
1007
1008         mono_mb_patch_short_branch (mb, nested_branch);
1009         mono_mb_emit_byte (mb, CEE_LDC_I4_1);
1010         mono_mb_emit_byte (mb, CEE_SUB);
1011         mono_mb_emit_byte (mb, CEE_STIND_I4);
1012         mono_mb_emit_byte (mb, CEE_RET);
1013
1014         /*
1015          obj_null, has_waiting:
1016           ldarg         0                                                       obj
1017           call          System.Threading.Monitor.Exit
1018           ret
1019          */
1020
1021         mono_mb_patch_short_branch (mb, obj_null_branch);
1022         mono_mb_patch_short_branch (mb, has_waiting_branch);
1023         mono_mb_emit_byte (mb, CEE_LDARG_0);
1024         mono_mb_emit_managed_call (mb, monitor_exit_method, NULL);
1025         mono_mb_emit_byte (mb, CEE_RET);
1026
1027         fast_monitor_exit = mono_mb_create_method (mb, mono_signature_no_pinvoke (monitor_exit_method), 5);
1028         mono_mb_free (mb);
1029
1030         return fast_monitor_exit;
1031 }
1032
1033 MonoMethod*
1034 mono_monitor_get_fast_path (MonoMethod *enter_or_exit)
1035 {
1036         if (strcmp (enter_or_exit->name, "Enter") == 0)
1037                 return mono_monitor_get_fast_enter_method (enter_or_exit);
1038         if (strcmp (enter_or_exit->name, "Exit") == 0)
1039                 return mono_monitor_get_fast_exit_method (enter_or_exit);
1040         g_assert_not_reached ();
1041         return NULL;
1042 }
1043
1044 /*
1045  * mono_monitor_threads_sync_member_offset:
1046  * @owner_offset: returns size and offset of the "owner" member
1047  * @nest_offset: returns size and offset of the "nest" member
1048  * @entry_count_offset: returns size and offset of the "entry_count" member
1049  *
1050  * Returns the offsets and sizes of three members of the
1051  * MonoThreadsSync struct.  The Monitor ASM fastpaths need this.
1052  */
1053 void
1054 mono_monitor_threads_sync_members_offset (int *owner_offset, int *nest_offset, int *entry_count_offset)
1055 {
1056         MonoThreadsSync ts;
1057
1058 #define ENCODE_OFF_SIZE(o,s)    (((o) << 8) | ((s) & 0xff))
1059
1060         *owner_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, owner), sizeof (ts.owner));
1061         *nest_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, nest), sizeof (ts.nest));
1062         *entry_count_offset = ENCODE_OFF_SIZE (G_STRUCT_OFFSET (MonoThreadsSync, entry_count), sizeof (ts.entry_count));
1063 }
1064
1065 gboolean 
1066 ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 ms)
1067 {
1068         gint32 res;
1069
1070         do {
1071                 res = mono_monitor_try_enter_internal (obj, ms, TRUE);
1072                 if (res == -1)
1073                         mono_thread_interruption_checkpoint ();
1074         } while (res == -1);
1075         
1076         return res == 1;
1077 }
1078
1079 gboolean 
1080 ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj)
1081 {
1082         MonoThreadsSync *mon;
1083         
1084         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
1085                   ": Testing if %p is owned by thread %d", obj, GetCurrentThreadId()));
1086
1087         mon = obj->synchronisation;
1088 #ifdef HAVE_MOVING_COLLECTOR
1089         {
1090                 LockWord lw;
1091                 lw.sync = mon;
1092                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
1093                         return FALSE;
1094                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
1095                 mon = lw.sync;
1096         }
1097 #endif
1098         if (mon == NULL) {
1099                 return FALSE;
1100         }
1101         
1102         if(mon->owner==GetCurrentThreadId ()) {
1103                 return(TRUE);
1104         }
1105         
1106         return(FALSE);
1107 }
1108
1109 gboolean 
1110 ves_icall_System_Threading_Monitor_Monitor_test_synchronised (MonoObject *obj)
1111 {
1112         MonoThreadsSync *mon;
1113
1114         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION
1115                   ": (%d) Testing if %p is owned by any thread", GetCurrentThreadId (), obj));
1116         
1117         mon = obj->synchronisation;
1118 #ifdef HAVE_MOVING_COLLECTOR
1119         {
1120                 LockWord lw;
1121                 lw.sync = mon;
1122                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
1123                         return FALSE;
1124                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
1125                 mon = lw.sync;
1126         }
1127 #endif
1128         if (mon == NULL) {
1129                 return FALSE;
1130         }
1131         
1132         if (mon->owner != 0) {
1133                 return TRUE;
1134         }
1135         
1136         return FALSE;
1137 }
1138
1139 /* All wait list manipulation in the pulse, pulseall and wait
1140  * functions happens while the monitor lock is held, so we don't need
1141  * any extra struct locking
1142  */
1143
1144 void
1145 ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj)
1146 {
1147         MonoThreadsSync *mon;
1148         
1149         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing %p", 
1150                 GetCurrentThreadId (), obj));
1151         
1152         mon = obj->synchronisation;
1153 #ifdef HAVE_MOVING_COLLECTOR
1154         {
1155                 LockWord lw;
1156                 lw.sync = mon;
1157                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
1158                         mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
1159                         return;
1160                 }
1161                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
1162                 mon = lw.sync;
1163         }
1164 #endif
1165         if (mon == NULL) {
1166                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
1167                 return;
1168         }
1169         if (mon->owner != GetCurrentThreadId ()) {
1170                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
1171                 return;
1172         }
1173
1174         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting",
1175                   GetCurrentThreadId (), g_slist_length (mon->wait_list)));
1176         
1177         if (mon->wait_list != NULL) {
1178                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
1179                           ": (%d) signalling and dequeuing handle %p",
1180                           GetCurrentThreadId (), mon->wait_list->data));
1181         
1182                 SetEvent (mon->wait_list->data);
1183                 mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
1184         }
1185 }
1186
1187 void
1188 ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj)
1189 {
1190         MonoThreadsSync *mon;
1191         
1192         LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing all %p",
1193                   GetCurrentThreadId (), obj));
1194
1195         mon = obj->synchronisation;
1196 #ifdef HAVE_MOVING_COLLECTOR
1197         {
1198                 LockWord lw;
1199                 lw.sync = mon;
1200                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
1201                         mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
1202                         return;
1203                 }
1204                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
1205                 mon = lw.sync;
1206         }
1207 #endif
1208         if (mon == NULL) {
1209                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
1210                 return;
1211         }
1212         if (mon->owner != GetCurrentThreadId ()) {
1213                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
1214                 return;
1215         }
1216
1217         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting",
1218                   GetCurrentThreadId (), g_slist_length (mon->wait_list)));
1219
1220         while (mon->wait_list != NULL) {
1221                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
1222                           ": (%d) signalling and dequeuing handle %p",
1223                           GetCurrentThreadId (), mon->wait_list->data));
1224         
1225                 SetEvent (mon->wait_list->data);
1226                 mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
1227         }
1228 }
1229
1230 gboolean
1231 ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms)
1232 {
1233         MonoThreadsSync *mon;
1234         HANDLE event;
1235         guint32 nest;
1236         guint32 ret;
1237         gboolean success = FALSE;
1238         gint32 regain;
1239         MonoThread *thread = mono_thread_current ();
1240
1241         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION
1242                   ": (%d) Trying to wait for %p with timeout %dms",
1243                   GetCurrentThreadId (), obj, ms));
1244         
1245         mon = obj->synchronisation;
1246 #ifdef HAVE_MOVING_COLLECTOR
1247         {
1248                 LockWord lw;
1249                 lw.sync = mon;
1250                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
1251                         mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
1252                         return FALSE;
1253                 }
1254                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
1255                 mon = lw.sync;
1256         }
1257 #endif
1258         if (mon == NULL) {
1259                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked"));
1260                 return FALSE;
1261         }
1262         if (mon->owner != GetCurrentThreadId ()) {
1263                 mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
1264                 return FALSE;
1265         }
1266
1267         /* Do this WaitSleepJoin check before creating the event handle */
1268         mono_thread_current_check_pending_interrupt ();
1269         
1270         event = CreateEvent (NULL, FALSE, FALSE, NULL);
1271         if (event == NULL) {
1272                 mono_raise_exception (mono_get_exception_synchronization_lock ("Failed to set up wait event"));
1273                 return FALSE;
1274         }
1275         
1276         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) queuing handle %p",
1277                   GetCurrentThreadId (), event));
1278
1279         mono_thread_current_check_pending_interrupt ();
1280         
1281         mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1282
1283         mon->wait_list = g_slist_append (mon->wait_list, event);
1284         
1285         /* Save the nest count, and release the lock */
1286         nest = mon->nest;
1287         mon->nest = 1;
1288         mono_monitor_exit (obj);
1289
1290         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocked %p lock %p",
1291                   GetCurrentThreadId (), obj, mon));
1292
1293         /* There's no race between unlocking mon and waiting for the
1294          * event, because auto reset events are sticky, and this event
1295          * is private to this thread.  Therefore even if the event was
1296          * signalled before we wait, we still succeed.
1297          */
1298         ret = WaitForSingleObjectEx (event, ms, TRUE);
1299
1300         /* Reset the thread state fairly early, so we don't have to worry
1301          * about the monitor error checking
1302          */
1303         mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1304         
1305         if (mono_thread_interruption_requested ()) {
1306                 CloseHandle (event);
1307                 return FALSE;
1308         }
1309
1310         /* Regain the lock with the previous nest count */
1311         do {
1312                 regain = mono_monitor_try_enter_internal (obj, INFINITE, TRUE);
1313                 if (regain == -1) 
1314                         mono_thread_interruption_checkpoint ();
1315         } while (regain == -1);
1316
1317         if (regain == 0) {
1318                 /* Something went wrong, so throw a
1319                  * SynchronizationLockException
1320                  */
1321                 CloseHandle (event);
1322                 mono_raise_exception (mono_get_exception_synchronization_lock ("Failed to regain lock"));
1323                 return FALSE;
1324         }
1325
1326         mon->nest = nest;
1327
1328         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Regained %p lock %p",
1329                   GetCurrentThreadId (), obj, mon));
1330
1331         if (ret == WAIT_TIMEOUT) {
1332                 /* Poll the event again, just in case it was signalled
1333                  * while we were trying to regain the monitor lock
1334                  */
1335                 ret = WaitForSingleObjectEx (event, 0, FALSE);
1336         }
1337
1338         /* Pulse will have popped our event from the queue if it signalled
1339          * us, so we only do it here if the wait timed out.
1340          *
1341          * This avoids a race condition where the thread holding the
1342          * lock can Pulse several times before the WaitForSingleObject
1343          * returns.  If we popped the queue here then this event might
1344          * be signalled more than once, thereby starving another
1345          * thread.
1346          */
1347         
1348         if (ret == WAIT_OBJECT_0) {
1349                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Success",
1350                           GetCurrentThreadId ()));
1351                 success = TRUE;
1352         } else {
1353                 LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed, dequeuing handle %p",
1354                           GetCurrentThreadId (), event));
1355                 /* No pulse, so we have to remove ourself from the
1356                  * wait queue
1357                  */
1358                 mon->wait_list = g_slist_remove (mon->wait_list, event);
1359         }
1360         CloseHandle (event);
1361         
1362         return success;
1363 }
1364