BDE 4.14.0 Production release
|
Provide an efficient queue for time events.
struct
) Templatized item in the time event queueThis component provides a thread-safe and efficient templatized time queue. The queue stores an ordered list of time values and associated DATA
. Each item added to the queue is assigned a unique identifier that can be used to efficiently remove the item making this queue suitable for conditions where time items are added and removed very frequently.
Class bdlcc::TimeQueue<DATA>
provides a public interface which is similar in structure and intent to bdlcc::Queue<DATA>
, with the exception that each item stored in the bdlcc::TimeQueue
is of type bdlcc::TimeQueueItem<DATA>
. This structure contains a single bsls::TimeInterval
value along with the DATA
value.
Idiomatic usage of bdlcc::TimeQueue
includes the member function popLE
, which finds all items on the queue whose bsls::TimeInterval
are less than a specified value, and transfers those items to a provided vector of items. Through the use of this member function, clients can retrieve and process multiple elements that have expired, that is, whose bsls::TimeInterval
values are in the past.
bdlcc::TimeQueue
also makes use of an opaque data type bdlcc::TimeQueue::Handle
which serves to identify an individual element on the Time Queue. A value of type Handle
is returned from the add
member function, and can then be used to remove or modify the corresponding element on the queue. In this way, the update
member function can update the time value for a specific bdlcc::TimeQueueItem
without removing it from the queue.
bdlcc::TimeQueue::Handle
is an alias for a 32-bit int
type. A handle consists of two parts, the "index section" and the "iteration section". The index section, which is the low-order numIndexBits
(which defaults to numIndexBits == 17
), uniquely identifies the node. Once a node is added, it never ceases to exist - it may be freed, but it will be kept on a free list to be eventually recycled, and the same index section will always identify that node. The iteration section, the high-order 32 - numIndexBits
, is changed every time a node is freed, so that an out-of-date handle can be identified as out-of-date. But since the iteration section has only a finite number of bits, if a node is freed and re-added enough times, old handle values will eventually be reused.
Up to 2 ** numIndexBits - 1
nodes can exist in a given time queue. A given handle won't be reused for a node until that node has been freed and reused 2 ** (32 - numIndexBits) - 1
times.
numIndexBits
is an optional parameter to the time queue constructors. If unspecified, it has a value of 17. The behavior is undefined unless the specified numIndexBits
is in the range 8 <= numIndexBits <= 24
.
It is safe to access or modify two distinct bdlcc::TimeQueue
objects simultaneously, each from a separate thread. It is safe to access or modify a single bdlcc::TimeQueue
object simultaneously from two or more separate threads.
It is safe to enqueue objects in a bdlcc::TimeQueue
object whose destructor may access or even modify the same bdlcc::TimeQueue
object. However, there is no guarantee regarding the safety of enqueuing objects whose copy constructors or assignment operators may modify or even merely access the same bdlcc::TimeQueue
object (except length
). Such attempts generally lead to a deadlock.
For a given bsls::TimeInterval
value, the order of item removal (via popFront
, popLE
, removeAll
, etc.) is guaranteed to match the order of item insertion (via add
) for a particular insertion thread or group of externally synchronized insertion threads.
The following shows a typical usage of the bdlcc::TimeQueue
class, implementing a simple threaded server my_Server
that manages individual Connections (my_Connection
) on behalf of multiple Sessions (my_Session
). Each Connection is timed, such that input requests on that Connection will "time out" after a user-specified time interval. When a specific Connection times out, that Connection is removed from the bdlcc::TimeQueue
and the corresponding my_Session
is informed.
In this simplified example, class my_Session
will terminate when its Connection times out. A more sophisticated implementation of my_Session
would attempt recovery, perhaps by closing and reopening the physical Connection.
Class my_Server
will spawn two service threads to monitor connections for available data and to manage time-outs, respectively. Two forward-declared "C" functions are invoked as the threads are spawned. The signature of each function follows the "C" standard "`void *`" interface for spawning threads. Each function will be called on a new thread when the start
method is invoked for a given my_Server
object. Each function then delegates processing for the thread back to the my_Server
object that spawned it.
The my_Connection
structure is used by my_Server
to manage a single physical connection on behalf of a my_Session
.
Protocol class my_Session
provides a pure abstract protocol to manage a single "session" to be associated with a specific connection on a server.
The constructor and destructor do nothing:
Protocol class my_Server
provides a partial implementation of a simple server that supports and monitors an arbitrary number of connections and handles incoming data for those connections. Clients must provide a concrete implementation that binds connections to concrete my_Session
objects and monitors all open connections for incoming requests. The concrete implementation calls my_Server::newConnection()
when a new connections is required, and implements the virtual function monitorConnections
to monitor all open connections.
The constructor is simple: it initializes the internal bdlcc::TimeQueue
and sets the I/O timeout value. The virtual destructor sets a shared completion flag to indicate completion, wakes up all waiting threads, and waits for them to join.
Member function newConnection
adds the connection
to the current set of connections to be monitored. This is done in two steps. First, the connection
is added to the internal array, and then a timer is set for the connection
by creating a corresponding entry in the internal bdlcc::TimeQueue
.
Member function monitorConnections
, provided by the concrete implementation class, can use the internal array to determine the set of connections to be monitored.
Member function removeConnection
removes the connection
from the current set of connections to be monitored. This is done in two steps, in reversed order from newConnection
. First, the connection
is removed from the internal bdlcc::TimeQueue
, and then the connection
is removed from the internal array.
The concrete implementation class must provide an implementation of virtual function closeConnection
; this implementation must call removeConnection
when the actual connection is to be removed from the my_Server
object.
Function closeConnection
is in turn called by function monitorTimers
, which manages the overall timer monitor thread. Because monitorTimers
takes responsibility for notifying other threads when the queue status changes, function removeConnection
does not address these concerns.
The dataAvailable
function will be called when data becomes available for a specific connection. It removes the connection from the timer queue while the connection is busy, processes the available data, and returns the connection to the queue with a new time value.
Function monitorTimers
manages the timer monitor thread; it is called when the thread is spawned, and checks repeatedly for expired timers; after each check, it does a timed wait based upon the minimum time value seen in the queue after all expired timers have been removed.
Function start
spawns two separate threads. The first thread will monitor connections and handle any data received on them. The second monitors the internal timer queue and removes connections that have timed out. Function start
calls bslmt::ThreadUtil::create
, which expects a function pointer to a function with the standard "C" callback signature void *fn(void *data)
. This non-member function will call back into the my_Server
object immediately.
Finally, we are now in a position to implement the two thread dispatchers:
In order to test our server, we provide two concrete implementations of a test session and of a test server as follows.
The program that would exercise this test server would simply consist of:
The output of this program would look something as follows: