Synchronization methods in Linux
Synchronization methods in Linux, There are two types of synchronization methods.
- Atomic operations
- Locking operations
Atomic Operations:
Operations that read or change data in a single uninterrupted step are called atomic operations.
GCC atomic memory access built-in functions:
Atomic fetch and operation functions:
1. type __sync_fetch_and_add (type *ptr, type v, …)
2. type __sync_fetch_and_sub (type *ptr, type v, …)
3. type __sync_fetch_and_or (type *ptr, type v, …)
4. type __sync_fetch_and_and (type *ptr, type value, …)
5. type __sync_fetch_and_xor (type *ptr, type value, …)
All above functions return the value that had previously been in memory before performing the suggested operations.
Atomic operation and fetch functions:
1. type __sync_add_and_fetch (type *ptr, type value, …)
2. type __sync_sub_and_fetch (type *ptr, type value, …)
3. type __sync_or_and_fetch (type *ptr, type value, …)
4. type __sync_and_and_fetch (type *ptr, type value, …)
5. type __sync_xor_and_fetch (type *ptr, type value, …)
6. type __sync_nand_and_fetch (type *ptr, type value, …)
All above functions return the new value in memory after performing the suggested operations.
#include<stdio.h>
#include<pthread.h>
int flag;
int val;
void *threadfun(void *arg)
{
while(flag != 0);
__sync_fetch_and_add (&flag,1);
val++;
__sync_fetch_and_sub (&flag,1);
}
void *threadfun1(void *arg)
{
while(flag != 0);
__sync_fetch_and_add (&flag,1);
printf("the val = %d \n", val);
__sync_fetch_and_sub (&flag,1);
}
int main()
{
pthread_t t1;
pthread_t t2;
void *res;
pthread_create(&t1,NULL,threadfun,NULL);
pthread_create(&t2,NULL,threadfun1,NULL);
pthread_join(t1,&res);
pthread_join(t2,&res);
return 0;
}
Locking Operations:
There are two types of locking mechanisms that can be used by threads for synchronizing global sources.
- Poll Locks
- Wait locks
Poll Locks:
Poll locks are those locking mechanisms in which threads will try to acquire a lock on global resources with the help of
atomic variable and if the lock is not available they will keep on polling for the lock till that lock is available these types
of locking mechanism will waste CPU slice but faster in locking.
Spin Locks:
Spin locks are a low-level synchronization mechanism suitable primarily for use on shared-memory multiprocessors. When the calling thread requests a spinlock that is already held by another thread, the second thread spins in a loop to test if the lock has become available.
Use spinlocks in interrupt context or signal handler context. if the critical section has few instructions to execute where no instruction will block then use spinlocks. Pthread spinlocks can be used to protect shared data accessed by multiple processes or a set of user-level threads within a process.
#include <pthread.h> int pthread_spin_lock(pthread_spinlock_t *lock); int pthread_spin_trylock(pthread_spinlock_t *lock); int pthread_spin_unlock(pthread_spinlock_t *lock); Compile and link with -pthread.
#include<stdio.h>
#include<pthread.h>
#include <unistd.h>
pthread_spinlock_t lock;
void *threadfun1(void *arg)
{
printf("thread 1 started\n");
pthread_spin_lock(&lock);
sleep(4);
printf("thread1 acquired lock\n");
pthread_spin_unlock(&lock);
printf("thread 1 released lock object\n");
return NULL;
}
void *threadfun2(void *arg)
{
sleep(1);
printf("thread 2 started \n");
pthread_spin_unlock(&lock);
pthread_spin_lock(&lock);
printf("thread 2 acquired lock\n");
pthread_spin_unlock(&lock);
printf("thread 2 release lock object\n");
return NULL;
}
int main()
{
pthread_t t1, t2;
void *res;
pthread_create(&t1,NULL,threadfun1,NULL);
pthread_create(&t2,NULL,threadfun2,NULL);
pthread_join(t1, &res);
pthread_join(t2, &res);
return 0;
}
Wait Locks:
These locking mechanisms are used to synchronize threads on a global resource. these locking mechanisms will maintain a queue
(called as wait queue)which is used to place all those threads which are waiting for a lock to acquire. threads that are the path of the wait queue are never scheduled by the scheduler.
Semaphores:
Semaphore can be used in the following ways.
- Mutual exclusion flags
- Global resource counter
- Wait event notification
Mutual exclusion flags:
Semaphore can used as a mutual exclusion flag which will allow only one thread to lock that semaphore at a point
of time also called a binary semaphore.
Global resource counter:
Semaphore can also used as a global resource counter which will allow multiple threads to lock the same semaphore at
a point of time called a countable semaphore.
Wait event notification:
Semaphore can also used as a wait notify flag, allowing threads to synchronize with other threads for an event.
POSIX IPC semaphore:
#include <fcntl.h> /* For O_* constants */ #include <sys/stat.h> /* For mode constants */ #include <semaphore.h> sem_t *sem_open(const char *name, int oflag); sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); Link with -pthread.
#include <semaphore.h> int sem_wait(sem_t *sem); int sem_trywait(sem_t *sem); int sem_timedwait(sem_t *restrict sem, const struct timespec *restrict abs_timeout); Link with -pthread.
#include <semaphore.h> int sem_post(sem_t *sem); Link with -pthread.
When semaphores are to be applied as a locking counter to enable synchronization of shared data, the following verifications must be supported.
- Acciedentl release: If a thread has lock a semaphore and some other thread trying to unlock same semaphore and if it succeeds then it calls as acciedental release.
#include<stdio.h>
#include<pthread.h>
#include <semaphore.h>
#include <unistd.h>
struct a{
int a;
int b;
}obj;
sem_t flag;
void *threadfun(void *arg)
{
sem_wait(&flag);
obj.a = 10;
sleep(4);
obj.b = 20;
sem_post(&flag);
return NULL;
}
void *threadfun1(void *arg)
{
sleep(1);
sem_post(&flag);
sem_wait(&flag);
printf("the a value %d\n",obj.a);
printf("the b value %d\n",obj.b);
sem_post(&flag);
return NULL;
}
int main()
{
pthread_t t1;
pthread_t t2;
void *res;
sem_init(&flag, 0, 1);
pthread_create(&t1,NULL,threadfun,NULL);
pthread_create(&t2,NULL,threadfun1,NULL);
pthread_join(t1,&res);
pthread_join(t2,&res);
return 0;
}
2. Owners death: If a thread who has locked a semaphore terminates without unlocking that semaphore than other threads waiting same semaphore will goto death lock state.
#include<stdio.h>
#include<pthread.h>
#include <semaphore.h>
struct a{
int a;
int b;
}obj;
sem_t flag;
void *threadfun(void *arg)
{
sem_wait(&flag);
obj.a = 10;
obj.b = 20;
pthread_exit(NULL);
sem_post(&flag);
return NULL;
}
void *threadfun1(void *arg)
{
sem_wait(&flag);
printf("the a value %d\n",obj.a);
printf("the b value %d\n",obj.b);
sem_post(&flag);
return NULL;
}
int main()
{
pthread_t t1;
pthread_t t2;
void *res;
sem_init(&flag, 0, 1);
pthread_create(&t1,NULL,threadfun,NULL);
pthread_create(&t2,NULL,threadfun1,NULL);
pthread_join(t1,&res);
pthread_join(t2,&res);
return 0;
}
3. Recursive lock: When a thread has lock the semaphore and trying to lock the same semaphore again before unlocking will calls dead locks.
#include<stdio.h>
#include<pthread.h>
#include <semaphore.h>
sem_t flag;
void fun();
void *threadfun(void *arg)
{
sem_wait(&flag);
printf("in thread\n");
fun();
sem_post(&flag);
return NULL;
}
void fun()
{
printf("function\n");
sem_wait(&flag);
printf("wait\n");
sem_post(&flag);
}
int main()
{
pthread_t t1;
void *res;
sem_init(&flag, 0, 1);
pthread_create(&t1,NULL,threadfun,NULL);
pthread_join(t1,&res);
return 0;
}
Mutex:
When the semaphore uses to implement only mutual exclusion flag is refferes as mutex. To implement mutex using semaphore, semaphore should be associates with the following.
- restrict the semaphore as binary semaphore(0 or 1).
- lock and unlock interfces should validate for ownership(which will allow the threads to use lock and unlock function as pair).
- threads which can not aquire lock should put into wait queue.
- implement the protocol which address all the limitations in semaphore.
POSIX IPC Mutex:
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex,
const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex);
#include<stdio.h>
#include<pthread.h>
#include <semaphore.h>
struct a{
int a;
int b;
}obj;
pthread_mutex_t mut= PTHREAD_MUTEX_INITIALIZER;
void *threadfun(void *arg)
{
pthread_mutex_lock(&mut);
obj.a = 10;
obj.b = 20;
pthread_mutex_unlock(&mut);
return NULL;
}
void *threadfun1(void *arg)
{
pthread_mutex_lock(&mut);
printf("the a value %d\n",obj.a);
printf("the b value %d\n",obj.b);
pthread_mutex_unlock(&mut);
return NULL;
}
int main()
{
pthread_t t1;
pthread_t t2;
void *res;
pthread_create(&t1,NULL,threadfun,NULL);
pthread_create(&t2,NULL,threadfun1,NULL);
pthread_join(t1,&res);
pthread_join(t2,&res);
return 0;
}
Pthread Mutex Attributes:
1.Error Checking Mutex:
This type of mutex provides error checking. A thread attempting to relock this mutex without first unlocking it shall return with an error. A thread attempting to unlock a mutex which another thread has locked shall return with an error. thread attempting to unlock an unlocked mutex shall return with an error.
#include <pthread.h> int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); int pthread_mutexattr_init(pthread_mutexattr_t *attr); int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type); int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type);
#include<stdio.h>
#include<pthread.h>
#include <semaphore.h>
#include<unistd.h>
#include <errno.h>
#include <string.h>
pthread_mutex_t mutex;
void *threadfun(void *arg)
{
printf("thread1 started\n");
pthread_mutex_lock(&mutex);
printf("thread1 after locking\n");
sleep(5);
printf("test\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
void *threadfun1(void *arg)
{
int ret;
sleep(1);
ret = pthread_mutex_unlock(&mutex);
if(ret) {
printf("trying unlock error %d \n", ret);
sleep(5);
}
printf("thread2 started\n");
pthread_mutex_lock(&mutex);
printf("thread2 after locking\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
pthread_t t1;
pthread_t t2;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK);
pthread_mutex_init(&mutex,&attr);
pthread_create(&t1,NULL,threadfun,NULL);
pthread_create(&t2,NULL,threadfun1,NULL);
pthread_join(t1,NULL);
pthread_join(t2,NULL);
return 0;
}
2.Recursive checking mutex:
A thread attempting to relock this mutex without first unlocking it shall succeed in locking the mutex. The relocking deadlock which can occur with mutexes of type PTHREAD_MUTEX_NORMAL cannot occur with this type of mutex. Multiple locks of this mutex shall require the same number of unlocks to release the mutex before another thread can acquire the mutex. thread attempting to unlock a mutex which another thread has locked shall return with an error. thread attempting to unlock an unlocked mutex shall return with an error.
#include<stdio.h>
#include<pthread.h>
#include <semaphore.h>
pthread_mutex_t mutex;
void *threadfun(void *arg)
{
printf("thread1 started\n");
pthread_mutex_lock(&mutex);
if(!pthread_mutex_lock(&mutex))
printf("thread error\n");
printf("thread1 after locking\n");
printf("test\n");
pthread_mutex_unlock(&mutex);
return NULL;
}
int main()
{
pthread_t t1;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&mutex,&attr);
pthread_create(&t1,NULL,threadfun,NULL);
pthread_join(t1,NULL);
return 0;
}
3.Roubst mutex:
This type of mutex will allow to identify owners death problem.
#include<stdio.h>
#include<pthread.h>
#include<errno.h>
pthread_mutex_t mutex;
void *threadfun1(void *arg)
{
printf("thread 1 started\n");
pthread_mutex_lock(&mutex);
printf("thread 1 acquired mutex\n");
pthread_exit(NULL);
}
void recover()
{
printf("data recovered\n");
pthread_mutex_consistent_np(&mutex);// making invalid mutex to valid mutex
pthread_mutex_unlock(&mutex);// unlockkiing the mutex object so that anybody can access that mutex
printf("made a valid mutex and unlocked\n");
}
void *threadfun2(void *arg)
{
int ret;
sleep(1);
ret = pthread_mutex_lock(&mutex);
printf("thread 2 started \n");
if(ret == EOWNERDEAD){// write this in all the threads, which executes 1st will sove the issues of mutex.
printf("owner dead\n");
recover();
}
pthread_mutex_lock(&mutex);
printf("thread 2 acquired mutex\n");
pthread_mutex_unlock(&mutex);
printf("thread 2 release mutex object\n");
return NULL;
}
int main()
{
pthread_t t1, t2;
void *res;
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setrobust_np(&attr, PTHREAD_MUTEX_ROBUST_NP);
pthread_mutex_init(&mutex, &attr);// dynamically assigning
pthread_create(&t1,NULL,threadfun1,NULL);
pthread_create(&t2,NULL,threadfun2,NULL);
pthread_join(t1, &res);
pthread_join(t2, &res);
return 0;
}
RWLocks(readwrite locks):
rw locks uses to control access to the shared data,allowing multiple threads to lock reader writer object for reading but
restricting access to single thread for writing. multiple threads can lock readwrite synchronization object for reading.if no writer has lock that object.
If any thread has lock readwrite synchronization object for writing the no other threads are allow to lock the same object either
for reading or writing. rw locks leads to a writer starvation(writer will block till readers has completed they job).
#include <pthread.h> int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock); int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock); int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
#include<pthread.h>
#include<stdio.h>
#include<string.h>
#include<unistd.h>
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER;
struct data {
int n;
char name[10];
}obj;
void *writethread(void *arg)
{
printf("write thread started\n");
pthread_rwlock_wrlock(&rwlock);
strcpy(obj.name, "hi");
obj.n = 1;
pthread_rwlock_unlock(&rwlock);
printf("writing done\n");
return NULL;
}
void *read1thread(void *arg)
{
sleep(3);
printf("read 1 thread started \n");
pthread_rwlock_rdlock(&rwlock);
printf("%s\n", obj.name);
printf("%d\n", obj.n);
pthread_rwlock_unlock(&rwlock);
printf("thread 2 release mutex object\n");
return NULL;
}
void *read2thread(void *arg)
{
sleep(5);
printf("read 2 thread started \n");
pthread_rwlock_rdlock(&rwlock);
printf("%s\n", obj.name);
printf("%d\n", obj.n);
pthread_rwlock_unlock(&rwlock);
printf("thread 3 release mutex object\n");
return NULL;
}
int main()
{
pthread_t t1, t2, t3;
void *res;
pthread_create(&t1,NULL,writethread,NULL);
pthread_create(&t2,NULL,read1thread,NULL);
pthread_create(&t3,NULL,read2thread,NULL);
pthread_join(t1, &res);
pthread_join(t2, &res);
pthread_join(t3, &res);
return 0;
}
Recent Comments