Chapter: PROCESSES

by Andrzej Salwicki

Plan


Introduction

The word process has two meanings: it may denote a module of a programm or it may denote an object of a module.
Once created a process-object may be activated. Its instructions are executed in parallel with the instructions of other processes-objects. The word multithread execution explains well the intuition of concurrent processes. While the objects of classes and of coroutines share a processor, the objects of processes do have a processor associated with each object of process. The processor can be either a physical processor e.g. a personal computer or workstation connected to other computers by means of LAN network, or it may be a virtual processor, in reality it can be an ability to acquire a time slice of a real computer. (More or less like a process in Unix system).

Syntax

The syntax of module process is similar to those of modules class or coroutine

unit {processTypeName}: {prefix} process ({formalParameters}):
     {declarations}
begin
     {instructions}
end {processTypeName};
The present status of the processes in Loglan82 is experimental. There are several contextual restrictions imposed. Some of them are explained by the experimental and temporary status ... Some of them are not checked by the compiler. Sorry! Attention please!

Context restrictions.

  1. All process modules should be declared as global units. They can not be nested. They can inherit however.
    This is explained by the distributed model of memory of processes.
  2. There is no shared memory. Every process can access only its private resources, whether declared locally or transmitted as parameters, or procedures and functions declared in other object of process if it is accessible to the process.
  3. The parameters of a process can be
    integer, real, char, Boolean, string i.e. of primitive type
    or
    process objects
    or
    procedure declared in a process object
    or
    procedure transmitted as a formal parameter.
  4. The procedure's instruction of the form

    call X.proced1(params)
    placed in the body of a process module has an effect of communicating the calling process with the callee X.
    Therefore such an instruction will be named an alien call of a procedure. We shall see later that the execution of an alien call needs a cooperation of both the calling and the callee processes.

  5. The operations in, is , qua, this are not defined on processes. The user should not put them in the body of a process module.

    Sorry, the compiler will not check it as an error!!!

  6. There is no dynamic type checking concerning objects of processes.
    Again, the compiler does not warn you. Be careful!
  7. Each process has its own subsystem of coroutines. The instructions attach and detach can not transfer the control beyond the subsystem.
  8. No process object can access the global variables declared in the MAIN process. This does not apply to the principal program=MAIN. Remark that MAIN is a process object too.

Allien call

Let Y be a process object. Let X be another process object. If a command of the following form

call X.procedr1(actual_params)
is to be executed in Y then we shall say of an allien call.
The called procedure, in this example procedr1, can be either:

Y is said a calling process, X is the callee process.

Mask.

Each process has a predefined variable MASK associated with it. The value of the variable is a subset of the set of names of procedures and functions that are declared inside the process. The initial value of the MASK is the empty set . The instructions ENABLE and DISABLE can change the value of the MASK variable.


		         MASK={q1, ... ,qn}
                                
                                
         enable p1,p2;          
		                      	disable p1, p2;
                                
                                
		MASK={q1, ... , qn, p1, p2}
As you see from the above diagram an instruction 'enable' adds the names to the MASK. An instruction 'disable' deletes the names from the mask. There are two other instructions RETURN ENABLE p1, ... , pn DISABLE q1, ... , qk; and ACCEPT p1, ... , pl; which modify the value of the mask. Their meaning is described below. The instruction RETURN ENABLE p1, ... , pn DISABLE q1, ... , qk; is legible in the body of a procedure or function only. The instruction ACCEPT can be put anywhere in a process module.

SEMANTICS

Let us repeat: a process can be initialised, its initialisation phase terminates when the return statement is reached. It can be given a name, say p, and it remains passive. When another proces executes the command resume(p) then the process p is activated, its actions are executed in parallel with the actions of the other active processes. Once activated it continues the execution of its commands. It may execute a stop command and become a passive process. Other processescan call for an allien call of a procedure (or function) declared in the process p. The permission to interrupt the execution of its own commands and to do a service for an external process will be granted iffthe process p is active and if the name of the called procedure is in MASK. The process can change the value of the MASK variable by means of commands enable and disable. One can use also the commands accept and return disable ... enable...

ACCEPT

The execution of the command accept p1, ... , pn is as follows: 1° the names p1, ... ,pn are added to the MASK 2° the process waits for an allien call of a procedure listed in the MASK. When an allien call is terminated the MASK is set to its previous value, i.e. to that before the ACCEPT was executed. This is a rule with an exception: see the return disable ... enable... command below.

