X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FPicoSpinLock.h;h=eb62bee21b2ef63d412f593f64b8daa0d3994a7e;hb=c60784b8bc21ff4a420da59e29fa0aec3abd0f8a;hp=f2ca45c2acbb2015d32049ff2033cf84442e679d;hpb=80c7ef06203a251bdc5ea88134847e386970f50d;p=folly.git diff --git a/folly/PicoSpinLock.h b/folly/PicoSpinLock.h index f2ca45c2..eb62bee2 100644 --- a/folly/PicoSpinLock.h +++ b/folly/PicoSpinLock.h @@ -1,5 +1,5 @@ /* - * Copyright 2015 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,7 +14,24 @@ * limitations under the License. */ +/* + * N.B. You most likely do _not_ want to use PicoSpinLock or any other + * kind of spinlock. Consider MicroLock instead. + * + * In short, spinlocks in preemptive multi-tasking operating systems + * have serious problems and fast mutexes like std::mutex are almost + * certainly the better choice, because letting the OS scheduler put a + * thread to sleep is better for system responsiveness and throughput + * than wasting a timeslice repeatedly querying a lock held by a + * thread that's blocked, and you can't prevent userspace + * programs blocking. + * + * Spinlocks in an operating system kernel make much more sense than + * they do in userspace. + */ + #pragma once +#define FOLLY_PICO_SPIN_LOCK_H_ /* * @author Keith Adams @@ -22,19 +39,19 @@ */ #include +#include #include -#include #include -#include #include -#include +#include #include -#include + #include +#include -#if !FOLLY_X64 && !FOLLY_A64 && !FOLLY_PPC64 -# error "PicoSpinLock.h is currently x64, aarch64 and ppc64 only." +#if !FOLLY_X64 && !FOLLY_AARCH64 && !FOLLY_PPC64 +#error "PicoSpinLock.h is currently x64, aarch64 and ppc64 only." #endif namespace folly { @@ -52,7 +69,7 @@ namespace folly { * have a real constructor because we want this to be a POD type so we * can put it into packed structs. */ -template +template struct PicoSpinLock { // Internally we deal with the unsigned version of the type. typedef typename std::make_unsigned::type UIntType; @@ -65,7 +82,7 @@ struct PicoSpinLock { public: static const UIntType kLockBitMask_ = UIntType(1) << Bit; - UIntType lock_; + mutable UIntType lock_; /* * You must call this function before using this class, if you @@ -77,7 +94,8 @@ struct PicoSpinLock { */ void init(IntType initialValue = 0) { CHECK(!(initialValue & kLockBitMask_)); - lock_ = initialValue; + reinterpret_cast*>(&lock_)->store( + UIntType(initialValue), std::memory_order_release); } /* @@ -90,7 +108,10 @@ struct PicoSpinLock { * as you normally get.) */ IntType getData() const { - return static_cast(lock_ & ~kLockBitMask_); + auto res = reinterpret_cast*>(&lock_)->load( + std::memory_order_relaxed) & + ~kLockBitMask_; + return res; } /* @@ -101,7 +122,10 @@ struct PicoSpinLock { */ void setData(IntType w) { CHECK(!(w & kLockBitMask_)); - lock_ = (lock_ & kLockBitMask_) | w; + auto l = reinterpret_cast*>(&lock_); + l->store( + (l->load(std::memory_order_relaxed) & kLockBitMask_) | w, + std::memory_order_relaxed); } /* @@ -111,7 +135,28 @@ struct PicoSpinLock { bool try_lock() const { bool ret = false; -#if FOLLY_X64 +#if defined(FOLLY_SANITIZE_THREAD) + // TODO: Might be able to fully move to std::atomic when gcc emits lock btr: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=49244 + ret = + !(reinterpret_cast*>(&lock_)->fetch_or( + kLockBitMask_, std::memory_order_acquire) & + kLockBitMask_); +#elif _MSC_VER + switch (sizeof(IntType)) { + case 2: + // There is no _interlockedbittestandset16 for some reason :( + ret = _InterlockedOr16( + (volatile short*)&lock_, (short)kLockBitMask_) & kLockBitMask_; + break; + case 4: + ret = _interlockedbittestandset((volatile long*)&lock_, Bit); + break; + case 8: + ret = _interlockedbittestandset64((volatile long long*)&lock_, Bit); + break; + } +#elif FOLLY_X64 #define FB_DOBTS(size) \ asm volatile("lock; bts" #size " %1, (%2); setnc %0" \ : "=r" (ret) \ @@ -126,8 +171,11 @@ struct PicoSpinLock { } #undef FB_DOBTS -#elif FOLLY_A64 - ret = __atomic_fetch_or(&lock_, 1 << Bit, __ATOMIC_SEQ_CST); +#elif FOLLY_AARCH64 + using SIntType = typename std::make_signed::type; + auto const lock = reinterpret_cast(&lock_); + auto const mask = static_cast(kLockBitMask_); + return !(mask & __atomic_fetch_or(lock, mask, __ATOMIC_ACQUIRE)); #elif FOLLY_PPC64 #define FB_DOBTS(size) \ asm volatile("\teieio\n" \ @@ -176,7 +224,20 @@ struct PicoSpinLock { * integer. */ void unlock() const { -#if FOLLY_X64 +#ifdef _MSC_VER + switch (sizeof(IntType)) { + case 2: + // There is no _interlockedbittestandreset16 for some reason :( + _InterlockedAnd16((volatile short*)&lock_, (short)~kLockBitMask_); + break; + case 4: + _interlockedbittestandreset((volatile long*)&lock_, Bit); + break; + case 8: + _interlockedbittestandreset64((volatile long long*)&lock_, Bit); + break; + } +#elif FOLLY_X64 #define FB_DOBTR(size) \ asm volatile("lock; btr" #size " %0, (%1)" \ : \ @@ -194,8 +255,11 @@ struct PicoSpinLock { } #undef FB_DOBTR -#elif FOLLY_A64 - __atomic_fetch_and(&lock_, ~(1 << Bit), __ATOMIC_SEQ_CST); +#elif FOLLY_AARCH64 + using SIntType = typename std::make_signed::type; + auto const lock = reinterpret_cast(&lock_); + auto const mask = static_cast(kLockBitMask_); + __atomic_fetch_and(lock, ~mask, __ATOMIC_RELEASE); #elif FOLLY_PPC64 #define FB_DOBTR(size) \ asm volatile("\teieio\n" \ @@ -224,4 +288,4 @@ struct PicoSpinLock { } }; -} +} // namespace folly