-
Notifications
You must be signed in to change notification settings - Fork 7
semaphore.hpp
Not Enough Standards' semaphores are defined in header <nes/semaphore.hpp>
Semaphores are synchronization primitives that use a resource counter. When trying to acquire a resource, the calling thread is blocked until a resource is available.
Unlike mutexes, semaphores don't have any notion of ownership: any thread can acquire or release a resource, at any time.
nes::semaphore
is the default semaphore implementation. A resource of nes::semaphore
can be acquired or release. You can also try to acquire a resource, in order to prevent the calling thread from waiting if no resource is available.
nes::timed_semaphore
provide the same interface as nes::semaphore
, plus two functions. One of them tries to acquire a resource in a certain delay, when the other tries to acquire it until a specific time point is reached.
namespace nes
{
class semaphore
{
public:
using native_handle_type = /*implementation-defined*/;
public:
explicit semaphore(std::size_t initial_count = 0);
~semaphore();
semaphore(const semaphore&) = delete;
semaphore& operator=(const semaphore&) = delete;
semaphore(semaphore&& other) noexcept = delete;
semaphore& operator=(semaphore&& other) noexcept = delete;
void acquire();
bool try_acquire();
void release();
native_handle_type native_handle() const noexcept;
};
class timed_semaphore
{
public:
using native_handle_type = /*implementation-defined*/;
public:
explicit timed_semaphore(std::size_t initial_count = 0);
~timed_semaphore();
timed_semaphore(const timed_semaphore&) = delete;
timed_semaphore& operator=(const timed_semaphore&) = delete;
timed_semaphore(timed_semaphore&& other) noexcept = delete;
timed_semaphore& operator=(timed_semaphore&& other) noexcept = delete;
void acquire();
bool try_acquire();
template<typename Rep, typename Period>
bool try_acquire_for(const std::chrono::duration<Rep, Period>& timeout);
template<typename Clock, typename Duration>
bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& time_point);
void release();
native_handle_type native_handle() const noexcept;
};
}
Here is an example in which we have a thread that give resources to another thread:
#include <thread>
#include <iostream>
#include <array>
#include <nes/semaphore.hpp>
void another_thread(const std::array<std::uint64_t, 8>& data, nes::semaphore& semaphore)
{
const auto tp1{std::chrono::high_resolution_clock::now()};
for(std::size_t i{}; i < 8; ++i)
{
//Wait until the next resource is available
semaphore.acquire();
const auto elapsed_time{std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - tp1)};
std::cout << "Value " << i << " ready after " << elapsed_time.count() << "ms: " << data[i] << std::endl;
}
}
int main()
{
std::array<std::uint64_t, 8> data{0, 1};
//Two resources are already available
nes::semaphore semaphore{2};
std::thread thread{another_thread, std::cref(data), std::ref(semaphore)};
for(std::size_t i{2}; i < 8; ++i)
{
//Simulate a long processing time
std::this_thread::sleep_for(std::chrono::milliseconds{250});
data[i] = i * i;
//Increment the resource counter of the semaphore
semaphore.release();
}
if(thread.joinable())
thread.join();
}
Value 0 ready after 0ms: 0
Value 1 ready after 0ms: 1
Value 2 ready after 249ms: 4
Value 3 ready after 499ms: 9
Value 4 ready after 749ms: 16
Value 5 ready after 1000ms: 25
Value 6 ready after 1251ms: 36
Value 7 ready after 1501ms: 49