Skip to content

Commit

Permalink
Improve spin lock for C++20
Browse files Browse the repository at this point in the history
  • Loading branch information
mogemimi committed Sep 17, 2024
1 parent 55b1a5d commit 9f5e168
Show file tree
Hide file tree
Showing 2 changed files with 13 additions and 30 deletions.
34 changes: 11 additions & 23 deletions pomdog/utility/spin_lock.cpp
Original file line number Diff line number Diff line change
@@ -1,41 +1,29 @@
// Copyright mogemimi. Distributed under the MIT license.

#include "pomdog/utility/spin_lock.h"
#include "pomdog/basic/conditional_compilation.h"
#include "pomdog/basic/platform.h"

POMDOG_SUPPRESS_WARNINGS_GENERATED_BY_STD_HEADERS_BEGIN
#if defined(POMDOG_PLATFORM_LINUX) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
#include <emmintrin.h>
#else
#include <thread>
#endif
POMDOG_SUPPRESS_WARNINGS_GENERATED_BY_STD_HEADERS_END

namespace pomdog::detail {

SpinLock::SpinLock() noexcept = default;

void SpinLock::lock() noexcept
{
while (flag.test_and_set(std::memory_order_acquire)) {
#if defined(POMDOG_PLATFORM_LINUX) && (defined(__x86_64__) || defined(_M_X64) || defined(_M_AMD64))
// NOTE: For both performance and power consumption, we use the
// pause instruction in the loop instead of fully spinning.
// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops/
// http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0514r0.pdf
_mm_pause();
#else
// NOTE: It is much faster to use `std::this_thread::yield()` than to
// call `_mm_pause()` directly on Windows and macOS.
std::this_thread::yield();
#endif
while (flag_.test_and_set(std::memory_order_acquire)) {
// NOTE: Instead of a busy loop, a wait instruction is inserted
// to put the CPU into low power consumption mode.
// When implementing spin, it is possible to call the __mm_pause(),
// _umwait(), TPAUSE, or std::this_thread::yield(), but here wait() is called for portability.
//
// https://software.intel.com/en-us/articles/benefitting-power-and-performance-sleep-loops/
// http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/p0514r0.pdf
flag_.wait(true, std::memory_order_relaxed);
}
}

void SpinLock::unlock() noexcept
{
flag.clear(std::memory_order_release);
flag_.clear(std::memory_order_release);
flag_.notify_one();
}

} // namespace pomdog::detail
9 changes: 2 additions & 7 deletions pomdog/utility/spin_lock.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,8 @@ POMDOG_SUPPRESS_WARNINGS_GENERATED_BY_STD_HEADERS_END
namespace pomdog::detail {

class POMDOG_EXPORT SpinLock final {
#if defined(__EMSCRIPTEN__)
// NOTE: for C++20
std::atomic_flag flag;
#else
// NOTE: for C++17
std::atomic_flag flag = ATOMIC_FLAG_INIT;
#endif
std::atomic_flag flag_;
static_assert(__cplusplus >= 202002L, "ATOMIC_FLAG_INIT was deprecated in C++20, https://cplusplus.github.io/LWG/issue3659");

public:
SpinLock() noexcept;
Expand Down

0 comments on commit 9f5e168

Please sign in to comment.