| //===------------------ Locks.h - Thread locks ----------------------------===// |
| // |
| // The Micro Virtual Machine |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef MVM_LOCKS_H |
| #define MVM_LOCKS_H |
| |
| #include <pthread.h> |
| #include <cassert> |
| #include <cstdio> |
| |
| #include "mvm/Threads/Thread.h" |
| |
| namespace mvm { |
| |
| extern "C" uint8 llvm_atomic_cmp_swap_i8(uint8* ptr, uint8 cmp, |
| uint8 val); |
| extern "C" uint16 llvm_atomic_cmp_swap_i16(uint16* ptr, uint16 cmp, |
| uint16 val); |
| extern "C" uint32 llvm_atomic_cmp_swap_i32(uint32* ptr, uint32 cmp, |
| uint32 val); |
| extern "C" uint64 llvm_atomic_cmp_swap_i64(uint64* ptr, uint64 cmp, |
| uint64 val); |
| |
| #ifndef WITH_LLVM_GCC |
| |
| // TODO: find what macro for gcc < 4.2 |
| |
| #define __sync_bool_compare_and_swap_32(ptr, cmp, val) \ |
| mvm::llvm_atomic_cmp_swap_i32((uint32*)(ptr), (uint32)(cmp), \ |
| (uint32)(val)) == (uint32)(cmp) |
| |
| #if (__WORDSIZE == 64) |
| |
| #define __sync_bool_compare_and_swap(ptr, cmp, val) \ |
| mvm::llvm_atomic_cmp_swap_i64((uint64*)(ptr), (uint64)(cmp), \ |
| (uint64)(val)) == (uint64)(cmp) |
| |
| #define __sync_val_compare_and_swap(ptr, cmp,val) \ |
| mvm::llvm_atomic_cmp_swap_i64((uint64*)(ptr), (uint64)(cmp), \ |
| (uint64)(val)) |
| |
| |
| #else |
| |
| |
| |
| #define __sync_bool_compare_and_swap(ptr, cmp, val) \ |
| mvm::llvm_atomic_cmp_swap_i32((uint32*)(ptr), (uint32)(cmp), \ |
| (uint32)(val)) == (uint32)(cmp) |
| |
| #define __sync_val_compare_and_swap(ptr, cmp,val) \ |
| mvm::llvm_atomic_cmp_swap_i32((uint32*)(ptr), (uint32)(cmp), \ |
| (uint32)(val)) |
| #endif |
| |
| |
| #endif |
| |
| class Cond; |
| class LockNormal; |
| class LockRecursive; |
| class Thread; |
| |
| /// Lock - This class is an abstract class for declaring recursive and normal |
| /// locks. |
| /// |
| class Lock { |
| friend class Cond; |
| |
| private: |
| virtual void unsafeLock(int n) = 0; |
| virtual int unsafeUnlock() = 0; |
| |
| protected: |
| /// owner - Which thread is currently holding the lock? |
| /// |
| mvm::Thread* owner; |
| |
| /// internalLock - The lock implementation of the platform. |
| /// |
| pthread_mutex_t internalLock; |
| |
| |
| public: |
| |
| /// Lock - Creates a lock, recursive if rec is true. |
| /// |
| Lock(); |
| |
| /// ~Lock - Give it a home. |
| /// |
| virtual ~Lock(); |
| |
| /// lock - Acquire the lock. |
| /// |
| virtual void lock() = 0; |
| |
| /// unlock - Release the lock. |
| /// |
| virtual void unlock() = 0; |
| |
| /// selfOwner - Is the current thread holding the lock? |
| /// |
| bool selfOwner(); |
| |
| /// getOwner - Get the thread that is holding the lock. |
| /// |
| mvm::Thread* getOwner(); |
| |
| }; |
| |
| /// LockNormal - A non-recursive lock. |
| class LockNormal : public Lock { |
| friend class Cond; |
| private: |
| virtual void unsafeLock(int n) { |
| owner = mvm::Thread::get(); |
| } |
| |
| virtual int unsafeUnlock() { |
| owner = 0; |
| return 0; |
| } |
| public: |
| LockNormal() : Lock() {} |
| |
| virtual void lock(); |
| virtual void unlock(); |
| |
| }; |
| |
| /// LockRecursive - A recursive lock. |
| class LockRecursive : public Lock { |
| friend class Cond; |
| private: |
| |
| /// n - Number of times the lock has been locked. |
| /// |
| int n; |
| |
| virtual void unsafeLock(int a) { |
| n = a; |
| owner = mvm::Thread::get(); |
| } |
| |
| virtual int unsafeUnlock() { |
| int ret = n; |
| n = 0; |
| owner = 0; |
| return ret; |
| } |
| |
| public: |
| LockRecursive() : Lock() { n = 0; } |
| |
| virtual void lock(); |
| virtual void unlock(); |
| virtual int tryLock(); |
| |
| /// recursionCount - Get the number of times the lock has been locked. |
| /// |
| int recursionCount() { return n; } |
| |
| /// unlockAll - Unlock the lock, releasing it the number of times it is held. |
| /// Return the number of times the lock has been locked. |
| /// |
| int unlockAll(); |
| |
| /// lockAll - Acquire the lock count times. |
| /// |
| void lockAll(int count); |
| }; |
| |
| #if (__WORDSIZE == 64) |
| static const uint64_t FatMask = 0x8000000000000000; |
| #else |
| static const uint64_t FatMask = 0x80000000; |
| #endif |
| |
| static const uint64_t ThinCountMask = 0xFF000; |
| static const uint64_t ThinCountShift = 12; |
| static const uint64_t ThinCountAdd = 0x1000; |
| // Mask for all GC objects. |
| static const uint64_t GCMask = 0xFFF; |
| // Mask for the hash code bits. |
| static const uint64_t HashMask = 0xFFC; |
| // Mask for the GC bits. |
| static const uint64_t GCBitMask = 0x3; |
| |
| |
| |
| #ifdef WITH_LLVM_GCC |
| extern "C" void __llvm_gcroot(const void**, void*) __attribute__((nothrow)); |
| #define llvm_gcroot(a, b) __llvm_gcroot((const void**)&a, b) |
| #else |
| #define llvm_gcroot(a, b) |
| #endif |
| |
| class FatLockNoGC { |
| public: |
| static void gcroot(void* val, void* unused) |
| __attribute__ ((always_inline)) {} |
| |
| static uintptr_t mask() { |
| return 0; |
| } |
| }; |
| |
| class FatLockWithGC { |
| public: |
| static void gcroot(void* val, void* unused) |
| __attribute__ ((always_inline)) { |
| llvm_gcroot(val, unused); |
| } |
| |
| static uintptr_t mask() { |
| return GCMask; |
| } |
| }; |
| |
| |
| /// ThinLock - This class is an implementation of thin locks. The template class |
| /// TFatLock is a virtual machine specific fat lock. |
| /// |
| template <class TFatLock, class Owner, class IsGC> |
| class ThinLock { |
| public: |
| uintptr_t lock; |
| |
| |
| |
| |
| /// overflowThinlock - Change the lock of this object to a fat lock because |
| /// we have reached 0xFF locks. |
| void overflowThinLock(Owner* O) { |
| IsGC::gcroot(O, 0); |
| TFatLock* obj = TFatLock::allocate(O); |
| obj->acquireAll((ThinCountMask >> ThinCountShift) + 1); |
| uintptr_t oldLock = lock; |
| lock = obj->getID() | (oldLock & IsGC::mask()); |
| } |
| |
| /// initialise - Initialise the value of the lock. |
| /// |
| void initialise() { |
| lock = lock & IsGC::mask(); |
| } |
| |
| /// ThinLock - Calls initialize. |
| ThinLock() { |
| initialise(); |
| } |
| |
| |
| /// changeToFatlock - Change the lock of this object to a fat lock. The lock |
| /// may be in a thin lock or fat lock state. |
| TFatLock* changeToFatlock(Owner* O) { |
| IsGC::gcroot(O, 0); |
| if (!(lock & FatMask)) { |
| TFatLock* obj = TFatLock::allocate(O); |
| uint32 count = (lock & ThinCountMask) >> ThinCountShift; |
| obj->acquireAll(count + 1); |
| uintptr_t oldLock = lock; |
| lock = obj->getID() | (oldLock & IsGC::mask()); |
| return obj; |
| } else { |
| TFatLock* res = TFatLock::getFromID(lock); |
| assert(res && "Lock deallocated while held."); |
| return res; |
| } |
| } |
| |
| /// acquire - Acquire the lock. |
| void acquire(Owner* O) { |
| IsGC::gcroot(O, 0); |
| start: |
| uint64_t id = mvm::Thread::get()->getThreadID(); |
| uintptr_t oldValue = lock; |
| uintptr_t newValue = id | (lock & IsGC::mask()); |
| uintptr_t val = __sync_val_compare_and_swap(&lock, oldValue & IsGC::mask(), |
| newValue); |
| |
| if (val != (oldValue & IsGC::mask())) { |
| //fat! |
| if (!(val & FatMask)) { |
| if ((val & Thread::IDMask) == id) { |
| if ((val & ThinCountMask) != ThinCountMask) { |
| lock += ThinCountAdd; |
| } else { |
| overflowThinLock(O); |
| } |
| } else { |
| TFatLock* obj = TFatLock::allocate(O); |
| uintptr_t val = obj->getID(); |
| loop: |
| while (lock & ~IsGC::mask()) { |
| if (lock & FatMask) { |
| obj->deallocate(); |
| goto end; |
| } |
| else mvm::Thread::yield(); |
| } |
| |
| oldValue = lock; |
| newValue = val | (lock & IsGC::mask()); |
| uintptr_t test = __sync_val_compare_and_swap(&lock, |
| oldValue & IsGC::mask(), |
| newValue); |
| if (test != (oldValue & IsGC::mask())) goto loop; |
| if (!obj->acquire(O)) goto start; |
| } |
| } else { |
| |
| end: |
| TFatLock* obj = TFatLock::getFromID(lock); |
| if (obj) { |
| if (!obj->acquire(O)) goto start; |
| } else { |
| goto start; |
| } |
| } |
| } |
| |
| assert(owner() && "Not owner after quitting acquire!"); |
| } |
| |
| /// release - Release the lock. |
| void release(Owner* O) { |
| IsGC::gcroot(O, 0); |
| assert(owner() && "Not owner when entering release!"); |
| uint64 id = mvm::Thread::get()->getThreadID(); |
| if ((lock & ~IsGC::mask()) == id) { |
| lock = lock & IsGC::mask(); |
| } else if (lock & FatMask) { |
| TFatLock* obj = TFatLock::getFromID(lock); |
| assert(obj && "Lock deallocated while held."); |
| obj->release(O); |
| } else { |
| lock -= ThinCountAdd; |
| } |
| } |
| |
| /// broadcast - Wakes up all threads waiting for this lock. |
| /// |
| void broadcast() { |
| if (lock & FatMask) { |
| TFatLock* obj = TFatLock::getFromID(lock); |
| assert(obj && "Lock deallocated while held."); |
| obj->broadcast(); |
| } |
| } |
| |
| /// signal - Wakes up one thread waiting for this lock. |
| void signal() { |
| if (lock & FatMask) { |
| TFatLock* obj = TFatLock::getFromID(lock); |
| assert(obj && "Lock deallocated while held."); |
| obj->signal(); |
| } |
| } |
| |
| /// owner - Returns true if the curren thread is the owner of this object's |
| /// lock. |
| bool owner() { |
| if (lock & FatMask) { |
| TFatLock* obj = TFatLock::getFromID(lock); |
| if (obj) return obj->owner(); |
| } else { |
| uint64 id = mvm::Thread::get()->getThreadID(); |
| if ((lock & Thread::IDMask) == id) return true; |
| } |
| return false; |
| } |
| |
| mvm::Thread* getOwner() { |
| if (lock & FatMask) { |
| TFatLock* obj = TFatLock::getFromID(lock); |
| if (obj) return obj->getOwner(); |
| return 0; |
| } else { |
| return (mvm::Thread*)(lock & mvm::Thread::IDMask); |
| } |
| } |
| |
| /// getFatLock - Get the fat lock is the lock is a fat lock, 0 otherwise. |
| TFatLock* getFatLock() { |
| if (lock & FatMask) { |
| return TFatLock::getFromID(lock); |
| } else { |
| return 0; |
| } |
| } |
| }; |
| |
| |
| /// SpinLock - This class implements a spin lock. A spin lock is OK to use |
| /// when it is held during short period of times. It is CPU expensive |
| /// otherwise. |
| class SpinLock { |
| public: |
| |
| /// locked - Is the spin lock locked? |
| /// |
| uint32 locked; |
| |
| /// SpinLock - Initialize the lock as not being held. |
| /// |
| SpinLock() { locked = 0; } |
| |
| |
| /// acquire - Acquire the spin lock, doing an active loop. |
| /// |
| void acquire() { |
| for (uint32 count = 0; count < 1000; ++count) { |
| uint32 res = __sync_val_compare_and_swap(&locked, 0, 1); |
| if (!res) return; |
| } |
| |
| while (__sync_val_compare_and_swap(&locked, 0, 1)) |
| mvm::Thread::yield(); |
| } |
| |
| void lock() { acquire(); } |
| |
| /// release - Release the spin lock. This must be called by the thread |
| /// holding it. |
| /// |
| void release() { locked = 0; } |
| |
| void unlock() { release(); } |
| }; |
| |
| |
| } // end namespace mvm |
| |
| #endif // MVM_LOCKS_H |