Wednesday, May 26, 2010

Handling concurrent events in ParaSail

In a real-time system it is common to have a situation where you are waiting for two or more events, and want to react to the first one that occurs, and cancel the "wait" for the other ones. In ParaSail, waiting for an event is generally represented by a call on an operation of a concurrent object with a queued operand.  For example, here is the concurrent buffer example from an earlier post:
concurrent interface Bounded_Buffer
  <Element_Type is Assignable<>;
   Index_Type is Integer<>> is
    function Create_Buffer(
      Max_In_Buffer : Index_Type {Max_In_Buffer > 0})
      -> Result : Bounded_Buffer;
      // Create buffer of given capacity

    procedure Put(Buffer : queued Bounded_Buffer; 
      Element : Element_Type);
      // Add element to bounded buffer; 
      // remain queued until there is room
      // in the buffer.

    function Get(Buffer : queued Bounded_Buffer)
      -> Element_Type;
      // Retrieve next element from bounded buffer;
      // remain queued until there is an element.
end interface Bounded_Buffer; 
To wait on a Get from two different buffers, you could do this using the select statement in ParaSail:
select
    var X := Get(Buf1) => ... // use X received from Buf1
||
    var X := Get(Buf2) => ... // use X received from Buf2
end select;
A select statement attempts to perform each of the queued operations concurrently. The first one to become dequeued causes the others to be canceled, and then proceeds to completion.

Time delays are also represented as queued operations on a concurrent object (a clock or a timer):
concurrent interface Clock<Time_Type is Assignable<>> 
is
    function Create_Clock(...) -> Clock;
    function Now(C : locked Clock) -> Time_Type;
    procedure Delay_Until
      (C : queued Clock; Wakeup : Time_Type);
end interface Clock;
Presumably a call of Delay_Until(C, Wakeup_Time) will queue the caller until Now >= Wakeup_Time. A possible implementation of the Delay_Until procedure could use that as its dequeue condition:
concurrent class Clock<Time_Type is Assignable<>>
is
    var Current_Time : Time_Type;
    ...
  exports
    ... 
    function Now(C : locked Clock) -> Time_Type is
        return Current_Time;
    end Now;

    procedure Delay_Until
      (C : queued Clock; Wakeup : Time_Type)
      queued until Current_Time >= Wakeup is
        return// nothing more to do
    end Delay_Until;
    ...
end class Clock;
We can effectively put a time bound on a select statement by adding an alternative that is a call on an operation like Delay_Until:
select
    var X := Get(Buf1) => ...
||
    var X := Get(Buf2) => ...
||
    Delay_Until(Next_Time) => ...
end select;

2 comments:

  1. The "select" construct does not seem fundamental, as it can effectively be accomplished using a block with multiple threads, where each thread exits with an indication of which call finished first. That indication can then be used to determine what to do next. In general, the "exit ... with" seems adequately flexible to make the special-purpose "select" unnecessary.

    Bottom line: we don't mention the "select" construct in the current ParaSail reference manual (available at https://groups.google.com/forum/#!forum/parasail-programming-language).

    ReplyDelete
    Replies
    1. The "select" construct has been completely removed from ParaSail, as of 11/2012. Use the "exit ... with" (or "return") construct instead. Both exit and return now support exiting from parallel contexts, automatically terminating the non-exiting/non-returning threads as appropriate (but first waiting for locked operations to complete).

      Delete