Mutex / Mutual Exclusion / Locking mechanism / Block / Sleep
-
if 1 thread is in CS other cannot enter, Return value: None, Parameters:
None
How Mutex is internally implemented?
Mutex is kernel maintained lock(a data structure) that we set before using a shared resource and release after using it.
Mutex keeps track of who currently has exclusive access to the data.
When the lock is set, no other thread can access the locked region of code.
Mutex lock will only be released by the thread who locked it.
Wake up?
When call to mutex.unlock() comes, signal is sent to scheduler. Scheduler checks all threads waiting on mutex.
if 1000 threads are waiting, wakeup call to activate 1000 comes(also called thundering herd), but scheduler wakes up 1 thread(at its discretion) & 999 falls to sleep.
Problems with Mutex
| Problem | Description |
|---|---|
| Priority Inversion |
Lower priority process is executing in Critical section, suddenly
High-Priority process is scheduled, lower-priority process is
preempted & thrown out of CS & higher priority process excecutes in
CS. Also if Higher priority thread/process is Busy Waiting then
lower priority process will never get CPU(ie never scheduled). Can PI happen on user-level threads? No, there is no preemption in user level threads. |
| Easy Deadlock | if order of mutex locking/unlocking is not correct, that can led to easy dead-lock situation. See Dead-lock example. |
| Thread holding mutex paniced | if thread-1 which holding the lock panics, whole process would panic. |
| Mutex and data are seperate Entities |
Thread-1,2 are accessing data using mutex, But thread-3 changed the
data without mutex, this should not Happen. Solutions: 1. Making mutex and data as single entity as done in Rust 2. All times keeping in mind that data should not handled outside mutex guards
|
Creating Mutex
Plain mutex
| Langugage | Code |
|---|---|
| C++11 |
|
| POSIX |
|
| Rust |
In Rust, data and mutex are not seperate. ie data can be accessed
inside mutex only. This solves the problem which we is present in CPP
|
Wrappers around mutex
-
Wrapper means, these classes owns the mutex. To create object of any of
these classes mutex has to be passed.
lock_guard
| Description | Code |
|---|---|
|
We do mutex.lock() before going into CS and mutex.unlock() at
exit. lock_guard owns the mutex, then this mutex is handled with lock_guard.
Templated class lock_guardThis is Wrapper over Mutex that provides a RAII, ie lock the mutex (mutex.lock()) no need to worry about unlocking (mutex.unlock()), when lock_guard object goes out of scope, mutex is automatically unlocked(destructor gets called). lock_guard provides mutex for duration of scoped block. We cannot copy lock_guard, because operator = is deleted. Hence its not copy or move assignable. Why? If someone locks the mutex and forgets to unlock, then whole process will block.
|
|
2. unique_lock
-
This also owns the mutex, then this mutex is handled with unique_lock.
This is similar to lock_guard, but supports additional locking
strategies(or types of unique_locks)
unique_lock <mutex> ulock(mtx); //Lock immdiately
unique_lock <mutex> ulock(mtx, defer_lock); //deferred locking: Acquire the mutex but donot lock immediately.
unique_lock <mutex> ulock(mtx); //time-constrained attempts at locking: try_lock_for(), try_lock_until()
mtx.try_lock_for(5 millisec);
//recursive locking
//unique_lock can be moved ie it is (MoveConstructible and MoveAssignable) but cannot be copied (not of CopyConstructible or CopyAssignable).