Coordinating access to a shared resource between multiple processes on Windows is much simpler and much more elegant than it is on Unix. For maximum portability on Unix, you must use a lock file and make sure to avoid a number of possible race conditions to make lock files work properly. On Windows, however, the use of named mutexes solves all the problems Unix has without introducing new ones.
A named mutex is a synchronization object that works by allowing only a single thread to acquire a lock at any given time. Mutexes can also exist without a name, in which case they are considered anonymous. Access to an anonymous mutex can only be obtained by somehow acquiring a handle to the object from the thread that created it. Anonymous mutexes are of no use to us in this recipe, so we won't discuss them further.
Mutexes have a namespace much like that of a filesystem. The mutex namespace is separate from namespaces used by all other objects. If two or more applications agree on a name for a mutex, access to the mutex can always be obtained to use it for synchronizing access to a shared resource.
A mutex is created with a call to the CreateMutex(
)
function. You will find it particularly useful in this recipe that
the mutex is created and a handle returned, or, if the mutex already
exists, a handle to the existing mutex is returned.
Once we have a handle to the mutex that will be used for synchronization, using it is a simple matter of waiting for the mutex to enter the signaled state. When it does, we obtain the lock, and other processes wait for us to release it. When we are finished using the resource, we simply release the lock, which places the mutex into the signaled state.
If our program terminates abnormally while it holds the lock on the
resource, the lock is released, and the return from
WaitForSingleObject(
)
in the next process to obtain the lock is
WAIT_ABANDONED
. We do not check for this condition
in our code because the code is intended to be used in such a way
that abandoning the lock will not have any adverse effects. This is
essentially the same type of behavior as that in the Unix lock file
code from Recipe 2.9, where it attempts to break the lock if the
process holding it terminates unexpectedly.
To obtain a lock, call SpcLockResource(
)
with the name of the lock. If the lock is
successfully obtained, the return will be a handle to the lock;
otherwise, the return will be NULL
, and
GetLastError( )
can be used to determine what went wrong.
When you're done with the lock, release it by
calling SpcUnlockResource(
)
with the handle returned by
SpcLockResource( )
.
#include <windows.h> HANDLE SpcLockResource(LPCTSTR lpName) { HANDLE hResourceLock; if (!lpName) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } if (!(hResourceLock = CreateMutex(0, FALSE, lpName))) return 0; if (WaitForSingleObject(hResourceLock, INFINITE) = = WAIT_FAILED) { CloseHandle(hResourceLock); return 0; } return hResourceLock; } BOOL SpcUnlockResource(HANDLE hResourceLock) { if (!ReleaseMutex(hResourceLock)) return FALSE; CloseHandle(hResourceLock); return TRUE; }