-- Copyright: Mario Schweigler (C) 2005-2006
-- Institution: Computing Laboratory, University of Kent, Canterbury, UK
-- Description: Pseudo-occam specification of the pony protocol-converters

-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation; either version 2 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program; if not, write to the Free Software
-- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
-- MA 02110-1301, USA.

PROC pony.protocol.decoder (INPUTCHANNELWORD in, PROTOCOLDESC prot.desc, PONY.DECODEHANDLE! dec.handle)
  INITIAL BOOL running IS TRUE:
  WHILE running
    dec.handle[to.decoder] ? CASE
      --{{{  Activate
      activate
        INITIAL BOOL active IS TRUE:
        WHILE active
          ALT
            --{{{  Get cancel from pony kernel (while no ULC pending)
            dec.handle[to.decoder] ? CASE cancel
              SEQ
                -- Deactivate
                active := FALSE
                -- Confirm `cancel'
                dec.handle[from.decoder] ! cancel.confirm
            --}}}
            --{{{  Get first CLC of next ULC from user-level application (extended input)
            in ?? ...
              INT di.nlcs.first, cte.nlcs.first:
              BOOL has.rest.clcs, was.cancelled:
              SEQ
                ...  -- Initialise `di.nlcs.first', `cte.nlcs.first', `has.rest.clcs'
                CHAN BOOL cancel.sync:
                PAR
                  --{{{  Output `first.clc' to pony kernel
                  SEQ
                    -- Carrying: number of data-item NLCs in first CLC;
                    --           number of channel-type-end NLCs in first CLC;
                    --           are there remaining CLCs?
                    dec.handle[from.decoder] ! first.clc; di.nlcs.first; cte.nlcs.first;
                                                          has.rest.clcs
                    -- Output of `first.clc' message finished
                    cancel.sync ! TRUE
                  --}}}
                  --{{{  Check for cancel while ULC pending
                  ALT
                    dec.handle[to.decoder] ? CASE cancel
                      SEQ
                        BOOL any:
                        cancel.sync ? any
                        was.cancelled := TRUE
                    BOOL any:
                    cancel.sync ? any
                      was.cancelled := FALSE
                  --}}}
                --{{{  Was there a `cancel'?
                IF
                  --{{{  There was a `cancel' before `first.clc' message was sent
                  was.cancelled
                    SEQ
                      ...  -- Cancel the ULC
                      ...  --   (don't release user-level process from extended rendevous)
                      -- Deactivate
                      active := FALSE
                      -- Confirm `cancel'
                      dec.handle[from.decoder] ! cancel.confirm
                  --}}}
                  --{{{  No `cancel', the first CLC's NLCs can now be output to pony kernel
                  TRUE
                    SEQ
                      ...  -- Output all NLCs of the first CLC
                      -- Get reply from pony kernel
                      dec.handle[to.decoder] ? CASE
                        --{{{  Cancel
                        cancel
                          SEQ
                            ...  -- Cancel the ULC
                            ...  --   (don't release user-level process from extended rendevous)
                            -- Deactivate
                            active := FALSE
                            -- Confirm `cancel'
                            dec.handle[from.decoder] ! cancel.confirm
                        --}}}
                        --{{{  First CLC has been taken by remote user-level process
                        ack
                          SEQ
                            ...  -- Reduce reference-count of CTBs for all channel-type-end
                            ...  --   NLCs in the first CLC
                            ...  -- Deallocate memory of dynmobs that were sent away
                            --{{{  Deal with remaining CLCs
                            IF
                              has.rest.clcs
                                INT di.nlcs.rest, cte.nlcs.rest:
                                SEQ
                                  ...  -- Input all remaining CLCs from the `in' channel (extended)
                                  ...  -- Initialise `di.nlcs.rest', `cte.nlcs.rest'
                                  -- Tell pony kernel about remaining CLCs
                                  -- Carrying: number of data-item NLCs in remaining CLCs;
                                  --           number of channel-type-end NLCs in remaining CLCs
                                  dec.handle[from.decoder] ! rest.clcs; di.nlcs.rest; cte.nlcs.rest
                                  ...  -- Output all NLCs for all remaining CLCs
                                  -- Get remote ack
                                  dec.handle[to.decoder] ? CASE ack
                                  ...  -- Reduce reference-count of CTBs for all channel-type-end
                                  ...  --   NLCs in the remaining CLCs
                                  ...  -- Deallocate memory of dynmobs that were sent away
                              TRUE
                                SKIP
                            --}}}
                            ...  -- Release user-level process (`in' channel) from extended rendevous
                        --}}}
                  --}}}
                --}}}
            --}}}
      --}}}
      --{{{  Cancel encode
      cancel.encode
        ALT
          ...  -- Input the `first CLC' that the matching encoder is sending
          ...  --   (from the `in' channel)
            SEQ
              ...  -- Cancel that CLC (includes decreasing the reference-count
              ...  --   of CTBs for all channel-type-end NLCs in that `first CLC'
              ...  --   and deallocating memory of dynmobs)
              ...  -- Release matching encoder from communication
              -- Wait for acknowledgement
              dec.handle[to.decoder] ? CASE cancel.encode.ack
              -- Return confirmation
              dec.handle[from.decoder] ! encode.cancelled
          -- Acknowledgement that the CLC was output by the encoder
          dec.handle[to.decoder] ? CASE cancel.encode.ack
            -- Return confirmation
            dec.handle[from.decoder] ! encode.not.cancelled
      --}}}
      --{{{  Terminate
      term
        SEQ
          running := FALSE
          ...  -- Decrease reference-count of `*dec_handle'
          ...  -- Deallocate `*dec_handle' if reference-count is zero
      --}}}
:

PROC pony.protocol.encoder (OUTPUTCHANNELWORD out, PROTOCOLDESC prot.desc, PONY.ENCODEHANDLE! enc.handle)
  INITIAL BOOL running IS TRUE:
  WHILE running
    enc.handle[to.encoder] ? CASE
      --{{{  Get first CLC of next ULC from pony kernel
      INT di.nlcs.first, cte.nlcs.first:
      BOOL has.rest.clcs:
      -- Carrying: number of data-item NLCs in first CLC;
      --           number of channel-type-end NLCs in first CLC;
      --           are there remaining CLCs?
      first.clc; di.nlcs.first; cte.nlcs.first; has.rest.clcs
        SEQ
          ...  -- Input all NLCs of the first CLC
          ...  -- Output first CLC to `out' channel (i.e. either to user-level
          ...  --   process or to cancelling `pony_protocol_decoder' process)
          -- Send `ack' to pony kernel
          enc.handle[from.encoder] ! ack
          IF
            has.rest.clcs
              enc.handle[to.encoder] ? CASE
                --{{{  Cancel
                --   (This means that the first CLC has been taken by the matching
                --   `pony_protocol_decoder' process which has cancelled it)
                cancel
                  --  No need to deal with the remaining CLCs
                  SKIP
                --}}}
                --{{{  Remaining CLCs of ULC
                INT di.nlcs.rest, cte.nlcs.rest:
                -- Carrying: number of data-item NLCs in remaining CLCs;
                --           number of channel-type-end NLCs in remaining CLCs
                rest.clcs; di.nlcs.rest; cte.nlcs.rest
                  SEQ
                    ...  -- Input all NLCs of all remaining CLCs and
                    ...  --   output all remaining CLCs to `out' channel
                    -- Send `ack' to pony kernel
                    enc.handle[from.encoder] ! ack
                --}}}
            TRUE
              SKIP
      --}}}
      --{{{  Terminate
      term
        SEQ
          running := FALSE
          ...  -- Decrease reference-count of `*enc_handle'
          ...  -- Deallocate `*enc_handle' if reference-count is zero
      --}}}
:

