+/*
+ * 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
+}
+