0% found this document useful (0 votes)
74 views

AOS - Lab - Skill - I&II - Synchronization, Shared Memory Programming

The document describes various functions for creating and managing threads, mutexes, condition variables, and semaphores in C using POSIX threads. It includes functions for creating threads with pthread_create(), joining threads with pthread_join(), terminating threads with pthread_exit(), comparing thread IDs with pthread_equal(), and getting the current thread ID with pthread_self(). It also describes functions for initializing, locking/unlocking, and destroying mutexes and condition variables, as well as waiting on and signaling condition variables. Finally, it briefly describes POSIX semaphore functions like sem_init(), sem_wait(), sem_post(), and sem_destroy().

Uploaded by

vishnuvardhan
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
74 views

AOS - Lab - Skill - I&II - Synchronization, Shared Memory Programming

The document describes various functions for creating and managing threads, mutexes, condition variables, and semaphores in C using POSIX threads. It includes functions for creating threads with pthread_create(), joining threads with pthread_join(), terminating threads with pthread_exit(), comparing thread IDs with pthread_equal(), and getting the current thread ID with pthread_self(). It also describes functions for initializing, locking/unlocking, and destroying mutexes and condition variables, as well as waiting on and signaling condition variables. Finally, it briefly describes POSIX semaphore functions like sem_init(), sem_wait(), sem_post(), and sem_destroy().

Uploaded by

vishnuvardhan
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 8

Advanced Operating Systems – Lab/Skill Synchronization, Shared Memory Programming

#include <pthread.h>
int pthread_create(pthread_t * thread, const pthread_attr_t * attr,
void * (*start_routine)(void *), void * arg);
The pthread_create() routine creates a new thread within a process. The new thread starts in the start routine start_routine which
has a start argument arg. The new thread has attributes specified with attr, or default attributes if attr is NULL.
If the pthread_create() routine succeeds it will return 0 and put the new thread id into thread, otherwise an error number shall be
returned indicating the error.
int pthread_equal(pthread_t thread_1, pthread_t thread_2);
The pthread_equal() routine compares the thread ids thread_1 and thread_2 and returns a non 0 value if the ids represent the same
thread otherwise 0 is returned.
void pthread_exit(void * status);
The pthread_exit() routine terminates the currently running thread and makes status available to the thread that successfully joins,
pthread_join(), with the terminating thread. In addition pthread_exit() executes any remaining cleanup handlers in the reverse
order they were pushed, pthread_cleanup_push(), after which all appropriate thread specific destructors are called.
An implicit call to pthread_exit() is made if any thread, other than the thread in which main() was first called, returns from the
start routine specified in pthread_create(). The return value takes the place of status.
The process exits as if exit() was called with a status of 0 if the terminating thread is the last thread in the process.
The pthread_exit() routine cannot return.
int pthread_join(pthread_t thread, void ** status);
If the target thread thread is not detached and there are no other threads joined with the specified thread then the pthread_join()
function suspends execution of the current thread and waits for the target thread thread to terminate. Otherwise the results are
undefined.
On a successful call pthread_join() will return 0, and if status is non NULL then status will point to the status argument of
pthread_exit(). On failure pthread_join() will return an error number indicating the error.
pthread_t pthread_self(void);
The pthread_self() routine returns the thread id of the calling thread.
int pthread_mutex_init(pthread_mutex_t * mutex,const pthread_mutex_attr *attr);
The pthread_mutex_init() routine creates a new mutex, with attributes specified with attr, or default attributes if attr is NULL.
If the pthread_mutex_init() routine succeeds it will return 0 and put the new mutex id into mutex, otherwise an error number shall
be returned indicating the error.
int pthread_mutex_destroy(pthread_mutex_t * mutex);
The pthread_mutex_destroy() routine destroys the mutex specified by mutex.
If the pthread_mutex_destroy() routine succeeds it will return 0, otherwise an error number shall be returned indicating the error.
int pthread_mutex_lock(pthread_mutex_t * mutex);
The pthread_mutex_lock() routine shall lock the mutex specified by mutex. If the mutex is already locked the calling thread blocks
until the mutex becomes available.
If the pthread_mutex_lock() routine succeeds it will return 0, otherwise an error number shall be returned indicating the error.
int pthread_mutex_trylock(pthread_mutex_t * mutex);
The pthread_mutex_trylock() routine shall lock the mutex specified by mutex and return 0, otherwise an error number shall be
returned indicating the error. In all cases the pthread_mutex_trylock() routine will not block the current running thread.
int pthread_mutex_unlock(pthread_mutex_t * mutex);
If the current thread is the owner of the mutex specifed by mutex, then the pthread_mutex_unlock() routine shall unlock the mutex.
If there are any threads blocked waiting for the mutex, the the scheduler will determine which thread obtains the lock on the
mutex, otherwise the mutex is available to the next thread that calls the routine pthread_mutex_lock(), or
pthread_mutex_trylock().
If the pthread_mutex_unlock() routine succeeds it will return 0, otherwise an error number shall be returned indicating the error.
int pthread_cond_init(pthread_cond_t * cond, const pthread_cond_attr *attr);
The pthread_cond_init() routine creates a new condition variable, with attributes specified with attr, or default attributes if attr is
NULL.
If the pthread_cond_init() routine succeeds it will return 0 and put the new condition variable id into cond, otherwise an error
number shall be returned indicating the error.
int pthread_cond_destroy(pthread_cond_t * cond);
The pthread_cond_destroy() routine destroys the condition variable specified by cond.
If the pthread_cond_destroy() routine succeeds it will return 0, otherwise an error number shall be returned indicating the error.
int pthread_cond_wait(pthread_cond_t * cond, pthread_mutex_t * mutex);
The pthread_cond_wait() routine atomically blocks the current thread waiting on condition variable specified by cond, and
unlocks the mutex specified by mutex. The waiting thread unblocks only after another thread calls pthread_cond_signal(), or
pthread_cond_broadcast() with the same condition variable, and the current thread reaquires the lock on the mutex.
If the pthread_cond_wait() routine succeeds it will return 0, and the mutex specified by mutex will be locked and owned by the
current thread, otherwise an error number shall be returned indicating the error.
int pthread_cond_timedwait(pthread_cond_t * cond, pthread_mutex_t * mutex,
const struct timespec * abstime);
The pthread_cond_timedwait() routine blocks in the same manner as pthread_cond_wait(). The waiting thread unblocks for the
same conditions and returns 0, or if the system time reaches or exceedes the time specified by abstime, in which case
ETIMEDOUT will be returned. In any case the thread will reaquire the mutex specified by mutex.
int pthread_cond_signal(pthread_cond_t * cond);

