1. INTRODUCTION This directory contains an example that illustrates how to use both threaded and reactive concurrency mechanisms in ACE. The example application schedules and processes heterogenerous user input and timer-based events in the context of a bounded packet relay mechanism. In this example, a transmission begins, packets arrive from an input device object, and are transferred to an output device object by a relay object at a specified pace. The transfer continues until all packets have been relayed, a duration limit expires, or the transmission is cancelled. User input is handled concurrently with a running transmission. You can run a transmission, cancel a transmission, change transmission parameters, view statistics from the most recent transmission, or exit the program, using selections from an interactive text-based menu. In addition, the example program can be run in batch mode, with the appropriate commands piped to the program's standard input stream. Transmission parameters are intialized to default values. Transmission parameter values persist until/unless they are subsequently modified by an appropriate command. If an invalid value for a command is given, or a run or report command is issued while a transmission is in progress, the offending command has no effect, and an error message is generated. 2. USER INTERFACE Commands that can be given to the program include the following: Settings commands: 1 Minimum value is 1 packet, defaults to 1000 packets. 2 Minimum value is 1 usec, defaults to 10000 usec (10 msec). 3 Minimum value is 1 usec, defaults to 10000 usec (10 msec). 4 Minimum value is 1 usec, defaults to 20000000 usec (20 sec). A value of 0 is also a valid input, in which case no limit will be placed on the duration of the transmission (it will end when all packets have been relayed from the input device to the output device). 5 0 - does no logging 1 - logs packets created by the input device 2 - logs packets consumed by the output device 4 - prints contents of packets consumed by the output device To set several flags, pass their sum as the logging level value: e.g., a logging level value of 7 turns on all possible logging. Action commands: 6 - runs a transmission using the current settings 7 - cancels a transmission if there is one running 8 - reports statistics from the most recent transmission 9 - quits the program 3. APPLICATION DESIGN 3.1. KEY COMPONENTS The design of this example application consists of four main components: the driver object, the relay object, the input device object, and the output device object. The driver object is responsible for receiving user input and overall handling of user input commands. The driver object is active, with separate threads for receiving user input and managing its transmission timer queue. This allows the user to issue commands while a transmission is in progress. The driver object uses an ACE_Thread_Timer_Queue_Adapter, which is derived from ACE_Task_Base. The driver object starts another active object, called User_Input_Task, which is also derived from ACE_Task_Base. This allows both the timer queue and the user input object to be made active, running in their own threads of control. The relay object is passive, managing a message queue and necessary locks to allow safe access from multiple threads. It provides methods to receive and enqueue a mesage from the input device, dequeue a message and send it to the output device, and to start or end a transmission. It uses ACE_Message_Queue (which contains ACE_Message_Block objects) and ACE_Thread_Mutex objects to implement this functionality. The input object is active, managing timeouts and input events in its own thread. The input object is also reactive, using an ACE_Reactor to allow response to multiple input handles as well as to do polling at specific timeouts. The input pseudo-device wrapper in this example does not make use of input handles and only does timed polling, but extending this only requires registering the appropriate input handles and handlers with the reactor. The input object is derived from ACE_Task_Base, and is activated by the relay object when a new transmission starts. The input object packages each data packet in an ACE_Message_Block before sending it to the relay object. The output object is passive. If logging is turned on, it will report the arrival time, relative to the start of the transmission, of each output message, and the contents of that message. The output object will also "consume" each ACE_Message_Block passed to it, calling delete on the passed pointer. 3.2. RUN-TIME CHARACTERSITICS When the user initiates a transmission, the appropriate settings are passed by the driver to the relay object's start transmission method. The relay object tries to start a new transmission. If another transmission is in progress, the method returns an error. Otherwise, the relay object's start transmission method initializes itself and the input and output device objects, activates the input device object, and stores the handle for the new input device thread. The driver then constructs a timeout handler with a count of the number of messages to relay and a send timeout value, and pushes a timer with this handler onto the timer queue. If there is a limit on the duration of the transmission, the driver constructs a different handler for end-of-transmission, and pushes a timer for the end of the transmission with this handler onto the timer queue as well. When the user issues a cancel transmission command, the driver calls the relay's end transmission method. When a send timeout expires, the handler (running in the timer queue thread) calls the send method of the relay. If there are any enqueued messages from the input device object in its queue, the relay object's send method will dequeue a message, pass it to the output device object, and return success. If there are no messages in the queue, the relay object's send method will return failure. If the send was successful, the handler will decrement its count of remaining messages. If the count is greater than zero, the handler will register a new send timer for itself and exit. If the count is zero, then the handler will call the relay's end transmission method, clear the timer queue, and mark itself inactive before exiting. Similarly, if the end-of-transmission timer expires before all packets have been sent, that handler will call the relay's end transmission method, clear the timer queue, release the semaphore, and then exit. When the relay's end transmission method is called, it marks the relay itself inactive, and calls the input device's deactivate method, which sets the input device's activity flag to inactive (see below). The relay's end transmission method then waits until the input device thread exits, before returning. While it is still active, the input device thread blocks on a reactor timeout for the duration it was given by the relay. When the timeout expires, the input device checks a flag to see if it is still active. If the flag says the input device is inactive, the thread simply exits. This allows cancellation of the input device when a transmission has ended. If the flag says it is still active, the thread builds a new character buffer, and wraps this with a new ACE_Message_Block. The input device passes this message block to the execution method of the send input message command object with which it was constructed. This level of indirection allows the input device to be used with arbitrary types, so that it could for example be connected directly to an output device. The input device also maintains a count of the number of messages it has sent. Once the input device has sent all its messages, it marks itself inactive, and its thread simply exits. 4. ACCESSING THE SOURCE CODE The files for this example are located in $ACE_ROOT/examples/Bounded_Packet_Relay in the latest release of ACE, which is located at http://www.cs.wustl.edu/~schmidt/ACE_wrappers/ACE.tar.gz Source Files: Thread_Bounded_Packet_Relay.h Thread_Bounded_Packet_Relay.cpp BPR_Driver.h BPR_Driver.cpp BPR_Driver_T.h BPR_Driver_T.cpp bpr_thread.cpp Make file: Makefile Doc file: README (this file) Executable: bpr_thread