X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fmetadata%2Fmonitor.c;h=55dddd39c616f893ead5bfff7bd066eb48cdf842;hb=dc1155ddcf876d543da9a162a69e14d4903d2ac8;hp=8f195de9025116d0eb4849f58e797e26cb6a97c6;hpb=2d30f49947f5d291359de7459c84e87093948d33;p=mono.git diff --git a/mono/metadata/monitor.c b/mono/metadata/monitor.c index 8f195de9025..55dddd39c61 100644 --- a/mono/metadata/monitor.c +++ b/mono/metadata/monitor.c @@ -53,8 +53,11 @@ struct _MonoThreadsSync { - guint32 owner; /* thread ID */ + gsize owner; /* thread ID */ guint32 nest; +#ifdef HAVE_MOVING_COLLECTOR + gint32 hash_code; +#endif volatile guint32 entry_count; HANDLE entry_sem; GSList *wait_list; @@ -69,6 +72,8 @@ struct _MonitorArray { 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; @@ -79,6 +84,12 @@ mono_monitor_init (void) { InitializeCriticalSection (&monitor_mutex); } + +void +mono_monitor_cleanup (void) +{ + DeleteCriticalSection (&monitor_mutex); +} /* LOCKING: this is called with monitor_mutex held */ static void @@ -105,7 +116,7 @@ mon_finalize (MonoThreadsSync *mon) /* LOCKING: this is called with monitor_mutex held */ static MonoThreadsSync * -mon_new (guint32 id) +mon_new (gsize id) { MonoThreadsSync *new; @@ -162,6 +173,96 @@ mon_new (guint32 id) 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. + */ + 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 +} + /* If allow_interruption==TRUE, the method will be interrumped if abort or suspend * is requested. In this case it returns -1. */ @@ -169,7 +270,7 @@ 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 = 0, now, delta; guint32 waitms; @@ -183,20 +284,85 @@ retry: /* If the object has never been locked... */ if (mon == NULL) { - EnterCriticalSection (&monitor_mutex); + mono_monitor_allocator_lock (); mon = mon_new (id); if (InterlockedCompareExchangePointer ((gpointer*)&obj->synchronisation, mon, NULL) == NULL) { mono_gc_weak_link_add (&mon->data, obj); - LeaveCriticalSection (&monitor_mutex); + mono_monitor_allocator_unlock (); /* Successfully locked */ return 1; } else { +#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); - LeaveCriticalSection (&monitor_mutex); - goto retry; + mono_monitor_allocator_unlock (); + mon = obj->synchronisation; +#endif } + } 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) { mon->nest++; @@ -213,7 +379,7 @@ retry: * 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 1; @@ -337,6 +503,16 @@ mono_monitor_exit (MonoObject *obj) mon = obj->synchronisation; +#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; @@ -401,6 +577,16 @@ ves_icall_System_Threading_Monitor_Monitor_test_owner (MonoObject *obj) ": 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 if (mon == NULL) { return FALSE; } @@ -421,6 +607,16 @@ ves_icall_System_Threading_Monitor_Monitor_test_synchronised (MonoObject *obj) ": (%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 if (mon == NULL) { return FALSE; } @@ -446,6 +642,18 @@ ves_icall_System_Threading_Monitor_Monitor_pulse (MonoObject *obj) 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 if (mon == NULL) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); return; @@ -477,6 +685,18 @@ ves_icall_System_Threading_Monitor_Monitor_pulse_all (MonoObject *obj) 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 if (mon == NULL) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); return; @@ -515,6 +735,18 @@ ves_icall_System_Threading_Monitor_Monitor_wait (MonoObject *obj, guint32 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 if (mon == NULL) { mono_raise_exception (mono_get_exception_synchronization_lock ("Not locked")); return FALSE;