1
Advanced Operating Systems – Lab/Skill Synchronization, Shared Memory Programming
The pthread_cond_signal() routine unblocks ONE thread blocked waiting for the condition variable specified by cond. The
scheduler will determine which thread will be unblocked.
If the pthread_cond_signal() routine succeeds it will return 0, otherwise an error number shall be returned indicating the error.
int pthread_cond_broadcast(pthread_cond_t * cond);
The pthread_cond_broadcast() routine unblocks ALL threads blocked waiting for the condition variable specified by cond.
If the pthread_cond_broadcast() routine succeeds it will return 0, otherwise an error number shall be returned indicating the error.
All POSIX semaphore functions and types are prototyped or defined in semaphore.h. To define a semaphore object, use
sem_t sem_name;
To initialize a semaphore, use sem_init:
int sem_init(sem_t *sem, int pshared, unsigned int value);
• sem points to a semaphore object to initialize
• pshared is a flag indicating whether or not the semaphore should be shared with fork()ed processes. LinuxThreads does
not currently support shared semaphores
• value is an initial value to set the semaphore to
To wait on a semaphore, use sem_wait:
int sem_wait(sem_t *sem);
To increment the value of a semaphore, use sem_post:
int sem_post(sem_t *sem);
To find out the value of a semaphore, use
int sem_getvalue(sem_t *sem, int *valp);
• gets the current value of sem and places it in the location pointed to by valp
To destroy a semaphore, use
int sem_destroy(sem_t *sem);
• destroys the semaphore; no threads should be waiting on the semaphore if its destruction is to succeed.
/* A solution to the deadlock problem. - deadlock.c */
#include <stdio.h> void* worker(void* arg) {
#include <stdlib.h> pthread_mutex_lock(&g);
#include <string.h> if ((long long) arg == 0) {
#include <pthread.h> pthread_mutex_lock(&m1);
pthread_mutex_t g = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&m2);
pthread_mutex_t m1 = PTHREAD_MUTEX_INITIALIZER; } else {
pthread_mutex_t m2 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&m2);
int main(int argc, char *argv[]) { pthread_mutex_lock(&m1);
pthread_t p1, p2; }
pthread_create(&p1, NULL, worker, (void *) (long long) 0); pthread_mutex_unlock(&m1);
pthread_create(&p2, NULL, worker, (void *) (long long) 1); pthread_mutex_unlock(&m2);
pthread_join(p1, NULL); pthread_mutex_unlock(&g);
pthread_join(p2, NULL); return NULL;
return 0; }
}
/*
vishnu@mannava:~/threads$ cc deadlock.c -lpthread
vishnu@mannava:~/threads$ ./a.out
vishnu@mannava:~/threads$ ./a.out */
/* A program to demonstrate mutex -increment problem. - mutex.c */
#include <pthread.h> void *count(void *ptr) {
#include <stdio.h> long i, max = MAX_COUNT/NUM_THREADS;
#include <stdlib.h> int tid = ((struct tdata *) ptr)->tid;
#include <sys/types.h>
#define NUM_THREADS 4 for (i=0; i < max; i++) {
#define MAX_COUNT 10000000 pthread_mutex_lock(&mutex);
struct tdata { counter += 1;
int tid; pthread_mutex_unlock(&mutex);
}; }
int counter = 0; printf("End %d counter: %d\n", tid, counter);
/* POSIX Thread mutex */ }
pthread_mutex_t mutex =
PTHREAD_MUTEX_INITIALIZER;
/* Don't confuse it with POSIX Semaphore */
int main (int argc, char *argv[]) {
pthread_t threads[NUM_THREADS];
int rc, i;
struct tdata id[NUM_THREADS];
for(i=0; i<NUM_THREADS; i++){
id[i].tid = i;
rc = pthread_create(&threads[i], NULL, count, (void *) &id[i]);
}
for(i=0; i<NUM_THREADS; i++){
pthread_join(threads[i], NULL);
}
printf("Counter value: %d Expected: %d\n", counter, MAX_COUNT);

2
Advanced Operating Systems – Lab/Skill Synchronization, Shared Memory Programming
return 0;
}
/* [vishnu@mannava ~]$ cc mutex.c -lpthread
[vishnu@mannava ~]$ ./a.out
End 0 counter: 9134409
End 2 counter: 9890340
End 1 counter: 9964224
End 3 counter: 10000000
Counter value: 10000000 Expected: 10000000 */
/* Thread Synchronization using semaphore */
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<semaphore.h>
//function definition for thread
void monitor(void *ptr);
sem_t mutex; //define semaphore
int counter;
int main(){
pthread_t thread1;
pthread_t thread2;
int i[2]; //argument to threads
i[0]=0;
i[1]=1;
//initialize semaphore as binary semaphore
sem_init(&mutex, 0, 1);
//thread creation
pthread_create(&thread1,NULL,(void *)&monitor,(void *)&i[0]);
pthread_create(&thread2,NULL,(void *)&monitor,(void *)&i[2]);
pthread_join(thread1,NULL);
pthread_join(thread2,NULL);
sem_destroy(&mutex);
exit(0);
}
void monitor(void *ptr){
int x;
x = *((int *) ptr);
printf("Thread %d: Waiting to enter critical region...\n", x);
sem_wait(&mutex); //down semaphore
//START CRITICAL REGION
printf("Thread %d: Now in critical region...\n", x);
printf("Thread %d: Counter Value: %d\n", x, counter);
printf("Thread %d: Incrementing Counter...\n", x);
counter++;
printf("Thread %d: New Counter Value: %d\n", x, counter);
printf("Thread %d: Exiting critical region...\n", x);
// END CRITICAL REGION
sem_post(&mutex); // up semaphore
pthread_exit(0);
}
[vishnu@mannava ~]$ cc semdemo.c -lpthread
[vishnu@mannava ~]$ ./a.out
Thread 0: Waiting to enter critical region...
Thread 0: Now in critical region...
Thread 0: Counter Value: 0
Thread 0: Incrementing Counter...
Thread 0: New Counter Value: 1
Thread 0: Exiting critical region...
Thread 4196032: Waiting to enter critical region...
Thread 4196032: Now in critical region...
Thread 4196032: Counter Value: 1
Thread 4196032: Incrementing Counter...
Thread 4196032: New Counter Value: 2
Thread 4196032: Exiting critical region...
/* implementation of C semaphores with pthreads. Used as mutex for counting a
variable below. */
#include <pthread.h> void sam_destroy(Sam * ps) {
#include <stdio.h> pthread_cond_destroy(&ps->cond);
#include <stdlib.h> pthread_mutex_destroy(&ps->mtx);
typedef struct { }
pthread_cond_t cond; void sam_acquire(Sam * ps) {
pthread_mutex_t mtx; pthread_mutex_lock(&ps->mtx);
volatile unsigned N; while(ps->N == 0) {
} Sam; pthread_cond_wait(&ps->cond,&ps->mtx);
3
Advanced Operating Systems – Lab/Skill Synchronization, Shared Memory Programming
#define SAM_INIT(n) \ }
{ \ --(ps->N);
.cond = PTHREAD_COND_INITIALIZER, \ pthread_mutex_unlock(&ps->mtx);
.mtx = PTHREAD_MUTEX_INITIALIZER, \ }
.N = n \ void sam_release(Sam * ps) {
}; pthread_mutex_lock(&ps->mtx);
void sam_init(Sam * ps, int N) { ++(ps->N);
pthread_mutex_init(&ps->mtx,NULL); pthread_mutex_unlock(&ps->mtx);
pthread_cond_init(&ps->cond,NULL); pthread_cond_signal(&ps->cond);
ps->N = N; }
} int main(void) {
static const int N = 20; Sam Mumm = SAM_INIT(1);
static const int Z = 200000;
static int COUNTER = 0; pthread_t threads[N];
void *thread_func(void *semaphore) { for(int i = 0; i < N; i++) {
Sam * mumm = semaphore;
pthread_create(&threads[i],NULL,thread_func,&Mumm);
sam_acquire(mumm); }
for(int i = 0; i < Z; i++) { for(int i = 0; i < N; i++) {
++COUNTER; pthread_join(threads[i],NULL);
} }
sam_release(mumm); fprintf(stderr,"%d\n", COUNTER);
return NULL; return EXIT_SUCCESS;
} }
[vishnu@mannava ~]$ cc sem.c -std=c99 -lpthread
[vishnu@mannava ~]$ ./a.out
4000000
THE DINING PHILOSOPHERS PROBLEM
The dining philosopher’s problem is a classical synchronization problem which models the allocation of resources in distributed
systems.
A number of philosophers are seated around a table and half-way between each pair of adjacent philosophers there is a single fork.
The life cycle of a philosopher consists of thinking, becoming hungry and trying to eat, eating, and then back to thinking, ad
infinitum. Thus, a philosopher is at any time in one of the following three possible states: thinking, hungry, or eating. A philosopher
may transit from a thinking state to a hungry state spontaneously, at any time. To move from the hungry state to the eating state, a
philosopher needs to get the two forks close to him. Hence, neighboring philosophers cannot eat at the same time. It is assumed
that eating times are finite. That is, a philosopher eats only for finite amount of time and after eating releases his two forks and
resumes thinking. The problem is to design a strategy (i.e., an algorithm) for each one of the philosophers which will never
deadlock. That is, to decide in which order a philosopher has to acquire, wait for and release forks to prevent a deadlock from ever
happening.
Solution – I
THE TOTAL ORDER TECHNIQUE
Assume that all the resources are numbered and that there is a total order between the numbers of the different resources. If each
processor requests resources in an increasing order of enumeration, then deadlock is not possible.
This simple solution has a major drawback: it is possible to get into a situation in which all the philosophers are hungry, and only
one of them can eat while all the others have to wait for him to finish eating and release the forks. Below we present a few solutions
that are more efficient.
Solution – II
HOLD AND WAIT STRATEGY: THE LR ALGORITHM
The LR algorithm: The philosophers are assigned fork acquisition strategies as follows: the philosophers whose numbers are even
are R-type, and the philosophers whose numbers are odd are L-type. Concurrency in the context of the dining philosophers
problem is a measure of how many philosophers can eat simultaneously (in the worst case) when all the philosophers are hungry.
Having high concurrency is a most desirable property. The LR algorithm does not achieve high concurrency: it is possible to get
into a situation in which only a quarter of the philosophers will be able to eat simultaneously when all of them are hungry.
Solution – III
HOLD AND WAIT STRATEGY: THE LLR ALGORITHM
The LLR algorithm: The philosophers are assigned fork acquisition strategies as follows: philosopher i is R-type if i is divisible by 3,
L-type otherwise. LLR algorithm is better than the LR algorithm in terms of concurrency. It guarantees that at least a third of the
philosophers will always be able to eat simultaneously when all of them are hungry.
Solution – IV
THE WAIT/RELEASE ALGORITHM
We now consider algorithms in which a philosopher may release a held fork before eating. In particular, if a philosopher obtains one
fork, and the other fork is not free, the held fork is released and the philosopher starts over. Recall that we assume that the
philosophers are ordered around the table from 1 to n.
The wait/release algorithm: A philosopher whose number is odd first waits until his left fork is free, and then takes it. If his right fork
is free, he takes it and then eats. Otherwise, he releases his left fork and begins again. A philosopher whose number is even first
waits until his right fork is free, and then takes it. If his left fork is free, he takes it and then eats. Otherwise, he releases his right
fork and begins again.
Solution – V
A RANDOMIZED ALGORITHM
A randomized algorithm is an algorithm that in the course of its action can toss a coin, and uses its outcome to decide what to do
next. An algorithm which is not randomized is called a deterministic algorithm. In the context of the dining philosophers problem, in
a randomized algorithm, a philosopher can use coin tossing and uses its outcome to decide what to do next.
main()

