Skip to content

Commit

Permalink
Add option to use futex APIs for spinlock
Browse files Browse the repository at this point in the history
During the discussion of [1] it has been suggested that the congestion
could be reduced by relying on C++20 futex APIs. Additionally we
incorporated the `try_lock()` design from [2].

[1]: #146
[2]: https://rigtorp.se/spinlock/

Co-authored-by: Milian Wolff <[email protected]>
  • Loading branch information
BurningEnlightenment and milianw committed Jul 7, 2022
1 parent 735a74b commit 6175e91
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 5 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ project(libcuckoo

# put these in the cache so they show up in ccmake
option (BUILD_EXAMPLES "build example libcuckoo programs")
option (LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT "Optimize the spinlock implementation with C++20 futex support" OFF)

# Add the libcuckoo interface target
add_subdirectory(libcuckoo)
Expand Down
4 changes: 4 additions & 0 deletions libcuckoo/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ add_library(libcuckoo::libcuckoo ALIAS libcuckoo)
# will have c++11 turned on in their compile when they use this target.
# XXX: newer cmakes have a "cxx_std_11" feature that could be used
target_compile_features (libcuckoo INTERFACE cxx_constexpr)
if (LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT)
target_compile_features(libcuckoo INTERFACE cxx_std_20)
target_compile_definitions(libcuckoo INTERFACE LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT=1)
endif ()

# Include relative to the base directory
target_include_directories(libcuckoo INTERFACE
Expand Down
44 changes: 39 additions & 5 deletions libcuckoo/cuckoohash_map.hh
Original file line number Diff line number Diff line change
Expand Up @@ -819,12 +819,29 @@ private:
LIBCUCKOO_SQUELCH_PADDING_WARNING
class LIBCUCKOO_ALIGNAS(64) spinlock {
public:
spinlock() : elem_counter_(0), is_migrated_(true) { lock_.clear(); }
spinlock() noexcept
:
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
lock_(),
#endif
elem_counter_(0), is_migrated_(true) {

#if !LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
lock_.clear();
#endif
}

spinlock(const spinlock &other) noexcept
: elem_counter_(other.elem_counter()),
:
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
lock_(),
#endif
elem_counter_(other.elem_counter()),
is_migrated_(other.is_migrated()) {

#if !LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
lock_.clear();
#endif
}

spinlock &operator=(const spinlock &other) noexcept {
Expand All @@ -834,14 +851,31 @@ private:
}

void lock() noexcept {
while (lock_.test_and_set(std::memory_order_acq_rel))
;
// Optimistically assume the lock is free
while (lock_.test_and_set(std::memory_order_acq_rel)) {
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
// Wait for lock to be released utilizing C++20 futex support
lock_.wait(true, std::memory_order::relaxed);
#endif
}
}

void unlock() noexcept { lock_.clear(std::memory_order_release); }
void unlock() noexcept {
lock_.clear(std::memory_order_release);
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
lock_.notify_one();
#endif
}

bool try_lock() noexcept {
#if LIBCUCKOO_USE_CXX20_FUTEX_SUPPORT
// First do a relaxed load to check if lock is free in order to prevent
// unnecessary cache misses if someone does while(!try_lock())
return !lock_.test(std::memory_order_relaxed) &&
!lock_.test_and_set(std::memory_order_acq_rel);
#else
return !lock_.test_and_set(std::memory_order_acq_rel);
#endif
}

counter_type &elem_counter() noexcept { return elem_counter_; }
Expand Down

0 comments on commit 6175e91

Please sign in to comment.