    [NAME]
    ALL.dao.concurrent.channel

    [TITLE]
    Communication Channel

    [DESCRIPTION]

    Tasklets (represented by future values) created from mt::start, asynchronous function
    call and the methods of asynchronous object can be assigned to native threads to run
    concurrently. Tasklets can communicate and synchronize with each other using
    communication channels.

    The channel type is implemented as a customized C data type that supports template-like
    type arguments. So to construct a channel that can send and receive integers, one can
    use,

       1  chan = mt::channel<int>()

    Here channel is prefixed with mt::, because channel is defined in the built-in mt
    multi-threading module.

    The type argument int indicates this channel can only be used to transmit integers.
    Currently, only primitive types ( int, float, double, complex, string, enum) plus array
    types, and their composition through list, map and tuple types are supported for
    channels.

    Each channel has a capacity for transmitting data, which are buffered in the channel.
    When the channel's buffer reached its capacity, any attempt to send data to it will
    result in the blocking of the sender, which will be resumed either after a timeout or
    after some data of the channel have been read out of the buffer to make room for the new
    data being sent by the blocked sender. Channel capacity can be passed to the constructor
    of the channel as an optional parameter, which is one by default.

    The channel type has a number of methods which include:
      *  buffer( self :channel<@V> ) => int
         It returns the number of unprocessed data items in the channel buffer.
      *  cap( self :channel<@V> ) => int
         Return the channel capacity.
      *  cap( self :channel<@V>, cap :int ) => int
         Set the channel capacity and returns the previous one. Set the capacity to zero to
         prevent it from receiving further data, and effectively close it when its buffer
         become empty.
      *  send( self :channel<@V>, data :@V, timeout :float = -1 ) => int
         Send a data item with a timeout. A negative timeout parameter means infinite
         waiting. It return 1 upon timeout, 0 otherwise.
      *  receive( self :channel<@V>, timeout :float = -1 ) => tuple<data :@V|none, status :
         enum<received,timeout,finished>>
         Receive a data item with a timeout. A negative timeout parameter means infinite
         waiting. It returns a tuple with the following possible values:
           *  (data, $received);
           *  (none, $timeout);
           *  (none, $finished);

      *  select( group :list<@T>, timeout = -1.0 ) => tuple<selected: none|@T, value :any,
         status :enum<selected,timeout,finished>>
         Select on a group of channels, wait with a timeout for data become available from
         any of the channels. It returns a tuple with the following possible values:
           *  (channel, data, $selected);
           *  (none, none, $timeout);
           *  (none, none, $finished);
         As the signature indicates, this is a static method.

    Here is a simple example, where integers are sent through one channel and strings through
    another, and these integers and strings are received using the select method:

       1  load sys
       2
       3  chans = { mt::channel<int>(1), mt::channel<string>(1) }
       4
       5  mt.start {
       6      index = 0;
       7      while( ++index <= 10 ){
       8          if( rand(2) ){
       9              io.writeln( "sending integer:", index );
      10              chans[0].send( index )
      11          }else{
      12              io.writeln( "sending string: S" + (string) index );
      13              chans[1].send( 'S' + (string) index )
      14          }
      15          sys.sleep(0.5)
      16      }
      17      # set channel buffer sizes to zero to close them:
      18      chans[0].cap(0)
      19      chans[1].cap(0)
      20  }
      21
      22  while( 1 ){
      23      data = mt::channel::select( chans, 0.2 )
      24      io.writeln( "received:", data );
      25      if( data.status == $finished ) break
      26  }