-- (c) Copyright, Real-Time Innovations, $Date:: 2012-02-16 #$
-- All rights reserved.
--
-- No duplications, whole or partial, manual or electronic, may be made
-- without express written permission. Any such copies, or
-- revisions thereof, must display this notice unaltered.
-- This code contains trade secrets of Real-Time Innovations, Inc.
------------------------------------------------------------------------------
-- Allows an application to wait until one or more of the attached
-- DDS.Condition objects has a trigger_value
-- of true or else until the timeout expires. More .. .
-- This mechanism is wait-based. Its general use pattern is as follows:
--
-- * The application indicates which relevant information it wants to get
-- by creating DDS.Condition objects
-- (DDS.StatusCondition,
-- DDS.ReadCondition or
-- DDS.QueryCondition) and attaching them to a DDS.WaitSet.
--
-- * It then waits on that DDS.WaitSet until the trigger_value of one
-- or several DDS.Condition objects become true.
--
-- * It then uses the result of the wait (i.e., active_conditions,
-- the list of DDS.Condition objects with trigger_value == true)
-- to actually get the information:
-- o by calling DDS.Entity.get_status_changes and then
-- get_<communication_status>() on the relevant DDS.Entity,
-- if the condition is a DDS.StatusCondition and the status changes,
-- refer to plain communication status;
--
-- o by calling DDS.Entity.get_status_changes and then
-- DDS.Subscriber.get_datareaders on the relevant DDS.Subscriber
-- (and then DDS.topic.example.FooDataReader.read()
-- or DDS.topic.example.FooDataReader.take on the returned
-- DDS.DataReader objects),
-- if the condition is a DDS.StatusCondition and the status
-- changes refers to StatusKind.DATA_ON_READERS_STATUS;
--
-- o by calling DDS.Entity.get_status_changes and then
-- DDS.topic.example.FooDataReader.read or
-- DDS.topic.example.FooDataReader.take on the
-- relevant DDS.DataReader,
-- if the condition is a DDS.StatusCondition and the
-- status changes refers to StatusKind.DATA_AVAILABLE_STATUS;
--
-- o by calling directly DDS.topic.example.FooDataReader.read_w_condition
-- or DDS.topic.example.FooDataReader.take_w_condition on
-- a DDS.DataReader with the DDS.Condition as a parameter if it is a
-- DDS.ReadCondition or a DDS.QueryCondition.
--
-- Usually the first step is done in an initialization phase,
-- while the others are put in the application main loop.
--
-- As there is no extra information passed from the middleware to the application
-- when a wait returns (only the list of triggered DDS.Condition objects),
-- DDS.Condition objects are meant to embed all that is needed to
-- react properly when enabled.
-- In particular, DDS.Entity-related conditions are related to exactly one
-- DDS.Entity and cannot be shared.
-- The result of a WaitSet.wait operation depends on the state of the DDS.WaitSet,
-- which in turn depends on whether at least one attached DDS.Condition has
-- a trigger_value of true. If the wait operation is called on DDS.WaitSet
-- with state BLOCKED, it will block the calling thread.
-- If wait is called on a DDS.WaitSet with state UNBLOCKED,
-- it will return immediately.
-- In addition, when the DDS.WaitSet transitions from BLOCKED to UNBLOCKED
-- it wakes up any threads that had called wait on it.
--
-- A key aspect of the DDS.Condition/DDS.WaitSet mechanism is the setting of
-- the trigger_value of each DDS.Condition.
-- Trigger State of a DDS.StatusCondition
-- The trigger_value of a DDS.StatusCondition is the boolean OR of the
-- ChangedStatusFlag of all the communication statuses (see Status Kinds)
-- to which it is sensitive.
-- That is, trigger_value == false only if all the values of the
-- ChangedStatusFlags are false.
--
-- The sensitivity of the DDS.StatusCondition to a particular communication
-- status is controlled by the list of enabled_statuses set on the condition
-- by means of the DDS.StatusCondition.set_enabled_statuses operation.
-- Trigger State of a DDS.ReadCondition
-- Similar to the DDS.StatusCondition, a DDS.ReadCondition also has a
-- trigger_value that determines whether the attached DDS.WaitSet is
-- BLOCKED or UNBLOCKED.
-- However, unlike the DDS.StatusCondition, the trigger_value of the
-- DDS.ReadCondition is tied to the presence of at least a sample managed by
-- Data Distribution Service with DDS.SampleStateKind and DDS.ViewStateKind
-- matching those of the DDS.ReadCondition.
-- Furthermore, for the DDS.QueryCondition to have a trigger_value == true,
-- the data associated with the sample must be such that the
-- query_expression evaluates to true.
--
-- The fact that the trigger_value of a DDS.ReadCondition depends on
-- the presence of samples on the associated DDS.DataReader implies
-- that a single take operation can potentially change the trigger_value of
-- several DDS.ReadCondition or DDS.QueryCondition conditions. For example,
-- if all samples are taken, any DDS.ReadCondition and
-- DDS.QueryCondition conditions associated with the DDS.DataReader
-- that had their trigger_value==TRUE before will see the trigger_value
-- change to FALSE. Note that this does not guarantee that
-- DDS.WaitSet objects that were separately attached to
-- those conditions will not be woken up.
-- Once we have trigger_value==TRUE on a condition,
-- it may wake up the attached DDS.WaitSet,
-- the condition transitioning to trigger_value==FALSE
-- does not necessarily 'unwakeup' the WaitSet as 'unwakening'
-- may not be possible in general.
--
-- The consequence is that an application blocked on a DDS.WaitSet
-- may return from the wait with a list of conditions,
-- some of which are not no longer 'active'.
-- This is unavoidable if multiple threads are concurrently
-- waiting on separate DDS.WaitSet objects and taking data
-- associated with the same DDS.DataReader entity.
--
-- To elaborate further, consider the following example:
-- A DDS.ReadCondition that has a
-- sample_state_mask = {SampleStateKind.NOT_READ_SAMPLE_STATE}
-- will have trigger_value of true whenever a new sample arrives
-- and will transition to false as soon as all the newly-arrived
-- samples are either read (so their sample state changes to READ)
-- or taken (so they are no longer managed by the Data Distribution Service).
-- However if the same DDS.ReadCondition had a
-- sample_state_mask = { SampleStateKind.READ_SAMPLE_STATE,
-- SampleStateKind.NOT_READ_SAMPLE_STATE },
-- then the trigger_value would only become false once all the newly-arrived
-- samples are taken (it is not sufficient to read them as that would only
-- change the sample state to READ),
-- which overlaps the mask on the DDS.ReadCondition.
-- Trigger State of a DDS.GuardCondition
-- The trigger_value of a DDS.GuardCondition is completely controlled by
-- the application via the operation DDS.GuardCondition.set_trigger_value.
--
-- Important:
-- The DDS.WaitSet allocates native resources.
-- When DDS.WaitSet is no longer being used, user should call
-- WaitSet.delete explicitly to properly cleanup all native resources.
------------------------------------------------------------------------------
pragma Ada_05;
with Ada.Finalization;
with DDS.WaitSet_Impl;
with DDS.ConditionSeq;
with DDS.Condition;
-- <dref>WaitSet</dref>
package DDS.WaitSet is
type Ref is new Ada.Finalization.Limited_Controlled with private;
-- <dref>WaitSet_new</dref>
type Ref_Access is access all Ref'Class;
procedure Initialize
(Self : in out Ref);
procedure Finalize
(Self : in out Ref);
procedure Wait
(Self : not null access Ref;
Active_Conditions : access DDS.ConditionSeq.Sequence;
Timeout : in DDS.Duration_T);
-- <dref>WaitSet_wait</dref>
procedure Wait
(Self : not null access Ref;
Active_Conditions : access DDS.ConditionSeq.Sequence;
Timeout : in Duration);
-- <dref>WaitSet_wait</dref>
-- <internal>
-- Allows an application thread to wait for the occurrence of certain conditions.
-- If none of the conditions attached to the dds.WaitSet have
-- a trigger_value of true, the wait operation will block suspending the calling thread.
-- The result of the wait operation is the list of all the attached conditions that have a trigger_value of true (i.e., the conditions that unblocked the wait).
-- The wait operation takes a timeout argument that specifies the maximum duration for the wait. If this duration is exceeded and none of the attached com.rti.dds.infrastructure.Condition objects is true, wait will return with the return code RETCODE_TIMEOUT. In this case, the resulting list of conditions will be empty.
-- RETCODE_TIMEOUT will not be returned when the timeout duration is exceeded if attached com.rti.dds.infrastructure.Condition objects are true, or in the case of a com.rti.dds.infrastructure.WaitSet waiting for more than one trigger event, if one or more trigger events have occurred.
-- It is not allowable for for more than one application thread to be waiting on the same com.rti.dds.infrastructure.WaitSet. If the wait operation is invoked on a com.rti.dds.infrastructure.WaitSet that already has a thread blocking on it, the operation will return immediately with the value RETCODE_PRECONDITION_NOT_MET.
-- </internal>
procedure Attach_Condition
(Self : not null access Ref;
Cond : access DDS.Condition.Ref'Class);
-- <dref>WaitSet_attach_condition</dref>
procedure Detach_Condition
(Self : not null access Ref;
Cond : access DDS.Condition.Ref'Class);
-- <dref>WaitSet_detach_condition</dref>
procedure Get_Conditions
(Self : not null access Ref;
Attached_Conditions : access DDS.ConditionSeq.Sequence);
-- <dref>WaitSet_get_conditions</dref>
procedure Free (This : in out Ref_Access);
-- <dref>WaitSet_delete</dref>
procedure Set_Property
(Self : not null access Ref;
prop : access WaitSetProperty_T);
-- <dref>WaitSet_set_property</dref>
procedure Get_Property
(Self : not null access Ref;
prop : access WaitSetProperty_T);
-- <dref>WaitSet_get_property</dref>
private
type Ref is new Ada.Finalization.Limited_Controlled with
record
Impl_Access : DDS.WaitSet_Impl.Ref_Access;
end record;
procedure Free_Impl is new Ada.Unchecked_Deallocation (Ref'Class, Ref_Access);
procedure Free (This : in out Ref_Access) renames Free_Impl;
end DDS.WaitSet;