OEP

114

Title

MOBILE structured channel types

Summary

Allow channel ends to be communicated over channels.

Owner

Fred Barnes <F.R.M.Barnes@kent.ac.uk>

Status

Accepted

Date-Accepted

2002-06-30

Keywords

language mobiles communication channel-types

Structured channel-types provide a mechanism for grouping related channels together inside a "RECORD" type. Ordinarily, these types are declared to be mobile. This provides a mechanism for moving bundles of channel ends around inside a process network. Starting with an example type declaration:

CHAN TYPE FOO
  MOBILE RECORD
    CHAN INT request?:
    CHAN INT response!:
:

A channel in occam has two ends -- one for reading and one for writing. Channel types (bundles) have two conceptual ends, termed "server" and "client". Channel direction-specifiers must be used inside the channel-type declaration, so that the compiler can enforce correct usage on a particular end (client or server).

Declaration and initialisation of a channel-bundle (mobile channel-type) is relatively simple. For example, using the above declaration of "FOO":

FOO! foo.c:         -- client end
FOO? foo.s:         -- server end
SEQ
  foo.c, foo.s := MOBILE FOO
  ...  processes using "foo.c" and "foo.s"

The allocating assignment is similar to that used with dynamic mobile arrays (see the mobiles section). A bundle of channels is dynamically created and the two ends assigned to "foo.c" and "foo.s". The order in which the end-variables are given is unimportant; what matters is that one is a client-end and the other a server-end of the same mobile channel type.

The channel-ends within a channel-bundle are accessed using the familiar record subscription syntax. For (a slightly expanded) example:

CHAN TYPE BUF.MGR
  MOBILE RECORD
    CHAN INT request?:
    CHAN MOBILE []BYTE response!:
    CHAN MOBILE []BYTE return?:
:

PROC server (BUF.MGR? link)
  WHILE TRUE
    INT n:
    MOBILE []BYTE b:
    SEQ
      link[request] ? n         -- input required size
      b := MOBILE [n]BYTE       -- allocate buffer
      SEQ i = 0 FOR SIZE b      -- zero buffer
        b[i] := 0 (BYTE)
      link[response] ! b        -- give to client
      link[return] ? b          -- take back from client
:

PROC client (BUF.MGR! link)
  MOBILE []BYTE buf:
  SEQ
    link[request] ! 1024        -- send request
    link[response] ? buf        -- get response
    ...  use "buf"
    link[return] ! buf          -- return buffer
:

PROC network ()
  BUF.MGR! cli:
  BUF.MGR? svr:
  SEQ
    cli, svr := MOBILE BUF.MGR
    PAR
      server (svr)
      client (cli)
:

Unfortunately, an array constructor cannot be used (at present) in the "server" process to create the array, since the size of an array-constructor must be a compile-time constant.

The above "network" allocates a "BUF.MGR" channel bundle then runs client and server processes in parallel, connected by that bundle. Because the channel-ends are mobile, however, they can be communicated (and assigned). Thus, we might "wrap" the earlier "client" and "server" processes such that the ends (on which to communicate with each other) are inputted first. For example:

PROC w.server (CHAN BUF.MGR? link.in?)
  BUF.MGR? link:
  SEQ
    link.in ? link
    server (link)
:

PROC w.client (CHAN BUF.MGR! link.in?)
  BUF.MGR! link:
  SEQ
    link.in ? link
    client (link)
:

The modified "network" process would be:

PROC w.network ()
  CHAN BUF.MGR! c.cli:
  CHAN BUF.MGR? c.svr:
  PAR
    w.server (c.svr?)
    w.client (c.cli?)

    BUF.MGR! cli:
    BUF.MGR? svr:
    SEQ
      cli, svr := MOBILE BUF.MGR
      c.cli ! cli
      c.svr ! svr
      -- "cli" and "svr" now undefined (moved)
:

Although such channel-bundle ends are mobile, applying the CLONE operator (to produce a mobile copy) makes little sense -- how would access between copies be controlled ? or, would new server processes be automatically generated..?.

The paradigm of a shared server is one that has long existed in occam -- usually controlled by ALTing over multiple channels. To capture this more effectively, channel-bundle ends may be declared "SHARED". This allows for four different arrangements of client/server connections: single client, single server; multiple clients, single server; single client, multiple servers; and multiple clients with multiple servers.

To control access to shared channel-ends, CLAIM blocks must be used. Modifying the "client" process for example:

PROC client (SHARED BUF.MGR! link)
  CLAIM link
    MOBILE []BYTE buf:
    SEQ
      link[request] ! 1024        -- send request
      link[response] ? buf        -- get response
      ...  use "buf"
      link[return] ! buf          -- return buffer
:

Parallel processes compete for access to a CLAIM block by means of a semaphore, on which they queue in FIFO order. The rules pertaining to nested CLAIMs are that no nested claims are allowed inside a client-end claim, and that server claims may be nested (possibly with a client claim at the innermost point). This prevents partially-aquired resource deadlock on clients, but not on servers. Neither does it prevent cyclic deadlock. Avoiding deadlock through the use of CLAIM is a program design issue. Possible (or partial) compiler-based solutions to this are being thought about -- strict ordering and multi-claims ("CLAIM x, y") may be one solution.

Inside a CLAIM block, the channel-end being CLAIMed behaves largely as its non-shared version. However, it may not be input or assigned to, or renamed. This ensures that whatever is CLAIMed doesn't move (and isn't given the opportunity to through renaming -- less obvious in separate compilation).

For shared ends, the CLONE operator is meaningful. In fact, when dealing with shared ends, the compiler will always CLONE for assignment or communication, regardless of whether the "CLONE" keyword is present. If the need ever arises to forcefully release a channel-end, it can be done by declaring it undefined to the compiler (that will insert code if it thinks otherwise). For example:

SHARED FOO! f.c:
SHARED FOO? f.s, f.other:
SEQ
  f.c, f.s := MOBILE FOO
  f.other := f.s
  -- auto-clone, "f.s" still valid
  #PRAMGA UNDEFINED f.s
  -- "f.s" no longer defined

The above examples show only a small amount of what can be achieved using mobile channel-types. For a fuller analysis, see the papers [Prioritised dynamic communicating processes: Part 1][pdcp1] and [Prioritised dynamic communicating processes: Part 2][pdcp2].

[pdcp1]: http://www.cs.kent.ac.uk/pubs/2002/1511/

[pdcp2]: http://www.cs.kent.ac.uk/pubs/2002/1512/

OEP/114 (last edited 2007-09-27 00:20:10 by ats1)