Sign In/My Account | View Cart  
advertisement


Listen Print

Going Up?
by Sam Tregar | Pages: 1, 2, 3, 4

The Elevator Class

Each elevator thread contains an object of the Elevator class. The Elevator::run() routine creates this object as its first activity:


  # run an Elevator thread, takes a numeric id as an argument and
  # creates a new Elevator object
  sub run {
      my $self = Elevator->new(@_);

Notice that since $self is not marked shared it is a thread-local variable. Thus, each elevator has its own private $self object. The new() method just sets up a hash with some useful state variables and returns a blessed object:


  # create a new Elevator object
  sub new {
      my $pkg = shift;
      my $self = { state => STARTING,
                   floor => 0,
                   dest  => 0,
                   @_,
                 };
      return bless($self, $pkg);
  }

All elevators start at the ground floor (floor 0) with no destination. The state attribute is set to STARTING which comes from this set of constants used to represent the state of the elevator:


  # state enumeration
  use constant STARTING   => 0;
  use constant STOPPED    => 1;
  use constant GOING_UP   => 2;
  use constant GOING_DOWN => 3;

After setting up the object, the elevator thread enters an infinite loop looking for button presses that will cause it to travel to a floor. At the top of the loop $self->next_dest() is called to determine where to go:


    # run until simulation is finished
    while (1) {
        # get next destination
        $self->{dest} = $self->next_dest;

The next_dest() method examines the shared array @BUTTON to determine if any people are waiting for an elevator. It also looks at %PANEL to see if there are people inside the elevator heading to a particular floor. Since next_dest() accesses shared variables it starts with a call to lock() for each shared variable:


  # choose the next destination floor by looking at BUTTONs and PANELs
  sub next_dest {
      my $self = shift;
      my ($id, $state, $floor) = @{$self}{('id', 'state', 'floor')};
      lock @BUTTON;
      lock %PANEL;

Perl's lock() is an advisory locking mechanism, much like flock(). When a thread locks a variable it will wait for any other threads to release their locks before proceeding. The lock obtained by lock() is lexical - that is, it lasts until the enclosing scope is exited. There is no unlock() call, so it's important to carefully scope your calls to lock(). In this case the locks on @BUTTON and %PANEL last until next_dest() returns.

next_dest()'s logic is simple, and largely uninteresting for the purpose of learning about thread programming. It does a simple scan across @BUTTON and %PANEL looking for 1s and takes the first one it finds.

Once next_dest() returns the elevator has its marching orders. By comparing the current floor ($self->{floor}) to the destination the elevator now knows whether it should stop, or travel up or down. First, let's look at what happens when the elevator decides to stop:


   # stopped?
   if ($self->{dest} == $self->{floor}) {
        # state transition to STOPPED?
        if ($self->{state} != STOPPED) {
            print "Elevator $id stopped at floor $self->{dest}.\n";
            $self->{state} = STOPPED;
        }

        # wait for passengers
        $self->open_door;
        sleep $ELEVATOR_WAIT;

The code starts by printing a message and changing the state attribute if the elevator was previously moving. Then it calls the open_door() method and sleeps for $ELEVATOR_WAIT seconds.

The open_door() method opens the elevator door. This allows waiting people to board to elevator.


  # open the elevator doors
  sub open_door {
      my $self = shift;
      lock %DOOR;
      $DOOR{"$self->{id}.$self->{floor}"} = 1;
      cond_broadcast(%DOOR);
  }

Like next_dest(), open_door() manipulates a shared variable so it starts with a call to lock(). It then sets the elevator door for the elevator on this floor to open by assigning 1 to the appropriate entry in %DOOR. Then it wakes up all waiting person threads by calling cond_broadcast() on %DOOR. I'll go into more detail about cond_broadcast() when I show you the Person class later on. For now suffice it to say that the people threads wait on the %DOOR variable and will be woken up by this call.

The other states, for going up and going down, are handled similarly:


  } elsif ($self->{dest} > $self->{floor}) {
      # state transition to GOING UP?
      if ($self->{state} != GOING_UP) {
          print "Elevator $id going up to floor $self->{dest}.\n";
          $self->{state} = GOING_UP;
          $self->close_door; 
      }

      # travel to next floor up
      sleep $ELEVATOR_SPEED;
      $self->{floor}++;

  } else {
      # state transition to GOING DOWN?
      if ($self->{state} != GOING_DOWN) {
          print "Elevator $id going down to floor $self->{dest}.\n";
          $self->{state} = GOING_DOWN;
          $self->close_door; 
      }

      # travel to next floor down
      sleep $ELEVATOR_SPEED;
      $self->{floor}--;
  }

The elevator looks at the last value for $self->{state} to determine whether it was already heading up or down. If not, then it prints a message and calls $self->close_door(). Then it sleeps for $ELEVATOR_SPEED seconds as it travels between floors and adjusts its current floor accordingly.

The close_door() method simply does the inverse of open_door(), but without the call to cond_broadcast() since there's no point waking people up if they can't get on the elevator:


  # close the elevator doors
  sub close_door {
      my $self = shift;
      lock %DOOR;
      $DOOR{"$self->{id}.$self->{floor}"} = 0;
  }

Finally, at the bottom of the elevator loop there is a check on the shared variable $FINISHED:


  # simulation over?
  { lock $FINISHED; return if $FINISHED; }

Since the elevator threads are in an infinite loop the main thread needs a way to tell them when the simulation is over. It uses the shared variable $FINISHED for this purpose. I'll go into more detail about why this is necessary later.

That's all there is to the Elevator class code. Elevators simply travel from floor to floor opening and closing doors in response to buttons being pushed by people.

Pages: 1, 2, 3, 4

Next Pagearrow