Skip to content

Commit

Permalink
Add spinlock
Browse files Browse the repository at this point in the history
This is a very efficient implementation of a spinlock.
  • Loading branch information
nilsdeppe committed Apr 15, 2024
1 parent cc2297f commit 11175df
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Parallel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ spectre_target_headers(
ReductionDeclare.hpp
ResourceInfo.hpp
Section.hpp
Spinlock.hpp
StaticSpscQueue.hpp
TypeTraits.hpp
)
Expand Down
58 changes: 58 additions & 0 deletions src/Parallel/Spinlock.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Distributed under the MIT License.
// See LICENSE.txt for details.

#pragma once

#include <atomic>

namespace Parallel {
/*!
* \brief A simple spinlock implemented in `std::atomic`s
*
* Implementation adapted from https://rigtorp.se/spinlock/
*/
class Spinlock {
public:
Spinlock() = default;
Spinlock(const Spinlock&) = delete;
Spinlock& operator=(const Spinlock&) = delete;
Spinlock(Spinlock&&) = delete;
Spinlock& operator=(Spinlock&&) = delete;
~Spinlock() = default;

/// \brief Acquire the lock. Will block until the lock is acquire.
void lock() {
for (;;) {
// Optimistically assume the lock is free on the first try
if (not lock_.exchange(true, std::memory_order_acquire)) {
return;
}
// Wait for lock to be released without generating cache misses
while (lock_.load(std::memory_order_relaxed)) {
// Issue X86 PAUSE or ARM YIELD instruction to reduce contention between
// hyper-threads
//
// If no hyperthreading is being used, this will actually slow down the
// code.
// __builtin_ia32_pause();
}
}
}

/// \brief Try to acquire the lock.
///
/// Returns `true` if the lock was acquired, `false` if it wasn't.
bool try_lock() {
// 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 not lock_.load(std::memory_order_relaxed) and
not lock_.exchange(true, std::memory_order_acquire);
}

/// \brief Release the lock.
void unlock() { lock_.store(false, std::memory_order_release); }

private:
std::atomic<bool> lock_{false};
};
} // namespace Parallel

0 comments on commit 11175df

Please sign in to comment.