Skip to content

named_semaphore.hpp

Alairion edited this page May 8, 2021 · 6 revisions

Named Semaphore

Not Enough Standards' named semaphores are defined in header <nes/named_semaphore.hpp>

Description

Named 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.

Named semaphores can be shared among multiple processes. A named semaphore is accessed using its name. On construction, it will create a new, or open an existing, named semaphore.

nes::named_semaphore is the default named 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_named_semaphore provide the same interface as nes::named_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.

Synopsis

namespace nes
{

static constexpr const char named_semaphore_root[] = /*implementation_defined*/;

class named_semaphore
{
public:
    using native_handle_type = /*implementation_defined*/;

public:
    explicit named_semaphore(const std::string& name, std::size_t initial_count = 0);

    ~named_semaphore();

    named_semaphore(const named_semaphore&) = delete;
    named_semaphore& operator=(const named_semaphore&) = delete;
    
    named_semaphore(named_semaphore&& other) noexcept = delete;
    named_semaphore& operator=(named_semaphore&& other) noexcept = delete;

    void acquire();
    bool try_acquire();
    
    void release();

    native_handle_type native_handle() const noexcept;
};

class timed_named_semaphore
{
public:
    using native_handle_type = /*implementation_defined*/;

public:
    explicit timed_named_semaphore(const std::string& name, std::size_t initial_count = 0);

    ~timed_named_semaphore();

    timed_named_semaphore(const timed_named_semaphore&) = delete;
    timed_named_semaphore& operator=(const timed_named_semaphore&) = delete;
    
    timed_named_semaphore(timed_named_semaphore&& other) noexcept = delete;
    timed_named_semaphore& operator=(timed_named_semaphore&& other) noexcept = delete;

    void acquire();
    bool try_acquire();
    
    template<class Rep, class Period>
    bool try_acquire_for(const std::chrono::duration<Rep, Period>& timeout);
    template<class Clock, class Duration>
    bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& time_point);

    void release();
    
    native_handle_type native_handle() const noexcept;
};

}

Example

Here is an example in which we can see cross-process semaphore usage.
main.cpp is the main file of the parent process.
other.cpp is the main file of the child process.
Possible output is the standard output of the parent process.

main.cpp

#include <thread>
#include <iostream>

#include <nes/process.hpp>
#include <nes/named_semaphore.hpp>

int main()
{
    //Create a new named semaphore, named "nes_example_named_semaphore"
    nes::named_semaphore semaphore{"nes_example_named_semaphore"};

    //Create the other process
    nes::process other{other_path, nes::process_options::grab_stdout};

    for(std::size_t i{}; i < 8; ++i)
    {
        //Wait before releasing a resource
        std::this_thread::sleep_for(std::chrono::milliseconds{100});
        //Increment the resource counter
        semaphore.release();
    }

    //Read the entire standard output of the child process. (nes::process_options::grab_stdout must be specified on process creation)
    std::cout << other.stdout_stream().rdbuf() << std::endl;

    if(other.joinable())
        other.join();
}

other.cpp

#include <chrono>
#include <iostream>

#include <nes/named_semaphore.hpp>

int main()
{
    //Open the named semaphore
    nes::named_semaphore semaphore{"nes_example_named_semaphore"};

    const auto tp1{std::chrono::high_resolution_clock::now()};
    for(std::size_t i{}; i < 8; ++i)
    {
        //Wait for a resource
        semaphore.acquire();

        //Display how long we have waited
        const auto elapsed_time{std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - tp1)};
        std::cout << "Acquired after " << elapsed_time.count() << "ms." << std::endl;
    }
}

Possible output

Acquired after 96ms.
Acquired after 196ms.
Acquired after 297ms.
Acquired after 398ms.
Acquired after 498ms.
Acquired after 599ms.
Acquired after 700ms.
Acquired after 800ms.
Clone this wiki locally