Execution of an allien call


  1. 1. The calling process Y calls the callee process X and waits,
  2. 2. If the callee process X is active and if it is before execution of its Loglan command C and if the name of the called procedure is in the value of MASK variable, (let us recall it is a set of names of procedures) then the callee X is interrupted and
  3. 3. The calling process Y transmits the actual parameters of the called procedure to the process X and waits.
  4. 4. The calllee X saves the MASK, next, the MASK is set to empty (it means that all further alien calls are to wait)
    REMARK that the called procedure can change the value of MASK.
  5. 5. The callee X executes the called procedure.
  6. 6. When the execution of the procedure reaches its end then the output parameters of the procedure are transmitted to the calling process Y which receives them.
  7. 7. The MASK is restored to its state before the call.
    REMARK. If the execution of the called procedure ends with the command
    return enable ... disable ...;
  8. Then the restored MASK is subject to the modifications described by this command.
  9. 8. Both processes resume their activities from before alien call
    - the calling process passes to the instruction next to the alien call,
    - the callee process executes the instruction C which was planned already to be executed.
The semantical phenomena of parallel programming are different than those of sequential programming. Example 1 Let us look what will happen if you execute the following program First. First of all, you will remark that the strings are mixed. This is because the commands write(a(i)) of the process w1 are executed in parallel with the commands write(a(i)) of the another process w2. The screen receives them interleaved and so the characters appear on the screen interleaved. Next, remark that the execution of a program is no longer determined by its text and its data. Execute the following program twice and compare the results. You will observe that the results displayed on the screen are different. However there is no visible reason for this difference.
program First;
     unit writer: process(node:integer, nr:integer,s: string);
         var i: integer,
             A: arrayof char;
     begin
           return;
        a:=unpack(s);
           for i := lower(a) to upper(a)
           do
              write(a(i));
           od;
           writeln;
     end writer;
 
     var w1, w2: writer, i: integer;
 
