We all, most programmers included, have effective intuitions about human relations
Tasks are independent entities
Why do servers need attendant tasks?
Proprietor `owns' a service, which usually means a resource.
notifierPid = Create( notifier ); //Should notifier code name be hard coded?
Send( notifierTid, MyTid( ), ... ); //On return notifier is known to be okay
RegisterAs( ); //On return requests can begin.
FOREVER {
requesterTid = Receive( request );
switch ( request.type ) {
case NOTIFIER:
Reply( notifier );
updateDataStructures( );
for each queued client {
if( serviceable( client ) ) {
provideService( client );
Reply( client );
}
}
break;
case CLIENT:
if ( serviceable( requester ) ) {
provideService( requester );
Reply( requesterTid );
else enQueue( requester );
}
}
// Initialize Hardware
Receive( requester, eventId );
Reply( requester );
EnableInterrupts( ); // In the device
// Should be the last thing before FOREVER
FOREVER {
AwaitEvent( eventid ); //Should event ids be hard coded?
// Acquire volatile data
// Enable interrupts
Send( server, .... );
Reply( .... )
}
Simplest is best
// Initialize Hardware
Receive( requester, eventId );
Reply( requester ); // Should be the last thing before FOREVER
FOREVER {
AwaitEvent( eventid ); //Should event ids be hard coded?
// Acquire volatile data
// Enable interrupts
Receive( .... );
Reply( .... )
}
Receive( requester, { notifierId, serverId } ); // Now knows the the server/notifier Pids
Reply( requester ); // Should be the last thing before FOREVER
FOREVER {
Send( notifierPid ... );
Send( serverPid ... );
}
notifierPid = Create( notifier ); //Should notifier code name be hard coded?
courierPid = Create( courier );
Send( courierPid, { notifierPid, MyPid( ) } ... ) // On return courier is okay
Send( notifierPid, eventId, ... ); //On return notifier is okay
RegisterAs( ); //On return requests can begin.
FOREVER {
requesterPid = Receive( request );
switch ( request.type ) {
case COURIER:
provideCourierService( ); // may release queued clients
doReplies( );
case CLIENT:
if ( provideClientService( ) ) Reply( requesterPid );
else enQueue( requesterPid );
}
}
This gets you through a bottleneck where no more than two events come too fast.
Remember that all the calls provide error returns. You can/should use them for error recovery
Another possible arrangement for initialization
Distributed gating
Add buffer before courier and server.
// Initialize Hardware
Receive( bufferPid )// Find server Pid
Reply( ) // Should be the last thing before FOREVER
FOREVER {
AwaitEvent( eventid ); //Should event ids be hard coded?
// Acquire volatile data
// Enable interrupts
Send( bufferPid, .... )
}
FOREVER {
Receive( requesterPid, request );
switch ( request.type ) {
case COURIER:
enQueue( queue, request.data );
Reply( );
case SERVER:
Reply( queue );
}
}
Receive( requester, ..., bufferPid, ); // Now knows the the server/buffer Pids
Reply( requester, ... ); // Should be the last thing before FOREVER
FOREVER {
Send( bufferPid ... );
Send( serverPid ... );
}
notifierPid = Create( notifier ); //Should notifier code name be hard coded?
courierPid = Create( courier );
bufferPid = Create( buffer );
Send( courierPid, { myPid, bufferPid } ... ); // On return courier is okay
Send( notifierPid, { bufferPid }, ... ); //On return notifier is okay
RegisterAs( ); //On return requests can begin.
FOREVER {
requesterPid = Receive( request );
switch ( request.type ) {
case COURIER:
provideCourierService( ); // may release queued clients
Reply( requesterPid );
doReplies( );
case CLIENT:
if ( provideClientService( ) ) Reply( requesterPid );
else enQueue( requesterPid );
}
}
This structure clears up problems when the notifier runs too fast for the server.
Two issues:
Define `bottleneck'.
Called a guard.
What this amounts to is
Return to: