(There's a bug in my Pi): Code pi.c computes Pi using numerical integration.
The program requires two integers as input:
- m the number of threads
- n the number of integration points
these are given on the command line.
Run the code interactively with the following input
> pi.exe 1 10000
The calculation should complete very quickly. Now run the code with
the following inputs:
> pi.exe 2 10000000
> pi.exe 4 10000000
> pi.exe 8 10000000
> pi.exe 2 10000
> pi.exe 4 10000
> pi.exe 1 100
> pi.exe 2 100
You should find that in some cases the code runs, but for other cases
it deadlocks. If the code fails to give an answer in 1 second
then use "cntrl-c" to kill the job. Whether the code runs or deadlocks
is not reproducible.
Note on Condition Variables
Each condition variable must be associated with a specific mutex, and
with a predicate condition. When a thread waits on a condition
variable it must always have the associated mutex locked. The
condition variable wait operation will unlock the mutex for
your before blocking the thread, and will relock the mutex
before returning to your code.
It is important that you test the predicate after locking the
appropriate mutex and before waiting on the condition variable. If a
thread signals or broadcasts a condition variable while no threads are
waiting, nothing happens. If some thread calls pthread_con_wait right
after that, it will keep waiting regardless of the fact that the
condition variable was just signaled - which means it waits for a long
time. (Sound familiar?)
It is equally important that you test the predicate again when the
thread wakes up. There are various reasons for this, eg:
- Intercepted wakeups: threads are asynchronous. Waking up from a
condition variable wait involves locking the associated mutex. But
what if another thread acquires the mutex first?
- Loose predicates: it is often easier to have lose
predicates, eg there
may be work available - so wake everyone up.
- Spurious wakeups: on multiprocessor machines making condition
wakeup completely predictable may substantially slow all condition
variables operations.
This good programming practice explains the bit of code in pi.c:
do {
pthread_cond_wait(&(mybarrier->barrier_cond),
&(mybarrier->barrier_mutex));
} while (mybarrier->cur_count!=0);
- Why does the code sometimes deadlock? How could your code be
changed to avoid potential deadlock while still conforming to the
good programming practice mentioned above?
- Implement your proposed code changes and verify that they
work.