9.5 Using the SystemEnv Fragment

A program using the systemEnv fragment will have the following structure:

INCLUDE '~beta/basiclib/systemenv'
--- program: descriptor ---
systemenv
(# process: @ |system(# ... #);
do ...
   process[] -> fork;
   ...
#)

9.5.1 The Monitor Example

The following is an example of a producer/consumer system with a shared buffer (implemented as a 20 element character buffer, protected as a monitor. The producer and consumer are concurrent objects:

Program 11: buffer.bet

ORIGIN '~beta/basiclib/systemenv';

---program: descriptor---
SystemEnv
(# buffer: @Monitor
     (# R: [20] @char; in,out: @integer;
        full,empty: @Condition;
        
        put: Entry
          (# ch: @char
          enter ch
          do (if in = out then full.wait if);
             ch->R[in]; (in mod R.range)+1 ->in;
             empty.signal; 
          #);
        get: Entry
          (# ch: @char
          do (if in = (out mod R.range)+1 then empty.wait if);
             R[(out mod R.range)+1->out]->ch; 
             full.signal; 
             (if ch=ascii.eot then stop if);
          exit ch
          #);
        init::< (# do 1->in; R.range->out #)
     #);
   
   prod: @| System(# do cycle(# do (if keyboard.EOS then ascii.eot->buffer.put else keyboard.get->buffer.put if) #) #);
   cons: @| System(# do cycle(# do buffer.get->screen.put #) #);
   
do buffer.init;
   conc(# do prod[]->start; cons[]->start #)
#)

9.5.2 The Monitor with Wait Example

This example is similar to the previous, except that wait is used instead of condition to control the medium-term scheduling of access to the buffer.

Program 12: OCbuf.bet

ORIGIN '~beta/basiclib/systemenv';

---program: descriptor--
systemenv
(# buffer: @Monitor
     (# R: [4] @char; in,out: @integer;
        full: (# exit in=out #);
        empty: (#exit (in = (out mod R.range)+1) #);
        Put: Entry
          (# ch: @char
          enter ch
          do wait(# do (not full)->cond #);
             ch->R[in]; (in mod R.range)+1 ->in;
          #);
        get: Entry
          (# ch: @char
          do wait(# do (not empty)->cond #);
             R[(out mod R.range)+1->out]->ch; 
          exit ch
          #);
        init::< (# do 1->in; R.range->out #)
     #);
   
   prod: @| System(# do cycle(# do (if keyboard.EOS then stop else keyboard.get->buffer.put if) #) #);
   cons: @| System(# do cycle(# do buffer.get->screen.put #) #);
   
do buffer.init;
   conc(# do prod[]->start; cons[]->start #)
#)

9.5.3 The Ports Example

The following is an example of three communicating objects: S, R1 and R2. R1 and R2 are similar (instances of the same Rtype pattern).

Rtype defines two ports: p1 and p2, with a get entry in p1 and a put entry in p2. The get entry prints the value of the x attribute on standard output, and put prints the y attribute. Rtype objects repeatedly accepts p1 communications followed by p2 communications (i.e. since in this example, only get is define in p1, and only put in p2, this implies get followed by put). Note that the use of ports, allows specializations of Rtype to define new entries in p1 and/or p2, such that these communications follow the same communication structure.

The S object defines two internal objects, C1 and C2, which are executed alternating. C1 is responsible for the communication with R1 and C2 is responsible with the communication with R2. It is in this way ensured that the communication pattern between S and R1 follows the get followed by put pattern (the same for S and R2), but these two communication patterns may be non-deterministically interleaved.

Program 13: altex1.bet

ORIGIN '~beta/basiclib/systemenv';

---program: descriptor--
SystemEnv
(# S: @| System
     (# C1: @| System
          (#
          do cycle(# do '1'->put; R1.get; '2'->put; R1.put #);
          #);
        C2: @| System
          (#
          do cycle(# do 'a'->put; R2.get; 'b'->put; R2.put #)
          #)
     do alt(# do C1[]->start; C2[]->start #)
     #);
   
   Rtype: System
     (# get: p1.entry(# do x->screen.put; #); p1: @port;
        put: p2.entry(# do y->screen.put; #); p2: @port;
        x,y: @char
     do cycle(# do p1.accept; p2.accept #)
     #);
   R1: @| Rtype;
   R2: @| Rtype;
   
do '('->R1.x; ')'->R1.y; '['->R2.x; ']'->R2.y;
   conc(# do S[]->start; R1[]->start; R2[]->start #)
#)

9.5.4 The ObjectPort Example

This example illustrates the use of the ObjectPort facility. The S object defines f1, f2, and f3 communication entries, where f1 is controlled by an objectPort. This enables S to control exactly which object which is allowed to communicate f3's in an accept. Note, that initially, R.R1 is allowed, and later R.R3 is allowed:

Program 14: sys1.bet

ORIGIN '~beta/basiclib/systemenv'; 

---program: descriptor--      
SystemEnv 
(# S: @| System
     (# P1: @ObjectPort;
        f1: P1.entry(# do 'f1 called' ->putline #);
        P2: @Port;
        f2: P2.entry(# do 'f2 called' ->putline #);
        P3: @Port;
        f3: P3.entry(# do 'f3 called' ->putline #);
     do R.R1[]->P1.accept; 
        P2.accept; 
        P3.accept; 
        R.R3[]->P1.accept
     #);
   
   R: @| System
     (# R1: @| System(# do S.f1; S.f3 #);
        R2: @| System(# do S.f2; #);
        R3: @| System(# do S.f1 #);
     do 'Start R'->putLine;
        conc(# do R1[]->start;  R2[]->start; R3[]->start #);
        'End R'->putLine
     #);
   
do 'Start'->putLine;
   conc(# do R[]->start; S[]->start  #);
   'End'->putLine
#)


Basic Libraries - Reference Manual
© 1990-2002 Mjølner Informatics
[Modified: Monday October 23rd 2000 at 11:49]