Mutex

From Rosetta Code

Jump to: navigation, search
Task
Mutex
You are encouraged to solve this task according to the task description, using any language you may know.

A mutex (abbreviated Mutually Exclusive access) is a synchronization object, a variant of semaphore with k=1. A 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 (or acquires) the mutex, then accesses the resource, and after that releases the mutex.

A mutex is a low-level synchronization primitive exposed to deadlocking. A deadlock can occur with just two tasks and two mutexes (if each task attempts to acquire both mutexes, but in the opposite order). Entering the deadlock is usually aggravated by a race condition state, which leads to sporadic hangups, which are very difficult to track down.

Contents

[edit] Variants of mutexes

[edit] Global and local mutexes

Usually the 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 a thousand times slower than seizing a local one.

[edit] 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 another task to seize it.

[edit] 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 for 'write. Read write mutexes are usually used to protect resources which can be accessed in 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 has seized the mutex for write releases it while keeping seized for read. Note that the reverse operation is potentially deadlocking and requires some additional access policy control.

[edit] Deadlock prevention

There exists a simple technique of deadlock prevention when mutexes are seized in some fixed order. This is discussed in depth in the Dining philosophers problem.

[edit] Sample implementations / APIs

[edit] Ada

Ada provides higher-level concurrency primitives, which are complete in the sense that they also allow implementations of the lower-level ones, like mutexes. Here is an implementation of a plain non-reentrant mutex based on protected objects.

The mutex interface:

protected type Mutex is
entry Seize;
procedure Release;
private
Owned : Boolean := False;
end Mutex;

The implementation of:

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;

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 true closing the barrier again. The procedure Release simply sets Owned to false. Both Seize and Release are protected actions whose execution causes reevaluation of all barriers, in this case one of Seize.

Use:

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;

It is also possible to implement mutex as a monitor task.

[edit] C

[edit] Win32

Works with: Win32 To create a mutex operating system "object":

HANDLE hMutex = CreateMutex(NULL, FALSE, NULL);

To lock the mutex:

WaitForSingleObject(hMutex, INFINITE);

To unlock the mutex

ReleaseMutex(hMutex);

When the program is finished with the mutex:

CloseHandle(hMutex);

[edit] POSIX

Works with: POSIX

Creating a mutex:

#include <pthread.h>
 
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

Or:

pthread_mutex_t mutex;
pthread_mutex_init(&mutex, NULL);

Locking:

int success = pthread_mutex_lock(&mutex);

Unlocking:

int success = pthread_mutex_unlock(&mutex);

