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