4
Advanced Operating Systems – Lab/Skill Synchronization, Shared Memory Programming
{
while(TRUE)
{
/* thinking section */
trying = TRUE;
while(trying)
{
choose side randomly and uniformly from {0,1};
otherside = complement of side;
wait until (forkkAvailable[i-side] is TRUE
and change it to FALSE);
if (forkAvailable[i-otherside] is TRUE and change it to FALSE)
then trying = FALSE;
else forkAvailable[i-side] = TRUE;
}
/* eating section */
forkAvailable[i-1] = forkAvailable[i] = TRUE;
}
}
The free philosopher’s algorithm: a philosopher first flips a fair coin and according to the outcome, decides whether he
should first try to obtain his left fork or his right fork. If the outcome is “heads,” the philosopher first waits until his left fork is free,
and then takes it. If his right fork is free, he takes it and then eats. Otherwise, he releases his left fork and starts over by flipping the
coin again. If the outcome is “tails,” the philosopher first waits until his right fork is free, and then takes it. If his left fork is free, he
takes it and then eats. Otherwise, he releases his right fork and starts over by flipping the coin again.
/* Dining philosophers Problem solution - Implementation in C:
Odd numbered philosophers pick up the right chopstick first and then the left, while
even numbered philosophers pick up the left chopstick first and then the right. */
#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <stdlib.h>
#define NUMBER_OF_PHILOSOPHERS 5
void *philosopher(void *);
void think(int);
void pickUp(int);
void eat(int);
void putDown(int);
pthread_mutex_t chopsticks[NUMBER_OF_PHILOSOPHERS];
pthread_t philosophers[NUMBER_OF_PHILOSOPHERS];
pthread_attr_t attributes[NUMBER_OF_PHILOSOPHERS];
int main() {
int i;
srand(time(NULL));
for (i = 0; i < NUMBER_OF_PHILOSOPHERS; ++i) {
pthread_mutex_init(&chopsticks[i], NULL);
}

for (i = 0; i < NUMBER_OF_PHILOSOPHERS; ++i) {


pthread_attr_init(&attributes[i]);
}

for (i = 0; i < NUMBER_OF_PHILOSOPHERS; ++i) {


pthread_create(&philosophers[i], &attributes[i], philosopher, (void *)(i));
}

for (i = 0; i < NUMBER_OF_PHILOSOPHERS; ++i) {


pthread_join(philosophers[i], NULL);
}
return 0;
}
void *philosopher(void *philosopherNumber) {
while (1) {
think(philosopherNumber);
pickUp(philosopherNumber);
eat(philosopherNumber);
putDown(philosopherNumber);
}
}
void think(int philosopherNumber) {
int sleepTime = rand() % 3 + 1;
printf("Philosopher %d will think for %d seconds\n", philosopherNumber, sleepTime);
sleep(sleepTime);
}

