CS452 - Real-Time Programming - Winter 2016

Lecture 22 - Anthropomorphic Programming

Public Service Annoucements

  1. Train Control I demo on Friday, 3 July.
  2. The exam will start at 12.30 on 19 April, 2016, and end at 20 April, 2016.

Anthropomorphic Programming II

Servers and Attendant Tasks

The initialization code shown below is only one of many different was of initializating a cooperating set of tasks. The principle is that we make the code so that modifiying the trask structure within the set requires no modification elsewhere in an application.

1. Proprietor with a Notifier: Initialization


	not-tid = Create( notifier );
	Send( not-tid,  ... );
When Send returns Notifier is ready.


	Receive( &proprietor-tid, ... );
	// When completely initialized
	Reply( proprietor-tid );

2. Using a Courier: Initialization


    courier-tid = Create( courier );
    notifier-tid = Create( notifier );
    Send( courier-tid, notifier-tid, ... );
    RegisterAs( "SerialIO" );


	Receive ( &proprietor-tid, ... );
	Send( notifier-tid, ... );
	Reply( proprietor-tid );


	Receive( &courier-tid, ... );
	Reply( courier-tid );

3. The Warehouse

Extend as above.

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.


  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.



	receptionist-tid = Create( receptionist );
	courier-tid = Create( courier );
	Send( courier-tid, receptionist-tid );
	Send( receptionist-tid, ... );


	Receive( &proprietor-tid, ... )
	Reply( proprietor-tid, ... );
	RegisterAs( "SerialIO" );


	Receive( &proprietor-tid, receptionist-tid );
	Reply( proprietor-tid, ... );

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 );
    	  workResult = doWork( workOrder );
    	  Send( administrator, workResult, 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


	for ( i = 0; i < NUM_WORKERS; i++ ) {
		worker[i] = Create( mypriority - 1, workerCode );
		Receive( &requester, ...);
		enqueue( employeeQ, requester );


   		Receive( requester, request );
    		switch( request.type ) {
    		case CLIENT:
      			enqueue( orderQ, {order = {client, request}} );
      			if ( !empty( employeeQ ) )
				Reply( dequeue( employeeQ ) ), dequeue( orderQ ) ); 
    		case WORKER:
      			enqueue( emploeeQ, requester);
      			if ( !empty( orderQ ) )
				Reply( dequeue( employeeQ ), { dequeue( orderQ ) );
      			if ( request != nil )
				Reply( request.client, request.request );

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.


  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,

But the notifier is restricted to detecting things that are very simple, and our train application requires the detection of complex events.

Complex Events

Inside the server code for serial I/O we detected more complex events, especially when transmitting. For example, when transmitting to the train we needed to find the conjunction (AND) of

In doing serial I/O we had this information available within a single task, but we could just as easily have retreived the information from three separate tasks using code like the following.
	Send( detector1, event1, ... );
	Send( detector2, event2, ... );
	Send( detector3, event3, ... );
When this code completes all three events have occurred. The detection is easy because conjunction (AND) is easy.

Disjunction, on the other hand is hard. For it we need the special properties of Receive, because it has the ability to accept a message from two or more different sources. For the detective, the sources are workers.

A typical example where a detective is needed is a time out. When a train is travelling toward a sensor you have an idea of when you expect it to arrive, and you know that if it hasn't arrived by the time you must do something to rescue the train. How is this done? A detective that searches for time outs has two workers,

If the sensor trigger comes first the detective responds with "success" and ignores the delay detector. If the delay comes first, the detective responds with "time out" and ignores the sensor report.

How does a time-out detective work?


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: