I rolled my own mutex for this, since I did not want to add a dependency to some heavyweight library just for this one functionality. It is used in production code and covered by our unit tests - please let me know if you find any issues with it.
It's a Windows-only solution though.
Usage example:
NamedSystemMutex m{ L"MyMutex" };
std::lock_guard<NamedSystemMutex> lock{ m };
Header NamedSystemMutex.h:
#pragma once
#include <string>
#include <windows.h> // Just needed for HANDLE
// Provides a system-wide, recursive, named lock.
// This class satisfies requirements of C++11 concept "Lockable", i.e. can be (and should be) used with unique_lock etc.
class NamedSystemMutex
{
public:
explicit NamedSystemMutex(const std::wstring& name);
~NamedSystemMutex();
// Moveable, not copyable
NamedSystemMutex(const NamedSystemMutex& other) = delete;
NamedSystemMutex(NamedSystemMutex&& other) = default;
NamedSystemMutex& operator=(const NamedSystemMutex& other) = delete;
NamedSystemMutex& operator=(NamedSystemMutex&& other) = default;
void lock();
void unlock();
bool try_lock();
private:
HANDLE handle_{};
};
Implementation:
#include "NamedSystemMutex.h"
#include <stdexcept>
NamedSystemMutex::NamedSystemMutex(const std::wstring& name) {
handle_ = CreateMutexW(nullptr, FALSE, name.c_str());
if (handle_ == NULL) {
throw std::runtime_error("Creation of mutex failed");
}
}
NamedSystemMutex::~NamedSystemMutex() {
const BOOL result = CloseHandle(handle_);
if (result == FALSE) {
// Error: Failed to close mutex handle (Error ignored since we are in destructor)
}
}
void NamedSystemMutex::lock() {
const auto result = WaitForSingleObject(handle_, INFINITE);
if (result == WAIT_ABANDONED) {
// Warning: Lock obtained, but on an abandoned mutex (was not correctly released before, e.g. due to a crash)
}
else if (result != WAIT_OBJECT_0) {
throw std::runtime_error("Failed to acquire lock");
}
}
void NamedSystemMutex::unlock() {
const BOOL result = ReleaseMutex(handle_);
if (result == FALSE) {
throw std::runtime_error("Failed to release lock: calling thread does not own the mutex");
}
}
bool NamedSystemMutex::try_lock() {
const auto result = WaitForSingleObject(handle_, 0);
if (result == WAIT_TIMEOUT) {
return false;
}
if (result == WAIT_OBJECT_0) {
return true;
}
throw std::runtime_error("Failed to acquire lock");
}