X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fmonitor.c;h=1e05eaddc43c7d9cdc9c7deda8bdf252ae36ba02;hb=c28164a651e968a5f4704db98d26e3d9e3f522aa;hp=341e2faaceefa7e4f74f9792ff3ca5f7780703cb;hpb=8702850b3f5f0e6d28603068ec302cddfcb6e1cf;p=mono.git diff --git a/mono/metadata/monitor.c b/mono/metadata/monitor.c index 341e2faacee..1e05eaddc43 100644 --- a/mono/metadata/monitor.c +++ b/mono/metadata/monitor.c @@ -13,11 +13,13 @@ #include #include #include +#include #include +#include +#include -#include - -#undef THREAD_LOCK_DEBUG +/*#define LOCK_DEBUG(a) do { a; } while (0)*/ +#define LOCK_DEBUG(a) /* * The monitor implementation here is based on @@ -49,82 +51,379 @@ * an object. */ - -static void mon_finalize (void *o, void *unused) +struct _MonoThreadsSync { - MonoThreadsSync *mon=(MonoThreadsSync *)o; - -#ifdef THREAD_LOCK_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync %p", mon); + gsize owner; /* thread ID */ + guint32 nest; +#ifdef HAVE_MOVING_COLLECTOR + gint32 hash_code; #endif + volatile gint32 entry_count; + HANDLE entry_sem; + GSList *wait_list; + void *data; +}; + +typedef struct _MonitorArray MonitorArray; + +struct _MonitorArray { + MonitorArray *next; + int num_monitors; + MonoThreadsSync monitors [MONO_ZERO_LEN_ARRAY]; +}; + +#define mono_monitor_allocator_lock() EnterCriticalSection (&monitor_mutex) +#define mono_monitor_allocator_unlock() LeaveCriticalSection (&monitor_mutex) +static CRITICAL_SECTION monitor_mutex; +static MonoThreadsSync *monitor_freelist; +static MonitorArray *monitor_allocated; +static int array_size = 16; + +void +mono_monitor_init (void) +{ + InitializeCriticalSection (&monitor_mutex); +} + +void +mono_monitor_cleanup (void) +{ + /*DeleteCriticalSection (&monitor_mutex);*/ +} + +static int +monitor_is_on_freelist (MonoThreadsSync *mon) +{ + MonitorArray *marray; + for (marray = monitor_allocated; marray; marray = marray->next) { + if (mon >= marray->monitors && mon < &marray->monitors [marray->num_monitors]) + return TRUE; + } + return FALSE; +} + +/** + * mono_locks_dump: + * @include_untaken: + * + * Print a report on stdout of the managed locks currently held by + * threads. If @include_untaken is specified, list also inflated locks + * which are unheld. + * This is supposed to be used in debuggers like gdb. + */ +void +mono_locks_dump (gboolean include_untaken) +{ + int i; + int used = 0, on_freelist = 0, to_recycle = 0, total = 0, num_arrays = 0; + MonoThreadsSync *mon; + MonitorArray *marray; + for (mon = monitor_freelist; mon; mon = mon->data) + on_freelist++; + for (marray = monitor_allocated; marray; marray = marray->next) { + total += marray->num_monitors; + num_arrays++; + for (i = 0; i < marray->num_monitors; ++i) { + mon = &marray->monitors [i]; + if (mon->data == NULL) { + if (i < marray->num_monitors - 1) + to_recycle++; + } else { + if (!monitor_is_on_freelist (mon->data)) { + MonoObject *holder = mono_gc_weak_link_get (&mon->data); + if (mon->owner) { + g_print ("Lock %p in object %p held by thread %p, nest level: %d\n", + mon, holder, (void*)mon->owner, mon->nest); + if (mon->entry_sem) + g_print ("\tWaiting on semaphore %p: %d\n", mon->entry_sem, mon->entry_count); + } else if (include_untaken) { + g_print ("Lock %p in object %p untaken\n", mon, holder); + } + used++; + } + } + } + } + g_print ("Total locks (in %d array(s)): %d, used: %d, on freelist: %d, to recycle: %d\n", + num_arrays, total, used, on_freelist, to_recycle); +} - if(mon->entry_sem!=NULL) { +/* LOCKING: this is called with monitor_mutex held */ +static void +mon_finalize (MonoThreadsSync *mon) +{ + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": Finalizing sync %p", mon)); + + if (mon->entry_sem != NULL) { CloseHandle (mon->entry_sem); + mon->entry_sem = NULL; } /* If this isn't empty then something is seriously broken - it * means a thread is still waiting on the object that owned * this lock, but the object has been finalized. */ - g_assert (mon->wait_list==NULL); + g_assert (mon->wait_list == NULL); + + mon->entry_count = 0; + /* owner and nest are set in mon_new, no need to zero them out */ + + mon->data = monitor_freelist; + monitor_freelist = mon; } -static MonoThreadsSync *mon_new(guint32 id) +/* LOCKING: this is called with monitor_mutex held */ +static MonoThreadsSync * +mon_new (gsize id) { MonoThreadsSync *new; + + if (!monitor_freelist) { + MonitorArray *marray; + int i; + /* see if any sync block has been collected */ + new = NULL; + for (marray = monitor_allocated; marray; marray = marray->next) { + for (i = 0; i < marray->num_monitors; ++i) { + if (marray->monitors [i].data == NULL) { + new = &marray->monitors [i]; + new->data = monitor_freelist; + monitor_freelist = new; + } + } + /* small perf tweak to avoid scanning all the blocks */ + if (new) + break; + } + /* need to allocate a new array of monitors */ + if (!monitor_freelist) { + MonitorArray *last; + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": allocating more monitors: %d", array_size)); + marray = g_malloc0 (sizeof (MonoArray) + array_size * sizeof (MonoThreadsSync)); + marray->num_monitors = array_size; + array_size *= 2; + /* link into the freelist */ + for (i = 0; i < marray->num_monitors - 1; ++i) { + marray->monitors [i].data = &marray->monitors [i + 1]; + } + marray->monitors [i].data = NULL; /* the last one */ + monitor_freelist = &marray->monitors [0]; + /* we happend the marray instead of prepending so that + * the collecting loop above will need to scan smaller arrays first + */ + if (!monitor_allocated) { + monitor_allocated = marray; + } else { + last = monitor_allocated; + while (last->next) + last = last->next; + last->next = marray; + } + } + } + + new = monitor_freelist; + monitor_freelist = new->data; + + new->owner = id; + new->nest = 1; -#if HAVE_BOEHM_GC - new=(MonoThreadsSync *)GC_MALLOC (sizeof(MonoThreadsSync)); - GC_REGISTER_FINALIZER (new, mon_finalize, NULL, NULL, NULL); -#else - /* This should be freed when the object that owns it is - * deleted + return new; +} + +/* + * Format of the lock word: + * thinhash | fathash | data + * + * thinhash is the lower bit: if set data is the shifted hashcode of the object. + * fathash is another bit: if set the hash code is stored in the MonoThreadsSync + * struct pointed to by data + * if neither bit is set and data is non-NULL, data is a MonoThreadsSync + */ +typedef union { + gsize lock_word; + MonoThreadsSync *sync; +} LockWord; + +enum { + LOCK_WORD_THIN_HASH = 1, + LOCK_WORD_FAT_HASH = 1 << 1, + LOCK_WORD_BITS_MASK = 0x3, + LOCK_WORD_HASH_SHIFT = 2 +}; + +#define MONO_OBJECT_ALIGNMENT_SHIFT 3 + +/* + * mono_object_hash: + * @obj: an object + * + * Calculate a hash code for @obj that is constant while @obj is alive. + */ +int +mono_object_hash (MonoObject* obj) +{ +#ifdef HAVE_MOVING_COLLECTOR + LockWord lw; + unsigned int hash; + if (!obj) + return 0; + lw.sync = obj->synchronisation; + if (lw.lock_word & LOCK_WORD_THIN_HASH) { + /*g_print ("fast thin hash %d for obj %p store\n", (unsigned int)lw.lock_word >> LOCK_WORD_HASH_SHIFT, obj);*/ + return (unsigned int)lw.lock_word >> LOCK_WORD_HASH_SHIFT; + } + if (lw.lock_word & LOCK_WORD_FAT_HASH) { + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + /*g_print ("fast fat hash %d for obj %p store\n", lw.sync->hash_code, obj);*/ + return lw.sync->hash_code; + } + /* + * while we are inside this function, the GC will keep this object pinned, + * since we are in the unmanaged stack. Thanks to this and to the hash + * function that depends only on the address, we can ignore the races if + * another thread computes the hash at the same time, because it'll end up + * with the same value. */ - new=(MonoThreadsSync *)g_new0 (MonoThreadsSync, 1); + hash = (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u; + /* clear the top bits as they can be discarded */ + hash &= ~(LOCK_WORD_BITS_MASK << 30); + /* no hash flags were set, so it must be a MonoThreadsSync pointer if not NULL */ + if (lw.sync) { + lw.sync->hash_code = hash; + /*g_print ("storing hash code %d for obj %p in sync %p\n", hash, obj, lw.sync);*/ + lw.lock_word |= LOCK_WORD_FAT_HASH; + /* this is safe since we don't deflate locks */ + obj->synchronisation = lw.sync; + } else { + /*g_print ("storing thin hash code %d for obj %p\n", hash, obj);*/ + lw.lock_word = LOCK_WORD_THIN_HASH | (hash << LOCK_WORD_HASH_SHIFT); + if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, NULL) == NULL) + return hash; + /*g_print ("failed store\n");*/ + /* someone set the hash flag or someone inflated the object */ + lw.sync = obj->synchronisation; + if (lw.lock_word & LOCK_WORD_THIN_HASH) + return hash; + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + lw.sync->hash_code = hash; + lw.lock_word |= LOCK_WORD_FAT_HASH; + /* this is safe since we don't deflate locks */ + obj->synchronisation = lw.sync; + } + return hash; +#else +/* + * Wang's address-based hash function: + * http://www.concentric.net/~Ttwang/tech/addrhash.htm + */ + return (GPOINTER_TO_UINT (obj) >> MONO_OBJECT_ALIGNMENT_SHIFT) * 2654435761u; #endif - new->owner=id; - new->nest=1; - - return(new); } -gboolean mono_monitor_try_enter (MonoObject *obj, guint32 ms) +/* If allow_interruption==TRUE, the method will be interrumped if abort or suspend + * is requested. In this case it returns -1. + */ +static gint32 +mono_monitor_try_enter_internal (MonoObject *obj, guint32 ms, gboolean allow_interruption) { MonoThreadsSync *mon; - guint32 id=GetCurrentThreadId (); + gsize id = GetCurrentThreadId (); HANDLE sem; - guint32 then, now, delta; + guint32 then = 0, now, delta; guint32 waitms; guint32 ret; -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": (%d) Trying to lock object %p (%d ms)", id, obj, ms); -#endif + LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION + ": (%d) Trying to lock object %p (%d ms)", id, obj, ms)); retry: - mon=obj->synchronisation; + mon = obj->synchronisation; /* If the object has never been locked... */ - if(mon==NULL) { - mon=mon_new(id); - if(InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL)==NULL) { + if (mon == NULL) { + mono_monitor_allocator_lock (); + mon = mon_new (id); + if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL) == NULL) { + mono_gc_weak_link_add (&mon->data, obj); + mono_monitor_allocator_unlock (); /* Successfully locked */ - return(TRUE); + return 1; } else { - /* Another thread got in first, so try again. - * GC will take care of the monitor record - */ -#ifndef HAVE_BOEHM_GC - mon_finalize (mon, NULL); +#ifdef HAVE_MOVING_COLLECTOR + LockWord lw; + lw.sync = obj->synchronisation; + if (lw.lock_word & LOCK_WORD_THIN_HASH) { + MonoThreadsSync *oldlw = lw.sync; + /* move the already calculated hash */ + mon->hash_code = lw.lock_word >> LOCK_WORD_HASH_SHIFT; + lw.sync = mon; + lw.lock_word |= LOCK_WORD_FAT_HASH; + if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) { + mono_gc_weak_link_add (&mon->data, obj); + mono_monitor_allocator_unlock (); + /* Successfully locked */ + return 1; + } else { + mon_finalize (mon); + mono_monitor_allocator_unlock (); + goto retry; + } + } else if (lw.lock_word & LOCK_WORD_FAT_HASH) { + mon_finalize (mon); + mono_monitor_allocator_unlock (); + /* get the old lock without the fat hash bit */ + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + mon = lw.sync; + } else { + mon_finalize (mon); + mono_monitor_allocator_unlock (); + mon = obj->synchronisation; + } +#else + mon_finalize (mon); + mono_monitor_allocator_unlock (); + mon = obj->synchronisation; #endif - goto retry; } + } else { +#ifdef HAVE_MOVING_COLLECTOR + LockWord lw; + lw.sync = mon; + if (lw.lock_word & LOCK_WORD_THIN_HASH) { + MonoThreadsSync *oldlw = lw.sync; + mono_monitor_allocator_lock (); + mon = mon_new (id); + /* move the already calculated hash */ + mon->hash_code = lw.lock_word >> LOCK_WORD_HASH_SHIFT; + lw.sync = mon; + lw.lock_word |= LOCK_WORD_FAT_HASH; + if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, lw.sync, oldlw) == oldlw) { + mono_gc_weak_link_add (&mon->data, obj); + mono_monitor_allocator_unlock (); + /* Successfully locked */ + return 1; + } else { + mon_finalize (mon); + mono_monitor_allocator_unlock (); + goto retry; + } + } +#endif } +#ifdef HAVE_MOVING_COLLECTOR + { + LockWord lw; + lw.sync = mon; + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + mon = lw.sync; + } +#endif + /* If the object is currently locked by this thread... */ - if(mon->owner==id) { + if (mon->owner == id) { mon->nest++; - return(TRUE); + return 1; } /* If the object has previously been locked but isn't now... */ @@ -132,15 +431,15 @@ retry: /* This case differs from Dice's case 3 because we don't * deflate locks or cache unused lock records */ - if(mon->owner==0) { + if (mon->owner == 0) { /* Try to install our ID in the owner field, nest * should have been left at 1 by the previous unlock * operation */ - if(InterlockedCompareExchange (&mon->owner, id, 0)==0) { + if (InterlockedCompareExchangePointer ((gpointer *)&mon->owner, (gpointer)id, 0) == 0) { /* Success */ - g_assert (mon->nest==1); - return(TRUE); + g_assert (mon->nest == 1); + return 1; } else { /* Trumped again! */ goto retry; @@ -150,23 +449,20 @@ retry: /* The object must be locked by someone else... */ /* If ms is 0 we don't block, but just fail straight away */ - if(ms==0) { -#ifdef THREAD_LOCK_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": (%d) timed out, returning FALSE", id); -#endif - - return(FALSE); + if (ms == 0) { + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out, returning FALSE", id)); + return 0; } /* The slow path begins here. We need to make sure theres a * semaphore handle (creating it if necessary), and block on * it */ - if(mon->entry_sem==NULL) { + if (mon->entry_sem == NULL) { /* Create the semaphore */ - sem=CreateSemaphore (NULL, 0, 0x7fffffff, NULL); - if(InterlockedCompareExchangePointer ((gpointer*)&mon->entry_sem, sem, NULL)!=NULL) { + sem = CreateSemaphore (NULL, 0, 0x7fffffff, NULL); + g_assert (sem != NULL); + if (InterlockedCompareExchangePointer ((gpointer*)&mon->entry_sem, sem, NULL) != NULL) { /* Someone else just put a handle here */ CloseHandle (sem); } @@ -182,105 +478,116 @@ retry: * handle technique from pulse/wait would involve locking the * lock struct and therefore slowing down the fast path. */ - if(ms!=INFINITE) { - then=GetTickCount (); - if(ms<100) { - waitms=ms; + if (ms != INFINITE) { + then = GetTickCount (); + if (ms < 100) { + waitms = ms; } else { - waitms=100; + waitms = 100; } } else { - waitms=100; + waitms = 100; } InterlockedIncrement (&mon->entry_count); - ret=WaitForSingleObject (mon->entry_sem, waitms); + ret = WaitForSingleObjectEx (mon->entry_sem, waitms, allow_interruption); InterlockedDecrement (&mon->entry_count); - - if(ms!=INFINITE) { - now=GetTickCount (); + + if (ms != INFINITE) { + now = GetTickCount (); - if(now= ms) { - ms=0; + delta = now - then; + if (delta >= ms) { + ms = 0; } else { - ms-=delta; + ms -= delta; } - if(ret==WAIT_TIMEOUT && ms>0) { + if ((ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) && ms > 0) { /* More time left */ goto retry; } } else { - if(ret==WAIT_TIMEOUT) { + if (ret == WAIT_TIMEOUT || (ret == WAIT_IO_COMPLETION && !allow_interruption)) { /* Infinite wait, so just try again */ goto retry; } } - if(ret==WAIT_OBJECT_0) { + if (ret == WAIT_OBJECT_0) { /* retry from the top */ goto retry; } /* We must have timed out */ -#ifdef THREAD_LOCK_DEBUG - g_message (G_GNUC_PRETTY_FUNCTION - ": (%d) timed out waiting, returning FALSE", id); -#endif + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) timed out waiting, returning FALSE", id)); - return(FALSE); + if (ret == WAIT_IO_COMPLETION) + return -1; + else + return 0; } -void mono_monitor_exit (MonoObject *obj) +gboolean +mono_monitor_enter (MonoObject *obj) +{ + return mono_monitor_try_enter_internal (obj, INFINITE, FALSE) == 1; +} + +gboolean +mono_monitor_try_enter (MonoObject *obj, guint32 ms) +{ + return mono_monitor_try_enter_internal (obj, ms, FALSE) == 1; +} + +void +mono_monitor_exit (MonoObject *obj) { MonoThreadsSync *mon; guint32 nest; -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p", - GetCurrentThreadId (), obj); -#endif + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocking %p", GetCurrentThreadId (), obj)); - mon=obj->synchronisation; + mon = obj->synchronisation; - if(mon==NULL) { - mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); +#ifdef HAVE_MOVING_COLLECTOR + { + LockWord lw; + lw.sync = mon; + if (lw.lock_word & LOCK_WORD_THIN_HASH) + return; + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + mon = lw.sync; + } +#endif + if (mon == NULL) { + /* No one ever used Enter. Just ignore the Exit request as MS does */ return; } - if(mon->owner!=GetCurrentThreadId ()) { - mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread")); + if (mon->owner != GetCurrentThreadId ()) { return; } - nest=mon->nest-1; - if(nest==0) { -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": (%d) Object %p is now unlocked", - GetCurrentThreadId (), obj); -#endif + nest = mon->nest - 1; + if (nest == 0) { + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION + ": (%d) Object %p is now unlocked", GetCurrentThreadId (), obj)); /* object is now unlocked, leave nest==1 so we don't * need to set it when the lock is reacquired */ - mon->owner=0; + mon->owner = 0; /* Do the wakeup stuff. It's possible that the last * blocking thread gave up waiting just before we @@ -289,50 +596,57 @@ void mono_monitor_exit (MonoObject *obj) * it means we don't have to waste time locking the * struct. */ - if(mon->entry_count>0) { + if (mon->entry_count > 0) { ReleaseSemaphore (mon->entry_sem, 1, NULL); } } else { -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": (%d) Object %p is now locked %d times", - GetCurrentThreadId (), obj, - nest); -#endif - mon->nest=nest; + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION + ": (%d) Object %p is now locked %d times", GetCurrentThreadId (), obj, nest)); + mon->nest = nest; } } -gboolean ves_icall_System_Threading_Monitor_Monitor_try_enter(MonoObject *obj, - guint32 ms) +gboolean +ves_icall_System_Threading_Monitor_Monitor_try_enter (MonoObject *obj, guint32 ms) { - MONO_ARCH_SAVE_REGS; + gint32 res; - return(mono_monitor_try_enter (obj, ms)); + do { + res = mono_monitor_try_enter_internal (obj, ms, TRUE); + if (res == -1) + mono_thread_interruption_checkpoint (); + } while (res == -1); + + return res == 1; } -void ves_icall_System_Threading_Monitor_Monitor_exit(MonoObject *obj) +void +ves_icall_System_Threading_Monitor_Monitor_exit (MonoObject *obj) { - MONO_ARCH_SAVE_REGS; - - return(mono_monitor_exit (obj)); + mono_monitor_exit (obj); } -gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj) +gboolean +ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj) { MonoThreadsSync *mon; - MONO_ARCH_SAVE_REGS; - -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": Testing if %p is owned by thread %d", obj, - GetCurrentThreadId()); + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION + ": Testing if %p is owned by thread %d", obj, GetCurrentThreadId())); + + mon = obj->synchronisation; +#ifdef HAVE_MOVING_COLLECTOR + { + LockWord lw; + lw.sync = mon; + if (lw.lock_word & LOCK_WORD_THIN_HASH) + return FALSE; + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + mon = lw.sync; + } #endif - - mon=obj->synchronisation; - if(mon==NULL) { - return(FALSE); + if (mon == NULL) { + return FALSE; } if(mon->owner==GetCurrentThreadId ()) { @@ -342,28 +656,34 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_test_owner(MonoObject *obj) return(FALSE); } -gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject *obj) +gboolean +ves_icall_System_Threading_Monitor_Monitor_test_synchronised (MonoObject *obj) { MonoThreadsSync *mon; - - MONO_ARCH_SAVE_REGS; -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION - ": (%d) Testing if %p is owned by any thread", - GetCurrentThreadId (), obj); + LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION + ": (%d) Testing if %p is owned by any thread", GetCurrentThreadId (), obj)); + + mon = obj->synchronisation; +#ifdef HAVE_MOVING_COLLECTOR + { + LockWord lw; + lw.sync = mon; + if (lw.lock_word & LOCK_WORD_THIN_HASH) + return FALSE; + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + mon = lw.sync; + } #endif - - mon=obj->synchronisation; - if(mon==NULL) { - return(FALSE); + if (mon == NULL) { + return FALSE; } - if(mon->owner!=0) { - return(TRUE); + if (mon->owner != 0) { + return TRUE; } - return(FALSE); + return FALSE; } /* All wait list manipulation in the pulse, pulseall and wait @@ -371,164 +691,200 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_test_synchronised(MonoObject * any extra struct locking */ -void ves_icall_System_Threading_Monitor_Monitor_pulse(MonoObject *obj) +void +ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj) { MonoThreadsSync *mon; - MONO_ARCH_SAVE_REGS; - -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing %p", - GetCurrentThreadId (), obj); + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing %p", + GetCurrentThreadId (), obj)); + + mon = obj->synchronisation; +#ifdef HAVE_MOVING_COLLECTOR + { + LockWord lw; + lw.sync = mon; + if (lw.lock_word & LOCK_WORD_THIN_HASH) { + mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); + return; + } + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + mon = lw.sync; + } #endif - - mon=obj->synchronisation; - if(mon==NULL) { + if (mon == NULL) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); return; } - if(mon->owner!=GetCurrentThreadId ()) { + if (mon->owner != GetCurrentThreadId ()) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread")); return; } -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting", - GetCurrentThreadId (), g_slist_length (mon->wait_list)); -#endif + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting", + GetCurrentThreadId (), g_slist_length (mon->wait_list))); - if(mon->wait_list!=NULL) { -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION + if (mon->wait_list != NULL) { + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) signalling and dequeuing handle %p", - GetCurrentThreadId (), mon->wait_list->data); -#endif + GetCurrentThreadId (), mon->wait_list->data)); SetEvent (mon->wait_list->data); - mon->wait_list=g_slist_remove (mon->wait_list, - mon->wait_list->data); + mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data); } } -void ves_icall_System_Threading_Monitor_Monitor_pulse_all(MonoObject *obj) +void +ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj) { MonoThreadsSync *mon; - MONO_ARCH_SAVE_REGS; - -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing all %p", - GetCurrentThreadId (), obj); + LOCK_DEBUG (g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Pulsing all %p", + GetCurrentThreadId (), obj)); + + mon = obj->synchronisation; +#ifdef HAVE_MOVING_COLLECTOR + { + LockWord lw; + lw.sync = mon; + if (lw.lock_word & LOCK_WORD_THIN_HASH) { + mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); + return; + } + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + mon = lw.sync; + } #endif - - mon=obj->synchronisation; - if(mon==NULL) { + if (mon == NULL) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); return; } - if(mon->owner!=GetCurrentThreadId ()) { + if (mon->owner != GetCurrentThreadId ()) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread")); return; } -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting", - GetCurrentThreadId (), g_slist_length (mon->wait_list)); -#endif - - while(mon->wait_list!=NULL) { -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) %d threads waiting", + GetCurrentThreadId (), g_slist_length (mon->wait_list))); + + while (mon->wait_list != NULL) { + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) signalling and dequeuing handle %p", - GetCurrentThreadId (), mon->wait_list->data); -#endif + GetCurrentThreadId (), mon->wait_list->data)); SetEvent (mon->wait_list->data); - mon->wait_list=g_slist_remove (mon->wait_list, - mon->wait_list->data); + mon->wait_list = g_slist_remove (mon->wait_list, mon->wait_list->data); } } -gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj, - guint32 ms) +gboolean +ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 ms) { MonoThreadsSync *mon; HANDLE event; guint32 nest; guint32 ret; - gboolean success=FALSE, regain; - - MONO_ARCH_SAVE_REGS; + gboolean success = FALSE; + gint32 regain; + MonoThread *thread = mono_thread_current (); -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Trying to wait for %p with timeout %dms", - GetCurrentThreadId (), obj, ms); + GetCurrentThreadId (), obj, ms)); + + mon = obj->synchronisation; +#ifdef HAVE_MOVING_COLLECTOR + { + LockWord lw; + lw.sync = mon; + if (lw.lock_word & LOCK_WORD_THIN_HASH) { + mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); + return FALSE; + } + lw.lock_word &= ~LOCK_WORD_BITS_MASK; + mon = lw.sync; + } #endif - - mon=obj->synchronisation; - if(mon==NULL) { + if (mon == NULL) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); - return(FALSE); + return FALSE; } - if(mon->owner!=GetCurrentThreadId ()) { + if (mon->owner != GetCurrentThreadId ()) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked by this thread")); - return(FALSE); + return FALSE; } + + /* Do this WaitSleepJoin check before creating the event handle */ + mono_thread_current_check_pending_interrupt (); - event=CreateEvent (NULL, FALSE, FALSE, NULL); - if(event==NULL) { + event = CreateEvent (NULL, FALSE, FALSE, NULL); + if (event == NULL) { mono_raise_exception (mono_get_exception_synchronization_lock ("Failed to set up wait event")); - return(FALSE); + return FALSE; } -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) queuing handle %p", - GetCurrentThreadId (), event); -#endif + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) queuing handle %p", + GetCurrentThreadId (), event)); - mon->wait_list=g_slist_append (mon->wait_list, event); + mono_monitor_enter (thread->synch_lock); + thread->state |= ThreadState_WaitSleepJoin; + mono_monitor_exit (thread->synch_lock); + + mon->wait_list = g_slist_append (mon->wait_list, event); /* Save the nest count, and release the lock */ - nest=mon->nest; - mon->nest=1; + nest = mon->nest; + mon->nest = 1; mono_monitor_exit (obj); -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Unlocked %p lock %p", - GetCurrentThreadId (), obj, mon); -#endif + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Unlocked %p lock %p", + GetCurrentThreadId (), obj, mon)); /* There's no race between unlocking mon and waiting for the * event, because auto reset events are sticky, and this event * is private to this thread. Therefore even if the event was * signalled before we wait, we still succeed. */ - ret=WaitForSingleObject (event, ms); + ret = WaitForSingleObjectEx (event, ms, TRUE); + + /* Reset the thread state fairly early, so we don't have to worry + * about the monitor error checking + */ + mono_monitor_enter (thread->synch_lock); + thread->state &= ~ThreadState_WaitSleepJoin; + mono_monitor_exit (thread->synch_lock); + if (mono_thread_interruption_requested ()) { + CloseHandle (event); + return FALSE; + } + /* Regain the lock with the previous nest count */ - regain=mono_monitor_try_enter (obj, INFINITE); - if(regain==FALSE) { + do { + regain = mono_monitor_try_enter_internal (obj, INFINITE, TRUE); + if (regain == -1) + mono_thread_interruption_checkpoint (); + } while (regain == -1); + + if (regain == 0) { /* Something went wrong, so throw a * SynchronizationLockException */ CloseHandle (event); mono_raise_exception (mono_get_exception_synchronization_lock ("Failed to regain lock")); - return(FALSE); + return FALSE; } - mon->nest=nest; + mon->nest = nest; -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Regained %p lock %p", - GetCurrentThreadId (), obj, mon); -#endif + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Regained %p lock %p", + GetCurrentThreadId (), obj, mon)); - if(ret==WAIT_TIMEOUT) { + if (ret == WAIT_TIMEOUT) { /* Poll the event again, just in case it was signalled * while we were trying to regain the monitor lock */ - ret=WaitForSingleObject (event, 0); + ret = WaitForSingleObjectEx (event, 0, FALSE); } /* Pulse will have popped our event from the queue if it signalled @@ -541,26 +897,20 @@ gboolean ves_icall_System_Threading_Monitor_Monitor_wait(MonoObject *obj, * thread. */ - if(ret==WAIT_OBJECT_0) { -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Success", - GetCurrentThreadId ()); -#endif - success=TRUE; + if (ret == WAIT_OBJECT_0) { + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Success", + GetCurrentThreadId ())); + success = TRUE; } else { -#ifdef THREAD_LOCK_DEBUG - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed", - GetCurrentThreadId ()); - g_message(G_GNUC_PRETTY_FUNCTION ": (%d) dequeuing handle %p", - GetCurrentThreadId (), event); -#endif + LOCK_DEBUG (g_message (G_GNUC_PRETTY_FUNCTION ": (%d) Wait failed, dequeuing handle %p", + GetCurrentThreadId (), event)); /* No pulse, so we have to remove ourself from the * wait queue */ - mon->wait_list=g_slist_remove (mon->wait_list, event); + mon->wait_list = g_slist_remove (mon->wait_list, event); } CloseHandle (event); - return(success); + return success; }