Part A: The keyboard and mouse interrupt service routines must place their data into a common queue. Mutual exclusion between these routines may be achieved by disabling interrupts, but a data-available semaphore is needed for this queue. Call it input-data.
For each queue that the window manager uses for communication with applications processes, we need a data available semaphore and a mutual exclusion semaphore (we'll call it mutex).
Part B:
window manager: initialize x = y = mid-screen current = lookup queue record for the window at x, y repeat wait( input-data ) disable interrupts (for mutex with interrupt routines) dequeue item from input queue enable interrupts (for mutex with interrupt routines) if item is a mouse movement code if item=up y=y-1 if item=down y=y+1 if item=left x=x-1 if item=right x=x+1 endif new = lookup queue record for window at x, y if current not equal new wait( current.mutex ) enqueue exit-window code on current.queue signal( current.mutex ) signal( current.data ) current = new wait( current.mutex ) enqueue enter-window code on current.queue enqueue x - current.xorigin on current.queue enqueue y - current.yorigin on current.queue signal( current.mutex ) signal( current.data ) signal( current.data ) \ one signal per item signal( current.data ) / enqueued else wait( current.mutex ) enqueue item on current.queue signal( current.mutex ) signal( current.data ) endif forever
For the signal operation to unblock the user-level code, for example, to signal that data is available in an input queue, the code shown in the notes could work, but the enable-interrupts code at the end of the routine could cause problems. As a general rule, it is dangerous to allow one interrupt service routine to interrupt another, so the enable should be deferred to the return from interrupt. Therefore, from within an interrupt service routine, the signal operation will usually need to be done by a version of the routine that does not enable interrupts at the end!
Part B: It is not possible to use a semaphore to provide synchronization between the user and the output interrupt handler because, as conventionally used, the handler would have to wait and the user would have to signal. An interrupt service routine cannot wait for a signal from a user process, because, so long as the interrupt service routine is running, no user process may run! This is not formally a deadlock, but it is certainly similar to a deadlock!
The usual solution is to have the interrupt service routine wait by disabling the interrupt request from the device it serves and then return. When the user-level code produces data for that device, it enables the device, allowing the device to request an interrupt. If the device was not waiting, the device was already enabled, and no damage will result. If the device was waiting, this will allow it to request an interrupt and continue operation.
The second problem may be more difficult to solve! When a signal is delivered, the signal handler must suspend one user process and resume another. That means that the signal handler must execute a setjmp() and then exit by a longjmp() to the new context. The UNIX documentation hints that entry to a signal handler may not be by a standard function call, and this suggests that a longjmp() that transfers control into a signal handler may be very dangerous! This may entirely prevent use of SIGALRM as a preemption mechanism.