You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
63 lines
1.5 KiB
63 lines
1.5 KiB
#pragma once
|
|
|
|
#include "compiler.h"
|
|
|
|
#include <atomic>
|
|
#include <cstdint>
|
|
|
|
// A reusable spinning barrier.
|
|
class spin_barrier
|
|
{
|
|
uint32_t init_ __mpalign__;
|
|
// Bit 63 is 0 for enter phase, 1 for exit phase. Remaining bits
|
|
// are the reached count.
|
|
std::atomic<uint64_t> entered_;
|
|
__padout__;
|
|
|
|
static constexpr uint64_t phase_mask = 1ull << 63;
|
|
static constexpr uint64_t reached_mask = ~phase_mask;
|
|
|
|
public:
|
|
spin_barrier() : init_(0), entered_(0) { }
|
|
spin_barrier(unsigned val) : init_(val), entered_(0) { }
|
|
spin_barrier(const spin_barrier&) = delete;
|
|
~spin_barrier()
|
|
{
|
|
// Wait if the barrier is in the exit phase, since it's not safe
|
|
// to delete until all completed waiters have exited
|
|
while (entered_ & phase_mask)
|
|
/* spin */;
|
|
}
|
|
|
|
void init(unsigned val)
|
|
{
|
|
init_ = val;
|
|
entered_ = 0;
|
|
}
|
|
|
|
void join()
|
|
{
|
|
// Wait if the barrier is in the exit phase
|
|
while (entered_ & phase_mask)
|
|
asm volatile("pause":::);
|
|
|
|
// Enter the barrier
|
|
auto v = ++entered_;
|
|
if (v == init_) {
|
|
// This is the last thread to reach the barrier. Switch to the
|
|
// exit phase and exit the barrier.
|
|
entered_ = (v | phase_mask) - 1;
|
|
return;
|
|
}
|
|
|
|
// Wait until the barrier switches to the exit phase
|
|
while (!(entered_.load(std::memory_order_relaxed) & phase_mask))
|
|
asm volatile("pause":::);
|
|
|
|
// Exit the batter
|
|
if ((v = --entered_) == phase_mask)
|
|
// Reached count is now zero. Switch to the enter phase.
|
|
entered_ = 0;
|
|
}
|
|
};
|