Trying to lock (but do not wait if it can't)

int success = pthread_mutex_trylock(&mutex);

[edit] C++

Works with: Win32 See C example

[edit] D

 
class Synced
{
public:
synchronized int func (int input)
{
num += input;
return num;
}
private:
static num = 0;
}
 

[edit] E

E's approach to concurrency is to never block, in favor of message passing/event queues/callbacks. Therefore, it is unidiomatic to use a mutex at all, and incorrect, or rather unsafe, to use a mutex which blocks the calling thread. That said, here is a mutex written in E.

def makeMutex() {
 
# The mutex is available (released) if available is resolved, otherwise it
# has been seized/locked. The specific value of available is irrelevant.
var available := null
 
# The interface to the mutex is a function, taking a function (action)
# to be executed.
def mutex(action) {
# By assigning available to our promise here, the mutex remains
# unavailable to the /next/ caller until /this/ action has gotten
# its turn /and/ resolved its returned value.
available := Ref.whenResolved(available, fn _ { action <- () })
}
return mutex
}

This implementation of a mutex is designed to have a very short implementation as well as usage in E. The mutex object is a function which takes a function action to be executed once the mutex is available. The mutex is unavailable until the return value of action resolves. This interface has been chosen over lock and unlock operations to reduce the hazard of unbalanced lock/unlock pairs, and because it naturally fits into E code.

Usage example:

Creating the mutex:
 
? def mutex := makeMutex()
# value: <mutex>
 
Creating the shared resource:
 
? var value := 0
# value: 0
 
Manipulating the shared resource non-atomically so as to show a problem:
 
? for _ in 0..1 {
> when (def v := (&value) <- get()) -> {
> (&value) <- put(v + 1)
> }
> }
 
? value
# value: 1
 
The value has been incremented twice, but non-atomically, and so is 1 rather
than the intended 2.
 
? value := 0
# value: 0
 
This time, we use the mutex to protect the action.
 
? for _ in 0..1 {
> mutex(fn {
> when (def v := (&value) <- get()) -> {
> (&value) <- put(v + 1)
> }
> })
> }
 
? value
# value: 2

when blocks and Ref.whenResolved return a promise for the result of the deferred action, so the mutex here waits for the gratuitously complicated increment to complete before becoming available for the next action.

[edit] Go

import "sync"
 
func main() {
m := new(sync.Mutex)
m.Lock() // locks
m.Unlock() // unlocks
}

Read-write mutex is provided by the sync.RWMutex type.

[edit] Java

Works with: Java version 1.5+

Java 5 added a Semaphore class which can act as a mutex (as stated above, a mutex is "a variant of semaphore with k=1").

import java.util.concurrent.Semaphore;
 
public class VolatileClass{
public Semaphore mutex = new Semaphore(1); //also a "fair" boolean may be passed which,
//when true, queues requests for the lock
public void needsToBeSynched(){
//...
}
//delegate methods could be added for acquiring and releasing the mutex
}

Using the mutex:

public class TestVolitileClass throws Exception{
public static void main(String[] args){
VolatileClass vc = new VolatileClass();
vc.mutex.acquire(); //will wait automatically if another class has the mutex
//can be interrupted similarly to a Thread
//use acquireUninterruptibly() to avoid that
vc.needsToBeSynched();
vc.mutex.release();
}
}

Java also has the synchronized keyword, which allows almost any object to be used to enforce mutual exclusion.

public class Main {
static Object mutex = new Object();
static int i = 0;
 
public void addAndPrint()
{
System.out.print("" + i + " + 1 = ");
i++;
System.out.println("" + i);
}
 
public void subAndPrint()
{
System.out.print("" + i + " - 1 = ");
i--;
System.out.println("" + i);
}
 
 
public static void main(String[] args){
final Main m = new Main();
new Thread() {
public void run()
{
while (true) { synchronized(m.mutex) { m.addAndPrint(); } }
}
}.start();
new Thread() {
public void run()
{
while (true) { synchronized(m.mutex) { m.subAndPrint(); } }
}
}.start();
}
}

The "synchronized" keyword actually is a form of monitor, which was a later-proposed solution to the same problems that mutexes and semaphores were designed to solve. More about synchronization may be found on Sun's website - http://java.sun.com/docs/books/tutorial/essential/concurrency/sync.html , and more about monitors may be found in any decent operating systems textbook.

[edit] Objective-C

NSLock *m = [[NSLock alloc] init];
 
[m lock]; // locks in blocking mode
 
if ([m tryLock]) { // acquire a lock -- does not block if not acquired
// lock acquired
} else {
// already locked, does not block
}
 
[m unlock];

Reentrant mutex is provided by the NSRecursiveLock class.

[edit] OCaml

OCaml provides a built-in Mutex module.
It is very simple, there are four functions:

let m = Mutex.create() in
Mutex.lock m; (* locks in blocking mode *)
 
if (Mutex.try_lock m)
then ... (* did the lock *)
else ... (* already locked, do not block *)
 
Mutex.unlock m;

[edit] Oz

Oz has "locks" which are local, reentrant mutexes.

Creating a mutex:

declare L = {Lock.new}

The only way to acquire a mutex is to use the lock syntax. This ensures that releasing a lock can never be forgotten. Even if an exception occurs, the lock will be released.

lock L then
{System.show exclusive}
end

To make it easier to work with objects, classes can be marked with the property locking. Instances of such classes have their own internal lock and can use a variant of the lock syntax:

class Test
prop locking
 
meth test
lock
{Show exclusive}
end
end
end

[edit] PicoLisp

PicoLisp uses several mechanisms of interprocess communication, mainly within the same process family (children of the same parent process) for database synchronization (e.g. 'lock', 'sync', 'tell' or 'pid').

For a simple synchronization of unrelated PicoLisp processes the 'acquire' / 'release' function pair can be used.

[edit] PureBasic

PureBasic has the following Mutex functions;

MyMutex=CreateMutex()
Result = TryLockMutex(MyMutex)
LockMutex(MyMutex)
UnlockMutex(MyMutex)
FreeMutex(MyMutex)

Example

Declare ThreadedTask(*MyArgument)
Define Mutex
 
If OpenConsole()
Define thread1, thread2, thread3
 
Mutex = CreateMutex()
thread1 = CreateThread(@ThreadedTask(), 1): Delay(5)
thread2 = CreateThread(@ThreadedTask(), 2): Delay(5)
thread3 = CreateThread(@ThreadedTask(), 3)
WaitThread(thread1)
WaitThread(thread2)
WaitThread(thread3)
 
PrintN(#CRLF$+"Press ENTER to exit"): Input()
FreeMutex(Mutex)
CloseConsole()
EndIf
 
Procedure ThreadedTask(*MyArgument)
Shared Mutex
Protected a, b
For a = 1 To 3
LockMutex(Mutex)
; Without Lock-/UnLockMutex() here the output from the parallel threads would be all mixed.
; Reading/Writing to shared memory resources are a common use for Mutextes i PureBasic
PrintN("Thread "+Str(*MyArgument)+": Print 3 numbers in a row:")
For b = 1 To 3
Delay(75)
PrintN("Thread "+Str(*MyArgument)+" : "+Str(b))
Next
UnlockMutex(Mutex)
Next
EndProcedure

[edit] Ruby

Ruby's standard library includes a mutex_m module that can be mixed-in to a class.

require 'mutex_m'
 
class SomethingWithMutex
include Mutex_m
...
end

Individual objects can be extended with the module too

an_object = Object.new
an_object.extend(Mutex_m)

An object with mutex powers can then:

# acquire a lock -- block execution until it becomes free
an_object.mu_lock
 
# acquire a lock -- return immediately even if not acquired
got_lock = an_object.mu_try_lock
 
# have a lock?
if an_object.mu_locked? then ...
 
# release the lock
an_object.mu_unlock
 
# wrap a lock around a block of code -- block execution until it becomes free
an_object.my_synchronize do
do critical stuff
end

[edit] Tcl

Tcl's mutexes have four functions.

package require Thread
 
# How to create a mutex
set m [thread::mutex create]
 
# This will block if the lock is already held unless the mutex is made recursive
thread::mutex lock $m
# Now locked...
thread::mutex unlock $m
# Unlocked again
 
# Dispose of the mutex
thread::mutex destroy $m

There are also read-write mutexes available.

set rw [thread::rwmutex create]
 
# Get and drop a reader lock
thread::rwmutex rlock $rw
thread::rwmutex unlock $rw
 
# Get and drop a writer lock
thread::rwmutex wlock $rw
thread::rwmutex unlock $rw
 
thread::rwmutex destroy $rw
Personal tools