5
Advanced Operating Systems – Lab/Skill Synchronization, Shared Memory Programming
void pickUp(int philosopherNumber) {
int right = (philosopherNumber + 1) % NUMBER_OF_PHILOSOPHERS;
int left = (philosopherNumber + NUMBER_OF_PHILOSOPHERS) % NUMBER_OF_PHILOSOPHERS;
if (philosopherNumber & 1) {
printf("Philosopher %d is waiting to pick up chopstick %d\n", philosopherNumber, right);
pthread_mutex_lock(&chopsticks[right]);
printf("Philosopher %d picked up chopstick %d\n", philosopherNumber, right);
printf("Philosopher %d is waiting to pick up chopstick %d\n", philosopherNumber, left);
pthread_mutex_lock(&chopsticks[left]);
printf("Philosopher %d picked up chopstick %d\n", philosopherNumber, left);
}
else {
printf("Philosopher %d is waiting to pick up chopstick %d\n", philosopherNumber, left);
pthread_mutex_lock(&chopsticks[left]);
printf("Philosopher %d picked up chopstick %d\n", philosopherNumber, left);
printf("Philosopher %d is waiting to pick up chopstick %d\n", philosopherNumber, right);
pthread_mutex_lock(&chopsticks[right]);
printf("Philosopher %d picked up chopstick %d\n", philosopherNumber, right);
}
}
void eat(int philosopherNumber) {
int eatTime = rand() % 3 + 1;
printf("Philosopher %d will eat for %d seconds\n", philosopherNumber, eatTime);
sleep(eatTime);
}
void putDown(int philosopherNumber) {
printf("Philosopher %d will will put down her chopsticks\n", philosopherNumber);
pthread_mutex_unlock(&chopsticks[(philosopherNumber + 1) % NUMBER_OF_PHILOSOPHERS]);
pthread_mutex_unlock(&chopsticks[(philosopherNumber + NUMBER_OF_PHILOSOPHERS) %
NUMBER_OF_PHILOSOPHERS]);
}
/*
[vishnu@mannava ~]$ ./a.out
Philosopher 1 will think for 2 seconds
Philosopher 0 will think for 3 seconds
Philosopher 2 will think for 2 seconds
Philosopher 4 will think for 2 seconds
Philosopher 3 will think for 2 seconds
Philosopher 1 is waiting to pick up chopstick 2
Philosopher 1 picked up chopstick 2
Philosopher 1 is waiting to pick up chopstick 1
Philosopher 1 picked up chopstick 1
Philosopher 1 will eat for 3 seconds
Philosopher 2 is waiting to pick up chopstick 2
Philosopher 4 is waiting to pick up chopstick 4
Philosopher 4 picked up chopstick 4
Philosopher 4 is waiting to pick up chopstick 0
Philosopher 4 picked up chopstick 0
Philosopher 4 will eat for 3 seconds
Philosopher 3 is waiting to pick up chopstick 4
Philosopher 0 is waiting to pick up chopstick 0
Philosopher 1 will will put down her chopsticks
Philosopher 1 will think for 2 seconds
Philosopher 2 picked up chopstick 2
Philosopher 2 is waiting to pick up chopstick 3
Philosopher 2 picked up chopstick 3
Philosopher 2 will eat for 3 seconds
Philosopher 4 will will put down her chopsticks
Philosopher 4 will think for 1 seconds
Philosopher 0 picked up chopstick 0
Philosopher 0 is waiting to pick up chopstick 1
Philosopher 0 picked up chopstick 1
Philosopher 3 picked up chopstick 4
Philosopher 3 is waiting to pick up chopstick 3
Philosopher 0 will eat for 1 seconds
^C
THE TOO MUCH BREAD PROBLEM
Alice and Bob are sharing an apartment. They have decided that at any time they will try to have precisely one loaf of bread in the
kitchen. Let’s assume that Alice arrives home in the afternoon, and finds that there is no bread. So, she leaves for the bakery to buy
bread. After she leaves, Bob arrives, he also finds that there is no bread and goes to buy bread. In the end, each one of them buys
a loaf of bread, and they end up with too much bread. So, Alice and Bob are looking for a solution to ensure the following.
1. Only one person buys a loaf of bread, when there is no bread.
2. Someone always buys a loaf of bread, when there is no bread.
A solution in which only Bob is responsible for buying bread is not acceptable. In such a solution, there is a scenario where Alice
arrives home and finds that there is no bread, and waits forever for Bob to show up. A proper solution should ensure that, when
there is no bread, someone always buys bread even if only one person shows up. Alice and Bob cannot see each other, and they
communicate only by sending each other short text messages. It is assumed that a message that is sent is never lost and arrives
immediately to its destination. However, between the time Alice finishes checking whether she has received a message and starts
sending a message, Bob may send her a message.
FIRST ATTEMPT

6
Advanced Operating Systems – Lab/Skill Synchronization, Shared Memory Programming
Alice and Bob have discussed the situation and agreed that to synchronize their actions they would communicate by sending two
type of text messages: acquire (an attempt to acquire permission to buy bread) and release (giving up permission to buy bread).
• Alice is present, if the last message Bob has received from Alice is acquire.
• Bob is present, if the last message Alice has received from Bob is acquire.
By using these messages, Alice and Bob came up with the following solution.
Alice: When Alice arrives home, she does the following: If she finds that Bob is not present and that there is no bread,
she sends the message acquire, buys a loaf of bread, puts it on the kitchen table, and sends the message release.
Bob: When Bob arrives home, he does the following: If he finds that Alice is not present and that there is no bread, he
sends the message acquire, buys a loaf of bread, puts it on the kitchen table, and sends the message release.
program for Alice: program for Bob:
1 if (no B) { 1 if (no A) {
2 if (no bread) { 2 if (no bread) {
3 send acquire 3 send acquire
4 buy bread 4 buy bread
5 send release 5 send release
6 } 6 }
7} 7}
The code for the first incorrect attempt. The statement “if (no A)” means if Alice is not present, and the statement “if (no B)” means if
Bob is not present.
SECOND ATTEMPT
To resolve the above problem Alice and Bob slightly modified their previous solution.
Alice: As soon as Alice arrives home, she sends the message acquire. Only then she checks, and if she finds that
Bob is not present and that there is no bread, she buys a loaf of bread, puts it on the kitchen table, and sends the
message release. Otherwise, if she finds that Bob is present, she sends the message release, and does nothing (until
the day after, when she tries again).
Bob: As soon as Bob arrives home, he sends the message acquire. Only then he checks, and if he finds that Alice is
not present and that there is no bread, he buys a loaf of bread, puts it on the kitchen table, and sends the message
release. Otherwise, if he finds that Alice is present, he sends the message release, and does nothing (until the day
after, when she tries again).
program for Alice: program for Bob:
1 send acquire 1 send acquire
2 if (no B) { 2 if (no A) {
3 if (no bread) {buy bread}} 3 if (no bread) {buy bread}}
4 send release 4 send release
The code for the second incorrect attempt.
THIRD ATTEMPT
Next, we present a solution which correctly works only if we make a timing assumption about the relative speed of Alice and Bob.
The algorithm for Alice is the same as in the previous solution.
Alice: As soon as Alice arrives home, she sends the message acquire. Only then she checks, and if she finds that
Bob is not present and that there is no bread, she buys a loaf of bread, puts it on the kitchen table, and sends the
message release. Otherwise, if she finds that Bob is present, she sends the message release and does nothing (until
the day after, when she tries again).
Bob: As soon as Bob arrives home, he sends the message acquire. Then, if he finds that Alice is present, he waits
until Alice is not present (that is until the last message received from Alice is not acquire). Once Bob finds that Alice is
not present, he checks if there is bread. If there is no bread, he buys a loaf of bread, puts it on the kitchen table, and
sends the message release. Otherwise, if there is bread, he sends the message release and does nothing.
program for Alice: program for Bob:
1 send acquire 1 send acquire
2 if (no B) { 2 while (A) {skip}
3 if (no bread) {buy bread}} 3 if (no bread) {buy bread}
4 send release 4 send release
The code for the third incorrect attempt. The statement “while (A) {skip}” means wait until Alice is not present.
FOURTH ATTEMPT: A CORRECT SOLUTION
Finally, we present a correct solution. Unlike the previous solution, it is symmetric: Alice and Bob behave similarly and hence have
the same chance to go and buy bread. For this solution, four types of messages are used: acquire1, release1, acquire2, and
release2. To simplify the presentation, we will use the following conventions: We say the following.
• A1 is present, if Bob has received at least one acquire1 message from Alice, and after the last acquire1 message received, he
has not received a release1 message.
• A2 is present, if Bob has received at least one acquire2 message from Alice, and after the last acquire2 message received, he
has not received a release2 message.
• B1 is present, if Alice has received at least one acquire1 message from Bob, and after the last acquire1 message received, she
has not received a release1 message.
• B2 is present, if Alice has received at least one acquire2 message from Bob, and after the last acquire2 message received, she
has not received a release2 message.
Alice: When Alice arrives home, she does the following.
1. First, she sends the message acquire1. Then, if B2 is present, she sends the message acquire2, otherwise she
sends the message release2. By doing so, Alice gives priority to Bob in buying bread.
2. Then, she waits as long as the following two conditions hold: (1) B1 is present and (2) either both A2 and B2 are
present or neither of them is present.

7
Advanced Operating Systems – Lab/Skill Synchronization, Shared Memory Programming
3. Once Alice finds that at least one of these two conditions is not satisfied, she checks if there is bread. If there is no
bread, she buys a loaf of bread and puts it on the kitchen table.
4. Finally, she sends the message release1.
Bob: When Bob arrives home, he does the following.
1. First, he sends the message acquire1. Then, if A2 is present, he sends the message acquire2, otherwise he sends
the message release2. By doing so, Bob gives priority to Alice in buying bread.
2. Then, he waits as long as the following two conditions hold: (1) A1 is present and (2) either A2 or B2 are present
(but not both).
3. Once Bob finds that at least one of these two conditions is not satisfied, he checks if there is bread. If there is no
bread, he buys a loaf of bread and puts it on the kitchen table.
4. Finally, he sends the message release1.
program for Alice: program for Bob:
1 send acquire1 1 send acquire1
2 if B2 {send acquire2} 2 if (no A2) {send acquire2}
3 else {send release2} 3 else {send release2}
4 while (B1 and 4 while (A1 and
5 ((A2 and B2) or 5 ((A2 and no B2) or
6 (no A2 and no B2)) ) {skip} 6 (no A2 and B2)) ) {skip}
7 if (no bread) {buy bread} 7 if (no bread) {buy bread}
8 send release1 8 send release1
The code for the fourth attempt. The statement “if B2” means if B2 is present; the statement “while (B1) {skip}” means wait until B1
is not present, etc.
/* Program to demonstrate Semaphores - Reader Writer Problem
#include<stdio.h> void *writer(void *arg)
#include<pthread.h> {
#include<semaphore.h> int f;
sem_t mutex,writeblock; f = ((int) arg);
int data = 0,rcount = 0; sem_wait(&writeblock);
void *reader(void *arg) data++;
{ printf("Data writen by the writer%d is
int f; %d\n",f,data);
f = ((int)arg); sleep(1);
sem_wait(&mutex); sem_post(&writeblock);
rcount = rcount + 1; }
if(rcount==1) int main()
sem_wait(&writeblock); {
sem_post(&mutex); int i,b;
printf("Data read by the reader%d is pthread_t rtid[5],wtid[5];
%d\n",f,data); sem_init(&mutex,0,1);
sleep(1); sem_init(&writeblock,0,1);
sem_wait(&mutex); for(i=0;i<=2;i++)
rcount = rcount - 1; {
if(rcount==0) pthread_create(&wtid[i],NULL,writer,(void *)i);
sem_post(&writeblock); pthread_create(&rtid[i],NULL,reader,(void *)i);
sem_post(&mutex); }
}/* for(i=0;i<=2;i++)
[vishnu@mannava ~]$ ./a.out {
Data read by the reader0 is 0 pthread_join(wtid[i],NULL);
Data read by the reader1 is 0 pthread_join(rtid[i],NULL);
Data read by the reader2 is 0 }
Data writen by the writer1 is 1 return 0;
Data writen by the writer0 is 2 }
Data writen by the writer2 is 3 */
Remote Method Invocation in Java
Remote Method Invocation (RMI) is an API which allows an object to invoke a method on an object that exists in another address
space, which could be on the same machine or on a remote machine. Through RMI, object running in a JVM present on a
computer (Client side) can invoke methods on an object present in another JVM (Server side). RMI creates a public remote server
object that enables client and server side communications through simple method calls on the server object.
To write an RMI Java application, you would have to follow the steps given below −
• Define the remote interface
• Develop the implementation class (remote object)
• Develop the server program
• Develop the client program
• Compile the application
• Execute the application

You might also like