[eglib] Prefer <langinfo.h> to <localcharset.h>
[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/abi-details.h>
16 #include <mono/metadata/monitor.h>
17 #include <mono/metadata/threads-types.h>
18 #include <mono/metadata/exception.h>
19 #include <mono/metadata/threads.h>
20 #include <mono/io-layer/io-layer.h>
21 #include <mono/metadata/object-internals.h>
22 #include <mono/metadata/class-internals.h>
23 #include <mono/metadata/gc-internal.h>
24 #include <mono/metadata/method-builder.h>
25 #include <mono/metadata/debug-helpers.h>
26 #include <mono/metadata/tabledefs.h>
27 #include <mono/metadata/marshal.h>
28 #include <mono/utils/mono-threads.h>
29 #include <mono/metadata/profiler-private.h>
30 #include <mono/utils/mono-time.h>
31 #include <mono/utils/atomic.h>
32
33 /*
34  * Pull the list of opcodes
35  */
36 #define OPDEF(a,b,c,d,e,f,g,h,i,j) \
37         a = i,
38
39 enum {
40 #include "mono/cil/opcode.def"
41         LAST = 0xff
42 };
43 #undef OPDEF
44
45 /*#define LOCK_DEBUG(a) do { a; } while (0)*/
46 #define LOCK_DEBUG(a)
47
48 /*
49  * The monitor implementation here is based on
50  * http://www.usenix.org/events/jvm01/full_papers/dice/dice.pdf and
51  * http://www.research.ibm.com/people/d/dfb/papers/Bacon98Thin.ps
52  *
53  * The Dice paper describes a technique for saving lock record space
54  * by returning records to a free list when they become unused.  That
55  * sounds like unnecessary complexity to me, though if it becomes
56  * clear that unused lock records are taking up lots of space or we
57  * need to shave more time off by avoiding a malloc then we can always
58  * implement the free list idea later.  The timeout parameter to
59  * try_enter voids some of the assumptions about the reference count
60  * field in Dice's implementation too.  In his version, the thread
61  * attempting to lock a contended object will block until it succeeds,
62  * so the reference count will never be decremented while an object is
63  * locked.
64  *
65  * Bacon's thin locks have a fast path that doesn't need a lock record
66  * for the common case of locking an unlocked or shallow-nested
67  * object, but the technique relies on encoding the thread ID in 15
68  * bits (to avoid too much per-object space overhead.)  Unfortunately
69  * I don't think it's possible to reliably encode a pthread_t into 15
70  * bits. (The JVM implementation used seems to have a 15-bit
71  * per-thread identifier available.)
72  *
73  * This implementation then combines Dice's basic lock model with
74  * Bacon's simplification of keeping a lock record for the lifetime of
75  * an object.
76  */
77
78
79 typedef struct _MonitorArray MonitorArray;
80
81 struct _MonitorArray {
82         MonitorArray *next;
83         int num_monitors;
84         MonoThreadsSync monitors [MONO_ZERO_LEN_ARRAY];
85 };
86
87 #define mono_monitor_allocator_lock() mono_mutex_lock (&monitor_mutex)
88 #define mono_monitor_allocator_unlock() mono_mutex_unlock (&monitor_mutex)
89 static mono_mutex_t monitor_mutex;
90 static MonoThreadsSync *monitor_freelist;
91 static MonitorArray *monitor_allocated;
92 static int array_size = 16;
93
94 static inline guint32
95 mon_status_get_owner (guint32 status)
96 {
97         return status & OWNER_MASK;
98 }
99
100 static inline guint32
101 mon_status_set_owner (guint32 status, guint32 owner)
102 {
103         return (status & ENTRY_COUNT_MASK) | owner;
104 }
105
106 static inline gint32
107 mon_status_get_entry_count (guint32 status)
108 {
109         gint32 entry_count = (gint32)((status & ENTRY_COUNT_MASK) >> ENTRY_COUNT_SHIFT);
110         gint32 zero = (gint32)(((guint32)ENTRY_COUNT_ZERO) >> ENTRY_COUNT_SHIFT);
111         return entry_count - zero;
112 }
113
114 static inline guint32
115 mon_status_init_entry_count (guint32 status)
116 {
117         return (status & OWNER_MASK) | ENTRY_COUNT_ZERO;
118 }
119
120 static inline guint32
121 mon_status_increment_entry_count (guint32 status)
122 {
123         return status + (1 << ENTRY_COUNT_SHIFT);
124 }
125
126 static inline guint32
127 mon_status_decrement_entry_count (guint32 status)
128 {
129         return status - (1 << ENTRY_COUNT_SHIFT);
130 }
131
132 static inline gboolean
133 mon_status_have_waiters (guint32 status)
134 {
135         return status & ENTRY_COUNT_WAITERS;
136 }
137
138 void
139 mono_monitor_init (void)
140 {
141         mono_mutex_init_recursive (&monitor_mutex);
142 }
143  
144 void
145 mono_monitor_cleanup (void)
146 {
147         MonoThreadsSync *mon;
148         /* MonitorArray *marray, *next = NULL; */
149
150         /*mono_mutex_destroy (&monitor_mutex);*/
151
152         /* The monitors on the freelist don't have weak links - mark them */
153         for (mon = monitor_freelist; mon; mon = mon->data)
154                 mon->wait_list = (gpointer)-1;
155
156         /*
157          * FIXME: This still crashes with sgen (async_read.exe)
158          *
159          * In mini_cleanup() we first call mono_runtime_cleanup(), which calls
160          * mono_monitor_cleanup(), which is supposed to free all monitor memory.
161          *
162          * Later in mini_cleanup(), we call mono_domain_free(), which calls
163          * mono_gc_clear_domain(), which frees all weak links associated with objects.
164          * Those weak links reside in the monitor structures, which we've freed earlier.
165          *
166          * Unless we fix this dependency in the shutdown sequence this code has to remain
167          * disabled, or at least the call to g_free().
168          */
169         /*
170         for (marray = monitor_allocated; marray; marray = next) {
171                 int i;
172
173                 for (i = 0; i < marray->num_monitors; ++i) {
174                         mon = &marray->monitors [i];
175                         if (mon->wait_list != (gpointer)-1)
176                                 mono_gc_weak_link_remove (&mon->data);
177                 }
178
179                 next = marray->next;
180                 g_free (marray);
181         }
182         */
183 }
184
185 static int
186 monitor_is_on_freelist (MonoThreadsSync *mon)
187 {
188         MonitorArray *marray;
189         for (marray = monitor_allocated; marray; marray = marray->next) {
190                 if (mon >= marray->monitors && mon < &marray->monitors [marray->num_monitors])
191                         return TRUE;
192         }
193         return FALSE;
194 }
195
196 /**
197  * mono_locks_dump:
198  * @include_untaken:
199  *
200  * Print a report on stdout of the managed locks currently held by
201  * threads. If @include_untaken is specified, list also inflated locks
202  * which are unheld.
203  * This is supposed to be used in debuggers like gdb.
204  */
205 void
206 mono_locks_dump (gboolean include_untaken)
207 {
208         int i;
209         int used = 0, on_freelist = 0, to_recycle = 0, total = 0, num_arrays = 0;
210         MonoThreadsSync *mon;
211         MonitorArray *marray;
212         for (mon = monitor_freelist; mon; mon = mon->data)
213                 on_freelist++;
214         for (marray = monitor_allocated; marray; marray = marray->next) {
215                 total += marray->num_monitors;
216                 num_arrays++;
217                 for (i = 0; i < marray->num_monitors; ++i) {
218                         mon = &marray->monitors [i];
219                         if (mon->data == NULL) {
220                                 if (i < marray->num_monitors - 1)
221                                         to_recycle++;
222                         } else {
223                                 if (!monitor_is_on_freelist (mon->data)) {
224                                         MonoObject *holder = mono_gc_weak_link_get (&mon->data);
225                                         if (mon_status_get_owner (mon->status)) {
226                                                 g_print ("Lock %p in object %p held by thread %d, nest level: %d\n",
227                                                         mon, holder, mon_status_get_owner (mon->status), mon->nest);
228                                                 if (mon->entry_sem)
229                                                         g_print ("\tWaiting on semaphore %p: %d\n", mon->entry_sem, mon_status_get_entry_count (mon->status));
230                                         } else if (include_untaken) {
231                                                 g_print ("Lock %p in object %p untaken\n", mon, holder);
232                                         }
233                                         used++;
234                                 }
235                         }
236                 }
237         }
238         g_print ("Total locks (in %d array(s)): %d, used: %d, on freelist: %d, to recycle: %d\n",
239                 num_arrays, total, used, on_freelist, to_recycle);
240 }
241
242 /* LOCKING: this is called with monitor_mutex held */
243 static void 
244 mon_finalize (MonoThreadsSync *mon)
245 {
246         LOCK_DEBUG (g_message ("%s: Finalizing sync %p", __func__, mon));
247
248         if (mon->entry_sem != NULL) {
249                 CloseHandle (mon->entry_sem);
250                 mon->entry_sem = NULL;
251         }
252         /* If this isn't empty then something is seriously broken - it
253          * means a thread is still waiting on the object that owned
254          * this lock, but the object has been finalized.
255          */
256         g_assert (mon->wait_list == NULL);
257
258         /* owner and nest are set in mon_new, no need to zero them out */
259
260         mon->data = monitor_freelist;
261         monitor_freelist = mon;
262 #ifndef DISABLE_PERFCOUNTERS
263         mono_perfcounters->gc_sync_blocks--;
264 #endif
265 }
266
267 /* LOCKING: this is called with monitor_mutex held */
268 static MonoThreadsSync *
269 mon_new (gsize id)
270 {
271         MonoThreadsSync *new;
272
273         if (!monitor_freelist) {
274                 MonitorArray *marray;
275                 int i;
276                 /* see if any sync block has been collected */
277                 new = NULL;
278                 for (marray = monitor_allocated; marray; marray = marray->next) {
279                         for (i = 0; i < marray->num_monitors; ++i) {
280                                 if (marray->monitors [i].data == NULL) {
281                                         new = &marray->monitors [i];
282                                         if (new->wait_list) {
283                                                 /* Orphaned events left by aborted threads */
284                                                 while (new->wait_list) {
285                                                         LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d): Closing orphaned event %d", mono_thread_info_get_small_id (), new->wait_list->data));
286                                                         CloseHandle (new->wait_list->data);
287                                                         new->wait_list = g_slist_remove (new->wait_list, new->wait_list->data);
288                                                 }
289                                         }
290                                         mono_gc_weak_link_remove (&new->data, TRUE);
291                                         new->data = monitor_freelist;
292                                         monitor_freelist = new;
293                                 }
294                         }
295                         /* small perf tweak to avoid scanning all the blocks */
296                         if (new)
297                                 break;
298                 }
299                 /* need to allocate a new array of monitors */
300                 if (!monitor_freelist) {
301                         MonitorArray *last;
302                         LOCK_DEBUG (g_message ("%s: allocating more monitors: %d", __func__, array_size));
303                         marray = g_malloc0 (sizeof (MonoArray) + array_size * sizeof (MonoThreadsSync));
304                         marray->num_monitors = array_size;
305                         array_size *= 2;
306                         /* link into the freelist */
307                         for (i = 0; i < marray->num_monitors - 1; ++i) {
308                                 marray->monitors [i].data = &marray->monitors [i + 1];
309                         }
310                         marray->monitors [i].data = NULL; /* the last one */
311                         monitor_freelist = &marray->monitors [0];
312                         /* we happend the marray instead of prepending so that
313                          * the collecting loop above will need to scan smaller arrays first
314                          */
315                         if (!monitor_allocated) {
316                                 monitor_allocated = marray;
317                         } else {
318                                 last = monitor_allocated;
319                                 while (last->next)
320                                         last = last->next;
321                                 last->next = marray;
322                         }
323                 }
324         }
325
326         new = monitor_freelist;
327         monitor_freelist = new->data;
328
329         new->status = mon_status_set_owner (0, id);
330         new->status = mon_status_init_entry_count (new->status);
331         new->nest = 1;
332         new->data = NULL;
333         
334 #ifndef DISABLE_PERFCOUNTERS
335         mono_perfcounters->gc_sync_blocks++;
336 #endif
337         return new;
338 }
339
340 /*
341  * Format of the lock word:
342  * thinhash | fathash | data
343  *
344  * thinhash is the lower bit: if set data is the shifted hashcode of the object.
345  * fathash is another bit: if set the hash code is stored in the MonoThreadsSync
346  *   struct pointed to by data
347  * if neither bit is set and data is non-NULL, data is a MonoThreadsSync
348  */
349 typedef union {
350         gsize lock_word;
351         MonoThreadsSync *sync;
352 } LockWord;
353
354 enum {
355         LOCK_WORD_THIN_HASH = 1,
356         LOCK_WORD_FAT_HASH = 1 << 1,
357         LOCK_WORD_BITS_MASK = 0x3,
358         LOCK_WORD_HASH_SHIFT = 2
359 };
360
361 #define MONO_OBJECT_ALIGNMENT_SHIFT     3
362
363 /*
364  * mono_object_hash:
365  * @obj: an object
366  *
367  * Calculate a hash code for @obj that is constant while @obj is alive.
368  */
369 int
370 mono_object_hash (MonoObject* obj)
371 {
372 #ifdef HAVE_MOVING_COLLECTOR
373         LockWord lw;
374         unsigned int hash;
375         if (!obj)
376                 return 0;
377         lw.sync = obj->synchronisation;
378         if (lw.lock_word & LOCK_WORD_THIN_HASH) {
379                 /*g_print ("fast thin hash %d for obj %p store\n", (unsigned int)lw.lock_word >> LOCK_WORD_HASH_SHIFT, obj);*/
380                 return (unsigned int)lw.lock_word >> LOCK_WORD_HASH_SHIFT;
381         }
382         if (lw.lock_word & LOCK_WORD_FAT_HASH) {
383                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
384                 /*g_print ("fast fat hash %d for obj %p store\n", lw.sync->hash_code, obj);*/
385                 return lw.sync->hash_code;
386         }
387         /*
388          * while we are inside this function, the GC will keep this object pinned,
389          * since we are in the unmanaged stack. Thanks to this and to the hash
390          * function that depends only on the address, we can ignore the races if
391          * another thread computes the hash at the same time, because it'll end up
392          * with the same value.
393          */
394         hash = (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u;
395         /* clear the top bits as they can be discarded */
396         hash &= ~(LOCK_WORD_BITS_MASK << 30);
397         /* no hash flags were set, so it must be a MonoThreadsSync pointer if not NULL */
398         if (lw.sync) {
399                 lw.sync->hash_code = hash;
400                 /*g_print ("storing hash code %d for obj %p in sync %p\n", hash, obj, lw.sync);*/
401                 lw.lock_word |= LOCK_WORD_FAT_HASH;
402                 /* this is safe since we don't deflate locks */
403                 obj->synchronisation = lw.sync;
404         } else {
405                 /*g_print ("storing thin hash code %d for obj %p\n", hash, obj);*/
406                 lw.lock_word = LOCK_WORD_THIN_HASH | (hash << LOCK_WORD_HASH_SHIFT);
407                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, NULL) == NULL)
408                         return hash;
409                 /*g_print ("failed store\n");*/
410                 /* someone set the hash flag or someone inflated the object */
411                 lw.sync = obj->synchronisation;
412                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
413                         return hash;
414                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
415                 lw.sync->hash_code = hash;
416                 lw.lock_word |= LOCK_WORD_FAT_HASH;
417                 /* this is safe since we don't deflate locks */
418                 obj->synchronisation = lw.sync;
419         }
420         return hash;
421 #else
422 /*
423  * Wang's address-based hash function:
424  *   http://www.concentric.net/~Ttwang/tech/addrhash.htm
425  */
426         return (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u;
427 #endif
428 }
429
430 static void
431 mon_decrement_entry_count (MonoThreadsSync *mon)
432 {
433         guint32 old_status, tmp_status, new_status;
434
435         /* Decrement entry count */
436         old_status = mon->status;
437         for (;;) {
438                 new_status = mon_status_decrement_entry_count (old_status);
439                 tmp_status = InterlockedCompareExchange ((gint32*)&mon->status, new_status, old_status);
440                 if (tmp_status == old_status) {
441                         break;
442                 }
443                 old_status = tmp_status;
444         }
445 }
446
447 /* If allow_interruption==TRUE, the method will be interrumped if abort or suspend
448  * is requested. In this case it returns -1.
449  */ 
450 static inline gint32 
451 mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_interruption)
452 {
453         MonoThreadsSync *mon;
454         gsize id = mono_thread_info_get_small_id ();
455         HANDLE sem;
456         guint32 then = 0, now, delta;
457         guint32 waitms;
458         guint32 ret;
459         guint32 new_status, old_status, tmp_status;
460         MonoInternalThread *thread;
461         gboolean interrupted = FALSE;
462
463         LOCK_DEBUG (g_message("%s: (%d) Trying to lock object %p (%d ms)", __func__, id, obj, ms));
464
465         if (G_UNLIKELY (!obj)) {
466                 mono_raise_exception (mono_get_exception_argument_null ("obj"));
467                 return FALSE;
468         }
469
470 retry:
471         mon = obj->synchronisation;
472
473         /* If the object has never been locked... */
474         if (G_UNLIKELY (mon == NULL)) {
475                 mono_monitor_allocator_lock ();
476                 mon = mon_new (id);
477                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL) == NULL) {
478                         mono_gc_weak_link_add (&mon->data, obj, TRUE);
479                         mono_monitor_allocator_unlock ();
480                         /* Successfully locked */
481                         return 1;
482                 } else {
483 #ifdef HAVE_MOVING_COLLECTOR
484                         LockWord lw;
485                         lw.sync = obj->synchronisation;
486                         if (lw.lock_word & LOCK_WORD_THIN_HASH) {
487                                 MonoThreadsSync *oldlw = lw.sync;
488                                 /* move the already calculated hash */
489                                 mon->hash_code = lw.lock_word >> LOCK_WORD_HASH_SHIFT;
490                                 lw.sync = mon;
491                                 lw.lock_word |= LOCK_WORD_FAT_HASH;
492                                 if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) {
493                                         mono_gc_weak_link_add (&mon->data, obj, TRUE);
494                                         mono_monitor_allocator_unlock ();
495                                         /* Successfully locked */
496                                         return 1;
497                                 } else {
498                                         mon_finalize (mon);
499                                         mono_monitor_allocator_unlock ();
500                                         goto retry;
501                                 }
502                         } else if (lw.lock_word & LOCK_WORD_FAT_HASH) {
503                                 mon_finalize (mon);
504                                 mono_monitor_allocator_unlock ();
505                                 /* get the old lock without the fat hash bit */
506                                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
507                                 mon = lw.sync;
508                         } else {
509                                 mon_finalize (mon);
510                                 mono_monitor_allocator_unlock ();
511                                 mon = obj->synchronisation;
512                         }
513 #else
514                         mon_finalize (mon);
515                         mono_monitor_allocator_unlock ();
516                         mon = obj->synchronisation;
517 #endif
518                 }
519         } else {
520 #ifdef HAVE_MOVING_COLLECTOR
521                 LockWord lw;
522                 lw.sync = mon;
523                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
524                         MonoThreadsSync *oldlw = lw.sync;
525                         mono_monitor_allocator_lock ();
526                         mon = mon_new (id);
527                         /* move the already calculated hash */
528                         mon->hash_code = lw.lock_word >> LOCK_WORD_HASH_SHIFT;
529                         lw.sync = mon;
530                         lw.lock_word |= LOCK_WORD_FAT_HASH;
531                         if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) {
532                                 mono_gc_weak_link_add (&mon->data, obj, TRUE);
533                                 mono_monitor_allocator_unlock ();
534                                 /* Successfully locked */
535                                 return 1;
536                         } else {
537                                 mon_finalize (mon);
538                                 mono_monitor_allocator_unlock ();
539                                 goto retry;
540                         }
541                 }
542 #endif
543         }
544
545 #ifdef HAVE_MOVING_COLLECTOR
546         {
547                 LockWord lw;
548                 lw.sync = mon;
549                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
550                 mon = lw.sync;
551         }
552 #endif
553
554         /* If the object has previously been locked but isn't now... */
555
556         /* This case differs from Dice's case 3 because we don't
557          * deflate locks or cache unused lock records
558          */
559         old_status = mon->status;
560         if (G_LIKELY (mon_status_get_owner (old_status) == 0)) {
561                 /* Try to install our ID in the owner field, nest
562                 * should have been left at 1 by the previous unlock
563                 * operation
564                 */
565                 new_status = mon_status_set_owner (old_status, id);
566                 tmp_status = InterlockedCompareExchange ((gint32*)&mon->status, new_status, old_status);
567                 if (G_LIKELY (tmp_status == old_status)) {
568                         /* Success */
569                         g_assert (mon->nest == 1);
570                         return 1;
571                 } else {
572                         /* Trumped again! */
573                         goto retry;
574                 }
575         }
576
577         /* If the object is currently locked by this thread... */
578         if (mon_status_get_owner (old_status) == id) {
579                 mon->nest++;
580                 return 1;
581         }
582
583         /* The object must be locked by someone else... */
584 #ifndef DISABLE_PERFCOUNTERS
585         mono_perfcounters->thread_contentions++;
586 #endif
587
588         /* If ms is 0 we don't block, but just fail straight away */
589         if (ms == 0) {
590                 LOCK_DEBUG (g_message ("%s: (%d) timed out, returning FALSE", __func__, id));
591                 return 0;
592         }
593
594         mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_CONTENTION);
595
596         /* The slow path begins here. */
597 retry_contended:
598         /* a small amount of duplicated code, but it allows us to insert the profiler
599          * callbacks without impacting the fast path: from here on we don't need to go back to the
600          * retry label, but to retry_contended. At this point mon is already installed in the object
601          * header.
602          */
603         /* This case differs from Dice's case 3 because we don't
604          * deflate locks or cache unused lock records
605          */
606         old_status = mon->status;
607         if (G_LIKELY (mon_status_get_owner (old_status) == 0)) {
608                 /* Try to install our ID in the owner field, nest
609                 * should have been left at 1 by the previous unlock
610                 * operation
611                 */
612                 new_status = mon_status_set_owner (old_status, id);
613                 tmp_status = InterlockedCompareExchange ((gint32*)&mon->status, new_status, old_status);
614                 if (G_LIKELY (tmp_status == old_status)) {
615                         /* Success */
616                         g_assert (mon->nest == 1);
617                         mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_DONE);
618                         return 1;
619                 }
620         }
621
622         /* If the object is currently locked by this thread... */
623         if (mon_status_get_owner (old_status) == id) {
624                 mon->nest++;
625                 mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_DONE);
626                 return 1;
627         }
628
629         /* We need to make sure there's a semaphore handle (creating it if
630          * necessary), and block on it
631          */
632         if (mon->entry_sem == NULL) {
633                 /* Create the semaphore */
634                 sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL);
635                 g_assert (sem != NULL);
636                 if (InterlockedCompareExchangePointer ((gpointer*)&mon->entry_sem, sem, NULL) != NULL) {
637                         /* Someone else just put a handle here */
638                         CloseHandle (sem);
639                 }
640         }
641
642         /*
643          * We need to register ourselves as waiting if it is the first time we are waiting,
644          * of if we were signaled and failed to acquire the lock.
645          */
646         if (!interrupted) {
647                 old_status = mon->status;
648                 for (;;) {
649                         if (mon_status_get_owner (old_status) == 0)
650                                 goto retry_contended;
651                         new_status = mon_status_increment_entry_count (old_status);
652                         tmp_status = InterlockedCompareExchange ((gint32*)&mon->status, new_status, old_status);
653                         if (tmp_status == old_status) {
654                                 break;
655                         }
656                         old_status = tmp_status;
657                 }
658         }
659
660         if (ms != INFINITE) {
661                 then = mono_msec_ticks ();
662         }
663         waitms = ms;
664         
665 #ifndef DISABLE_PERFCOUNTERS
666         mono_perfcounters->thread_queue_len++;
667         mono_perfcounters->thread_queue_max++;
668 #endif
669         thread = mono_thread_internal_current ();
670
671         mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
672
673         /*
674          * We pass TRUE instead of allow_interruption since we have to check for the
675          * StopRequested case below.
676          */
677         MONO_PREPARE_BLOCKING
678         ret = WaitForSingleObjectEx (mon->entry_sem, waitms, TRUE);
679         MONO_FINISH_BLOCKING
680
681         mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
682         
683 #ifndef DISABLE_PERFCOUNTERS
684         mono_perfcounters->thread_queue_len--;
685 #endif
686
687         if (ret == WAIT_IO_COMPLETION && !allow_interruption) {
688                 interrupted = TRUE;
689                 /* 
690                  * We have to obey a stop/suspend request even if 
691                  * allow_interruption is FALSE to avoid hangs at shutdown.
692                  */
693                 if (!mono_thread_test_state (mono_thread_internal_current (), (ThreadState_StopRequested|ThreadState_SuspendRequested))) {
694                         if (ms != INFINITE) {
695                                 now = mono_msec_ticks ();
696                                 if (now < then) {
697                                         LOCK_DEBUG (g_message ("%s: wrapped around! now=0x%x then=0x%x", __func__, now, then));
698
699                                         now += (0xffffffff - then);
700                                         then = 0;
701
702                                         LOCK_DEBUG (g_message ("%s: wrap rejig: now=0x%x then=0x%x delta=0x%x", __func__, now, then, now-then));
703                                 }
704
705                                 delta = now - then;
706                                 if (delta >= ms) {
707                                         ms = 0;
708                                 } else {
709                                         ms -= delta;
710                                 }
711                         }
712                         /* retry from the top */
713                         goto retry_contended;
714                 }
715         } else if (ret == WAIT_OBJECT_0) {
716                 interrupted = FALSE;
717                 /* retry from the top */
718                 goto retry_contended;
719         } else if (ret == WAIT_TIMEOUT) {
720                 /* we're done */
721         }
722
723         /* Timed out or interrupted */
724         mon_decrement_entry_count (mon);
725
726         mono_profiler_monitor_event (obj, MONO_PROFILER_MONITOR_FAIL);
727
728         if (ret == WAIT_IO_COMPLETION) {
729                 LOCK_DEBUG (g_message ("%s: (%d) interrupted waiting, returning -1", __func__, id));
730                 return -1;
731         } else if (ret == WAIT_TIMEOUT) {
732                 LOCK_DEBUG (g_message ("%s: (%d) timed out waiting, returning FALSE", __func__, id));
733                 return 0;
734         } else {
735                 g_assert_not_reached ();
736                 return 0;
737         }
738 }
739
740 gboolean 
741 mono_monitor_enter (MonoObject *obj)
742 {
743         return mono_monitor_try_enter_internal (obj, INFINITE, FALSE) == 1;
744 }
745
746 gboolean 
747 mono_monitor_try_enter (MonoObject *obj, guint32 ms)
748 {
749         return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1;
750 }
751
752 void
753 mono_monitor_exit (MonoObject *obj)
754 {
755         MonoThreadsSync *mon;
756         guint32 nest;
757         guint32 new_status, old_status, tmp_status;
758         
759         LOCK_DEBUG (g_message ("%s: (%d) Unlocking %p", __func__, mono_thread_info_get_small_id (), obj));
760
761         if (G_UNLIKELY (!obj)) {
762                 mono_raise_exception (mono_get_exception_argument_null ("obj"));
763                 return;
764         }
765
766         mon = obj->synchronisation;
767
768 #ifdef HAVE_MOVING_COLLECTOR
769         {
770                 LockWord lw;
771                 lw.sync = mon;
772                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
773                         return;
774                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
775                 mon = lw.sync;
776         }
777 #endif
778         if (G_UNLIKELY (mon == NULL)) {
779                 /* No one ever used Enter. Just ignore the Exit request as MS does */
780                 return;
781         }
782
783         old_status = mon->status;
784         if (G_UNLIKELY (mon_status_get_owner (old_status) != mono_thread_info_get_small_id ())) {
785                 return;
786         }
787         
788         nest = mon->nest - 1;
789         if (nest == 0) {
790                 /*
791                  * Release lock and do the wakeup stuff. It's possible that
792                  * the last blocking thread gave up waiting just before we
793                  * release the semaphore resulting in a negative entry count
794                  * and a futile wakeup next time there's contention for this
795                  * object.
796                  */
797                 for (;;) {
798                         gboolean have_waiters = mon_status_have_waiters (old_status);
799         
800                         new_status = mon_status_set_owner (old_status, 0);
801                         if (have_waiters)
802                                 new_status = mon_status_decrement_entry_count (new_status);
803                         tmp_status = InterlockedCompareExchange ((gint32*)&mon->status, new_status, old_status);
804                         if (tmp_status == old_status) {
805                                 if (have_waiters)
806                                         ReleaseSemaphore (mon->entry_sem, 1, NULL);
807                                 break;
808                         }
809                         old_status = tmp_status;
810                 }
811                 LOCK_DEBUG (g_message ("%s: (%d) Object %p is now unlocked", __func__, mono_thread_info_get_small_id (), obj));
812         
813                 /* object is now unlocked, leave nest==1 so we don't
814                  * need to set it when the lock is reacquired
815                  */
816
817         } else {
818                 LOCK_DEBUG (g_message ("%s: (%d) Object %p is now locked %d times", __func__, mono_thread_info_get_small_id (), obj, nest));
819                 mon->nest = nest;
820         }
821 }
822
823 void**
824 mono_monitor_get_object_monitor_weak_link (MonoObject *object)
825 {
826         LockWord lw;
827         MonoThreadsSync *sync = NULL;
828
829         lw.sync = object->synchronisation;
830         if (lw.lock_word & LOCK_WORD_FAT_HASH) {
831                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
832                 sync = lw.sync;
833         } else if (!(lw.lock_word & LOCK_WORD_THIN_HASH)) {
834                 sync = lw.sync;
835         }
836
837         if (sync && sync->data)
838                 return &sync->data;
839         return NULL;
840 }
841
842 /*
843  * mono_monitor_threads_sync_member_offset:
844  * @status_offset: returns size and offset of the "status" member
845  * @nest_offset: returns size and offset of the "nest" member
846  *
847  * Returns the offsets and sizes of two members of the
848  * MonoThreadsSync struct.  The Monitor ASM fastpaths need this.
849  */
850 void
851 mono_monitor_threads_sync_members_offset (int *status_offset, int *nest_offset)
852 {
853         MonoThreadsSync ts;
854
855 #define ENCODE_OFF_SIZE(o,s)    (((o) << 8) | ((s) & 0xff))
856
857         *status_offset = ENCODE_OFF_SIZE (MONO_STRUCT_OFFSET (MonoThreadsSync, status), sizeof (ts.status));
858         *nest_offset = ENCODE_OFF_SIZE (MONO_STRUCT_OFFSET (MonoThreadsSync, nest), sizeof (ts.nest));
859 }
860
861 gboolean 
862 ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 ms)
863 {
864         gint32 res;
865
866         do {
867                 res = mono_monitor_try_enter_internal (obj, ms, TRUE);
868                 if (res == -1)
869                         mono_thread_interruption_checkpoint ();
870         } while (res == -1);
871         
872         return res == 1;
873 }
874
875 void
876 ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (MonoObject *obj, guint32 ms, char *lockTaken)
877 {
878         gint32 res;
879         do {
880                 res = mono_monitor_try_enter_internal (obj, ms, TRUE);
881                 /*This means we got interrupted during the wait and didn't got the monitor.*/
882                 if (res == -1)
883                         mono_thread_interruption_checkpoint ();
884         } while (res == -1);
885         /*It's safe to do it from here since interruption would happen only on the wrapper.*/
886         *lockTaken = res == 1;
887 }
888
889 void
890 mono_monitor_enter_v4 (MonoObject *obj, char *lock_taken)
891 {
892         if (*lock_taken == 1)
893                 mono_raise_exception (mono_get_exception_argument ("lockTaken", "lockTaken is already true"));
894
895         ves_icall_System_Threading_Monitor_Monitor_try_enter_with_atomic_var (obj, INFINITE, lock_taken);
896 }
897
898 gboolean 
899 ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj)
900 {
901         MonoThreadsSync *mon;
902         
903         LOCK_DEBUG (g_message ("%s: Testing if %p is owned by thread %d", __func__, obj, mono_thread_info_get_small_id()));
904
905         mon = obj->synchronisation;
906 #ifdef HAVE_MOVING_COLLECTOR
907         {
908                 LockWord lw;
909                 lw.sync = mon;
910                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
911                         return FALSE;
912                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
913                 mon = lw.sync;
914         }
915 #endif
916         if (mon == NULL) {
917                 return FALSE;
918         }
919         
920         if (mon_status_get_owner (mon->status) == mono_thread_info_get_small_id ()) {
921                 return(TRUE);
922         }
923         
924         return(FALSE);
925 }
926
927 gboolean 
928 ves_icall_System_Threading_Monitor_Monitor_test_synchronised (MonoObject *obj)
929 {
930         MonoThreadsSync *mon;
931
932         LOCK_DEBUG (g_message("%s: (%d) Testing if %p is owned by any thread", __func__, mono_thread_info_get_small_id (), obj));
933         
934         mon = obj->synchronisation;
935 #ifdef HAVE_MOVING_COLLECTOR
936         {
937                 LockWord lw;
938                 lw.sync = mon;
939                 if (lw.lock_word & LOCK_WORD_THIN_HASH)
940                         return FALSE;
941                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
942                 mon = lw.sync;
943         }
944 #endif
945         if (mon == NULL) {
946                 return FALSE;
947         }
948         
949         if (mon_status_get_owner (mon->status) != 0) {
950                 return TRUE;
951         }
952         
953         return FALSE;
954 }
955
956 /* All wait list manipulation in the pulse, pulseall and wait
957  * functions happens while the monitor lock is held, so we don't need
958  * any extra struct locking
959  */
960
961 void
962 ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj)
963 {
964         MonoThreadsSync *mon;
965         
966         LOCK_DEBUG (g_message ("%s: (%d) Pulsing %p", __func__, mono_thread_info_get_small_id (), obj));
967         
968         mon = obj->synchronisation;
969 #ifdef HAVE_MOVING_COLLECTOR
970         {
971                 LockWord lw;
972                 lw.sync = mon;
973                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
974                         mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked"));
975                         return;
976                 }
977                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
978                 mon = lw.sync;
979         }
980 #endif
981         if (mon == NULL) {
982                 mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked"));
983                 return;
984         }
985         if (mon_status_get_owner (mon->status) != mono_thread_info_get_small_id ()) {
986                 mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
987                 return;
988         }
989
990         LOCK_DEBUG (g_message ("%s: (%d) %d threads waiting", __func__, mono_thread_info_get_small_id (), g_slist_length (mon->wait_list)));
991         
992         if (mon->wait_list != NULL) {
993                 LOCK_DEBUG (g_message ("%s: (%d) signalling and dequeuing handle %p", __func__, mono_thread_info_get_small_id (), mon->wait_list->data));
994         
995                 SetEvent (mon->wait_list->data);
996                 mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
997         }
998 }
999
1000 void
1001 ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj)
1002 {
1003         MonoThreadsSync *mon;
1004         
1005         LOCK_DEBUG (g_message("%s: (%d) Pulsing all %p", __func__, mono_thread_info_get_small_id (), obj));
1006
1007         mon = obj->synchronisation;
1008 #ifdef HAVE_MOVING_COLLECTOR
1009         {
1010                 LockWord lw;
1011                 lw.sync = mon;
1012                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
1013                         mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked"));
1014                         return;
1015                 }
1016                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
1017                 mon = lw.sync;
1018         }
1019 #endif
1020         if (mon == NULL) {
1021                 mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked"));
1022                 return;
1023         }
1024         if (mon_status_get_owner (mon->status) != mono_thread_info_get_small_id ()) {
1025                 mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
1026                 return;
1027         }
1028
1029         LOCK_DEBUG (g_message ("%s: (%d) %d threads waiting", __func__, mono_thread_info_get_small_id (), g_slist_length (mon->wait_list)));
1030
1031         while (mon->wait_list != NULL) {
1032                 LOCK_DEBUG (g_message ("%s: (%d) signalling and dequeuing handle %p", __func__, mono_thread_info_get_small_id (), mon->wait_list->data));
1033         
1034                 SetEvent (mon->wait_list->data);
1035                 mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data);
1036         }
1037 }
1038
1039 gboolean
1040 ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms)
1041 {
1042         MonoThreadsSync *mon;
1043         HANDLE event;
1044         guint32 nest;
1045         guint32 ret;
1046         gboolean success = FALSE;
1047         gint32 regain;
1048         MonoInternalThread *thread = mono_thread_internal_current ();
1049
1050         LOCK_DEBUG (g_message ("%s: (%d) Trying to wait for %p with timeout %dms", __func__, mono_thread_info_get_small_id (), obj, ms));
1051         
1052         mon = obj->synchronisation;
1053 #ifdef HAVE_MOVING_COLLECTOR
1054         {
1055                 LockWord lw;
1056                 lw.sync = mon;
1057                 if (lw.lock_word & LOCK_WORD_THIN_HASH) {
1058                         mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked"));
1059                         return FALSE;
1060                 }
1061                 lw.lock_word &= ~LOCK_WORD_BITS_MASK;
1062                 mon = lw.sync;
1063         }
1064 #endif
1065         if (mon == NULL) {
1066                 mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked"));
1067                 return FALSE;
1068         }
1069         if (mon_status_get_owner (mon->status) != mono_thread_info_get_small_id ()) {
1070                 mono_set_pending_exception (mono_get_exception_synchronization_lock ("Not locked by this thread"));
1071                 return FALSE;
1072         }
1073
1074         /* Do this WaitSleepJoin check before creating the event handle */
1075         mono_thread_current_check_pending_interrupt ();
1076         
1077         event = CreateEvent (NULL, FALSE, FALSE, NULL);
1078         if (event == NULL) {
1079                 mono_set_pending_exception (mono_get_exception_synchronization_lock ("Failed to set up wait event"));
1080                 return FALSE;
1081         }
1082         
1083         LOCK_DEBUG (g_message ("%s: (%d) queuing handle %p", __func__, mono_thread_info_get_small_id (), event));
1084
1085         mono_thread_current_check_pending_interrupt ();
1086         
1087         mono_thread_set_state (thread, ThreadState_WaitSleepJoin);
1088
1089         mon->wait_list = g_slist_append (mon->wait_list, event);
1090         
1091         /* Save the nest count, and release the lock */
1092         nest = mon->nest;
1093         mon->nest = 1;
1094         mono_monitor_exit (obj);
1095
1096         LOCK_DEBUG (g_message ("%s: (%d) Unlocked %p lock %p", __func__, mono_thread_info_get_small_id (), obj, mon));
1097
1098         /* There's no race between unlocking mon and waiting for the
1099          * event, because auto reset events are sticky, and this event
1100          * is private to this thread.  Therefore even if the event was
1101          * signalled before we wait, we still succeed.
1102          */
1103         MONO_PREPARE_BLOCKING
1104         ret = WaitForSingleObjectEx (event, ms, TRUE);
1105         MONO_FINISH_BLOCKING
1106
1107         /* Reset the thread state fairly early, so we don't have to worry
1108          * about the monitor error checking
1109          */
1110         mono_thread_clr_state (thread, ThreadState_WaitSleepJoin);
1111         
1112         if (mono_thread_interruption_requested ()) {
1113                 /* 
1114                  * Can't remove the event from wait_list, since the monitor is not locked by
1115                  * us. So leave it there, mon_new () will delete it when the mon structure
1116                  * is placed on the free list.
1117                  * FIXME: The caller expects to hold the lock after the wait returns, but it
1118                  * doesn't happen in this case:
1119                  * http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=97268
1120                  */
1121                 return FALSE;
1122         }
1123
1124         /* Regain the lock with the previous nest count */
1125         do {
1126                 regain = mono_monitor_try_enter_internal (obj, INFINITE, TRUE);
1127                 if (regain == -1) 
1128                         mono_thread_interruption_checkpoint ();
1129         } while (regain == -1);
1130
1131         if (regain == 0) {
1132                 /* Something went wrong, so throw a
1133                  * SynchronizationLockException
1134                  */
1135                 CloseHandle (event);
1136                 mono_set_pending_exception (mono_get_exception_synchronization_lock ("Failed to regain lock"));
1137                 return FALSE;
1138         }
1139
1140         mon->nest = nest;
1141
1142         LOCK_DEBUG (g_message ("%s: (%d) Regained %p lock %p", __func__, mono_thread_info_get_small_id (), obj, mon));
1143
1144         if (ret == WAIT_TIMEOUT) {
1145                 /* Poll the event again, just in case it was signalled
1146                  * while we were trying to regain the monitor lock
1147                  */
1148                 MONO_PREPARE_BLOCKING
1149                 ret = WaitForSingleObjectEx (event, 0, FALSE);
1150                 MONO_FINISH_BLOCKING
1151         }
1152
1153         /* Pulse will have popped our event from the queue if it signalled
1154          * us, so we only do it here if the wait timed out.
1155          *
1156          * This avoids a race condition where the thread holding the
1157          * lock can Pulse several times before the WaitForSingleObject
1158          * returns.  If we popped the queue here then this event might
1159          * be signalled more than once, thereby starving another
1160          * thread.
1161          */
1162         
1163         if (ret == WAIT_OBJECT_0) {
1164                 LOCK_DEBUG (g_message ("%s: (%d) Success", __func__, mono_thread_info_get_small_id ()));
1165                 success = TRUE;
1166         } else {
1167                 LOCK_DEBUG (g_message ("%s: (%d) Wait failed, dequeuing handle %p", __func__, mono_thread_info_get_small_id (), event));
1168                 /* No pulse, so we have to remove ourself from the
1169                  * wait queue
1170                  */
1171                 mon->wait_list = g_slist_remove (mon->wait_list, event);
1172         }
1173         CloseHandle (event);
1174         
1175         return success;
1176 }
1177