Homework 5

22C:116, Fall 1998

Due Monday Sept 28, 1998, in class

Douglas W. Jones
  1. 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
    

  2. Part A: The mouse interrupt service routine doesn't need a wait operation, although if the queue is shared, mutual exclusion becomes an issue, and something will be needed -- typically, this will use disable and enable operations on the interrupt enable mechanism to lock out other interrupt service routines during the critical section.

    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.

  3. If SIGALRM was used to force preemptive context switching, we'd have to solve two problems. First, we must prevent SIGALRM from being delivered during scheduler operations such as relinquish, wait and signal. To do this, we can use sigsetmask() to disable signals before the critical sections in these routines and to reenable signals after these sections. In this context, sigsetmask() corresponds exactly to the enable-interrupts and disable-interrupts mechanisms in the pseudocode presented in the notes.

    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.