>

Tuesday, 15 December 2015

Mutex

Mutex:
A mutex is a mutual exclusion lock. Only one thread can hold the lock.Mutexes are used to protect data or other resources from concurrent access. A mutex has attributes, which specify the characteristics of the mutex.
Mutexes have two basic operations, lock and unlock. If a mutex is unlocked and a thread calls lock, the mutex locks and the thread continues. If however the mutex is locked, the thread blocks until the thread holding the lock calls unlock.
Locking a mutex is an atomic operation, meaning that the operating system (or threads library) assures while locking a mutex, no other thread succeeded in locking this mutex at the same time.
If we have a resource we need to share between threads, we associate a mutex with it and use the mutex to synchronise resource access. All we need to do is ensure our code locks the mutex before using the resource, and unlocks it after it is finished. This will prevent race conditions related to multiple threads simultaneously accessing that resource.
Diagram 1. Two thread contention for a mutex

Example: Add semaphores to the following example to enforce mutual exclusion to the shared variable count.
Thread A                                    Thread B
count = count + 1                        count = count + 1

Solution:
Create a semaphore named mutex that is initialized to 1. A value of one means that a thread may proceed and access the shared variable; a value of zero means that it has to wait for another thread to release the mutex.

Thread A                                             Thread B
mutex.wait()                                        mutex.wait()
# critical section            # critical section
count = count + 1                               count = count + 1
mutex.signal()                                     mutex.signal()



Posix:
Creating and initializing a mutex:
In order to create a mutex,declare a variable of type pthread_mutex_t and then initialize it using the function
      int pthread_mutex_init (pthread_mutex_t *mut, const pthread_mutexattr_t *attr);
The first argument is a pointer to the mutex. To second argument is used to set the mutex attributes. To use the default mutex attributes, pass NULL to it.
      pthread_mutex_t a_mutex;
      pthread_mutex_init (&a_mutex, NULL);

Locking and unlocking a mutex
In order to lock a mutex, use the function pthread_mutex_lock(). This function attempts to lock the mutex, or block the thread if the mutex is already locked by another thread. In this case, when the mutex is unlocked by the first thread, the function will return with the mutex locked by our thread. Here is how to lock a mutex
      int rc = pthread_mutex_lock(&a_mutex);
      if (rc) {               /* an error has occurred */
          perror("pthread_mutex_lock");
          pthread_exit(NULL);
      }
      /* mutex is now locked - do your stuff. */
After the thread did what it had to (change variables or data structures, handle file, or whatever it intended to do), it should free the mutex, using the pthread_mutex_unlock() function, like this:
      rc = pthread_mutex_unlock(&a_mutex);
      if (rc) {
          perror("pthread_mutex_unlock");
          pthread_exit(NULL);
      }

Destroying a mutex
Once usage of the mutex is complete, it needs to destroyed. However, if only one thread finished with the mutex, it should left alive for the other threads that might still need to use it. Once all finished using it, the last one can destroy it using the pthread_mutex_destroy() function:
      rc = pthread_mutex_destroy(&a_mutex);
After this call, this variable (a_mutex) may not be used as a mutex any more, unless it is initialized again. Thus, if one destroys a mutex too early and another thread tries to lock or unlock it, that thread will get an error from the lock or unlock function.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
using namespace std;
#define NUM_THREADS 5
int cnt = 0;
pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
void *Incrcount(void *v)
{
int n;
for(n=1;n<=NUM_THREADS;n++)
{
pthread_mutex_lock(&m);
cnt++;
cout << "the count is " << cnt << endl;
pthread_mutex_unlock(&m);
}
return NULL;
}
int main()
{
cout << "****************************************"<<endl;
void *status;
pthread_t threads[NUM_THREADS];
int rc;
long t;
long *taskids[NUM_THREADS];
for(t=0;t<NUM_THREADS;t++)
{
taskids[t] = (long*)malloc(sizeof(long));
*taskids[t] = t;
cout << "creating thread->"<<t <<endl;
rc = pthread_create(&threads[t],NULL,Incrcount,(void*)taskids[t]);
if(rc)
{
cout<<"error in creating thread"<<endl;
exit(-1);
}
}
for(t=0;t<NUM_THREADS;t++)
{
rc = pthread_join(threads[t],&status);
cout <<"completed joining threads"<<t<< "status is "<<(long)status <<endl;

}
pthread_exit(NULL);


return 0;
}
Types of mutexes:
The type of mutex determines how the mutex behaves when it is operated on. The following types of mutexes exist:
PTHREAD_MUTEX_DEFAULT or PTHREAD_MUTEX_NORMAL
Results in a deadlock if the same pthread tries to lock it a second time using the pthread_mutex_lock subroutine without first unlocking it. This is the default type.
PTHREAD_MUTEX_ERRORCHECK
Avoids deadlocks by returning a non-zero value if the same thread attempts to lock the same mutex more than once without first unlocking the mutex.
PTHREAD_MUTEX_RECURSIVE
Allows the same pthread to recursively lock the mutex using the pthread_mutex_lock subroutine without resulting in a deadlock or getting a non-zero return value from pthread_mutex_lock. The same pthread has to call the pthread_mutex_unlock subroutine the same number of times as it called pthread_mutex_lock subroutine in order to unlock the mutex for other pthreads to use.

Mutex Deadlock: This condition occurs when a mutex is applied but then not "unlocked". This causes program execution to halt indefinitely. It can also be caused by poor application of mutexes or joins. Be careful when applying two or more mutexes to a section of code. If the first pthread_mutex_lock is applied and the second pthread_mutex_lock fails due to another thread applying a mutex, the first mutex may eventually lock all other threads from accessing data including the thread which holds the second mutex. The threads may wait indefinitely for the resource to become free causing a deadlock. It is best to test and if failure occurs, free the resources and stall before retrying.
      pthread_mutex_lock(&mutex_1);
while ( pthread_mutex_trylock(&mutex_2) )  /* Test if already locked   */
{
  pthread_mutex_unlock(&mutex_1);  /* Free resource to avoid deadlock */
  ...
  /* stall here   */
  ...
  pthread_mutex_lock(&mutex_1);
}
count++;
pthread_mutex_unlock(&mutex_1);
pthread_mutex_unlock(&mutex_2);
...
The order of applying the mutex is also important. The following code segment illustrates a potential for deadlock:
void *function1()
{
...
pthread_mutex_lock(&lock1);           // Execution step 1
pthread_mutex_lock(&lock2);           // Execution step 3 DEADLOCK!!!
...
...
pthread_mutex_lock(&lock2);
pthread_mutex_lock(&lock1);
...
}

void *function2()
{
...
pthread_mutex_lock(&lock2);           // Execution step 2
pthread_mutex_lock(&lock1);
...
...
pthread_mutex_lock(&lock1);
pthread_mutex_lock(&lock2);
...
}

main()
{
...
pthread_create(&thread1, NULL, function1, NULL);
pthread_create(&thread2, NULL, function2, NULL);
...
}

0 comments:

Post a Comment