Skip to content
Snippets Groups Projects

Add WaitFreeCountingSemaphore

Merged Florian Schmaus requested to merge flow/emper:wait-free-counting-semaphore into master
5 files
+ 164
0
Compare changes
  • Side-by-side
  • Inline
Files
5
+ 63
0
 
// SPDX-License-Identifier: LGPL-3.0-or-later
 
// Copyright © 2020-2022 Florian Schmaus
 
#include "WaitFreeCountingPrivateSemaphore.hpp"
 
 
#include <cassert>
 
#include <limits>
 
#include <ostream>
 
 
#include "Context.hpp"
 
#include "Debug.hpp"
 
 
WaitFreeCountingPrivateSemaphore::WaitFreeCountingPrivateSemaphore()
 
: WaitFreeCountingPrivateSemaphore(0) {}
 
 
WaitFreeCountingPrivateSemaphore::WaitFreeCountingPrivateSemaphore(unsigned int counter)
 
: requiredSignalCount(counter),
 
counter(std::numeric_limits<unsigned int>::max()),
 
blockedContext(nullptr) {}
 
 
void WaitFreeCountingPrivateSemaphore::incrementCounterByOne() { requiredSignalCount++; }
 
 
void WaitFreeCountingPrivateSemaphore::incrementCounter(unsigned int count) {
 
requiredSignalCount += count;
 
}
 
 
void WaitFreeCountingPrivateSemaphore::wait() {
 
unsigned int delta = std::numeric_limits<unsigned int>::max() - requiredSignalCount;
 
LOGD("wait() counter: " << counter << " delta: " << delta);
 
 
if (counter - delta == 0) {
 
return;
 
}
 
 
Context* blockedContext = Context::getCurrentContext();
 
block([this, blockedContext, delta] {
 
assert(blockedContext > (Context*)4096);
 
 
// this->blockedContext.store(blockedContext); // MO relaxed possible
 
this->blockedContext = blockedContext;
 
unsigned int oldCounter = counter.fetch_sub(delta); // MO release
 
LOGD("wait() block oldCounter: " << oldCounter << " delta: " << delta);
 
if (oldCounter == delta) {
 
unblock(blockedContext);
 
}
 
});
 
}
 
 
auto WaitFreeCountingPrivateSemaphore::signalInternal() -> Context* {
 
unsigned int oldCounter = counter.fetch_sub(1);
 
assert(oldCounter >= 1);
 
LOGD("signalInternal(): oldCounter: " << oldCounter);
 
 
// If the counter is still non-zero after the decrement, somebody
 
// else is responsible for scheduling the fiber.
 
if (oldCounter > 1) {
 
return nullptr;
 
}
 
 
// Context* context = blockedContext.load();
 
Context* context = blockedContext;
 
EMPER_ASSERT_MSG(!context || context > (Context*)4096, "Unexpected context value: " << context);
 
return context;
 
}
Loading