• Nenhum resultado encontrado

The Concurrent Programming Language CLASS

5.2 Hello World: A Concurrent Counter

We will go step-by-step through the implementation of a simple “hello world” concurrent counter with two operations to get-and-reset and increment. The purpose is to illustrate some features and details of our language implementation CLLSj. Once in the main directory execute./CLLSjto run the interactive REPL.

A counter is just a reference cell that stores an integer, the datatypeCounteris defined with the following instruction

typeCounter { state lint};;

This instructs the REPL to associate the type idCounterwith the type expression state lint, which denotes a state full of a linear integer. After typing the above expression the REPL prints

Type Counter: defined.

informing that the new datatypeCounterwas successfully defined.

All the instructions to interact with the REPL end with a double semi-colon, processes and type expressions are enclosed in curly braces. The keyword state is the concrete syntax expression for the modalityS𝑓 fromCLASS. We denote the empty state modality S𝑒 with the concrete expression statelwhich stands forstate locked. Similarly, the concrete syntax expressions usageand usagelstand for the usage modalitiesU𝑓 andU𝑒 ofCLASS, respectively.

We will now define a process namedcounter, which offers the protocolCounteron a sessionc

proccounter(c: Counter){ cellc(n: affine lint. affinen; letn 42) };;

This instructs the REPL to type check the process expression enclosed in curly braces with the typing contextc:Counterand, provided type checking is successful, to associate it the idcounter(c).

In this case the typing context is just made of a linear part which associates the name id cwith the previously defined typeCounter. We use a semicolon to separate the linear and the unrestricted typing context as inn:Nat ; b:Bool. When the unrestricted typing context is empty we do not need to use the semicolon, writingc:Counterinstead ofc:Counter;.

The process expression cellc(n: affine lint. affinen; letn 42)denotes a reference cell on sessioncthat is storing an affine linear integern. The affine linear integer is initially set to42. In our concrete process languageCLLSj, terms are written with bound names type-annotated, which from a pragmatic point of view guides the type checking algorithm and eases the task of writing complex programs.

The duality related pair of modalities∧/∨is denoted in the language implementation with the terms affine/ coaffine. The keyword affine is also used in the process term language to introduce affine sessions in the example above, where we are defining an affine session onnand continuing as letn 42, an expression that defines on sessionnthe linear integer42.

For convenience, the implementation language CLLSj also includes efficient prag-matic basic datatypes (native integers, booleans, strings), ML-style let expressions and primitive operations (arithmetic sum, if-conditional, string concatenation, to name a few). These basic datatypes and primitive operations can, anyway, be encoded in the fundamental classical linear logic based pure session-typed calculus𝜇CLL. For example, we implement the booleans and the natural datatypes inexamples/pure/booleans.cllsand examples/pure/naturals.clls.

After typing the above expression, the REPL outputs Process counter: defined.

informing the process counter was defined, this implies that type cheking was successful.

We will now define some basic processes that operate in the counter. We start with the following operation

procgetAndReset(c:~Counter){

takec(n:coaffine colint);

usen;

println("GOT "+n+" and RESET");

putc(v:affine lint. affinev; letv 0);

releasec };;

It gets the linear integer stored in the counterc, prints the integer and resets the counter.

It type checks withc:~Counter. The type concrete expression~Ain CLLSj denotes the dual type𝐴inCLASS. Instead of writing~Counterwe could alternatively have written usage colint, but we let the type checker carry out the computation of the dual type.

ProcessgetAndReset(c)starts by taking the usagecon sessionn:coaffine colint. Notice how the type annotation can inform us what to do next: we have to use the take session to

unstrip the coaffinemodality after which we get a sessionnof type colint. Now we must do some operation that consumes the linear integer, which, in this case, is done by the print instruction. Finally, we put the affine linear0, and then release the usagec.

The increment operation is defined by procinc(c:~Counter){

takec(n:coaffine colint);

println"INC";

putc(v:affine lint. affinev; usen; letv n+1);

releasec };;

It also type checks withc:~Counterand follows a similar usage pattern, already described forgetAndReset(c). It prints a message"INC"informing that the increment operation is being carried out. The taken linear integer in this case is consumed by the let expression to define the incremented value being put. After feeding the REPL with the above process definitions we obtain:

Process getAndReset: defined.

Process inc: defined.

To conclude, we assemble everything in the following process definition procsystem(){

cut{

counter(c)

|c:~Counter|

sharec {

inc(c) || getAndReset(c) || inc(c) || getAndReset(c) }

}};;

This composes a counter with 4 atomic threads, two of which are incrementing and another two are printing and resetting.

After typing the expression the REPL prints Process system: defined.

Once a closed process (with an empty typing context) is type checked we can run it multiple times by writing the process name. For example, if we input the REPL with

> system();;

itmayprint the log

INC INC

GOT 44 and RESET GOT 0 and RESET

The emphasis is to stress the fact that the printed log depends on the scheduling of the atomic actions. For example, the above log corresponds to the scheduling in which the two atomic increment instructions were executed before the two get-and-reset instructions.

If we run process system() again we might obtain GOT 42 and RESET

INC INC

GOT 2 and RESET

This corresponds to the scheduling in which the two increment instructions are executed after and before a get-and-reset.

Our implementation exposes nondeterminism arising from concurrency as real com-mitted nondeterminism, so that the sum operator from the metatheoretical modelCLASS, while crucial to establish a propositions-as-types model and to characterise the semantics of our language, is not present in our practical runtime systemCLLSj. On the other hand, the metatheoretical model allows us to reason about the behaviour of concurrent pro-grams in the implementation( as illustrated in Example15) since the result produced by running a process in the implementationCLLSjis always a summand of a sum, obtained by→-reducing the process in the metatheoretical modelCLASS.

Complete code for this example can be found inexamples/state/simple-counter.clls. The whole file can be type checked at once by typing

> include ‘‘examples/state/simple-counter.clls’’;;