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