Mutex: Difference between revisions
Some explanation of Ada implementation |
→{{header|C}}: posix mutex |
||
Line 68: | Line 68: | ||
It is also possible to implement mutex as a monitor task. |
It is also possible to implement mutex as a monitor task. |
||
=={{header|C}}== |
=={{header|C}}== |
||
===Win32=== |
|||
{{works with|Win32}} |
{{works with|Win32}} |
||
To create a mutex operating system "object": |
To create a mutex operating system "object": |
||
< |
<c> |
||
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL); |
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL); |
||
</ |
</c> |
||
To lock the mutex: |
To lock the mutex: |
||
< |
<c> |
||
WaitForSingleObject(hMutex, INFINITE); |
WaitForSingleObject(hMutex, INFINITE); |
||
</ |
</c> |
||
To unlock the mutex |
To unlock the mutex |
||
< |
<c> |
||
ReleaseMutex(hMutex); |
ReleaseMutex(hMutex); |
||
</ |
</c> |
||
When the program is finished with the mutex: |
When the program is finished with the mutex: |
||
< |
<c> |
||
CloseHandle(hMutex); |
CloseHandle(hMutex); |
||
</ |
</c> |
||
===POSIX=== |
|||
{{works with|POSIX}} |
|||
Creating a mutex: |
|||
<c>#include <pthread.h> |
|||
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;</c> |
|||
Locking: |
|||
<c>pthread_mutex_lock(&mutex);</c> |
|||
Unlocking: |
|||
<c>pthread_mutex_unlock(&mutex);</c> |
|||
=={{header|C++}}== |
=={{header|C++}}== |
||
{{works with|Win32}} |
{{works with|Win32}} |
Revision as of 12:02, 17 December 2008
Mutex (abbreviated Mutually Exclusive access) is a synchronization object, a variant of semaphore with k=1. Mutex is said to be seized by a task decreasing k. It is released when the task restores k. Mutexes are typically used to protect a shared resource from concurrent access. A task seizes the mutex, then accesses the resource, and after that releases the mutex.
Mutex is a low-level synchronization primitive exposed to deadlocking. A deadlock can be constructed already with two tasks and two mutexes. Entering the deadlock is usually aggravated by a race condition state, which leads to sporadic hangups, very difficult to track down.
Variants of mutexes
Global and local mutexes
Usually OS provides various implementations of mutexes corresponding to the variants of tasks available in the OS. For example, system-wide mutexes can be used by processes. Local mutexes can be used only by threads etc. This distinction is maintained because, depending on the hardware, seizing a global mutex might be thousand times slower than seizing a local one.
Reentrant mutex
A reentrant mutex can be seized by the same task multiple times. Each seizing of the mutex is matched by releasing it, in order to allow other task to seize it.
Read write mutex
A read write mutex can be seized at two levels for read and for write. The mutex can be seized for read by any number of tasks. Only one task may seize it 'write. Read write mutex are usually used to protect resources which can be accessed in a mutable and immutable ways. Immutable (read) access is granted concurrently for many tasks because they do not change the resource state. Read write mutexes can be reentrant, global or local. Further, promotion operations may be provided, that's when a task that have seized the mutex for write releases it keeping seized for read. Note that the reverse operation is potentially deadlocking and requires some additional access policy control.
Deadlock prevention
There exists a simple technique of deadlock prevention when mutexes are seized in some fixed order.
Sample implementations / APIs
Ada
Ada provides higher-level concurrency primitives, which are complete in the sense that they also allow implementations of the lower-level ones, like mutex. Here is an implementation of a plain non-reentrant mutex based on protected objects.
The mutex interface: <ada> protected type Mutex is
entry Seize; procedure Release;
private
Owned : Boolean := False;
end Mutex; </ada> The implementation of: <ada> protected body Mutex is
entry Seize when not Owned is begin Owned := True; end Seize; procedure Release is begin Owned := False; end Release;
end Mutex; </ada> Here the entry Seize has a queue of the tasks waiting for the mutex. The entry's barrier is closed when Owned is true. So any task calling to the entry will be queued. When the barrier is open the first task from the queue executes the entry and Owned becomes false closing the barrier again. The procedure Release simply sets Owned to false. Both Seize and Release are protected actions which execution causes reevaluation of all barriers, in this case one of Seize.
Use: <ada> declare
M : Mutex;
begin
M.Seize; -- Wait infinitely for the mutex to be free ... -- Critical code M.Release; -- Release the mutex ... select M.Seize; -- Wait no longer than 0.5s or delay 0.5; raise Timed_Out; end select; ... -- Critical code M.Release; -- Release the mutex
end; </ada> It is also possible to implement mutex as a monitor task.
C
Win32
To create a mutex operating system "object": <c>
HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);
</c> To lock the mutex: <c>
WaitForSingleObject(hMutex, INFINITE);
</c> To unlock the mutex <c>
ReleaseMutex(hMutex);
</c> When the program is finished with the mutex: <c>
CloseHandle(hMutex);
</c>
POSIX
Creating a mutex:
<c>#include <pthread.h>
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;</c>
Locking:
<c>pthread_mutex_lock(&mutex);</c>
Unlocking:
<c>pthread_mutex_unlock(&mutex);</c>
C++
See C example