LMU ☀️ CMSI 673
CONCURRENT AND DISTRIBUTED PROGRAMMING
Practice
  1. 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".
  2. 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.
  3. 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()
  4. 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.
  5. 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.
  6. Describe why concurrent programming often leads to code that is more modular (in particular, more loosely coupled).
  7. Sketch fragments of code which illustrates livelock in as many languages as you can.
  8. Give cobegin/coend style pseudocode for 3x3 matrix multiplication that maximizes concurrency.
  9. 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;
        }
    
  10. 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?
  11. 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.
  12. Explain how synchronous communication can be simulated in a lanuguage that support only asynchrounous communication primitives.
  13. A mailbox can be made completely generic and reusable in Ada, but you cannot say this about relays. Why?
  14. 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?
  15. 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.
  16. 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.
  17. 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).
  18. 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.
  19. 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.

  20. Write a driver program which "tests" the resource allocators in my Resource_Allocators package, and that exhibits the insecurity of the insecure solution.
  21. 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.
  22. 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.
  23. Implement the "concurrent bounded buffer" class in C++ twice: once using pthreads and once using the Windows API.
  24. 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.
  25. 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?

  26. 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);}
        }
    
  27. 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().

  28. In my WinAPI DiningPhilosophers program, could I have used Critical Section (CRITICAL_SECTION) objects in place of mutexes? Why or why not?
  29. 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.
  30. Why is calling GetExitCodeThread() not a reliable way to determine whether or not a thread has finished?
  31. 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.
  32. 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.)
  33. 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;
          }                                  }
    
  34. Why do busy high-capacity servers in the world today more than likely not use one thread per client?
  35. Write a WinAPI application which creates a thread with priority level 31 that busy waits for 30 seconds. Run it and explain what happened.
  36. 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.
  37. 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.
  38. 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.
  39. 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.
  40. 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.
  41. Implement a priority queue data type in Ada, using a server task to provide synchronization.
  42. Implement a priority queue data type in Ada, where each priority queue object is a protected object.
  43. 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.
  44. 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.
  45. 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.
  46. 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.
  47. Why do Java programmers not have to worry about the situation in the previous problem (interleaving of text output written to a stream)?
  48. 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 CountDownLatches, meaning you can only use them once.

  49. 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.
  50. 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;
    
  51. In Java, what happens if you invoke a method on a thread that has completed?
  52. 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.
  53. 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.
  54. 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.
  55. 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.
  56. 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.
  57. 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.
  58. 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?
  59. 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.)
  60. 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.
    1. 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.
    2. 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....)