Tuesday, 15 December 2015

Locking types:

Locking types:
A spinlock is a lock where the thread simply waits in a loop (“spins”) and repeatedly checks until the lock becomes available. Because the thread remains active but is not performing a useful task, the use of such a lock may be termed “busy waiting.”
Spin-locks provide the most efficient means for locking a small piece of code.
The primary disadvantage of a spinlock is that while waiting to acquire the lock time is being spent that might be used productively elsewhere. Spin locks are also non re-entrant (mutex’s on the other-hand, support an option to work properly in re-entrant code).
Spinlock APIs
int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
o Creates a spin variable that is initially unlocked
int pthread_spin_lock(pthread_spinlock_t *lock);
o If unlocked, locks the spin variable and proceeds
o If already locked, waits until the spin variable is unlocked by another thread
int pthread_spin_unlock(pthread_spinlock_t *lock);
o Unlocks the spinlock variable
int pthread_spin_destroy(pthread_spinlock_t *lock);
o Destroys the spinlock variable when you are done with it.
o This API is very important because, if you do not destroy the mutex after it is finished, you could start leaking memory.


A mutex is a more general purpose type of locking mechanism used to implement thread synchronization and protect shared data where necessary.
Primitive that provides interface to mutual exclusion blocks often referred to as a mutex
A mutex is owned by at most one thread
 Attempts to acquire ownership of an already owned mutex will block the thread
Releasing an owned mutex make it available for other threads to acquire

A mutex variable acts like a lock by protecting access to a shared data resource. Unique characteristics of mutex variables include the following:
Mutex’s support an option to allow re-entrant behavior.
Mutex’s allow a thread to go to sleep (free up resource) while waiting on the lock to become available.
Mutex: General flow (1 of 3)
The first step when using mutexes is to create and initialize the mutex variable using the API pthread_mutex_t.
pthread_mutex_init (mutex, attr);
o Creates a mutex variable that is initially unlocked
Pthread_mutexattr_init (attr);
o Creates a mutex variable that is initially unlocked
pthread_mutexattr_destroy (attr);
o Destroys the mutex variable when you are done with it.
o This API is very important because, if you do not destroy the mutex after it is finished, you could start leaking memory.
When creating a mutex, you can set three possible attributes:
Protocol to prevent priority inversions
Priority ceiling
Process sharing
step 2:
At any given time, many threads may attempt to execute a critical section of code which alters a shared memory location. To ensure that only one thread has exclusive access to a critical section of code, use the following APIs:
pthread_mutex_lock (mutex);
o Blocks on this statement until it can lock the mutex variable and get sole control of the variable
pthread_mutex_trylock (mutex);
o If possible, locks the mutex variable and proceeds.
o If not possible to lock, returns to this point after a specific interval and tries to lock again.
o This API allows the thread to continue doing work until the mutex is available for locking.
Step 3:
Once a thread successfully locks the mutex variable, the following flow occurs:
1. The thread that locks the mutex variable owns the mutex.
2. While the mutex is locked, all other threads that are trying to to enter the critical section of code are blocked.
3. After executing the critical section of code, the owning thread unlocks the mutex using the following API and makes the mutex available to other threads for locking.
o pthread_mutex_unlock (mutex);
4. Another thread (at random) may then acquire and lock the mutex.
o It is possible to set priorities and allow certain threads priority in locking the mutex, even though other threads are waiting to lock it.
5. Finally, the mutex is destroyed using the following API:
o pthread_mutex_destroy (mutex);
o If the mutex is not destroyed as soon as it finishes its work, your program could start leaking memory.
Sample code for a mutex variable:

The following code sample illustrates how a mutex variable is used.

1. Declares and creates the mutex variable mutexsum code: pthread_mutex_t mutexsum;
2. Initializes mutexsum and sets attributes code: pthread_mutex_init(&mutexsum, NULL);
3. Locks mutexsum so that no other thread can alter the variable code: pthread_mutex_lock (&mutexsum);
4. Unlocks mutexsum when the work is finished, making it available to another thread

A deadlock is a situation where a thread is waiting on a lock that logically will never unlock. The graphic below illustrates a deadlock.

Avoiding deadlocks
If a function can be called recursively, make sure to use mutex instead of spinlocks, and initialize with PTHREAD_MUTEX_RECURSIVE.
Avoid situations where different pieces of code may lock in a different order. For example:

Be careful if a piece of code requires multiple locks.
o If all locks cannot be obtained, make sure to release previously obtained locks (and then try again if necessary).

Semaphores are a useful means to signal between threads in order to prevent race conditions.
The two main semaphore functions are:
o sem_init(): Initialize semaphore. Provide an initial numeric value.
o sem_wait(): Test for value of semaphore. If negative, thread waits until awoken by another thread calling sem_post.
o sem_post(): Increments the value of the semaphore and wakes up a blocked process waiting on the semaphore, if any.
o sem_destroy(): Deallocate semaphore
The “sem_post” call will wake up exactly one thread
o Selection of which thread is awoken will be OS dependent (if multiple threads are waiting on the same semaphore, usually the thread that’s been waiting the longest will receive the signal)

Example using semaphores (2 of 2)

Sample program output is shown on the right.
Note that the use of semaphores guarantees that worker and main thread are interleaved.
Remember, each call to sem_wait() decrements the semaphore value and tests if the result is nonnegative.
o If the post-decrement result is negative, the thread is blocked pending another thread calling sem_post().
And each call to sem_post() increments the semaphore value and wakes up any processes that are blocked (waiting for a nonnegative post decrement value).


Post a Comment