CS452 - Real-Time Programming - Fall 2009

Lecture 21 - Task Structure: Receptionist

Reminders


Servers and Attendant Tasks

1. Proprietor with a Notifier

2. Using a Courier

3. Using a Warehouse


4. Adding a Receptionist

In the solution above there should be, on average,

We also know that the Warehouse will buffer messages coming and going from the hardware.

A task that can screen requests can lower the buffering required in the Proprietor. We call such a task a receptionist.

Receptionist Code

Proprietor Code

Note


6. Proprietor compared to Monitor

Controls a resource

Provides mutual exclusion by owning the code that accesses the resource

Proprietor provides service to clients

Like a subroutine call to the client

Proprietors versus Monitors

Monitor Proprietor Comments
access to resource localized localized Means one at a time.
service model self-service full-service Easily adapted to multi-core
service structure subroutine of client independent task Easily adapted to multi-core
priority client probably different Do we want client priority or resource priority?
address space client separate Hardware vs software protection
permissions client different Does client have permission to run certain instructions?
CPU same could be different Modern hardware works hard to support threads.

Generic Problem: Local Delay

Proprietor begins processing

Can other requests be processed? Possible strategies

  1. refusal
  2. siblings
  3. nested receive
  4. early reply

7. Administrator, Worker

Generalization of proprietor

Administrator maintains an array of records

Administrator works as he can on any one of the tasks.

Real administrators manage workers

Worker code

Worker( ) {
  Initialize( );
  Receiver ( serverPid, ... );
  Reply( serverPiD )
    // Avoid critical races
  workResult = null;
  FOREVER {
    Send( administrator, workResult, workOrder );
    doWork( workOrder );
  }
}

Administrator code

Administrator( ) {
  Initialize( );
  for ( i = 0; i < NUM_WORKERS; i++ ) worker[i] = Create( workerCode );
  for ( i = 0; i < NUM_WORKERS; i++ ) Send( worker[i], ... , ... );
    // Initialize orderQueue, workerQueue
    // RegisterAs
  FOREVER {
    error = Receive( requester, request );
    switch( request.type ) {
    case CLIENT:
       workOrder = {client = requester, request};
       if( worker = dequeue( workerQueue ) && dataAvailable( ) ) Reply( worker, workOrder );
       else enqueue( orderQueue, workOrder );
       break;
    case NOTIFIER:
       // Acquire data
       // Update state
       // for each queued client
       //    if complete data available && worker available
       //       workorder = {client, request}
       //       Reply( worker, workOrder );
       //    else enqueue client 
       break;
    case WORKER:
      Reply( request.client, request.result );
      if( ( workOrder = deQueue( orderQueue) ) ) {
        workOrder = {workOrder.client, workOrder.request};
        Reply( requester, workOrder );
      } else enqueue( workerQueue, requester );
    }
  }
}

Alternative Administrator Code

Administrator( ) {
  Initialize( );
  for ( i = 0; i < NUM_WORKERS; i++ ) worker[i] = Create( workerCode );
  for ( i = 0; i < NUM_WORKERS; i++ ) Send( worker[i], ... , ... );
    // Initialize orderQueue, workerQueue
    // RegisterAs
  FOREVER {
    error = Receive( requester, request );
    switch( request.type ) {
    case CLIENT:
       enqueue ( workfifo, workorder );
       while ( !empty( workfifo ) )
          if ( complete ( workorder, data ) && workerTid = next( workerQ ) ) { 
             Reply( client, workerTid, ... );
             Reply( worker, workorder );
          }
       break;
    case NOTIFIER:
       // Acquire data
       // Update state
       // for each queued client
       //    if complete data available && worker available
       //       workorder = {client, request}
       //       Reply( worker, workOrder );
       //    else requeue client 
       break;
    case WORKER:
      if( ( workOrder = deQueue( orderQueue) ) ) {
        workOrder = {workOrder.client, workOrder.request};
        Reply( requester, workOrder );
      } else enqueue( workerQueue, requester );
    }
  }
}

8. The Detective

Simple Events

The notifier is a task that waits on events.

You could call a notifier a detective,

Complex Events

In an application there is likely to be lots of waiting on combinations of events.

We use the detective to discover that a complex event has occurred.

Conjunction

Code could be

FOREVER {
  Send( part1 );
  Send( part2 );
  ...
  Send( master );
}

Disjunction

Code above doesn't work! Try instead

// InitializeDB;
// Create workers and synchronize;
// Synchronize with client;
FOREVER {
  Receive( *requester, request );
  if ( request.type == CLIENT ) {
    parsedRequest = parse( request );
    if ( happened( parsedRequest, DB ) ) Reply( requester );
    else enqueue( parsedRequest );
  }
  else if (request.type == WORKER ) {
    updateDB ( request );
    Reply( requester );
    foreach ( queuedRequest )
      if ( happened( parsedRequest, DB ) ) {
        dequeue( parsedRequest );
        Reply( client );
      }
  }
}

This is the code of a detective.

Not

We can say that an event has not happened yet.

Only at the end of the universe can we say that an event simply has not happened.

Time-outs are needed for NOT

Who is the client of the detective

  1. Initiator of a normal action
  2. Housekeeper of the system who will clean up pathologies (Idletask?))

R


Return to: