We are supposed to support
int Get( int port )
and
int Put( int port, char c )
These are wrappers for sends to one or more serial servers.
one server | two servers | four servers | |
one
notifier |
likely queue
congestion in server |
likely queue
congestion in notifer |
|
two
notifiers |
one per channel?
one per direction? |
how should they
be paired? |
|
four
notifiers |
certain queue
congestion in server |
likely queue
congestion in server |
best performance,
most tasks |
Issues
Either
Or
Many other issues come up below as we consider possible task structures.
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.
Kernel is handling hardware in this example
Receive( &serverTid, eventId ); Reply( serverTid, ... );
FOREVER { data = AwaitEvent( eventid ); // data includes event type and volatile data switch( event-type ) { case RCV_INT: Send( serverTid, {NOT_RCV, byte}, {void} ); break; case XMT_INT: Send( serverTid, {NOT_XMIT, void}, {data} ); // byte is to be transmitted enqueue( data ); break; default: // This will never happen because your kernel is bug-free. }
// queues & fifos notifierTid = 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 { Receive( &requesterTid, {request-type, data} ); switch ( request-type ) { case NOT_RCV: Reply( requesterTid, {data} ); enqueue( rcvfifo, data ); if ( ! empty( rcvQ ) ) Reply( dequeue( rcvQ ), dequeue( rcvfifo ) ); break; case NOT_XMIT: Reply( requesterTid break; case CLIENT_RCV: enqueue( rcvQ, requesterTid ); if ( !empty( rcvfifo ) Reply( dequeue( rcvQ ), dequeue( rcvfifo ) ); break; case CLIENT_XMIT: Reply( requesterTid, ... ); enqueue( xmitfifo, data ); if ( xmitRdy ) { Reply( xmitNotTid, dequeue( xmitfifo ) ); xmitRdy = false; } break; default: Reply( requesterTid, "Sorry. I don't have any spare change.\n" ); } }
Simplest is best
Receive( &courierTid, ... ); Reply( courierTid, ... );
FOREVER { Receive( &courierTid, ... ); data = AwaitEvent( eventid ); // data includes event-type and volatile data switch( event-type ) { case RCV_INT: Reply( courierTid, data ); break; case XMT_INT: // test transmitter? Reply( courierTid, NOT_XMIT, ); // byte is to be transmitted break; default: // This will never happen because your kernel is bug-free. }
Receive( &serverTid, notifierTid, ... ); Send( notifierTid, ... ); Reply( serverTid );
FOREVER { Send( notifierTid, ..., {req, data} ); Send( serverTid, {req, data} ); }
// queues & fifos notifierTid = Create( notifier ); //Should notifier code name be hard coded? courierTid = Create( courier ); Send( courierTid, notifierTid, ... ); // On return courier & notifier are known to be okay RegisterAs( ); //On return client requests can begin.
FOREVER { requesterTid = Receive( request, {request-type, data} ); switch ( request-type ) { case NOT_RCV: Reply( requesterTid, ... ); enqueue( rcvfifo, data ); if ( ! empty( rcvQ ) ) Reply( dequeue( rcvQ ), dequeue( rcvfifo ) ); break; case NOT_XMIT: Reply( requesterTid, ... ); if ( ! empty( xmitfifo ) ) write( UART, dequeue( xmitfifo ) ); else xmitRdy = true; break; case CLIENT_RCV: enqueue( rcvQ, requesterTid ); if ( !empty( rcvfifo ) Reply( dequeue( rcvQ ), dequeue( rcvfifo ) ); break; case CLIENT_XMIT: Reply( requesterTid, ... ); enqueue ( xmitfifo, data ); if ( xmitRdy ) { write( UART, dequeue( xmitfifo ) ); xmitRdy = false; } break; default: Reply( requesterTid, "Sorry. I don't have any spare change.\n" ); } }
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 a warehouse between the courier and the notifier.
Receive( &warhouseTid, ... ); Reply( warhouseTid, ... );
FOREVER { data = AwaitEvent( eventid ); // data includes event-type and volatile data switch( event-type ) { case RCV_INT: Send( warehouseTid, data ); break; case XMT_INT: // test transmitter? Reply( warehouseTid, NOT_XMIT, ); // byte is to be transmitted break; default: // This will never happen because your kernel is bug-free. }
// data structures Receive( &courierTid, notifierTid, ... ); Send( notifierTid, ... ); Reply( courierTid, ... );
FOREVER { Receive( &requester, {req-type, data}, ); switch( req-type ) { case COUR_RCV: enqueue( rcvQ, requester ); if( !empty( msgbuf ) ) Reply( dequeue( rcvQ ), extract( msgbuf ), ... ); break; case COUR_XMIT: Reply( requester, ... ); enqueue( xmitfifo, unpack( data ) ); if ( xmitRdy ) { write( UART, dequeue( xmitfifo ) ); xmitRdy = false; } break; case NOT_RCV: Reply( requester , ... ); install( msgbuf, pack( data ) ); if( !empty( rcvQ ) && !empty( msgfifo ) ) Reply( dequeue( rcvQ ), extract( msgbuf ), ); break; case NOT_XMIT: Reply( requester, ... ); if( !empty( xmitfifo ) ) write( UART, dequeue( xmitfifo ) ); else xmitRdy = true; break; default: } }
Receive( &serverTid, {notifierTid, warehouseTid} ... ); Send( warehouseTid, notifierTid, ... ); Reply( serverTid );
FOREVER { Send( warehouseTid, {req, data} ); Send( serverTid, {req, data} ); }
// queues & fifos notifierTid = Create( notifier ); // Should notifier code name be hard coded? warehouseTid = Create( warehouse ); courierTid = Create( courier ); Send( courierTid, notifierTid, ... ); // On return courier, warehouse & notifier are known to be okay RegisterAs( ); // On return client requests can begin.
FOREVER { Receive( &requesterTid, {request-type, data} ); switch ( request-type ) { case COUR_RCV: Reply( requesterTid, ... ); enqueue( rcvfifo, data ); if ( !empty( rcvQ ) ) Reply( dequeue( rcvQ ), dequeue( rcvfifo ) ); break; case COUR_XMIT: enqueue( xmitQ, requesterTid ); if ( !empty( xmitfifo ) ) Reply( dequeue( xmitQ ), dequeue( xmitfifo ) ); break; case CLIENT_RCV: enqueue( rcvQ, requesterTid ); if ( !empty( rcvfifo ) ) Reply( dequeue( rcvQ ), dequeue( rcvfifo ) ); break; case CLIENT_XMIT: Reply( requesterTid, ... ); enqueue( xmitfifo, data ); if ( !empty( xmitQ ) ) Reply( dequeue( xmitQ ), dequeue( xmitfifo ) ); break; default: Reply( requesterTid, "Sorry. I don't have any spare change.\n" ); } }
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
Server should be lean and hungry
Return to: