-- ==================================================================================
--  WHAT CONCURRENT_HELLO_3 DOES
--
--  This program has two 'writer' processes and one 'output.collector' process. The writers each have a bespoke channel for communication with
--  output.collector. Each writer sends series of digits (int) down it's channel to output.collector. Number of messages sent is passed upon 
--  invocation of the writer process. 
--  Output.collector receives data from each writer, and prints that data to the terminal. There is a timer that determines when the output.collector
--  process should terminate.   

--  communication between writers and output.collector is through channels that have defined (mobile) channel types. Client and server channel type
--  ends are created and passed to processes as appropriate. 
--
--  In this architecture (and those that follow in other programms) the writers act as clients to output.collector, which is a server prividing
--  a print-to-the-terminal service.
--
--  This program should be compiled with "kroc -d -di concurrent_hello_3.occ"
--  and then run with ./concurrent_hello_3
-- ==================================================================================

-- ==================================================================================
-- FUNCTIONALITY/FEATURES INTRODUCED
--  1) Introduced channel types
-- ==================================================================================

-- ==================================================================================
-- ITEMS STILL TODO
--  1) shared channel for communication between writers and output collector
--  2) barrier syncronisation
--  3) syncronised termination of output collector, rather than timed
-- ==================================================================================


--{{{ this module contains utilities for pringint useful types (like strings)
--    (like strings) to a chanel (which can be linked to the terminal screen)
#INCLUDE "course.module" 
--}}} 
        
        
    
        
        
--{{{ outer most procedure, with a parameter list that corresponds to that required to run in unix
PROC outer(CHAN BYTE in?, out!, err!)
  
  --{{{ declarations and types
  CHAN TYPE COMMS         -- channel type for communication between writers and output.collector
    MOBILE RECORD         -- the specifiers (? and !) are mandatory, and specified according to the server's point of view.
      CHAN INT data?:     -- so, server would be receiving data.
  : 
  
  TIMER timer:  -- timer used to dictate when output.collector terminates. 
  --}}}
  
  
  --{{{ PROC writer.1
  PROC writer.1 (VAL INT loops, COMMS! write.to.server)
    VAL INT id IS 1 :
    SEQ i = 0 FOR loops
      write.to.server[data] ! id
  :
  --}}}
  
  
  --{{{ PROC writer.2
  PROC writer.2 (VAL INT loops, COMMS! write.to.server)
    VAL INT id IS 2 :
    SEQ i = 0 FOR loops
      write.to.server[data] ! id
  :
  --}}}
  
  
  --{{{ PROC output.collector
  -- process receives data from the writers and outputs it to the terminal
  -- process terminates itself after a timeout has been exceeeded
  PROC output.collector (COMMS? in.from.w1, in.from.w2)
    INT temp:                                       -- temporarily store data to be output
    INT start.time, end.time:                       -- used to calculate when to terminate this process
    VAL INT running.time IS 1000:                   -- length of time for which the process should run
    INITIAL BOOL terminate IS FALSE:                -- flag used to indicate satisfaction of termination condition
    --{{{ start receiving and outputting data
    SEQ
      timer ? start.time                            -- get time at process startup
      end.time := start.time + running.time         -- calculate when to terminate this process
      WHILE NOT terminate                           -- loop until termination condition met
        ALT                                         -- receive data, and run timeout in parallel
          in.from.w1[data] ? temp                -- receive data from writer 1
            SEQ
              out.int(temp, 0, out!)
              out.string("*n", 0, out!)
          in.from.w2[data] ? temp                         -- receive data from writer 2
            SEQ
              out.int(temp, 0, out!)
              out.string("*n", 0, out!)
          timer ? AFTER end.time                    -- timeout function, this process suspends until end.time is exceeded by timer
            SEQ
              terminate := TRUE                     -- set termination conditiion
    --}}}
  :
  --}}}
  
  
  --{{{ the main body of the program
  COMMS! client.w1:     -- channel type client and server ends for writer 1
  COMMS? server.w1:
  COMMS! client.w2:     -- channel type cleint and server ends for writer 2
  COMMS? server.w2:
  SEQ
    out.string("starting the writers...*n", 0, out!) 
    client.w1, server.w1 := MOBILE COMMS
    client.w2, server.w2 := MOBILE COMMS
    
    PAR
      output.collector (server.w1, server.w2)
      PAR           -- run two writers together in parallel
        writer.1 (4, client.w1)
        writer.2 (10, client.w2)
    out.string("writers have finished.*n", 0, out!)
  --}}}
:
--}}}



