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