CS452 - Real-Time Programming - Spring 2015

Lecture 22 - Anthropomorphic Programming

Public Service Annoucements

  1. Train Control I demo on Friday, 3 July.
  2. The exam has three start times. The end times are 26.5 hours after the start time.
    Answers to questions asked from 20.30, 4 August to 22.00, 4 August will be answered on the newsgroup, whether they arrive by e-mail or on the newsgroup.
  3. Where on the continuum from human to inanimate to people perceive computers to be?


Anthropomorphic Programming II

Servers and Attendant Tasks

2. Using a Courier

Transmit Notifier Code

Transmit Courier Code

Transmit Proprietor Code

Notes

Another possible arrangement for task creation

Another possible arrangement for initialization

Distributed gating

I am showing you collections of tasks implemented together because sets of related tasks is a level of organization above the individual task.

E.g., the decision to add a courier requires revision of code within the group, but not outside it.


3. The Warehouse

4. Secretary, Receptionist

The warehouse guards the notifier from the effect of multiple clients on the server. Another method of guarding the notifier is to guard the proprietor so that its sendQ is always empty. The secretary/receptionist does so.

The secretary receives requests from many clients and forwards them one at a time to the proprietor. Because the secretary Receives it cannot Send. It communicates with the Proprietor using a Courier.

Notes

  1. When all the clients have been given service
  2. The Courier is almost always Reply-blocked. If it spends a significant amount of time Receive-blocked the system is overloaded.
  3. If there is a temporary overload of Client requests, the Courier may sit for a while in the Secretary's SendQ while the Proprietor continues to interact with the Notifier.
  4. If there is a permanent overload of requests you are in trouble.

5. Administrator, Worker

Administrator is a proprietor who does no work but only assigns work to others

Where do the workers come from?

Most workers prefer employee status to consultant status.

Worker code

  Send( administrator, nil, workOrder );
  FOREVER {
    Send( administrator, workResult, workOrder );
    workResult = doWork( workOrder );
  }

doWork might require further sends to servers or warehouses, which is harmless in this context.

What kind of work might a worker do?

Administrator code

Initialization

Administrator( ) {
  for ( i = 0; i < NUM_WORKERS; i++ ) worker[i] = Create( mypriority - 1, workerCode );
    
  FOREVER {
    Receive( requester, request );
    switch( request.type ) {
    case CLIENT:
      enqueue( orderQ, {order = {client, request}} );
      if ( !empty( employeeQ ) ) Reply( dequeue( employeeQ ) ), dequeue( orderQ ) ); 
      break;
    case WORKER:
      enqueue( emploeeQ, requester);
      if ( !empty( orderQ ) ) Reply( dequeue( employeeQ ), { dequeue( orderQ ) );
      if ( request != nil ) { Reply( request.client, request.request );
      break;
    }
}

Note: Tid of the client is included in the workorder to the administrator does not have to maintain and search a list of jobs being done. (Administrators are by nature lazy!)

Alternative Worker/Administrator Model

  1. As above, Administrator includes Tid of the client in the order.
  2. Worker replies directly to client with result and to administrator with request for another order to process.

Comments

  1. The administrator can add a little more value.

6. 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 even pretend to work!

Try instead, something like

FOREVER {
  Receive( *requester, request );
  switch ( request.type ) {
  case CLIENT:
    case = {requester, request.event, request.delay} );
    insert( caseDB, case );
    if ( !empty( delayQ ) && !empty( irregularQ ) ) {
      Reply( dequeue( delayQ ), case );
      Reply( dequeue( irregularQ ), case );
    }
  case DELAY:
    enqueue( delayQ, requester );
    if ( case = pending( caseDB, nil ) && !empty( irregularQ ) ) {
      Reply( dequeue( delayQ ), case );
      Reply( dequeue( irregularQ ), case );
    } 
    case = extract( caseDB, request.requester );
    if ( case != nil ) Reply( case.requester, TIME_OUT );
    break;
  case CLUE:
    enqueue( irregularQ, requester );
    if ( ( case = pending( caseDB, nil )!=nil && !empty( delayQ ) ) {
      Reply( dequeue( delayQ ), case );
      Reply( dequeue( irregularQ ), case );
    } 
    case = extract( caseDB, request.requester );
    if ( case != nil ) Reply( case.requester, EVENT );
    break;
  }
}

This is the code of a particular detective, one that notifies you which occurs first, a time-out or an event.

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: how to do them is shown above.

Who is the client of the detective

  1. Initiator of a normal action

7. Housekeeper

Housekeeper of the system who will detect, and possibly clean up pathologies.

8. The Grim Reaper

Most often pathologies are handled by destroying one or more tasks, the recreating them. Destroying is usually done in the context of programming conventions. One convention followed here is that when tasks work together as a unit task creation is done internally with one master task directly or indirectly creating all tasks in the unit. Then

Resources must be returned for reuse. Some are easy.

Others are hard. Suppose a task owns a piece of track. It must be given back or your project won't survive very much task destruction. How do we keep the time for this process bounded above?


Return to: