Skip to content

named_mutex.hpp

Alairion edited this page May 8, 2021 · 7 revisions

Named Mutex

Not Enough Standards' named mutexes are defined in header <nes/named_mutex.hpp>

Description

Named mutexes are synchronization primitives that can be used to protect shared data from being simultaneously accessed by multiple threads or processes. On construction, it will create a new, or open an existing, named mutex.

This header provides four types of mutexes: nes::named_mutex, nes::timed_named_mutex, nes::recursive_named_mutex, nes::recursive_timed_named_mutex. Those are analogous with standard mutexes: std::mutex, std::timed_mutex, std::recursive_mutex, std::recursive_timed_mutex.

Because they can be shared among multiple processes, named mutexes are robust. Robust means that if a process exits (for whatever reason), without unlocking a named mutex, it is automatically unlocked by the operating system.

nes::named_mutex is the default named mutex implementation. It can be locked and unlocked. You can try to lock it in order to prevent the calling thread from waiting if it is already owned by another thread.

nes::timed_named_mutex provide the same interface as nes::named_mutex, plus two functions. One of them tries to acquire the ownership in a certain delay, when the other tries to acquire it until a specific time point is reached.

nes::recursive_named_mutex is similar to nes::named_mutex except that it can be locked recursively by the same thread. The owning thread must unlock the mutex the same count of time it locked it.

nes::recursive_timed_named_mutex is similar to nes::timed_named_mutex except that it can be locked recursively by the same thread. The owning thread must unlock the mutex the same count of time it locked it.

Synopsis

namespace nes
{
    
static constexpr const char named_mutex_root[] = /*implementation-defined*/;

class named_mutex
{
public:
    using native_handle_type = /*implementation-defined*/;

public:
    explicit named_mutex(const std::string& name);
    ~named_mutex();

    named_mutex(const named_mutex&) = delete;
    named_mutex& operator=(const named_mutex&) = delete;
    
    named_mutex(named_mutex&&) noexcept = delete;
    named_mutex& operator=(named_mutex&&) noexcept = delete;

    void lock();
    bool try_lock();
    
    void unlock();

    native_handle_type native_handle() const noexcept;
};

class timed_named_mutex
{
public:
    using native_handle_type = /*implementation-defined*/;

public:
    explicit timed_named_mutex(const std::string& name);

    ~timed_named_mutex();

    timed_named_mutex(const timed_named_mutex&) = delete;
    timed_named_mutex& operator=(const timed_named_mutex&) = delete;
    
    timed_named_mutex(timed_named_mutex&&) noexcept = delete;
    timed_named_mutex& operator=(timed_named_mutex&&) noexcept = delete;

    void lock();
    bool try_lock();
    
    template<class Rep, class Period>
    bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout);
    template<class Clock, class Duration>
    bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time_point);

    void unlock();

    native_handle_type native_handle() const noexcept;
};

class recursive_named_mutex
{
public:
    using native_handle_type = /*implementation-defined*/;

public:
    explicit recursive_named_mutex(const std::string& name);

    ~recursive_named_mutex();

    recursive_named_mutex(const recursive_named_mutex&) = delete;
    recursive_named_mutex& operator=(const recursive_named_mutex&) = delete;
    
    recursive_named_mutex(recursive_named_mutex&&) noexcept = delete;
    recursive_named_mutex& operator=(recursive_named_mutex&&) noexcept = delete;

    void lock();
    bool try_lock();
    
    void unlock();
    
    native_handle_type native_handle() const noexcept;
};

class recursive_timed_named_mutex
{
public:
    using native_handle_type = /*implementation-defined*/;

public:
    explicit recursive_timed_named_mutex(const std::string& name);
    
    ~recursive_timed_named_mutex();

    recursive_timed_named_mutex(const recursive_timed_named_mutex&) = delete;
    recursive_timed_named_mutex& operator=(const recursive_timed_named_mutex&) = delete;
    
    recursive_timed_named_mutex(recursive_timed_named_mutex&&) noexcept = delete;
    recursive_timed_named_mutex& operator=(recursive_timed_named_mutex&&) noexcept = delete;

    void lock();
    bool try_lock();
    
    template<class Rep, class Period>
    bool try_lock_for(const std::chrono::duration<Rep, Period>& timeout);
    template<class Clock, class Duration>
    bool try_lock_until(const std::chrono::time_point<Clock, Duration>& time_point);

    void unlock();

    native_handle_type native_handle() const noexcept;

};

}

Example

Here is an example in which we can see cross-process mutex locking.
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 <mutex>
#include <thread>

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

int main()
{
    //Create a new named mutex
    nes::named_mutex mutex{"nes_example_named_mutex"};
    //Acquire ownership of the new mutex
    std::unique_lock lock{mutex};

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

    //Wait few time before unlocking the mutex
    std::this_thread::sleep_for(std::chrono::milliseconds{500});
    lock.unlock();

    //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 <mutex>
#include <thread>
#include <iostream>

#include <nes/named_mutex.hpp>

int main()
{
    auto tp1 = std::chrono::high_resolution_clock::now();

    //Open the named mutex
    nes::named_mutex mutex{"nes_example_named_mutex"};
    std::lock_guard lock{mutex};

    auto tp2 = std::chrono::high_resolution_clock::now();
    std::cout << "Gained ownership of the mutex after " << std::chrono::duration_cast<std::chrono::duration<double>>(tp2 - tp1).count() << "s." << std::endl;
}

Possible output

Gained ownership of the mutex after 0.494937s.
Clone this wiki locally