Skip to content
Raymond Chen edited this page Aug 23, 2021 · 7 revisions

The unique_mutex mutex handle wrapper is defined in wil/resource.h as part of the RAII resource wrappers library. It is a unique_any specialization that adds methods specific to the typical needs of dealing with a mutex. There are three variants that can be chosen based upon the error handling style the calling code desires.

// Create an empty unique_mutex.
wil::unique_mutex m1;           // failures throw
wil::unique_mutex_nothrow m2;   // failures return HRESULTs
wil::unique_mutex_failfast m3;  // failures terminate

// Create during construction (not allowed with unique_mutex_nothrow)
wil::unique_mutex m4(L"MyMutexName");

// Standard operations:
m1.ReleaseMutex();

// Release the mutex when the object goes out of scope.
// Assumes that somebody else has already acquired the mutex.
auto releaseOnExit = m1.ReleaseMutex_scope_exit();

// Acquire the mutex ourselves (blocking if necessary),
// and release when going out of scope.
auto releaseOnExit = m1.acquire();

// Create or Open a mutex
m1.create();
m1.open(L"MyMutexName");
if (m1.try_create(L"MyMutexName")) {}
if (m1.try_open(L"MyMutexName")) {}

Methods

The unique_mutex types inherit all the methods of unique_ptr:

  • Move-only type supports move construction and move assignment.
  • bool is_valid() returns true if the object is wrapping a mutex.
  • void reset(HANDLE other) closes any currently-wrapped mutex and begins wrapping the passed-in mutex.
  • HANDLE get() returns the wrapped mutex handle, if any.
  • HANDLE release() detaches the wrapped mutex handle and returns it. The caller is responsible for closing the handle.
  • HANDLE* addressof() returns a pointer to the wrapped mutex handle, so that you can call a function that stores a handle.
  • Destructor closes the wrapped mutex handle, if any. The handle is not unlocked as part of destruction.

Constructors

  • unique_mutex() and unique_mutex(nullptr) create an empty unique_mutex object.
  • unique_mutex(HANDLE existing) begins wrapping an existing mutex handle.
  • unique_mutex(PCWSTR name) creates a new mutex handle for a named mutex.

The constructor for unique_mutex is unusual in that passing a string creates a new mutex, behavior that is normally the responsibility of a make_unique_xxx-style function. The expression unique_mutex(nullptr) creates an empty unique_mutex, rather than a unique_mutex containing an freshly-created anonymous mutex. If you want the latter, use unique_mutex((wchar_t*)nullptr) or (better) explicitly call the create() method.

Another gotcha is that passing a pointer to an ANSI string will misinterpret the pointer as a mutex handle, rather than as a mutex name.

Creation-like methods

In the below discussion, "success/failure reported in a type-specific manner" means

Type Success Failure
unique_mutex void Throw an exception
unique_mutex_nothrow HRESULT HRESULT
unique_mutex_failfast void Fail fast
  • mutex.try_create(PCWSTR name, DWORD dwFlags = 0, DWORD desiredAccess = MUTEX_ALL_ACCESS, PSECURITY_ATTRIBUTES sa = nullptr) attempts to open or create a named mutex (or an anonymous mutex if name is nullptr). If successful, wraps the resulting mutex handle and returns true. If unsuccessful, returns false.
  • mutex.create(PCWSTR name, DWORD dwFlags = 0, DWORD desiredAccess = MUTEX_ALL_ACCESS, PSECURITY_ATTRIBUTES sa = nullptr) behaves the same as try_create except that the success/failure is reported in a type-specific manner.
  • mutex.try_open(PCWSTR Name, DWORD desiredAccess = SYNCHRONIZE | MUTEX_MODIFY_STATE, bool inheritHandle = false) attempts to open an existing named mutex. If successful, wraps the resulting mutex handle and returns true. If unsuccessful, returns false.
  • mutex.open(PCWSTR Name, DWORD desiredAccess = SYNCHRONIZE | MUTEX_MODIFY_STATE, bool inheritHandle = false) behaves the same as try_open except that the success/failure is reported in a type-specific manner.

Other methods

  • mutex.ReleaseMutex() releases the wrapped mutex. It assumes that the mutex had already been acquired by somebody else.
  • mutex.ReleaseMutex_scope_exit() returns an RAII object that unlocks the mutex when the object destructs. It assumes that the mutex had already been locked by somebody else.
  • mutex.acquire() locks the mutex, and then returns an RAII object that unlocks the mutex when the object destructs.
  • mutex.acquire(DWORD* status = nullptr, DWORD timeout = INFINITE, BOOL alertable = FALSE) locks the mutex, optionally returning the result of WaitForSingleObjectEx in status, with an optional timeout, and with the option to wait alertably. If the acquisition times out, then the returned RAII object is empty (does not unlock the mutex at destruction).

Other mutex-related helpers

When dealing with mutex handles outside of unique_mutex, WIL defines some simple methods to emulate the same RAII patterns:

// Release the mutex when the returned value goes out of scope
auto releaseOnExit = wil::ReleaseMutex_scope_exit(handle);

Gotchas

Lock guard object

There are some gotchas in the use of the lock guard object returned by the ReleaseMutex_scope_exit() and acquire() methods. See Lock guard object for details.

See also