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