mirror of
https://github.com/mutouyun/cpp-ipc.git
synced 2025-12-07 01:06:45 +08:00
upd: [ipc] spin lock
This commit is contained in:
parent
985ef22f64
commit
4e4f22e3a8
@ -9,59 +9,18 @@
|
|||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <limits>
|
|
||||||
#include <type_traits>
|
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "libimp/detect_plat.h"
|
#include "libimp/export.h"
|
||||||
|
|
||||||
#include "libipc/def.h"
|
#include "libipc/def.h"
|
||||||
|
|
||||||
|
LIBIPC_NAMESPACE_BEG_
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Gives hint to processor that improves performance of spin-wait loops.
|
* @brief Gives hint to processor that improves performance of spin-wait loops.
|
||||||
*/
|
*/
|
||||||
|
LIBIMP_EXPORT void pause() noexcept;
|
||||||
#pragma push_macro("LIBIPC_LOCK_PAUSE_")
|
|
||||||
#undef LIBIPC_LOCK_PAUSE_
|
|
||||||
|
|
||||||
#if defined(LIBIPC_CC_MSVC)
|
|
||||||
# include <Windows.h> // YieldProcessor
|
|
||||||
/**
|
|
||||||
* @brief Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168
|
|
||||||
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx
|
|
||||||
*/
|
|
||||||
# define LIBIPC_LOCK_PAUSE_() YieldProcessor()
|
|
||||||
#elif defined(LIBIPC_CC_GNUC)
|
|
||||||
# if defined(LIBIPC_INSTR_X86_64)
|
|
||||||
/**
|
|
||||||
* @brief Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2
|
|
||||||
* PAUSE-Spin Loop Hint, 4-57
|
|
||||||
* @see http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference
|
|
||||||
*/
|
|
||||||
# define LIBIPC_LOCK_PAUSE_() __asm__ __volatile__("pause")
|
|
||||||
# elif defined(LIBIPC_INSTR_I64)
|
|
||||||
/**
|
|
||||||
* @brief Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3
|
|
||||||
* hint - Performance Hint, 3:145
|
|
||||||
* @see http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html
|
|
||||||
*/
|
|
||||||
# define LIBIPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause")
|
|
||||||
# elif defined(LIBIPC_INSTR_ARM)
|
|
||||||
/**
|
|
||||||
* @brief ARM Architecture Reference Manuals (YIELD)
|
|
||||||
* @see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html
|
|
||||||
*/
|
|
||||||
# define LIBIPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield")
|
|
||||||
# endif
|
|
||||||
#endif /*compilers*/
|
|
||||||
|
|
||||||
#if !defined(LIBIPC_LOCK_PAUSE_)
|
|
||||||
/**
|
|
||||||
* @brief Just use a compiler fence, prevent compiler from optimizing loop
|
|
||||||
*/
|
|
||||||
# define LIBIPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst)
|
|
||||||
#endif /*!defined(LIBIPC_LOCK_PAUSE_)*/
|
|
||||||
|
|
||||||
LIBIPC_NAMESPACE_BEG_
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Yield to other threads
|
* @brief Yield to other threads
|
||||||
@ -71,7 +30,7 @@ template <typename K>
|
|||||||
inline void yield(K &k) noexcept {
|
inline void yield(K &k) noexcept {
|
||||||
if (k < 4) { /* Do nothing */ }
|
if (k < 4) { /* Do nothing */ }
|
||||||
else
|
else
|
||||||
if (k < 16) { LIBIPC_LOCK_PAUSE_(); }
|
if (k < 16) { pause(); }
|
||||||
else
|
else
|
||||||
if (k < 32) { std::this_thread::yield(); }
|
if (k < 32) { std::this_thread::yield(); }
|
||||||
else {
|
else {
|
||||||
@ -96,86 +55,32 @@ inline void sleep(K &k, F &&f) {
|
|||||||
template <std::size_t N = 32, typename K>
|
template <std::size_t N = 32, typename K>
|
||||||
inline void sleep(K &k) {
|
inline void sleep(K &k) {
|
||||||
sleep<N>(k, [] {
|
sleep<N>(k, [] {
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
std::this_thread::sleep_for(std::chrono::microseconds(1));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Basic spin lock
|
* @brief Basic spin lock
|
||||||
*/
|
*/
|
||||||
class spin_lock {
|
class LIBIMP_EXPORT spin_lock {
|
||||||
std::atomic<unsigned> lc_ {0};
|
std::atomic<unsigned> lc_ {0};
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void lock(void) noexcept {
|
void lock() noexcept;
|
||||||
for (unsigned k = 0;
|
void unlock() noexcept;
|
||||||
lc_.exchange(1, std::memory_order_acquire);
|
|
||||||
yield(k)) ;
|
|
||||||
}
|
|
||||||
void unlock(void) noexcept {
|
|
||||||
lc_.store(0, std::memory_order_release);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Support for shared mode spin lock
|
* @brief Support for shared mode spin lock
|
||||||
*/
|
*/
|
||||||
class rw_lock {
|
class LIBIMP_EXPORT rw_lock {
|
||||||
using lc_ui_t = unsigned;
|
std::atomic<unsigned> lc_ {0};
|
||||||
|
|
||||||
std::atomic<lc_ui_t> lc_ {0};
|
|
||||||
|
|
||||||
enum : lc_ui_t {
|
|
||||||
w_mask = (std::numeric_limits<std::make_signed_t<lc_ui_t>>::max)(), // b 0111 1111
|
|
||||||
w_flag = w_mask + 1, // b 1000 0000
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
rw_lock() noexcept = default;
|
void lock() noexcept;
|
||||||
|
void unlock() noexcept;
|
||||||
rw_lock(const rw_lock &) = delete;
|
void lock_shared() noexcept;
|
||||||
rw_lock &operator=(const rw_lock &) = delete;
|
void unlock_shared() noexcept;
|
||||||
rw_lock(rw_lock &&) = delete;
|
|
||||||
rw_lock &operator=(rw_lock &&) = delete;
|
|
||||||
|
|
||||||
void lock() noexcept {
|
|
||||||
for (unsigned k = 0;;) {
|
|
||||||
auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel);
|
|
||||||
if (!old) return; // got w-lock
|
|
||||||
if (!(old & w_flag)) break; // other thread having r-lock
|
|
||||||
yield(k); // other thread having w-lock
|
|
||||||
}
|
|
||||||
// wait for reading finished
|
|
||||||
for (unsigned k = 0;
|
|
||||||
lc_.load(std::memory_order_acquire) & w_mask;
|
|
||||||
yield(k)) ;
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock() noexcept {
|
|
||||||
lc_.store(0, std::memory_order_release);
|
|
||||||
}
|
|
||||||
|
|
||||||
void lock_shared() noexcept {
|
|
||||||
auto old = lc_.load(std::memory_order_acquire);
|
|
||||||
for (unsigned k = 0;;) {
|
|
||||||
// if w_flag set, just continue
|
|
||||||
if (old & w_flag) {
|
|
||||||
yield(k);
|
|
||||||
old = lc_.load(std::memory_order_acquire);
|
|
||||||
}
|
|
||||||
// otherwise try cas lc + 1 (set r-lock)
|
|
||||||
else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// set r-lock failed, old has been updated
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock_shared() noexcept {
|
|
||||||
lc_.fetch_sub(1, std::memory_order_release);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
LIBIPC_NAMESPACE_END_
|
LIBIPC_NAMESPACE_END_
|
||||||
|
|
||||||
#pragma pop_macro("LIBIPC_LOCK_PAUSE_")
|
|
||||||
|
|||||||
116
src/libipc/spin_lock.cpp
Normal file
116
src/libipc/spin_lock.cpp
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include "libimp/detect_plat.h"
|
||||||
|
|
||||||
|
#include "libipc/spin_lock.h"
|
||||||
|
|
||||||
|
LIBIPC_NAMESPACE_BEG_
|
||||||
|
|
||||||
|
#if defined(LIBIPC_CC_MSVC)
|
||||||
|
# include <Windows.h> // YieldProcessor
|
||||||
|
/**
|
||||||
|
* @brief Not for intel c++ compiler, so ignore http://software.intel.com/en-us/forums/topic/296168
|
||||||
|
* @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms687419(v=vs.85).aspx
|
||||||
|
*/
|
||||||
|
# define LIBIPC_LOCK_PAUSE_() YieldProcessor()
|
||||||
|
#elif defined(LIBIPC_CC_GNUC)
|
||||||
|
# if defined(LIBIPC_INSTR_X86_64)
|
||||||
|
/**
|
||||||
|
* @brief Intel(R) 64 and IA-32 Architectures Software Developer's Manual V2
|
||||||
|
* PAUSE-Spin Loop Hint, 4-57
|
||||||
|
* @see http://www.intel.com/content/www/us/en/architecture-and-technology/64-ia-32-architectures-software-developer-instruction-set-reference-manual-325383.html?wapkw=instruction+set+reference
|
||||||
|
*/
|
||||||
|
# define LIBIPC_LOCK_PAUSE_() __asm__ __volatile__("pause")
|
||||||
|
# elif defined(LIBIPC_INSTR_I64)
|
||||||
|
/**
|
||||||
|
* @brief Intel(R) Itanium(R) Architecture Developer's Manual, Vol.3
|
||||||
|
* hint - Performance Hint, 3:145
|
||||||
|
* @see http://www.intel.com/content/www/us/en/processors/itanium/itanium-architecture-vol-3-manual.html
|
||||||
|
*/
|
||||||
|
# define LIBIPC_LOCK_PAUSE_() __asm__ __volatile__ ("hint @pause")
|
||||||
|
# elif defined(LIBIPC_INSTR_ARM)
|
||||||
|
/**
|
||||||
|
* @brief ARM Architecture Reference Manuals (YIELD)
|
||||||
|
* @see http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.subset.architecture.reference/index.html
|
||||||
|
*/
|
||||||
|
# define LIBIPC_LOCK_PAUSE_() __asm__ __volatile__ ("yield")
|
||||||
|
# endif
|
||||||
|
#endif /*compilers*/
|
||||||
|
|
||||||
|
#if !defined(LIBIPC_LOCK_PAUSE_)
|
||||||
|
/**
|
||||||
|
* @brief Just use a compiler fence, prevent compiler from optimizing loop
|
||||||
|
*/
|
||||||
|
# define LIBIPC_LOCK_PAUSE_() std::atomic_signal_fence(std::memory_order_seq_cst)
|
||||||
|
#endif /*!defined(LIBIPC_LOCK_PAUSE_)*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Gives hint to processor that improves performance of spin-wait loops.
|
||||||
|
*/
|
||||||
|
void pause() noexcept {
|
||||||
|
LIBIPC_LOCK_PAUSE_();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Basic spin lock
|
||||||
|
*/
|
||||||
|
|
||||||
|
void spin_lock::lock() noexcept {
|
||||||
|
for (unsigned k = 0;
|
||||||
|
lc_.exchange(1, std::memory_order_acquire);
|
||||||
|
yield(k)) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void spin_lock::unlock() noexcept {
|
||||||
|
lc_.store(0, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief Constants for shared mode spin lock
|
||||||
|
enum : unsigned {
|
||||||
|
w_mask = (std::numeric_limits<std::make_signed_t<unsigned>>::max)(), // b 0111 1111
|
||||||
|
w_flag = w_mask + 1, // b 1000 0000
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Support for shared mode spin lock
|
||||||
|
*/
|
||||||
|
|
||||||
|
void rw_lock::lock() noexcept {
|
||||||
|
for (unsigned k = 0;;) {
|
||||||
|
auto old = lc_.fetch_or(w_flag, std::memory_order_acq_rel);
|
||||||
|
if (!old) return; // got w-lock
|
||||||
|
if (!(old & w_flag)) break; // other thread having r-lock
|
||||||
|
yield(k); // other thread having w-lock
|
||||||
|
}
|
||||||
|
// wait for reading finished
|
||||||
|
for (unsigned k = 0;
|
||||||
|
lc_.load(std::memory_order_acquire) & w_mask;
|
||||||
|
yield(k)) ;
|
||||||
|
}
|
||||||
|
|
||||||
|
void rw_lock::unlock() noexcept {
|
||||||
|
lc_.store(0, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rw_lock::lock_shared() noexcept {
|
||||||
|
auto old = lc_.load(std::memory_order_acquire);
|
||||||
|
for (unsigned k = 0;;) {
|
||||||
|
// if w_flag set, just continue
|
||||||
|
if (old & w_flag) {
|
||||||
|
yield(k);
|
||||||
|
old = lc_.load(std::memory_order_acquire);
|
||||||
|
}
|
||||||
|
// otherwise try cas lc + 1 (set r-lock)
|
||||||
|
else if (lc_.compare_exchange_weak(old, old + 1, std::memory_order_release)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// set r-lock failed, old has been updated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rw_lock::unlock_shared() noexcept {
|
||||||
|
lc_.fetch_sub(1, std::memory_order_release);
|
||||||
|
}
|
||||||
|
|
||||||
|
LIBIPC_NAMESPACE_END_
|
||||||
Loading…
x
Reference in New Issue
Block a user