begin
     w1:=new writer(0,1,"ici un texte tres long,
             		tres long, tres long tres long tres long tres long");
     w2:=new writer(0,2,"zdies otche'n dolgoj tiekst, otche'n dolgoj     	 	                 tiekst, otche'n dolgoj tiekst");
        resume(w1);
        resume(w2);
     writeln("give me a character");
     readln;
end First
 
We are going now to remede the interleaving the characters. For this purpose we are going to construct a semaphore. But what it is a semaphore? Well, it is a device that allows a train to pass iff it is in a state permitting to pass and in the same moment of the passage it changes its state to blocking one. Therefore only one train is authoised to pass. In the state blocking it accepts only the demand to liberate the semaphore i.e. the state of the semaphore changes from blocking to free. By default, it is assumed that it is only the train that passed who will execute the command: liberate (when it leaves the station).
program Second;

  unit aSemaphore: process(node:integer);
     unit pass: procedure;
     end pass;
     unit free: procedure;
     end free;
  begin
     return;
     do
	  accept pass;
	  accept free;
     od
  end aSemaphore;

  unit writer: process(node:integer, nr:integer,s: string, sem: aSemaphore);
    var i: integer,
        A: arrayof char;
  begin
     return;
     call sem.pass;
     a:=unpack(s);
     for i := lower(a) to upper(a)
     do
       write(a(i));
     od;
     writeln;
     call sem.free;
  end writer;
 
  var s: aSemaphore, w1, w2: writer, i: integer;
 
begin
  s := new aSemaphore(0);
  resume(s);
  w1:=new writer(0,1,"ici un texte tres long,
          		tres long, tres long tres long tres long tres long",s);
  w2:=new writer(0,2,"zdies otche'n dolgoj tiekst, otche'n dolgoj     	 	                     			tiekst, otche'n dolgoj tiekst", s);
  resume(w1);
  resume(w2);
  writeln("give me a character");
  readln;
end Second
Theorem The texts shown on the screen will never be mixed. We can prove even stronger theorem that for any number of objects-processes of type writer defined as in the program Second they critical sections (here it means printing on the screen) will be executed in mutual exclusivity. Therefore with the use of semaphores one is able to assure the mutual exclusivity of critical sections of given processes. A new question appears: is it true that semaphores garantee the mutual exclusion? The answer is no, as it can be seen from the Third program.
program Third;

  unit Semaphore: process(node:integer);
     unit pass: procedure;
     end pass;
     unit free: procedure;
     end free;
  begin
     return;
     do
	  accept pass;
	  accept free;
     od
  end Semaphore;

  unit writer1: process(node:integer, nr:integer,s: string, sem: semaphore);
    var i: integer,
        A: arrayof char;
  begin
     return;
     call sem.pass;
     a:=unpack(s);
     for i := lower(a) to upper(a)
     do
       write(a(i));
     od;
     writeln;
     call sem.free;
  end writer1;
  unit writer2: process(node:integer, nr:integer,s: string, sem: semaphore);
    var i: integer,
        A: arrayof char;
  begin
     return;
     call sem.free;
     a:=unpack(s);
     for i := lower(a) to upper(a)
     do
       write(a(i));
     od;
     writeln;
     call sem.pass;
  end writer2;
  var s: semaphore, w1: writer1, w2: writer2, i: integer;
begin
  s := new semaphore(0);
  resume(s);
  w1:=new writer1(0,1,"ici un texte tres long,
          		tres long, tres long tres long tres long tres long",s);
  w2:=new writer2(0,2,"zdies otche'n dolgoj tiekst, otche'n dolgoj     	 	                     			tiekst, otche'n dolgoj tiekst", s);
  resume(w1);
  resume(w2);
  writeln("give me a character");
  readln;
end Third
The example above reveals that one should use semaphores with rigour. It may be the case that both processes use a semaphore but due to an error their critical sections are executed in parallel causing a chaos. In most cases it would be better to conceive the architecture of the system of parallel processes as clients and servers. In the example below we create one server: ecran for serving the resourc eof screen. The processes are calling the process server asking for a service. In this case it will be printing on the screen.
program Fourth;
  unit ecran: process(node: integer);    (* it is a server *)
    unit print: procedure(s: string);
       var i: integer,
           A: arrayof char;
    begin
     a:=unpack(s);
     for i := lower(a) to upper(a)
     do
       write(a(i));
     od;
     writeln;
    end print;
  begin
    return;
    do accept print od             (* it offers just the print service *)
  end ecran;

  unit writer: process(node:integer, nr:integer,s: string, ec:ecran); 
    (* it is a client *)
  begin
     return;
     call ec.print(s)
  end writer;
 
  var e: ecran, w1, w2: writer, i: integer;
 
begin
  e := new ecran(0);
  resume(e);
  w1:=new writer(0,1,"ici un texte tres long,
          		tres long, tres long tres long tres long tres long", e);
  w2:=new writer(0,2,"zdies otche'n dolgoj tiekst, otche'n dolgoj     	 	                 				tiekst, otche'n dolgoj tiekst", e);
  resume(w1);
  resume(w2);
  writeln("give me a character");
  readln;
end Fourth
Theorem The critical sections of printing the texts supplied by the processes w1 and w2 is done in mutual exclusion. ============================================================================ Example 5 In the example below we shall illustrate an asynchronous cooperation of processes. The case we are going to discuss now is as follows: there are several processes " writers ". Any of writers may print a file on a designated printer. In order to increase the throughput and in order to avoid an intermixed printing we have spoolers - one for each printer. A printer prints files taking them out of a queue.
program Five;
 
  unit writer1: process(node: integer, printer1: spooler);

  begin

  end writer1;

  unit writer2: process(node: integer, printer1, printer2: spooler);
     (* this process may print on any of 2 printers *)
  end writer2

  unit spooler: process(node: integer);

    var Q: queue,
	  f: file,
     tick: integer;
 
    unit print: procedure(f: file, ticket: integer);
    begin
       call Q.insert(f);
       if Q.full then return disable print fi;
       tick := tick + 1;
       ticket := tick;
    end print;

  begin (*spooler*)
    Q := new queue;
    return;

     do
       disable print;
       if Q.empty then accept print fi;
       f := Q.out;
       enable print;
       (* printing the file *)
        ...
     od
   end spooler;

 var s1, s2: spooler,
     w1, w2: writer1,
     w3    : writer2;
begin

   ...
   s1 := new spooler(0);
   resume(s1);

   w1:= new writer1(0, s1);
   w2 := new writer1(0, s1);
   resume(s1);
   resume(s2);

   
end Five
What are the properties of the program Five? Is it possible to obtain a mixture of texts coming from different files?

ò