Semaphores on the Railroad
and Their Computer Scientific Definition
Semaphore
Explained in Children's Terms
Semaphore Power: 3 applications
Producer-Consumer problem solved by semaphores: Tanenbaum Fig. 2-24
There are 3 kinds of resources:The buffer: only one thread at a time may access the buffer. We have seen race bugs in unprotected buffers. The buffer is protected by a semaphore named mutex.Empty buffer slots, like empty appartments, are resources. The semaphore named empty holds the number of empty slots as its value.
Occupied buffer slots are resouces. They hold the data items produced by the producer and consumed by the consumer. Semaphore full holds the number of data items currently stored in the buffer as its value.
Readers and
Writers Problem Definition and Idea for solution using Semaphores
Programming Style Criticism-Please Name Objects to Document their Purpose
Solution to the Readers and Writers Problem from Tanenbaum Fig. 2-35 discussed in detail.
A common pattern makes
programming easier.
Wait and Signal Operations in MONITORS
What is a monitor? 2 features:
(1) Set of functions where the body of each one is a critical
section (region) for a common mutex.
(The programming language automatically inserts the lock and unlock operations.)(2) Set of condition variables where each one can be wait()'d or signal()'d from within a monitor function body.
Monitors in the form defined by Brinch Hanson are not implemented in today's popular languages. Their effect can be programmed in ways depending on the system environment (POSIX and C, for example) or the capabilities of certain languages (especially Java).
POSIX with pthreads:
mutex m;should be used in a disciplined manner (see below).
condition c;
lock(m);
unlock(m);
condition_wait(c,m); (Both c and m are parameters).
condition_signal(c);
Java:
Each object can "be" the monitor whose functions are its synchronized methods.The secret of Java:
wait(); makes the current thread sleep.
notify(); awakens one thread sleeping in the monitor.
notifyAll(); awakens all threads sleeping in the monitor.
In the Java code String s = String("Hi"); the variable s is NOT A STRING!! s is a reference variable which is made, by the assignment operator, to hold a reference to the actual String object. A reference is (really implenented by) a POINTER, familiar from C++ and CSI310.
Therefore, if we code String t; t = s; t.append("There"); System.out.println(
s );
we get "HiThere" printed, NOT "Hi"
Condition Variables in POSIX: Programmers must carefully code
critical regions protected by mutex's because C/C++ has NO language support
for monitors.
Posix pthreads condition variables must be used in conjunction with
mutexes : Producer-Consumer Example from Sun :
My notes:Threads and Monitors in Java:POSIX System Programming Digression: Handling interrupted blocked system calls .
- The buffer object includes a mutex plus 2 condition variables.
- The assert() macro makes the program crash (with a message) to alert the programmer that there is a bug.
- STRONGLY ENCOURAGED for debugging and expressing invariants for readability.
- DO NOT USE for checking for runtime errors and do not put normal operations (like incrementing or setting a variable) into an assertion:
- A runtime error is a situation that is expected to happen because of the current exection environment.
- When software is compiled for production, the NODEBUG preprocessor is defined which turns assert()statements into empty statements.
- The posix condition variable merely controls which thread is awakened. The actual condition test is arbitrary C code.
- The condition must be reevaluated whenever wait returns. Sun's recommended pattern:
pthread_mutex_lock();
while(condition_is_false)
pthread_cond_wait();
We now know the condtion is true, provided all code that can falisify it has been protected by the mutex.(sdc)
pthread_mutex_unlock();
Should the system try to restart them or should they return with errno set to EINTR?
I roughly interpret the POSIX answer to be that for old fashioned blocking system calls, the system should by default not restart them automatically (because this gives the programmers flexibility to program whether to give up or restart depending on the situation. However, sigaction() should be used to install signal handlers in new software, so the programmer can control with the SA_RESTART flag whether or not the system shall restart.For modern POSIX facilities supporting multiple threads, some functions will never return with EINTR, and others might on old systems.
Here's a page of the relevent quotes from POSIX.
Example 4-11 The Producer/Consumer Problem and Condition Variables
typedef struct {
char buf[BSIZE];
int occupied;
int nextin;
int nextout;
pthread_mutex_t mutex;
pthread_cond_t more; The more condition signifies the buffer has at least one item in it.
pthread_cond_t less; The less condition signifies the buffer has at least one empty slot.
} buffer_t;buffer_t buffer;
Example 4-12 The Producer/Consumer Problem-the Producervoid producer(buffer_t *b, char item)
{
pthread_mutex_lock(&b->mutex);while (b->occupied >= BSIZE)
pthread_cond_wait(&b->less, &b->mutex);assert(b->occupied < BSIZE);
b->buf[b->nextin++] = item;
b->nextin %= BSIZE;
b->occupied++;/* now: either b->occupied < BSIZE and b->nextin is the index
of the next empty slot in the buffer, or
b->occupied == BSIZE and b->nextin is the index of the
next (occupied) slot that will be emptied by a consumer
(such as b->nextin == b->nextout) */pthread_cond_signal(&b->more);
pthread_mutex_unlock(&b->mutex);
}
Example 4-13 The Producer/Consumer Problem-the Consumer
char consumer(buffer_t *b)
{
char item;
pthread_mutex_lock(&b->mutex);
while(b->occupied <= 0)
pthread_cond_wait(&b->more, &b->mutex);assert(b->occupied > 0);
item = b->buf[b->nextout++];
b->nextout %= BSIZE;
b->occupied--;/* now: either b->occupied > 0 and b->nextout is the index
of the next occupied slot in the buffer, or
b->occupied == 0 and b->nextout is the index of the next
(empty) slot that will be filled by a producer (such as
b->nextout == b->nextin) */pthread_cond_signal(&b->less);
pthread_mutex_unlock(&b->mutex);return(item);
}
- The producer thread runs a function which has a local variable int item;
- The consumer thread runs a function which has a (separate) local variable int item;
- There is one our_monitor object that has variables buffer (array of integers), and count, lo and hi (integers)
- The producer and consumer functions sometimes call the insert() and remove() functions belonging to the (single) monitor object.
- mon is a class variable belonging to the ProducerConsumer class. Its value refers to the the single, monitor object. This variable is accessible from each of the 2 functions run by the 2 threads. (These 2 functions are both named run because a Java thread must have a method named run. A Java thread calls this method when it begins running.)
We then displayed and walked through the code from Fig. 2-28 from Tanenbaum.
One instance of the our_monitor class, referred to by variable mon, inplements the entire buffer which includes it's storage space, variables to index the next place to insert an item and the next place to remove an item, a count of how many items in the buffer now, plus the synchronized methods insert() and remove() that belong to the monitor.