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