next up previous contents
Next: Iterators Up: Interfaces: Applications Previous: Interfaces: Applications   Contents


Callback functions

As we shall see later, Java permits parallel execution. One function can invoke another function to run in a parallel thread. Unlike a conventional function call, where the calling function is suspended till the called function terminates and returns, in this case the calling function proceeds to the next statement after the function invocation, while the invoked function executes in parallel. This parallelism may be actual, such as on a multiprocessor machine, where each thread is assigned to a separate processor, or virtual, such as on a single processor, where the threads are interleaved artibrarily to simulate parallel execution.

Suppose now that we have two classes, Myclass and Timer, where an object m of Myclass can create a Timer object t and invoke a function t.f() to run in parallel with m. Recall that we have assumed that m is not suspended after invoking t.f(). We would like a mechanism for t.f() to report back to m when it has completed its work. To do this, t needs to have a reference back to m. This can be accomplished by passing the reference when we create t: each instance of Timer is created by an object and the instance of Timer remembers the identity of the object that created it. For instance, we might have:

  class Myclass{
    ...
    public void some_function(){
      ...
      Timer t = new Timer(this);  // Tell t that this object
                                  // created t
      ...
      t.f();                      // Start off t.f() in parallel
      ...
    }
    ...
  }

We need the following complementary structure in Timer:

  class Timer implements Runnable{  // Says that Timer can be invoked
                                    // in a parallel thread
    private Object owner;

    public Timer(Object o){         // Constructor
      owner = o;                    // Remember who created me
    }
    ...
    public void f(){
      ...
      o.notify();                   // Tell my creator that I'm done
    }
    ...
  }

The problem with this definition is that the function o.notify() cannot be invoked as stated without casting o (which is an Object) back to Myclass. Of course, we could fix this by changing the type of owner from Object to Myclass, but then we would have to write a different Timer class for each type of owner.

In order to define a generic Timer class, all we need is that the owner have a method called notify() and that there is a uniform way to cast the owner to a type that can access this method. This can be achieved via an interface, say

  interface Timerowner{
    public abstract void notify();
  }

Now, we modify Myclass to implement Timerowner:

  class Myclass implements Timerowner{
    ...
    public void some_function(){
      ...
      Timer t = new Timer(this);  // Tell t that this object
                                  // created t
      ...
      t.f();                      // Start off t.f() in parallel
      ...
    }

    public void notify(){ ... }   // Implement the interface
    ...
  }

Finally, we modify Timer to insist that the owner of a Timer object must implement Timerowner:

  class Timer implements Runnable{  // Can be invoked in a
                                    // parallel thread
    private Timerowner owner;       // Owner must implement Timerowner

    public Timer(Timerowner o){     // Constructor
      owner = o;                    // Remember who created me
    }
    ...
    public void f(){
      ...
      o.notify();                   // Tell my creator that I'm done
    }
    ...
  }

Even though the timer t gets a reference to its parent object, the parent only ``exposes'' its Timerowner features to t.


next up previous contents
Next: Iterators Up: Interfaces: Applications Previous: Interfaces: Applications   Contents
Madhavan Mukund 2004-04-29