Semaphores

- Review: to avoid race conditions, we require:

	* process cannot enter its critical section if
	  another process is already in its corresponding
	  critical section (e.g. one that affects the same variable)

	* in general, we need a way to make process block until desired
	  event occurs in other process (e.g. leaves critical section)

- general solution must satisfy at least these two conditions:
	- only one process in critical section at a time (mutual exclusion)
	- once a process attempts to enter its critical section,
	  it must eventually be allowed to do so (time bounded)

- semaphore: non-negative integer variable set/tested only by atomic ops:

	increment: V(s): [s = s + 1]
	test:	   P(S): [while (s == 0) { wait }; s= s-1]

	- these operations cannot be interrupted, except during { wait }
	- very similar to enter/exit lock seen earlier but more powerful

- general example of use:

	semaphore mutex = 1;

	P1:				P2:
	while (TRUE) {			while (TRUE) {
		< do some work >		< do some work >
		P(mutex);			P(mutex);
		< critical section >		< critical section >
		V(mutex);			V(mutex);
	}				}

- specific example: shared balance problem

	semaphore mutex = 1;

	P1:				P2:
		...				...
		P(mutex);			P(mutex);
		balance += amount;		balance -= amount;
		V(mutex);			V(mutex);
		...				...

- problem: solve the following synchronization problem using semaphores
	(P2 must wait for P1 to set x before proceeding;
	 P1 must wait for P2 to set y before proceeding)

	shared int x, y;

	P1:				P2:
	while (TRUE) {			while (TRUE) {
		< compute A1 >			/* wait for x */
		set x;				get x;
		< compute A2 > 			< compute B1 >
		/* wait for y */		set y;
		get y;				< compute B2 >
	}				}

- what's wrong with the following?

	shared int x, y;
	semaphore mutex = 1;

	P1:				P2:
	while (TRUE) {			while (TRUE) {
		< compute A1 >			P (mutex);
		set x;				get x;
		V (mutex);			< compute B1 >
		< compute A2 > 			set y;
		P (mutex);			V (mutex);
		get y;				< compute B2 >
	}				}

	- first, there's nothing stopping P1 from running by itself!
	- what happens if P1 gets to V(mutex) before P2 gets to P(mutex)
	  and this continues?  (no synchronization achieved)

- solution: use two semaphores, s1 and s2, and start each at 0

	shared int x, y;
	semaphore s1 = 0;
	semaphore s2 = 0;

	P1:				P2:
	while (TRUE) {			while (TRUE) {
		< compute A1 >			P (s1);
		set x;				get x;
		V (s1);				< compute B1 >
		< compute A2 > 			set y;
		P (s2);				V (s2);
		get y;				< compute B2 >
	}				}

- bounded buffer problem:

producer() {				consumer() {
   buf_type *next, *item;	   	   buf_type *next, *item;

   while (TRUE) {		   	   while (TRUE) {
	produce (item);				/* wait for full buffer */
						next = obtain(fullPool);
	/* wait for empty buffer */
	next = obtain(emptyPool);		copy (next, item);
						release (next, emptyPool);
	copy(item, next);		
	release(next, fullPool);		consume (item);
   }				   	   }
}					}

- solution requires the introduction of 'counting' semaphores:
	semaphore full = 0;	/* number of full buffers */
	semaphore empty = N;	/* number of empty buffers */
	
- let producer signal creation of full buffer by incrementing full sem
		and use of an empty buffer by decrementing empty sem
- let consumer signal creation of empty buffer by incrementing empty sem
		and use of a full buffer by decrementing full sem
- also need one more general purpose mutex semaphore for any use of pools 

semaphore full = 0;	/* number of full buffers */
semaphore empty = N;	/* number of empty buffers */

producer() {				consumer() {
   buf_type *next, *item;	   	   buf_type *next, *item;

   while (TRUE) {		   	   while (TRUE) {
	produce (item);				/* wait for full buffer */
						P(full);
						next = obtain(fullPool);
	/* wait for empty buffer */
	P(empty);
	next = obtain(emptyPool);		copy (next, item);
						release (next, emptyPool);
	copy(item, next);			
	release (next, fullPool);		V(empty);
	V(full);				consume (item);
   }				   	   }
}					}

Dining Philosopher's Problem revisited:
	- homework question: will this avoid deadlock?

semaphore hashi[5];

philosopher(int i) {
	while (TRUE) {
		Ponder(universe);

		/* prepare to eat */
		P(hashi[i]);
		P(hashi[(i+1) mod 5];

		Eat();

		/* release chopsticks */
		V(hashi[(i+1) mod 5];
		V(hashi[i]);
	}
}

main () {
	/* all chopsticks are on table */

	for (i = 0; i < 5; i++)
		hashi[i] = 1;

	for (i = 0; i < 5; i++)
		ThreadCreate(philosopher, i);	/* assume we can do this */
}