Mutual exclusion: Difference between revisions
still doesn't make too much sense but is better than it was... |
→Enforcing mutual exclusion: Spinlocks are time and power wasters. In almost all cases, context switches are preferrable. |
||
Line 15: | Line 15: | ||
On a [[uniprocessor]] system a common way to achieve mutual exclusion inside [[Kernel (computing)|kernel]]s is to disable [[interrupt]]s for the smallest possible number of instructions that will prevent corruption of the shared data structure, the [[critical section]]. This prevents interrupt code from running in the critical section, that also protects against interrupt-based process-change. |
On a [[uniprocessor]] system a common way to achieve mutual exclusion inside [[Kernel (computing)|kernel]]s is to disable [[interrupt]]s for the smallest possible number of instructions that will prevent corruption of the shared data structure, the [[critical section]]. This prevents interrupt code from running in the critical section, that also protects against interrupt-based process-change. |
||
In a computer in which several processors share memory, an indivisible [[test-and-set]] of a [[Flag (computing)|flag]] |
In a computer in which several processors share memory, an indivisible [[test-and-set]] of a [[Flag (computing)|flag]] could be used in a tight loop to wait until the other processor clears the flag. The test-and-set performs both operations without releasing the memory bus to another processor. When the code leaves the critical section, it clears the flag. This is called a "[[spinlock]]" or "[[busy-wait]]." |
||
Similar [[Atomic operation|atomic]] multiple-operation instructions, e.g., [[compare-and-swap]], are commonly used for [[ |
Similar [[Atomic operation|atomic]] multiple-operation instructions, e.g., [[compare-and-swap]], are commonly used for [[Non-blocking synchronization|lock-free]] manipulation of [[linked list]]s and other [[data structure]]s. |
||
However, spin locks and busy waiting are wasteful of processor time and power and are considered [[anti-pattern]]s in almost every case. The solution to this problem is to use the synchronization facilities in an operating system's multithreading library, which will take advantage of hardware solutions if possible. For example, when the operating system's lock library is used and a thread tries to acquire an already acquired lock, the operating system will suspend the thread using a [[context switch]] and swaps it out with another thread that is ready to be run, or could put that processor into a low power state if there is no other thread that can be run. However, if the time that is spent suspending a thread and then restoring it can be proven to be always more than the time that must be waited for a thread to become ready to run in a particular situation, then spinlocks are a fine solution for that situation only. |
|||
===Software solutions=== |
===Software solutions=== |
||
Beside the hardware supported solution, some software solutions exist that use "[[busy-wait]]" to achieve the goal. |
Beside the hardware supported solution, some software solutions exist that use "[[busy-wait]]" or context switches to achieve the goal. |
||
Examples of these include: |
Examples of these include: |
||
* [[Dekker's algorithm]] |
* [[Dekker's algorithm]] |
||
Line 31: | Line 33: | ||
* [[Message passing]] |
* [[Message passing]] |
||
* [[Tuple space]] |
* [[Tuple space]] |
||
Operating systems running on hardware without hardware that facilitates atomic operations and programs running on operating systems without synchronization facilities must impldement these software algorithms in order to perform mutual exclusion. |
|||
Most classical mutual exclusion methods attempt to reduce [[Latency (engineering)|latency]] and busy-waits by using queuing and [[context switch]]es. |
Most classical mutual exclusion methods attempt to reduce [[Latency (engineering)|latency]] and busy-waits by using queuing and [[context switch]]es. |
Revision as of 21:12, 23 November 2009
Mutual exclusion (often abbreviated to mutex) algorithms are used in concurrent programming to avoid the simultaneous use of a common resource, such as a global variable, by pieces of computer code called critical sections. A critical section is a piece of code where a process or thread accesses a common resource. The critical section by itself is not a mechanism or algorithm for mutual exclusion. A program, process, or thread can have critical section in it without any mechanism or algorithm, which implements mutual exclusion.
Examples of such resources are fine-grained flags, counters or queues, used to communicate between code that runs concurrently, such as an application and its interrupt handlers. The synchronization of access to those resources is an acute problem because a thread can be stopped or started at any time.
To illustrate: suppose a section of code is altering a piece of data over several program steps, when another thread, perhaps triggered by some unpredictable event, starts executing. If this second thread reads from the same piece of data, the data, which is in the process of being overwritten, is in an inconsistent and unpredictable state. If the second thread tries overwriting that data, the ensuing state will probably be unrecoverable. These shared data being accessed by critical sections of code, must therefore be protected, so that other processes which read from or write to the chunk of data are excluded from running.
A mutex is also a common name for a program object that negotiates mutual exclusion among threads, also called a lock.
Enforcing mutual exclusion
There exist both software and hardware solutions for enforcing mutual exclusion. The different solutions are shown below.
Hardware solutions
On a uniprocessor system a common way to achieve mutual exclusion inside kernels is to disable interrupts for the smallest possible number of instructions that will prevent corruption of the shared data structure, the critical section. This prevents interrupt code from running in the critical section, that also protects against interrupt-based process-change.
In a computer in which several processors share memory, an indivisible test-and-set of a flag could be used in a tight loop to wait until the other processor clears the flag. The test-and-set performs both operations without releasing the memory bus to another processor. When the code leaves the critical section, it clears the flag. This is called a "spinlock" or "busy-wait."
Similar atomic multiple-operation instructions, e.g., compare-and-swap, are commonly used for lock-free manipulation of linked lists and other data structures.
However, spin locks and busy waiting are wasteful of processor time and power and are considered anti-patterns in almost every case. The solution to this problem is to use the synchronization facilities in an operating system's multithreading library, which will take advantage of hardware solutions if possible. For example, when the operating system's lock library is used and a thread tries to acquire an already acquired lock, the operating system will suspend the thread using a context switch and swaps it out with another thread that is ready to be run, or could put that processor into a low power state if there is no other thread that can be run. However, if the time that is spent suspending a thread and then restoring it can be proven to be always more than the time that must be waited for a thread to become ready to run in a particular situation, then spinlocks are a fine solution for that situation only.
Software solutions
Beside the hardware supported solution, some software solutions exist that use "busy-wait" or context switches to achieve the goal. Examples of these include:
- Dekker's algorithm
- Peterson's algorithm
- Lamport's bakery algorithm
- The black-white bakery algorithm
- Semaphores
- Monitors
- Message passing
- Tuple space
Operating systems running on hardware without hardware that facilitates atomic operations and programs running on operating systems without synchronization facilities must impldement these software algorithms in order to perform mutual exclusion.
Most classical mutual exclusion methods attempt to reduce latency and busy-waits by using queuing and context switches.
Many forms of mutual exclusion have side-effects. For example, classic semaphores permit deadlocks, in which one process gets a semaphore, another process gets a second semaphore, and then both wait forever for the other semaphore to be released. Other common side-effects include starvation, in which a process never gets sufficient resources to run to completion, priority inversion in which a higher priority thread waits for a lower-priority thread, and "high latency" in which response to interrupts is not prompt.
Much research is aimed at eliminating the above effects, such as by guaranteeing non-blocking progress. No perfect scheme is known.
Further reading
- Michel Raynal: Algorithms for Mutual Exclusion, MIT Press, ISBN 0-262-18119-3
- Sunil R. Das, Pradip K. Srimani: Distributed Mutual Exclusion Algorithms, IEEE Computer Society, ISBN 0-8186-3380-8
- Thomas W. Christopher, George K. Thiruvathukal: High-Performance Java Platform Computing, Prentice Hall, ISBN 0-13-016164-0
- Gadi Taubenfeld, Synchronization Algorithms and Concurrent Programming, Pearson/Prentice Hall, ISBN 0-13-197259-6