- Write a one-line C (or C++) program using the Windows API
that shuts down a single
executing Notepad process (if one exists). All you need to use are the
WinAPI functions FindWindow() and PostMessage(). The window
class for the Notepad application is "Notepad".
- Write two little WinAPI programs that illustrate how kernel objects can
be used for interprocess synchronization. The first program should
create an event object with a name. The second program should open that
event then wait on it (right after displaying a message that it is going
to wait). A button in the first program can be pressed to pulse the
event, which will release the waiting thread in the second process.
The second process should display a message that it was released.
You don't need any fancy graphics here, but you must be sure to clean
up handles, etc.
- Normally one uses low-level constructs such as mutexes,
semaphores and events to build higher-level constructs such
as monitors. The reverse is possible. Make an AutoResetEvent
class in Java, which functions like a Windows API AutoReset
Event. You'll need set(), reset() and pulse() members to
correspond to Windows API SetEvent(), ResetEvent() and PulseEvent()
APIs. Implement the functionality of CreateEvent in the
constructor. You do not need to emulate OpenEvent(), nor do you
need to make the event object usable in multiple processes at
the same time. Note that you'll need a method to wait on the event,
since Java has no real equivalent to WaitForSingleObject()
- Note that the designers of Java specified that the calls to wait(),
notify() and notifyAll() raise an exception if the calling thread does
not own the object's monitor. This suggests that the check to see
whether a calling thread owns a monitor cannot be made at compile time.
Is this true? If so, give an example of a case where the compile time
check can not be made. If not, prove that the compiler can always
check the legality of wait() and notify() calls.
- Write two versions of an Ada program to generate all primes from 1..n
where each candidate is tested for primality by a different task.
In the first version, each task is responsible for getting its "assignment"
from some server boss task. In the second version, there is
no "boss" task, but rather all the work orders are spread out on a
table and each task has to go pick one up. Implement the table as a
protected resource.
- Describe why concurrent programming often leads to code that is
more modular (in particular, more loosely coupled).
- Sketch fragments of code which illustrates livelock in as many
languages as you can.
- Give cobegin/coend style pseudocode for 3x3 matrix multiplication
that maximizes concurrency.
- Show how to program mutual exclusion using only a hardware "Test and Set"
operation. Test and Set is an atomic operation whose
effect is as follows:
bool testAndSet (bool& b) {
bool valueToReturn = b;
b = true;
return valueToReturn;
}
- Modify the dining philosophers simulation so that all five philosophers
can be seated at the same time but deadlock is avoided by having
the philosophers putting down their first chopstick if their second
one cannot be picked up 3 seconds after the first. (After putting
down the chopstick the philosopher waits a bit then tries to get
them both again.) Include in your comments whether or not this is
a good solution. Is livelock or starvation possible?
- One of the nice features of
Quicksort is that it allows a great deal of parallelism in an
implementation. After partitioning, the slices on either side of
the pivot can be sorted in parallel. It is very easy to set things
up to do this in languages such as occam, but tedious in Ada and
Java. Code up a parallel version of Quicksort in Ada or Java
and explain why it is messy.
- Explain how synchronous communication can be simulated in a lanuguage
that support only asynchrounous communication primitives.
- A mailbox can be made completely generic and reusable in Ada, but you
cannot say this about relays. Why?
- What is the time complexity (in
Θ-notation) of a sequntial procedure to
print out the exact value of 2n, where n is
a nonnegative integer? The procedure described is supposed to print
a result no matter how large the result may be. How can you
write a parallel version of this algorithm that has a better
asymptotic time complexity?
- The Ada Reference Manual contains the following example of how
not to write tasks (here
Resource
is some task type with
an entry Sieze
):
procedure Spin (R: Resource) is
begin
loop
select
R.Sieze;
return;
else
null;
end select;
end loop;
end Spin;
Rewrite this procedure like a normal person.
- Show how someone might implement the callback
message-passing paradigm in Ada so that the
implementation will deadlock. What exactly causes
deadlock in this case? Describe two approaches to implement
deadlock-free callback.
- If you wanted to implement the
Enhanced Dining Philosophers Problem using plain Windows threads and
synchronization objects then you wouldn't require each philosopher
to notify a host of their death; instead, this condition can
be waited on by a single statement. Write the code for this (and
sketch any necessary type declarations that are needed for the code
to make sense).
- Write a multithreaded C++ function
using Windows threads to quicksort a given array. In the comments
included in the source code, explain why it is not necessary to use
any synchronization objects in your solution.
- One way to implement a transient broadcast signal is as follows:
protected type Transient_Broadcast_Signal is
entry Wait;
entry Send;
private
entry Reset;
Signalled: Boolean := False;
end Transient_Broadcast_Signal;
protected body Transient_Broadcast_Signal is
entry Wait when Signalled is
begin
null;
end Wait;
entry Send when True is
begin
if Wait'Count > 0 then
Signalled := True;
requeue Reset;
end if
end Send;
entry Reset when Wait'Count > 0 is
begin
Signalled := True;
end Reset;
end Transient_Broadcast_Signal;
But you can reimplement it so that it doesn't use requeue, doesn't
need private entries, and in fact has null bodies for all the protected
operations! In other words all the functionality is in the barriers.
Implement this cool solution.
- Write a driver program which "tests" the resource allocators in
my Resource_Allocators package, and that exhibits the insecurity of
the insecure solution.
- The Java Language Specification states, in Section 17.8.1, entitled
"Wait", the following: "If thread t is interrupted, an InterruptedException
is thrown and t's interruption status is set to false." In Section
17.8.3 entitled "Interruptions" it states: "Let t be the thread
invoking u.interrupt, for some thread u, where t and u may be the same.
This action causes u's interruption status to be set to true."
One says the status is set to false while the other says its set
to true. Explain why this is not inconsistent.
- Write a Java prime number generator to compute the set of
primes from 1 to n, where n is a parameter, that works by spawing
tasks for each of the divisors 2 through sqrt(n), in which each
of the tasks set the slots in an initially all-true boolean[2..n] array
corresponding to their multiples to false. You'll have to first
decipher the previous dense run-on sentence, of course.
- Implement the "concurrent bounded buffer" class in C++
twice: once using pthreads and once using the Windows API.
- Suppose you were running a simulation in which multiple threads ran
around transferring money between bank accounts, where transfer() was
some procedure or method. If an account object were not protected
(as in Ada) or the transfer() method was not made synchronized (as in
Java), we could expect the total sum of money in all the accounts
to fluctuate during the simulation as money is lost or gained because
of thread interference. But where the underlying implementation
does not use time-slicing we will not notice this effect, unless we
take pains to force rescheduling. Show how to do this without
putting sleep or delay calls in the transfer method itself.
- In the following code fragment, it is not necessarily true that an
IllegalMonitorStateException will be thrown by a thread executing
the method f(), even though f() is unsynchronized.
class C {
X x = new X();
void f() {notify();}
synchronized void g() {x.q();}
}
The reason is that the body of X.q() could contain a call to C.f()
so a thread that calls C.g() would obtain the lock on entry to g(),
and retain it through its execution of X.q() and C.f(). However,
in the following code, can we say for sure that an
IllegalMonitorStateException WILL be thrown by any thread
executing C.f()?
class C {
void f() {notify();}
}
Why or why not?
- Here's a piece of a solar system simulation. It's a class for planets
that revolve around a sun. Each planet is a thread whose run() method
continuously updates its current position along a circular path of
radius r. A separate thread runs along periodically drawing all of
the planets by calling getPosition() for each. There's something
wrong here. Explain what could happen here and also show how to
fix the problem.
class Planet extends Thread {
private double r, u, x, y, du;
public Planet(double r, double du) {this.r=r; this.du=du; x=r; y=u=0;}
public void run() {while (true) {u+=du; x=r*cos(u); y=r*sin(u);}}
public Point getPosition() {return new Point(x, y);}
}
- Here's a silly little counter class. Counter objects can have a value
between 0 and max. Threads can add to or subtract from the counter,
but if the new value would be out of range they must block.
class Counter {
private int value;
private int max;
public Counter(int max) {value = 0; this.max = max;}
public synchronized void add(int n) {
while (value+n > max) try {wait();} catch (InterruptedException e) {}
value += n;
notifyAll();
}
public synchronized void subtract(int n) {
while (value < n) try {wait();} catch (InterruptedException e) {}
value -= n;
notifyAll();
}
}
Explain in detail a scenario in which deadlock occurs only if the
notifyAll() calls were replaced with notify() calls. That is, there
is no deadlock with notifyAll() but there IS deadlock with notify().
- In my WinAPI DiningPhilosophers program, could I have used Critical
Section (CRITICAL_SECTION) objects in place of mutexes? Why or why not?
- Speaking of my WinAPI DiningPhilosophers program, what happens if a
philosopher dies while holding a chopstick? Suppose that we wanted
it to be the case that if a philosopher does die while holding a
chopstick, her neighbor can pick it up and continue with her
thinking-eating life cycle. If my program already does this,
explain why; if not, rewrite the necessary parts of the code so
that it does.
- Why is calling
GetExitCodeThread()
not a reliable way to determine
whether or not a thread has finished?
- Novice Java programmers sometimes try to run a thread t
by calling t.run() instead of t.start(). Explain what happens
in the former case.
- Suppose we implemented a DiningPhilosophers simulation in which
four of the philosophers always picked up the left chopstick first
and the fifth always picked up the right chopstick first. Explain
how this system could deadlock or prove that deadlock is impossible.
(There is no "table" here; all philosophers are seated at the
same time.)
- What's wrong with the following attempt to enforce mutual exclusion,
besides the fact that it polls? After all, it doesn't deadlock and
starvation is impossible. Here "turn" is a global variable
initialized to 1.
thread1: thread2:
while (true) { while (true) {
NON_CRITICAL_SECTION_1 NON_CRITICAL_SECTION_2
while (turn != 1); while (turn != 2);
CRITICAL_SECTION_1 CRITICAL_SECTION_2
turn = 2; turn = 1;
} }
- Why do busy high-capacity servers in the world today more than
likely not use one thread per client?
- Write a WinAPI application which creates a thread with priority level 31
that busy waits for 30 seconds. Run it and explain what happened.
- Write a WinAPI application which deadlocks, by having one
thread inside a CRITICAL_SECTION wait for the completion of another
thread waiting to get in to the critical section.
- Write the following distributed program using RMI in Java. The client
displays a 400 x 400 canvas in a frame window. The server has a remote
object of class Ball, say, which bounces around in a 400 x 400 virtual
square. Everytime the client asks the server to move the ball,
the server does so and returns the new position to the client so
the client can update its display. You can fix the radius of the
ball at 10 pixels if you like. Start off the ball by moving in
increments of 3 pixels horizontally and 2 vertically. See if you
notice any performance change when running on separate machines
vs. on 2 processes in the same machine.
- Prove that the 5-person DiningPhilosophers solution that allows only 4
philosophers at the table at any one time is free of deadlock. Assume
philosophers pick up their right chopstick first and then their left,
blocking indefinitely to obtain each one.
- Write, in both Java and C with pthreads, a prime number
printing program that constructs
-- on the fly -- a chain of threads, each
representing a prime number, which filter out all candidates
passed through them. Start with a single thread whose job it is to test
"inputs" for divisibility by 2. If a number is divisible by 2, go back and
listen for more inputs. If a number is not divisible by 2,
pass it along to the "next" thread. If there is no "next"
thread, create a new thread for that number and assign this
new thread as the "next" thread.
- Write a Java application in which the main thread spawns five
helper threads. Each helper thread messes around for a random
amount of time (say, between 5 and 20 seconds). When a helper
thread finishes, the main thread displays a message saying
which helper thread finished. The main thread has to display
these messages in the proper order. Of course, do this without
polling.
- Implement a priority queue data type in Ada, using a server task
to provide synchronization.
- Implement a priority queue data type in Ada, where each priority
queue object is a protected object.
- In the example Ada package implementing the Set data type with a
guardian task, there is a serious problem with the package design:
errors in insertion are not handled well! What happens if we
run out of memory? (Answer in terms of the system interfaces.)
Show how to add robust error handling to the package and comment
on the amount of parallelism permitted with your solution.
- Discuss the difficulties of
implementing a secure Post Office object in Ada that meets the
following requirements. The post office is to maintain a collection
of P.O. boxes, each belonging to some task. Any task can put a
letter into another task's box, but only the owner of a particular
box can open it and read the letters.
- A relay is an agent task
created by one task to relay a message to another. For example, if
a calling task wishes to send a message to another but does not wish
to wait for a rendezvous, the caller can create a relay task to send
the message. (Note that relays are only appropriate to use when
there are no out
parameters in the called entry.) Sketch in detail a body for an Ada
task T that uses a relay R to call entry E of
task U, passing message X.
- In Ada, if two tasks are executing
a
Put
procedure at the same time, their outputs may be interleaved. (This
could produce amusing and even distasteful results, e.g. writing
"sole" in parallel with "ash") Show how to set
things up so only one task is writing at a time.
- Why do Java programmers not have
to worry about the situation in the previous problem (interleaving
of text output written to a stream)?
- Show how to implement a trivial count down latch with a Semaphore
in Java. That is, fill in the body of this class:
public class MySemaphoreBasedCountDown {
Semaphore s;
...
public MySemaphoreBasedCountDown(int count) {...}
public void await() throws InterruptedException {...}
public void countDown() {...}
}
Make objects of your class work like Java
CountDownLatch
es, meaning you can only use
them once.
- Rewrite the Java dining philosophers application to
use a semaphore for the table and explicit locks for
chopsticks. Add some rudimentary graphics to animate
the simulation.
- In Ada, what happens when you try
to call an entry in a task that has terminated? Comment on the
following code fragment as a possible approach to calling entry E of
task T only if T has not terminated.
if not T'Terminated then
T.E;
end if;
- In Java, what happens if you invoke a method on a thread that has
completed?
- Study the source code for java.util.concurrent.Semaphore
and write an analysis of why tryAcquire() without
a timeout can break fairness, but tryAcquite(0, TimeUnit.SECONDS)
does not.
- Write a Java application that returns the sum of the
values in an integer array by partitioning the array
into N parts, each of which are summed by a unique thread.
Use a cyclic barrier whose runnable sums the partial
sums.
- Write a JPanel that contains a beautifully colored
picture of the Mandelbrot set. The points should be generated
on a thread other than the GUI thread, though plotted on it.
Allow the drawing to be interrupted at anytime. Make a right-click
pop up a dialog in which the user can change the world-coordinate
system, the maximum number of iterations, and the color scheme.
- Write a Java application which renders spinning squares.
Each square is a random solid color, of size 50 x 50,
and spins for a random duration between 3 and 9 seconds.
Your application should keep firing off new squares, but
only 10 squares can be active at any time. When a square
is done spinning, it dies. Use a thread pool to make this
happen.
- Write a multithreaded Java server running at port 9838
which when a client connects, starts sending that client
random decimal digits. The server sends digits until the
client sends the string "STOP" or until 30 seconds
have elapsed.
- Write a multithreaded Java server running at port 9850
which accepts a string from the user that represents a non-negative
integer (call it n) and sends back the first n decimal digits
of pi. If the number submitted is very large, the generation
and transmission of the reply could take an enormously long
time. Therefore set a time limit of 30 seconds on the amount
of work for any request: if the server isn't done generating
n digits in 30 seconds, just stop sending. Use whatever
kind of Timer object or scheduled service you like. Use
a thread pool for the server rather than spawning a new thread
for each request.
- Suppose a Windows thread t1 has stupidly entered CRITICAL_SECTION
c and while inside calls WaitForSingleObject(t2, INFINITE) on a thread
t2 which is blocked because it is trying to enter c. Can a third
thread t3 break the deadlock by doing a "leave" or a "delete" on c?
Why or why not?
- Under what situations would you use an ArrayBlockingQueue for
a ThreadPoolExecutor? What bad things could happen if you
chose a DelayQueue for the
ThreadPoolExecutor? (Make sure you work in the notions
of the core and the maximum pool sizes in your answer.)
- The classic producer-consumer problem can be implemented with
a java.concurrent.util.Exchange object. A producer fills a plain
old (unsynchronized) queue. A consumer starts with a plain
old (unsynchronized) queue which is empty. When the producer's
queue fill up, it exchanges with the consumer, who then processes
each of the items.
- Assuming the plain old FIFO queue has methods
add(Object)
Object remove()
isEmpty()
isFull()
and assuming the existence of methods
Object produce()
consume(Object)
write the run() methods for the producer and consumer.
Assume both methods have access to the same Exchanger object.
- Can this solution be easily generalized to a situation in
which there is 1 producer but n consumers? Why or why
not? (I'm looking for a kind of specific answer here....)