Charles LaKoS loop n

48
Table of Contents 1 Introduction 1 2 Petri Nets 1 2.1 Black and white nets 2 2.2 Condition-event net systems 4 2.3 Place-transition nets 5 2.4 Coloured nets 5 2.5 Predicate-Transition Nets 7 2.6 Traditional language constructs as petri nets 8 3 The Definition of the Language LOOPN++ 9 3.1 LOOPN++ grammar 9 3.2 Elementary syntactic issues 10 3.2 Programs, classes and instances 11 3.3 Types 12 3.4 Values and expressions 12 3.5 Fields 13 3.6 Functions 14 3.7 Actions 15 3.8 Transitions 16 3.9 Places 16 4 The LOOPN++ Run-time Support Environment 17 4.1 Input-output 17 4.2 Statistics gathering 17 4.3 Other support functions 18 4.4 Tracing 18 4.5 Interactive debugging 18 5 Examples 20 5.1 Black and white dining philosophers 20 5.2 Black and white dining philosophers – solution 2 20 5.3 Coloured dining philosophers 21 5.4 Critical regions 22 5.5 Traffic lights - solution 1 23 5.6 Traffic lights - solution 2 24 5.7 Street of traffic lights 25 6 Running LOOPN++ at the University of Tasmania 26 6.1 The commands l++, l++go 26 6.2 The LOOPN++ simulator 27 7 The Object Orientation of LOOPN++ 29 7.1 The motivation for Object Orientation 29 7.2 Review of Object-Oriented Petri Net Proposals 30 7.3 Example of the Russian philosophers 32 7.4 Super places 35 7.5 Example of the active philosophers 37 7.6 Example of Electronic Data Interchange 39

description

Book about loopn++

Transcript of Charles LaKoS loop n

Page 1: Charles LaKoS loop n

Table of Contents

1 Introduction 1

2 Petri Nets 1

2.1 Black and white nets 2

2.2 Condition-event net systems 4

2.3 Place-transition nets 5

2.4 Coloured nets 5

2.5 Predicate-Transition Nets 7

2.6 Traditional language constructs as petri nets 8

3 The Definition of the Language LOOPN++ 9

3.1 LOOPN++ grammar 9

3.2 Elementary syntactic issues 10

3.2 Programs, classes and instances 11

3.3 Types 12

3.4 Values and expressions 12

3.5 Fields 13

3.6 Functions 14

3.7 Actions 15

3.8 Transitions 16

3.9 Places 16

4 The LOOPN++ Run-time Support Environment 17

4.1 Input-output 17

4.2 Statistics gathering 17

4.3 Other support functions 18

4.4 Tracing 18

4.5 Interactive debugging 18

5 Examples 20

5.1 Black and white dining philosophers 20

5.2 Black and white dining philosophers – solution 2 20

5.3 Coloured dining philosophers 21

5.4 Critical regions 22

5.5 Traffic lights - solution 1 23

5.6 Traffic lights - solution 2 24

5.7 Street of traffic lights 25

6 Running LOOPN++ at the University of Tasmania 26

6.1 The commands l++, l++go 26

6.2 The LOOPN++ simulator 27

7 The Object Orientation of LOOPN++ 29

7.1 The motivation for Object Orientation 29

7.2 Review of Object-Oriented Petri Net Proposals 30

7.3 Example of the Russian philosophers 32

7.4 Super places 35

7.5 Example of the active philosophers 37

7.6 Example of Electronic Data Interchange 39

Page 2: Charles LaKoS loop n

8 Conclusions 41

References 42

Page 3: Charles LaKoS loop n

1 Introduction

The language and simulator referred to in this technical report as LOOPN++ has evolved over a

period of about eight years. Originally, it was motivated by the need to provide practical

experience with network protocols for the third year Computer Science Networks course at the

University of Tasmania. Initially, the Protocol Workshop of J. Colville [2] was used, but while

that tool was appropriate for the demonstration of a number of fixed protocols, it left little room

for experimentation with others. At the other extreme, real-life protocols were considered to be

so complex as to be beyond the scope of a first course in networking. As a step towards an

alternative solution, Simon Milton implemented the inital version of a petri net simulator called

PETSI (PETri net SImulator) as an Honours project in 1987. Since then, it has been rewritten a

couple of times by Charles Lakos, with collaboration from Chris Keen, and with the programming

assistance of Stephen Quan and Carl Lewis, resulting in the language and simulator called

LOOPN (Language for Object-Oriented Petri Nets). LOOPN has been relatively stable since

1992. In 1993, while on sabbatical leave, Charles Lakos proposed a formalism for Object Petri

Nets and a revised language called LOOPN++. These built on the experience with LOOPN, and

represented a more thorough integration of object-oriented technology, while still retaining the

important theoretical property of being able to translate Object Petri Nets into behaviourally

equivalent Coloured Petri Nets. Michael Cahill began implementation of LOOPN++ in late-1994,

and Charles Lakos extended it to a full implementation in 1996, with the assistance of Wei

Shang, Nicole Harvey, and Richard Cockerill.

This document represents the status of the current version of LOOPN++ as it is available on the

Unix™ workstations and the IBM RISC 6000 at the University of Tasmania. The document is

written with the modelling of network protocols in mind, though LOOPN++ is also used for

general simulation. Petri nets and their general use in modelling concurrent processes are

introduced in §2 while the grammar of LOOPN++ together with the semantic implications of

each construct are presented in §3. The run-time support environment for LOOPN++ is described

in §4. Sample LOOPN++ programs are presented in §5, while §6 describes how LOOPN++ is

used on the University of Tasmania Unix™ workstations. The object-oriented features of

LOOPN++ are discussed in §7, together with their motivation and examples of use. Finally, §8

contains some concluding comments.

2 Petri Nets

Petri Nets have been popular as a formalism and practical tool for modelling, simulating and

analysing concurrent systems [6, 9, 20, 22]. There is an attractive simplicity about the basic

constructs of places (which hold state information in the form of tokens), transitions (which

indicate possible changes of state), and arcs (which link the two together). In spite of this

essential simplicity of so-called Place-Transition Nets (or PT-nets), enhancements involving the

use of coloured tokens and the use of functional languages for net inscriptions can provide

powerful tools for specifying concurrent systems [4, 11]. Kurt Jensen [9] cites examples of the

application of these Coloured Petri Nets (or CP-nets) to the design and validation of VLSI chips,

the modelling of a radar surveillance command post, and the design and implementation of

1

software to control the electronic transfer of money between banks.

Page 4: Charles LaKoS loop n

Petri nets were introduced by C.A.Petri in the early 1960s as a mathematical tool for modelling

distributed systems and, in particular, notions of concurrency, non-determinism, communication

and synchronisation. Thiagarajan [27] points out that petri nets model the twin notions of state

and change-of-state (or transitions), within the guiding principles:

• states and transitions are two intertwined but distinct notions that deserve an even-handed

treatment

• both states and transitions are distributed entities

• the extent of change caused by a transition is fixed and does not depend on the state at

which it occurs

• a transition is enabled to occur at a state if and only if the fixed extent of change associated

with the transition is possible at that state.

There are many variations of petri nets [20] from black and white or colourless nets, which are

conceptually simple and straightforward to analyse, to more complex nets such as coloured nets

which allow the modelling of complex systems.

The form of net used in LOOPN++ is an extension of coloured nets to include object-oriented

features. In this section, we first consider colourless nets and their interpretation as condition-event

systems, before covering the coloured net extensions and the form incorporated into LOOPN++.

2.1 Black and white nets

A simple (black and white) petri net is a bipartite digraph. A digraph is a directed graph with

nodes and directed edges, while the fact that it is bipartite means that it has two different kinds of

nodes, called places and transitions. Edges can connect places to transitions (known as input

arcs , and the corresponding places known as input places) or transitions to places (known as

output arcs, and the corresponding places known as output places). A petri net can be marked

by indicating the tokens which are contained in each place at a point in time (drawn as dots). If

the input places of a transition all contain (at least) one token, then the transition is enabled for

firing. If it does fire, then one token is removed from each input place and one token is added to

each output place. A petri net is executed by establishing an initial marking and then, at each

subsequent point in time, one or more enabled transitions are chosen for firing.

In the example of fig 2.1, and in the rest of this document, we follow the convention of drawing

the places as circles and the transitions as lines or rectangles. In fig 2.1(a) the places to the left

are the input places while those on the right are the output places. Since there are tokens in each

of the input places, the transition is enabled to fire. If it does fire, a token will be removed from

each input place and a token will be added to each output place, giving the marking of fig 2.1(b).

(a) Before (b) After

2

Fig 2.1 Firing a transition

Page 5: Charles LaKoS loop n

It is worth noting at this stage that the ability of a transition to fire is determined solely by local

conditions, namely the presence of tokens in the adjacent input places. This is a desirable

feature in modelling concurrent, distributed systems where locality of reference is desirable. The

only global consideration in the description above concerns the choice of an enabled transition

for firing. However, this description is merely conceptual (and reflects the current uniprocessor

implementation). It is possible to implement petri nets as sets of parallel processes. Care must

be taken in conflict situations as in fig 2.2, where both transitions are enabled but the firing of

one disables the other.

Fig 2.2 Conflict between transitions

Even with the simplicity of black and white nets, it is possible to model interesting concurrent

systems. Consider the example of modelling two concurrent processes with critical regions,

such that only one of the two processes is allowed to be in its critical region at any time. A petri

net for this is given in fig 2.3.

Proc1 Crit1 Sem Crit2 Proc2

Enter2Enter1

Leave1 Leave2

Legend:

CritProcSemEnterLeave

Critical regionProcessingSemaphoreEnter critical regionLeave critical region

Fig 2.3 Modelling critical regions

The initial marking is indicated, with one token in each of the places labelled Proc1 and Proc2

(indicating that the two processes proceed independently), and with one token in the place

labelled Sem (for the semaphore). Subsequently, either the left-hand or the right-hand process

can enter its critical region, but not both. This restriction is guaranteed by the presence of only

one token in the place Sem . The result of firing transition Enter1 would be to remove the tokens

from places Proc1 and Sem and place a token in Crit1 . At this point, only the transition Leave1

is eligible for firing, and when it does fire, the original marking is restored. Analysis of this net

would show that at all times only one of the processes can enter its critical region, as required.

It should be noted that the state of the system is given by the marking of the places. The

3

transitions (normally) do not hold any state information, but represent changes-of-state.

Page 6: Charles LaKoS loop n

2.2 Condition-event net systems

Black and white nets are often interpreted as condition-event systems. Here, each place is

interpreted as a condition, which holds if and only if a token is present at the place. Each

transition is interpreted as an event, the occurrence of which leads to a change in the associated

conditions (indicated by the adjacent places). This interpretation of a black and white net puts

additional constraints on the net: a place can only hold one token at a time (since a condition can

only be true or false); and a transition cannot fire if a token is already present in an output place

(a consequence of the first constraint). Thus the net of fig 2.1(a) would not be eligible for firing

as a condition-event system.

The net of fig 2.3 can be interpreted as a condition event system with the appropriate conditions

associated with the places:

Proc1: process 1 is performing its general processing

Crit1: process 1 is in its critical region

Sem: one process may enter its critical region

Such interpretations, together with the changing of conditions when events occur, can be used to

prove properties about the activity of the system being modelled using propositional logic.

Further properties or invariants can be derived using linear algebra techniques [20].

Forks

Eating Thinking

Starteating

Startthinking

Fig 2.4 The Dining Philosophers

Another example of a black and white net, which can be interpreted as a condition event system,

is that of the dining philosophers. This scenario was proposed by E.W. Dijkstra to illustrate the

problems of deadlock and starvation in operating systems. Five philosophers are seated at a

round table, with one fork or chopstick between each pair of philosophers and one bowl of

spaghetti in the centre of the table. Initially, the philosophers are all thinking. At random

intervals, each philosopher becomes hungry and decides to eat. This is possible if the philosopher

can get hold of the fork on each side. This will not be possible if an adjacent philosopher is

already eating. In this case, the hungry philosopher must wait. The dining philosophers problem

is interesting since it reflects the common computer resource allocation primitives which allow

4

access to only one resource at a time. As a result, deadlock is possible (if each philosopher lifts

Page 7: Charles LaKoS loop n

their left fork simultaneously). The petri net modelling this problem (but without the possibility

of deadlock) is shown in fig 2.4 with one place for each fork and the complete subnet for only

one philosopher shown.

Note that a philosopher can start eating if there is a token in the associated fork places and one in

the philosopher's thinking place. When the philosopher starts thinking again, the forks are

replaced, as well as a token added to the thinking place. With petri nets, as in the above case, it

is possible for a transition to be dependent on more than one input place. Hence a philosopher

can only pick up a fork if both forks are available. Therefore, deadlock cannot occur unless the

petri net is redrawn to model the raising of one fork at a time, an exercise which is left to the

reader.

2.3 Place-transition nets

Black and white nets can be generalised to place-transition systems which allow the presence of

multiple tokens in a place, the removal and addition of multiple tokens from input and output

places (respectively) when a transition fires, and the limitation of the number of tokens in each

place. This is still essentially a black and white net, and can be mapped into the basic form

already introduced.

2.4 Coloured nets

Coloured nets extend the concept of a net by differentiating between tokens (i.e. giving them

colours), and making the firing of the net dependent on the availability of the appropriately

coloured tokens. Coloured nets, like place-transition systems, can be mapped to black and white

nets, thus indicating that they (theoretically) do not have any greater modelling power . However,

the differentiation of tokens gives, in practice, much greater descriptive power , and helps to

avoid a lot of the duplication that would be required in black and white nets.

Forks

Eatingphilosophers

Thinkingphilosophers

j = i+1

i

i

i

i

i

i

123

4

0

123

4

0

ji+1

thinkeat

Fig 2.5 Coloured Petri Net for the dining philosophers

Consider, for example, the dining philosophers problem above. In our presentation, we only

indicated the subnet for one philosopher, noting that the others were just duplicates. Such

duplication becomes extremely tedious and may be enough to make the modelling of a significant

5

concurrent system infeasible. In the dining philosophers net, the forks are all the same, apart

Page 8: Charles LaKoS loop n

from their position at the table. It would therefore be possible to model them as one place with

five differently coloured tokens, the colour indicating the position. Similarly, the subnet for the

philosophers will occur only once with the firing of the transitions dependent on the availability

of the appropriately coloured tokens. The resultant net is shown in fig 2.5.

In this net, the colours of both forks and philosophers are indicated by numerals taken from the

set {0,1,2,3,4}. Addition is assumed to be modulo 5. The tokens required for a transition to fire

are indicated by the inscriptions on each arc and the condition inscribed on each transition. The

net indicates that philosopher i can start eating if forks i and i+1 are available. Similarly,

philosopher i can stop eating and start thinking, in which case forks i and i+1 are returned to the

table. Once again, it is left as an exercise to model the dining philosophers where only one fork

is raised at a time and hence with the possibility of deadlock.

Another coloured net example is adapted from K. Jensen's paper on coloured nets [7]. It models

the behaviour of traffic lights at a cross intersection. The traffic lights have the traditional red,

amber and green lights but, as in Denmark and Melbourne (Australia), the red and the amber are

displayed together just before turning green. In other words, the cycle followed by the lights is

green → amber → red → red,amber → green … The petri net given by Jensen is presented

below as fig 2.6.

toRed

fromGreen

fromRed

toGreen

Green

Amber

Red

NS NS

EW

x

x≠y

x x

x x

x x

x y

x x

x x

xx

Fig 2.6 Coloured traffic lights

All places can hold either NS tokens or EW tokens. The presence of (at least) one NS token in

the place Green indicates that the green light is illuminated in the north-south direction. Similarly,

the presence of an EW token indicates the illumination of the light in the east-west direction.

The initial marking indicates that the green light is illuminated for the north-south traffic and the

red light is showing to the east-west traffic. The annotation of the arcs indicates the requirements

of tokens for the associated transition to fire, with two arcs labelled x indicating that two tokens

with the same colour are required. The only transition with an additional restriction is the

transition toRed which requires that two differently coloured tokens x,y are added to the place

Red .

6

Page 9: Charles LaKoS loop n

With two NS tokens in the place Green , the only transition that can fire is fromGreen, which

removes the two tokens from Green and adds them to Amber , thus illuminating that light for the

north-south traffic. Then, the only transition that can fire is toRed , with the result that both

tokens are removed from place Amber and one NS and one EW token are added to place Red,

which now has two EW tokens and one NS. Traffic in both directions now faces red lights. The

only transition that can now fire is fromRed which has the overall effect of removing one EW

token from Red and adding one EW token to Amber . The east-west traffic sees a red and an

amber light. Then transition toGreen is the only one that can fire, removing the EW tokens from

Red and Amber and adding two EW tokens to Green . We return to the initial marking except

with the role of NS and EW tokens reversed. The sequence of lights is thus given by:

NS: green → amber → red → red,amber → green → …

EW: red → red,amber → green → amber → red → …

2.5 Predicate-Transition Nets

We have already seen that black and white nets can be interpreted as condition-event systems

based on propositional logic, i.e. a condition or proposition is associated with each place and the

presence of the token indicates that the condition is true. In a similar way, coloured nets can be

interpreted as systems based on predicate logic, leading to the notion of Predicate-Transition

nets [3]. Here, a predicate is associated with each place and the presence of a token in the place

indicates that the predicate holds for that token value. Compare, for example, the two black and

white petri net segments in fig 2.7(a),(b) with the corresponding coloured net segment in fig

2.7(c).

Pa

QbRa,b

Pb

QaRb,a

(a) (b)

P

Q R

<x>

<y>

x≠y

<x,y>

a

a

[b,a]

b

(c)

Fig 2.7 Using predicates to collapse nets

The condition-event interpretation of fig 2.7(a) is that the event represented by the transition is

enabled if the conditions represented by Pa and Qb hold, which they do. If the event occurs,

then Pa and Qb will cease to hold, and Ra,b will then hold. Similarly, the event of fig 2.7(b) is

7

enabled if the conditions represented by Pb and Qa hold, which they don’t. If the event occurs,

Page 10: Charles LaKoS loop n

then Rb,a will hold, which it already does. Fig 2.7(c) represents the above two situations as a

coloured net. Now, the conditions and events are modelled by places annotated by predicates.

The fact that the place labelled Q has tokens a and b present is interpreted to mean that the

predicates Q(a) and Q(b) hold. The transition can fire if different tokens are present in the

places P and Q and, when it does fire, the token with a tuple value is added to place R, to

augment the current marking.

Again, the interpretation of coloured nets as predicate-transition systems introduces certain

restrictions. For example, output conditions should not hold prior to the firing of a transition.

Despite these restrictions, the association of a predicate with a place can be a helpful abstraction

for understanding or documenting a coloured net.

2.6 Traditional language constructs as petri nets

By now, it should be clear that petri nets are ideally suited to the description and modelling of

concurrent systems. Jensen [9] shows that it is also possible to combine petri net components to

model traditional control constructs of imperative programming languages (figs 2.8 and 2.9).

A1 A2 A3

Start

End

b not b

S1 S2

(a) Alternatives A1, A2, A3

(b) IF b THEN S1 ELSE S2

Fig 2.8 Modelling conditionals in petri nets

S

WHILE b DO S

End

Start

b not b

8

Fig 2.9 Modelling loops in petri nets

Page 11: Charles LaKoS loop n

3 The Definition of the Language LOOPN++

This section specifies the syntax and informal semantics of LOOPN++. It first presents the basic

grammar and then the following subsections consider the various components of the language in

turn, including classes, fields , functions , actions , transitions and places . We illustrate the various

language features with the dining philosophers nets from figs 2.4 and 2.5.

3.1 LOOPN++ grammar

class → CLASS id [ : [ parent ] {, parent } ] -- parents

EXPORT ident {, ident } -- exported identifiers

{ field } -- data fields

{ func } -- functions

{ trans } -- transitions

{ action } -- token and anonymous actions

END id

field → type ident [ = value ] [ | guard ] -- data field with initial value + guard

{ , ident [ = value ] [ | guard ] } ;

func → type ident ( parms ) [ = fvalue ] -- function definition

fvalue → expr ; --possible function values

→ FORALL type ident -- place [ | guard ] ; end FORALL

→ EXISTS type ident -- place [ | guard ] ; end EXISTS

→ COUNT type ident -- place [ | guard ] ; end COUNT

→ REDUCE expr ; type ident -- place [ | guard ] ;

[ type ident2 = expr2 ; ] end REDUCE

trans → TRANS id [ : [ parent ] {, parent } ] -- transition definition

{ field } -- data fields

{ action } -- token and anonymous actions

END id

action → type ident <- place [ | guard ] -- input action + selection condition

→ type ident -> place [ | guard ] -- output action + output value

→ type ident -- place [ | guard ] -- test action +selection condition

→ procedure-call -- interact with environment

type → basic-type-ident -- int, bool, real, string, etc.

→ class-ident -- predefined or user-defined

→ type '*' -- multiset type for places

place → ident -- a place is an identifier

value → ident -- copy of specified object

→ '['ident:value {, ident:value }']' -- new object with specified fields

→ ident'['ident:value {,ident:value }']' -- modify object by specified fields

→ '['value {, value} [ | value ]']' -- multiset value constructor

→ expr -- value given by expression

guard → expr -- boolean expression

expr → const -- constant literal

→ ident -- object identifier

→ ident ( [ expr {, expr } ] ) -- function call

→ exprs-combined-with-operators -- operators not specified

→ IF expr THEN expr ELSE expr END -- conditional expression

9

Fig 3.1 The basic grammar of LOOPN++

Page 12: Charles LaKoS loop n

The design of LOOPN++ attempts to provide a minimal set of contructs with orthogonal

combinations. It may well be appropriate to provide syntactic sugar which resembles traditional

constructs, but it is intended that the underlying constructs should be more general and should be

able to be combined in arbitrary ways. As a result LOOPN++ supports concepts not previously

available, including place types, substitution places, and synchronous interaction between subnets.

In the grammar above we have adopted the following conventions:

• the symbol → is used to separate the non-terminal from its definition(s)

• the order of symbols in a definition is significant, i.e. a definition A → B C D is to be

read as an A is a B followed by a C, followed by a D

• brackets, ie '['…']', indicate that the enclosing construct(s) is optional

• braces, ie '{'…'}', indicate that the enclosing construct(s) can occur zero or more times

• terminal symbols, ie symbols that will occur in a LOOPN++ specification, are given in

upper case or as special characters (colon, semicolon, etc) or enclosed in quotes (e.g.

brackets)

• terminal symbols (which are written in upper case letters) are case insensitive

• non-terminal symbols, i.e. symbols for which grammar rules are explicitly or implicitly

provided, are given in lower case

• a non-terminal with suffix ident is an identifier of the specified kind, eg class-ident is a

type identifier

• identifiers are case sensitive

3.2 Elementary syntactic issues

In a LOOPN++ specification comments are enclosed in braces, i.e. {…}, or appear between a

double slash, i.e. //, and the end of line. Numeric literals take the usual format, while character

and string literals are enclosed in single and double quotes respectively, e.g. "Here is a string".

Some identifiers are reserved words . These include the reserved words of C++ which may never

appear in a LOOPN++ program. They also include the following identifiers specific to LOOPN++,

which should never be redefined (except for deliberate modification of the system behaviour):

null the basic class for passive data (tokens)

generic the basic class for active data

trans the basic transition class

null* the basic place class

self the identifier for the current instance

token the identifier for the current token (in a token action)

char, integer, real, boolean, string the predefined basic types

true, false the boolean constants

first, last, delay, empty, tail, focus the predefined token functions

Some identifiers are predefined in LOOPN++ and may be redefined for specific behaviour.

These include:

report report the status of the current token/object

Functions which can take parameters can specify empty parameter lists with either the Pascal or

10

the C convention, i.e. by including the parentheses or by omitting them altogether. However, for

Page 13: Charles LaKoS loop n

functions and procedures defined externally (in supporting C files), the C convention (of including

the parentheses) must be adopted.

3.2 Programs, classes and instances

class → CLASS id [ : [ parent ] {, parent } ] -- parents

EXPORT ident {, ident } -- exported identifiers

{ field } -- data fields

{ func } -- functions

{ trans } -- transitions

{ action } -- token and anonymous actions

END id

A LOOPN++ specification or program consists of one or more class definitions. One class is

designated the root class for the purposes of the compilation, and a single instantiation of this

class constitutes the main program.

A class defines a set of objects , the instances of the class. The term type is used interchangeably

with class . Types may be classes defined by the user or basic, predefined types such as

boolean, integer, real, char, string.

In general, a class consists of fields (or data), functions , transitions and actions . Each component

or feature of a class is of one of these four kinds. Each component is allocated and initialised on

instantiation of the enclosing object.

A class may be declared to inherit the features of one or more parents , in which case all the

features of the parents, together with the additional features declared within the class constitute

the features of the new class. It is not possible to override one feature by another of a different

kind – it is therefore not possible to override a field by a function or an action.

A class which inherits (directly or indirectly) from the pseudo class trans is called a transition

class . A transition is a shorthand notation for a class definition with a singleton instance, and

therefore, by default, a transition inherits from the pseudo class trans. A transition class may

contain arbitrary actions, but may not contain transitions. A class which does not inherit

(directly or indirectly) from the pseudo class trans is called a state class. A state class may

contain transitions but may not contain actions (other than procedure calls).

The export clause of a class specifies which features are externally accessible. It does so simply

by listing the relevant identifiers. Any field or function, whether declared locally or inherited,

may be exported. E.g. a simple class with a single integer field which is exported could be:

class int_class

export i;

integer i;

end class

Such a class is useful since the current implementation does not support the declaration of

11

multisets of predefined types, e.g. you can have a multiset of int_class but not of integer.

Page 14: Charles LaKoS loop n

3.3 Types

type → basic-type-ident -- int, bool, real, string, etc.

→ class-ident -- predefined or user-defined

→ type '*' -- multiset type for places

Various net components may have a type which may be any built-in, predefined type or a

user-defined class. The type may also be of the form type* which indicates a multiset (or bag)

of objects each of type type, and hence is referred to as a multiset type. (The form type* is

used to declare petri net places. By implication, type** is also possible, but not necessarily

useful!)

3.4 Values and expressions

value → ident -- copy of specified object

→ '['ident:value {, ident:value }']' -- new object with specified fields

→ ident'['ident:value {,ident:value }']' -- modify object by specified fields

→ '['value {, value} [ | value ]']' -- multiset value constructor

→ expr -- value given by expression

expr → const -- constant literal

→ ident -- object identifier

→ ident ( [ expr {, expr } ] ) -- function call

→ exprs-combined-with-operators -- operators not specified

→ IF expr THEN expr ELSE expr END -- conditional expression

In certain contexts – in field and function definitions and in guards – it is possible to specify

values. These values may indicate a copy of an existing object, the generation of a new object

(by specifying values for some of the exported fields), a copy of an existing object with certain

specified fields modified, a multiset of values, or a value computed by an expression. For a

multiset of values, the values preceding the vertical bar are elements of the multiset, while the

value following the vertical bar is the tail or remainder of the multiset. The use of the tail is

helpful in defining a recursive function to generate a multiset.

Expression may occur in various contexts in LOOPN++ classes. The grammar does not fully

specify the format of expressions since they are determined by the target language, in this case

C++. Thus const, values-combined-with-operators, procedure-call follow the format of

C++. Note, however, that the unary operators recognised by LOOPN++ are arithmetic and

boolean negation ('–', 'NOT') and the C++ address-of operator ('&'); the binary operators

supported by LOOPN++ are the comparison operators ('=', '<>', '<', '<=', '>', '>='), the arithmetic

operators ('+', '–', '*', '/', 'mod'), and the boolean operators ('and', 'or'). The relative priority of

these operators is determined by the target language, in this case C++, i.e. the boolean operators

have lesser priority than the comparison operators which, in turn, have lesser priority than the

arithmetic operators. This means that the expression below has the implied bracketting shown

but, of course, the programmer is free to use parentheses to enforce any priority of operators.i > 0 and buf[i] <> 0 (i > 0) and (buf[i] <> 0)

LOOPN++ also includes operators to support conditional expressions in additon to the operators

listed above. For example, the expression below returns the maximum of two integers i and j.if i > j then i else j end

12

Page 15: Charles LaKoS loop n

Note: It is important to note that expressions are grammatically components of values and not

vice versa. This means that once you are nested within an expression (with operators or a

conditional expression), then the arbitrary object or multiset notation cannot be used. This

significant restriction arises because the LOOPN++ parser performs only simple syntax analysis

and translation on expressions before passing them to the C++ compiler. Since C++ does not

support such flexible object and multiset creation within expressions, neither does LOOPN++.

This occasionally means that expressions will need to be rearranged. E.g. given below is the

definition of functions to generate a multiset of n tokens:

int_class* empty_list() = [];

int_class* form_list(integer n) =

[[i:n] | if n=0 then empty_list() else form_list(n-1)];

The above minimal processing of C++ expressions means that the LOOPN++ compiler may

accept an expression as valid which may later be rejected by the C++ compiler. Worse still, the

C++ compiler may not detect the error, in which case a run-time error may result. However, it is

anticipated that this will be a rare occurrence.

3.5 Fields

field → type ident [ = value ] [ | guard ] -- data field with initial value + guard

{ , ident [ = value ] [ | guard ] } ;

A field is a class component of some type which normally holds data (and hence determines part

of the state of the object). A default initial value may be specified for each field using the

notation:

type ident = value;

For every instance of the containing class, this default value will be associated with the field on

initialisation, unless the field is exported. In this case, the instantiation of the containing class

may specify an alternative initial value for the field, using the notation:

type object = [ident:value, ...];

in which case, each instance of the containing class may have a different value for this field.

For example, given below is the declaration of the fork and eating places for the colourless

dining philosophers of fig 2.4, with the forks each holding a single token.

null* fork0=[[]], fork1=[[]], fork2=[[]], fork3=[[]], fork4=[[]];

null* eat0, eat1, eat2, eat3, eat4;

As another example, we present the declaration of the fork and eating places for the coloured

solution to the dining philosophers problem of fig 2.5. Note that the fork place now holds

int_class tokens, and the five tokens in the initial marking have their exported i-fields initialised.

int_class* fork = [[i:0], [i:1], [i:2], [i:3], [i:4]];

int_class* eat;

The value of a field is fixed when its containing class is instantiated, and is in effect for the

lifetime of the instance. Note that if the field is of some class type, then the identity of the object

to which it is bound is fixed, but the contents may not be fixed. For example, if a field acts as a

petri net place, then the particular place is fixed, but its contents or marking is not.

13

A field specification may also specify a guard which is a boolean expression. The interpretation

Page 16: Charles LaKoS loop n

of the guard varies depending on whether the field occurs in a state class or a transition class. In

a state class, the guard specifies an integrity constraint . If the integrity constraint is ever

violated, then the program will abort with an appropriate error message. In a transition class, the

guard specifies an enabling condition. If the guard is not satisfied, then the transition is not

enabled and will not fire.

3.6 Functions

func → type ident ( parms ) [ = fvalue ] -- function definition

fvalue → expr ; --possible function values

→ FORALL type ident -- place [ | guard ] ; end FORALL

→ EXISTS type ident -- place [ | guard ] ; end EXISTS

→ COUNT type ident -- place [ | guard ] ; end COUNT

→ REDUCE expr ; type ident -- place [ | guard ] ;

[ type ident2 = expr2 ; ] end REDUCE

A function defines a parameterised expression, which returns a value based on the other features

(and hence state) of an object. Functions take parameters and return values of some type (which

may be a predefined type, a class type, or a multiset class).

For example, the following functions allow the fork place from the dining philosophers problem

of fig 2.5 to be initialised to hold 100 forks.

int_class* null_list() = [];

int_class* make_list(integer n) =

[[i:0] | if n = 0 then null_list() else make_list(n-1)];

int_class* fork = make_list(100);

Functions can use quantifiers: forall, exists, count, and reduce to determine a value from the

multiset (or bag) of tokens resident in a place. Reduce is the most general form and requires

further explanation. It supplies an expression (here expr) which is the default initial value and is

assigned to the pseudo variable reduce. For each token (ident) from place satisfying guard,

the following field definition (for ident2) is evaluated, and the result becomes the value of the

pseudo variable reduce. (If no such field definition is provided, the value of the token is used.)

Note that the pseudo variable reduce can be referenced in the expression expr2. The final value

of the pseudo variable reduce becomes the function result. This form of quantified function can

be used for list comprehensions as well as the extraction of summary values from the tokens in a

place. For example, the following function forms a list of fork tokens with odd value fields:

int_class* odd_forks() = reduce [];

int_class f -- fork | odd(f.i);

int_class* result = [f | reduce];

end reduce

LOOPN++ allows expressions to include references to externally defined C/C++ functions. In

order to perform some type checking of these functions, LOOPN++ requires the user to provide

a prototype for each such function. The prototype is in the format of a LOOPN++ function

declaration, except that the result expression is not specified. The same notation can be used for

14

forward declaration of functions.

Page 17: Charles LaKoS loop n

3.7 Actions

action → type ident <- place [ | guard ] -- input action + selection condition

→ type ident -> place [ | guard ] -- output action + output value

→ type ident -- place [ | guard ] -- test action + selection condition

→ procedure-call -- interact with environment

An action is the only construct for changing the state of an object. Actions may be input

actions, output actions, test actions, or anonymous actions. Apart from anonymous actions (or

procedure calls), actions may only occur within transition classes. The procedure calls of a state

class are executed on instantiation of the class. The actions of a transition class are executed

each time the (transition) instance fires. The actions which are immediate components of an

object are always synchronised with each other, but not necessarily with the actions contained

within nested component objects.

An input action extracts a value from an object and is written:type x <- p | guard;

where the constraint "| guard" is optional. For such an input action to occur, the value of x

obtained from p must be of an appropriate type and satisfy the condition, if any. The value x is

called a token (or tokens), while the object p is called an input place. The guard is a boolean

expression which may contain terms of the form:x = value

which is interpreted as testing that the components of x match those specified by the value.

An test action examines a value in an object and is written:type x -- p | guard;

where the constraint "| guard" is optional. For such a test action to occur, the value of x in p

must be of an appropriate type and satisfy the condition, if any. The guard is a boolean

expression which may contain terms of the form:x = value

which is interpreted as testing that the components of x match those specified by the value.

An output action deposits a value into an object and is written:type x -> p | guard;

where "| guard" is optional. For such an output action to occur, the value of x must be

acceptable to p. Again, the value x is called a token (or tokens), while the object p is called an

output place . The guard is a boolean expression which will be of the form:x = value

which is interpreted as generating an object with components matching that of the value.

It is important to note that output tokens are always newly-generated objects or newly-generated

copies of existing objects. In this way, it is not possible to carry a reference to a remote object

(such as a place) around a petri net.

An anonymous action interacts with the environment of the petri net and is of the form:procedure-call

In order to support formal analysis it is necessary for anonymous actions to have no effect on the

firing of transitions in the petri net.

15

Page 18: Charles LaKoS loop n

For example, the following is a set of actions for the dining philosophers problem of fig 2.5 to

pick up the forks for a philosopher and report that this has been done.

int_class p1 <- think; -- choose the thinking philosopher

int_class f1 <- fork | f1.i = p1.i; -- get the left fork

int_class f2 <- fork | f2.i = (p1.i+1) mod 5; -- get the right fork

int_class p2 -> eat | p2 = p1; -- generate the eating philosopher

printf("philosopher %d starts eating\n",p1.i); --print the message

3.8 Transitions

trans → TRANS id [ : [ parent ] {, parent } ] -- transition definition

{ field } -- data fields

{ action } -- token and anonymous actions

END id

Traditional petri net formalisms include the fundamental concepts of a transition. In LOOPN++,

these are built from more basic components. A transition class is one which inherits (directly or

indirectly) from the pseudo class trans. A transition is syntactic sugar for declaring a transition

class with a singleton instance, as in:

Trans ident

type x <- p | …; -- zero or more input actions

type y -> q | …; -- zero or more output actions

procedure-calls -- zero or more anonymous actions

End ident

Note that it is possible to include field declarations in transitions. These are used for temporary

variables, which are instantiated for the duration of firing the transition. The guard on the field

(if any), will contribute to determining whether the transition is enabled.

3.9 Places

LOOPN++ does not supply specific syntax for places. A place is simply an object which

supports input and output actions, by supplying or accepting tokens. In the simplest case, a place

is a field of multiset type and is declared in the form:type* ident

In this case, the support for input and output actions is predefined.

In general, however, any class which inherits from a multiset class can be instantiated to form a

place. The inherited multiset class determines the kind of tokens which the place can accept or

offer to its environment. The new class may use the built-in operations for accepting and

offering tokens or may redefine them. This is covered in more detail in §7.4.

16

Page 19: Charles LaKoS loop n

4 The LOOPN++ Run-time Support Environment

4.1 Input-output

The primary means by which LOOPN++ programs interact with the machine environment is

through the procedure calls included as the anonymous actions of a transition. These actions

may specify any externally defined C/C++ function. Such functions can also be used in expressions

including those restricting the visibility of tokens, but this should be done only with extreme

caution. If such a function depends on an externally determined event, then the LOOPN++

simulator may fail to recognise that the event has occurred (and the corresponding function

result has changed), leading to erroneous operation of the petri net.

Familiarity with the C/C++ library functions is therefore highly desirable for the LOOPN++

programmer. However, for those unfamiliar with the standard C/C++ input output functions, a

set of functions is provided based on those available in Modula 2:

Read () Return the next character from input

ReadInt () Return the next integer from input

ReadReal () Return the next real number from input

ReadString (var s:string; w:integer) Return the next string from input.1 The

string will be terminated at w characters, when a newline

character or end-of-file is read, whichever occurs first.

Write (c:char) Write the character to output

WriteLn() Terminate the current line on output

WriteInt (n,w:integer) Write the integer n to output in w columns or use as many

columns as required if w is zero

WriteReal (r:real; w,d:integer) Write the real number r to output in w columns

and d decimal places, or use as many columns as required

if w is zero

WriteString (c:string; w:integer)Write the string to output, right- justified in w

columns, or use as many columns as required if w is zero

4.2 Statistics gathering

LOOPN++ provides built-in mechanisms for statistics gathering. The statistics associated with

places and transitions can be accessed using the following functions:

totaltokens() Defined on places to return the total number of tokens which

have visited the place

totaltime() Defined on places to return the total accumulated time that

tokens have been resident at the place. (The above functions

can be combined to determined the average waiting time

17

1Note that the first argument to call of ReadString must be a string variable, possibly a token component, the

value of which will be replaced by the string read from input.

for each token at a place.)

Page 20: Charles LaKoS loop n

totalfired() Defined on transitions to return the number of times that

the transition has fired. (Combined with globaltime(), it

can be used to determine the firing frequency.)

4.3 Other support functions

The following external functions are provided for controlling the simulator and for the generation

of random numbers:

globaltime() Return the current simulated time

setTimeLimit(real time) Set the maximum simulation time for this run (default =

50)

setRetryLimit(real r) Set the probability of extending a step with further

transitions

randomint(integer lo,hi) Return a uniformly distributed integer random number in

the range [lo..hi]

randomreal(integer seed) Return a uniformly distributed real random number in the

range [0..1]

globalseed() Return the current random number seed

negexp(real lambda) Return an exponentially distributed random number with

mean 1/lambda

meminuse() Return the amount of heap space currently used

timeused() Return the number of seconds of run-time used so far

4.4 Tracing

It can be difficult to track down bugs in concurrent systems. Accordingly, the LOOPN++

run-time environment includes a function to control tracing of net activity for classes derived

from generic (which includes places and transitions) for providing trace information. This

function is normally called as an anonymous action in a transition, and thereby can selectively

turn tracing on and off.

The tracing of a place indicates when tokens have been added to or removed from the place. The

tracing of transitions indicates when the transition has been armed and whether firing was

successful or not. The function is:

settrace(boolean on) Turn tracing on for the specified place

4.5 Interactive debugging

Tracing a simulation often produces too much information, making it hard to track down problems.

A more selective approach is interactive debugging, which is also supported by LOOPN++.

Interactive debugging is enabled by using the option -d on the compiled output file (a.out) or

on the l++go command (see §6). At each cycle of the simulator, or as directed by the user, the

user will be prompted with the name of the current net component being examined, to which it is

possible to reply with a number of commands. The list of commands can be obtained with the

18

command help, which responds as follows:

Page 21: Charles LaKoS loop n

help - produce this message

quit - terminate simulation

step - simulate the next event

focus obj - change focus to specified object

status obj - print status of specified object

gofor t - simulate for t ticks

gowhile t - simulate while trans t enabled

gountil t - simulate until trans t enabled

Some of these commands are explained further below. It is important to note that an object can

be specified in the notation feature1.feature2. … where feature1 is a feature of the current

net component, which in turn has a named component feature2, etc. The main module is

identified by a leading dot.

quit

An alias for this command is an end-of-file character. This command terminates the simulation.

step

An alias for this command is simply a carriage return character. The command will get the

simulator to execute one pending event. This may be the examination of a transition to see if it

is enabled, the firing of a transition, or the advancement of the simulated time.

gofor t

This command will simulate the net for a simulated time of t.

gowhile t, gountil t

These commands allow you to instruct the simulator to keep executing while a specified transition

is enabled or while it is disabled. The transition can be specified as an object (as above).

focus obj

The component object is specified as above. The interactive debugger now makes the specified

component object the focus for subsequent debugging commands.

status obj

This command asks the interactive debugger to report on the status of the specified object – for a

place, the list of tokens is given; for a transition, its enabled/disabled status is reported; for a

class instance, its components are given. In the case of the component tokens in places, and the

components of classes, the status is reported by calling the built-in function report. By default,

this function prints a numeric value for each component. If the component is a composite object,

then the number printed is the address of the component. It is possible to provide more descriptive

output by overriding the report function in the relevant token type or module declaration.

19

Page 22: Charles LaKoS loop n

5 Examples

5.1 Black and white dining philosophers

The petri net diagram for this example is found in fig 2.4. This initial solution is given as a

single class. We therefore avoid unnecessary duplication by the judicial use of the ellipsis ('…').

class philos1

null* fork0 = [[]], ... fork4 = [[]];

null* think0 = [[]], ... think4 = [[]];

null* eat0, ... eat4;

trans starteating0

null p1 <- think0;

null f1 <- fork0;

null f2 <- fork1;

null p2 -> eat0;

printf("*** philosopher 0 picks up forks\n");

end starteating0

...

trans starteating4

null p1 <- think4;

null f1 <- fork4;

null f2 <- fork0;

null p2 -> eat4;

printf("*** philosopher 4 picks up forks\n");

end starteating4

trans stopeating0

null p1 <- eat0;

null f1 -> fork0;

null f2 -> fork1;

null p2 -> think0;

printf("*** philosopher 0 puts down forks\n");

end stopeating0

...

trans stopeating4

null p1 <- eat4;

null f1 -> fork4;

null f2 -> fork0;

null p2 -> think4;

printf("*** philosopher 4 puts down forks\n");

end stopeating4

end philos1

5.2 Black and white dining philosophers – solution 2

In §5.1 we modelled the dining philosophers problem as a black and white net. There we

avoided tedious repetition by judicious use of an ellipsis ('…'). Here, we present a modular

20

solution, where repetition is minimised by the use of supporting classes.

Page 23: Charles LaKoS loop n

This solution is a natural reflection of the presentation of fig 2.4, since we define one module for

a philosopher, and then the main module instantiates five philosophers. Note that each philosopher

instance initialises its own thinking and eating place, while the main module initialises the forks

which are shared between the philosophers.

class philos0

export id, left, right;

integer id;

null* left, right;

null* think = [[]];

null* eat;

trans starteating0

null p1 <- think;

null f1 <- left;

null f2 <- right;

null p2 -> eat;

printf("*** philosopher %d picks up forks\n", id);

end starteating0

trans stopeating0

null p1 <- eat;

null f1 -> left;

null f2 -> right;

null p2 -> think;

printf("*** philosopher %d puts down forks\n", id);

end stopeating0

end philos0

class philos2

null* fork0 = [[]], ... fork4 = [[]];

philos0 phil0 = [id:0, left:fork0, right:fork1];

philos0 phil1 = [id:1, left:fork1, right:fork2];

philos0 phil2 = [id:2, left:fork2, right:fork3];

philos0 phil3 = [id:3, left:fork3, right:fork4];

philos0 phil4 = [id:4, left:fork4, right:fork0];

end philos2

5.3 Coloured dining philosophers

The petri net diagram for this example is found in fig 2.5. The reader will note that unlike the

example in §5.1, the coloured net solution is complete. This demonstrates the additional descriptive

power of coloured nets, which can obviate the need for tedious duplication.

class int_class

export i;

integer i;

21

end int_class

Page 24: Charles LaKoS loop n

class philos3

int_class* fork = [[i:0], [i:1], [i:2], [i:3], [i:4]];

int_class* think = [[i:0], [i:1], [i:2], [i:3], [i:4]];

int_class* eat;

trans starteating

int_class p1 <- think;

int_class f1 <- fork | f1.i = p1.i;

int_class f2 <- fork | f2.i = (p1.i+1) mod 5;

int_class p2 -> eat | p2 = p1;

printf("*** philosopher %d picks up forks\n", p1.i);

end starteating

trans stopeating

int_class p1 <- eat;

int_class f1 -> fork | f1.i = p1.i;

int_class f2 -> fork | f2.i = (p1.i+1) mod 5;

int_class p2 -> think | p2 = p1;

printf("*** philosopher %d puts down forks\n", p1.i);

end stopeating

end philos3

5.4 Critical regions

The critical regions problem (as introduced in §2) can be modelled in LOOPN++ with the

following specification. As in the previous example, we avoid duplication by defining a module

for a process and instantiating it twice. Clearly, it would be possible to instantiate it more times

if required.

class crit_proc

export which, sem;

integer which;

null* sem;

null* processing = [[]];

null* critical;

trans enter

null s <- sem;

null p <- processing;

null c -> critical | c=p;

printf("Process %d enters its critical region\n", which);

end enter

trans leave

null c <- critical;

null p -> processing | p=c;

null s -> sem;

printf("Process %d leaves its critical region\n", which);

end leave

end crit_proc

22

Page 25: Charles LaKoS loop n

class crit_test

null* sem = [[]];

crit_proc proc1 = [which:1, sem:sem];

crit_proc proc2 = [which:2, sem:sem];

crit_proc proc3 = [which:3, sem:sem];

end crit_test

5.5 Traffic lights - solution 1

The Danish traffic lights of fig 2.6 can be modelled closely by the LOOPN++ description below.

Note how we can extract multiple tokens from each place in a single transition, as shown in the

figure.

class light

export dir, other;

integer dir;

integer other() = 1-dir;

end light

class lights

export isgreen;

integer EW = 0, NS = 1;

light* red = [[dir:EW]],

amber,

green = [[dir:NS], [dir:NS]];

boolean isgreen(integer d) = exists x -- green | x.dir = d; end exists

trans fromgreen

light gn1 <- green | gn1.delay(5);

light gn2 <- green | gn2.dir = gn1.dir;

light am1 -> amber | am1 = gn1;

light am2 -> amber | am2 = gn1;

printf("Direction %d changes from green\n", gn1.dir);

end fromgreen

trans togreen

light am <- amber | am.delay(1);

light rd <- red | rd.dir = am.dir;

light gn1 -> green | gn1 = rd;

light gn2 -> green | gn2 = rd;

printf("Direction %d changes to green\n", rd.dir);

end togreen

trans fromred

light rd1 <- red | rd1.delay(5);

23

light rd2 <- red | rd2.dir = rd1.dir;

Page 26: Charles LaKoS loop n

light am -> amber | am = rd1;

light rd3 -> red | rd3 = rd1;

printf("Direction %d changes from red\n", rd1.dir);

end fromred

trans tored

light am1 <- amber | am1.delay(1);

light am2 <- amber | am2.dir = am1.dir;

light rd1 -> red | rd1 = am1;

light rd2 -> red | rd2 = [dir:am1.other];

printf("Direction %d changes to red\n", am1.dir);

end tored

end lights

5.6 Traffic lights - solution 2

An alternative solution to the Danish traffic lights of fig 2.6 is given below. Here only one token

of any direction is present in a place at any time, and the associated conditions are satisfied by

careful choice of functions and firing conditions. (Note that this example has not yet been run

by the loopn++ system pending complete implementation of quantifiers.)

class light2 : light*

export ison, noton, alloff;

boolean ison(integer d) = EXISTS x -- self | x.dir = d; END EXISTS

boolean noton(integer d) = FORALL x -- self | x.dir <> d; END FORALL

boolean alloff() = FORALL x -- self | false; END FORALL

end light2

class lights2

export isgreen;

integer EW = 0, NS = 1;

light2 red = [[dir:EW]],

amber,

green = [[dir:NS]];

boolean isgreen(integer d) = exists x -- green | x.dir = d; end exists

trans fromgreen

light gn1 <- green | gn1.delay(5);

light am2 -> amber | am2 = gn1;

printf("Direction %d changes from green\n", gn1.dir);

end fromgreen

trans togreen

light am <- amber | am.delay(1);

light rd <- red | rd.dir = am.dir;

light gn1 -> green | gn1 = rd;

printf("Direction %d changes to green\n", rd.dir);

24

end togreen

Page 27: Charles LaKoS loop n

trans fromred

light rd1 <- red | rd1.delay(5) and rd1.first() and

red.ison(rd1.other()) and amber.alloff();

light am -> amber | am = rd1;

light rd2 -> red | rd2 = rd1;

printf("Direction %d changes from red\n", rd1.dir);

end fromred

trans tored

light am1 <- amber | am1.delay(1) and red.noton(am1.dir);

light rd1 -> red | rd1 = am1;

printf("Direction %d changes to red\n", am1.dir);

end tored

end lights2

5.7 Street of traffic lights

The example below demonstrates how three instances of the traffic lights modules (either from

§5.5 or §5.6) can be combined in another module, which can then refer to the status of the

component instances without affecting their performance. The street of lights has one transition

to report when there is a clear run in the north-south direction through all the traffic lights. Note

that a dummy place needs to be declared to provide an input place for the transition.

class street

integer EW = 0, NS = 1;

null* dummy = [[]];

lights first_set;

lights second_set;

lights third_set;

trans isclear

null d <- dummy | d.delay(1) and first_set.isgreen(NS) and

second_set.isgreen(NS) and third_set.isgreen(NS);

null d2 -> dummy;

WriteString("Clear run through lights at time ");

WriteReal(globaltime(),6,2);

WriteLn();

end isclear

end street

25

Page 28: Charles LaKoS loop n

6 Running LOOPN++ at the University of Tasmania

6.1 The commands l++, l++go

Each LOOPN++ class is prepared in a separate file which is given the same name as the class,

with a suffix '.l'. The modules are compiled using the command l++, which can specify one or

more files, the last one being assumed to be the root class for the petri net. In addition, the l++

command may specify a number of options, and a number of auxiliary files, including C and

C++ source files (with a '.c' or '.C' suffix respectively) and object files (with a '.o' suffix).

A petri net class, cls say, found in the file cls.l is first translated by the l++ command into its

intermediate clausal form, in a file with name cls.cl (i.e. the original file name but with a '.cl'

suffix). From there, the clausal form is translated into C++ code, in files named cls.C and

cls.h. The C++ files are then compiled by the C++ compiler to form an object file, called

cls.o. The object file is linked with the appropriate support libraries to form the executable file

a.out. Finally, the C++ and object files are removed, unless the relevant options are specified

to override this default.

Where a complete petri net consists of several classes, the relevant files can all be specified in

the l++ command provided they are in dependency order (i.e. dependent modules follow the

modules on which they depend). Alternatively it is sufficient to specify the main driver module,

and the l++ command will locate the others provided they are in the same directory, or in

directories specified in the include path (see option 'I' below). With these provisos, the following

commands will achieve identical results (assuming that filen is the main driver module):

l++ file1 file2 file3 ... filen

and

l++ filen

The arguments which can be specified with the l++ command are:

cls.l compile the specified LOOPN++ source file containing class cls

cls.cl compile the specified LOOPN++ clause file containing class cls

fyl.c include the specified C source file in the executable program

fyl.C include the specified C++ source file in the executable program

fyl.o include the specified object file in the executable program

-d set interactive debugging (relevant to l++go command or a.out)

-r do not remove any of the generated files on completion of compilation

-c do not remove the C++ files on completion of compilation

-o do not remove the object files on completion of compilation

-Idir the directory dir should be added to the include path for locating supporting

classes or C header files

-Ldir the directory dir should be added to the include path for locating supporting

libraries

-lx the library libx.a should be bound with the executable program

A variant of the l++ command, called l++go, is also available. This has identical arguments to

that of l++, but it assembles the executable file in the temporary file area /tmp. This avoids

26

excessive file space demands for students with small file space allocations.

Page 29: Charles LaKoS loop n

6.2 The LOOPN++ simulator

The firing of petri net transitions results in the addition and removal of tokens at places. Each

place is implemented as a queue of tokens of the relevant type. When a token is added to a place

it is added at the end of the queue (so that first and last functions can be easily evaluated),

and is time-stamped with the simulation time at which it was added to the place. In the course of

testing and firing eligible transitions we may determine (through a call to the delay function)

that some tokens should only be visible at some future point of simulated time. In this case, an

event notice is placed on an event queue so that the token will be a reconsidered later.

At each cycle of the simulator, transitions from the event queue are examined. Once an enabled

transition is identified, it is added to the step and a random real number is generated in the range

0..1. If this random number is less than the retry limit, the simulator attempts to extend the step

with another enabled transition from the event queue. If successful, another random number is

generated, etc. The default retry limit is 0.5, and it can be modified by calling the function

setRetryLimit(limit). If the retry limit is set to 0.0, each step will contain only one transition.

If the retry limit is set to 1.0, each step will be maximal.

When all eligible transitions at the current simulated time have been considered, the simulation

time is advanced to the earliest time for which an event notice appears in the event queue.

When the event queue has been emptied or the simulation time exceeds the value maxtime,

then the simulation stops. The value of maxtime may be changed by a call to the function

setTimeLimit(newmaxtime).

Sample output

Given below is the sample output from running the traffic lights specification. Note that the

compilation passes through three stages – the translation of the specification into an intermediate

clausal form, the translation of the clausal form to C, the compilation of the C, and finally, its

execution.

% l++ lights.l

Loopn translator: version 3.115 (27 Dec 95)

Generating clausal form for loopn++ files...

lights.l:

light.l:

8 lines compiled

42 lines compiled

Generating C code for loopn++ clause files...

light.cl:

9 lines read, 187 lines generated

lights.cl:

85 lines read, 1507 lines generated

light.C:

lights.C:

Loading to form a.out

Cleaning up

% a.out

Direction 1 changes from green

Direction 1 changes to red

27

Direction 0 changes from red

Page 30: Charles LaKoS loop n

Direction 0 changes to green

Direction 0 changes from green

...

Sample output with interactive debugging

Given below is the sample output from running the traffic lights specification with the loopngo

command and with interactive debugging enabled.

% l++ lights.l

Loopn translator: version 3.115 (27 Dec 95)

Generating clausal form for loopn++ files...

lights.l:

light.l:

8 lines compiled

42 lines compiled

Generating C code for loopn++ clause files...

light.cl:

9 lines read, 187 lines generated

lights.cl:

85 lines read, 1507 lines generated

light.C:

lights.C:

Loading to form a.out

Cleaning up

% a.out -d

command> status

EW is integer 0

NS is integer 1

red 122824

amber 123520

green 124120

fromgreen 124912

togreen 125072

fromred 125232

tored 125392

command> status red

Token 1:

dir is integer 0

command> focus amber

command> status

command> focus .green

command> status

Token 1:

dir is integer 1

Token 2:

dir is integer 1

command> step

Direction 1 changes from green

command> status

command> quit

%

28

Page 31: Charles LaKoS loop n

7 The Object Orientation of LOOPN++

In this section we consider the more advanced object-oriented features of LOOPN++. This

section is adapted from a paper published on LOOPN++. It therefore considers the motivations

for object-orientation, a review of other approaches, plus some examples to demonstrate the

features of LOOPN++.

7.1 The motivation for Object Orientation

In §2.4 we drew the distinction between the modelling power and descriptive power of a petri net

formalism. The former describes what can theoretically be modelled, while the latter describes

what can be conveniently modelled in practice. It is a common property of high-level net

formalisms that they can be transformed into behaviourally equivalent PT-nets (e.g. see [9]),

thus indicating that they have no greater modelling power. Such transformations can form the

basis for adapting known analysis techniques to new petri net formalisms [10].

The importance of increased descriptive power should not be underestimated. A practitioner

may well face a situation of being able to model a concurrent system as a high level net (such as

a CP-net) but not as a simple net (such as a PT-net). This may be due to unfamiliarity with the

formal results quoted above, or it may be because the amount of duplication required for a

PT-net is simply impractical. For example, we might consider the simple case of the dining

philosophers problem where philosophers pick up one fork at a time. A CP-net solution for this

problem (as presented by Valmari [28]) is given in fig 7.1. The initial marking indicates that the

places freeForks and thinking hold the numeric tokens 1..n, thus indicating that these forks

are available and that these philosophers are initially thinking. The arc inscriptions constrain the

enabling of the transitions – transition takeLeft can fire if a token numbered i can be removed

both from the place thinking and the place freeForks and if a similarly numbered token can

be added to the place hasLeft. The firing of this transition corresponds to philosopher i picking

up their left fork.

thinking takeLeft hasLeft

freeForks takeRightretRight

hasRight retLeft eating

1:n

1:n

i

i+1 i+1

i

iii

iii

i i

Fig 7.1 Dining philosophers Petri Net

Even though the inscriptions on this CP-net are relatively simple, it is still a significant advance

over modelling the system as a PT-net. The behaviourally equivalent PT-net would require 5n

places and 4n transitions. This may be quite manageable for n equal to 5, but impractical for n

29

equal to 50.

Page 32: Charles LaKoS loop n

CP-nets therefore constitute a significant advance over PT-nets, but the absence of powerful

structuring primitives has still been identified as a weakness. As Jensen says: the absence of

compositionality has been one of the main critiques raised against Petri net models [8]. This has

led to the development of Hierarchical Coloured Petri Nets (or HCP-nets) which introduce a

facility for building a petri net out of subnets or modules. More recently, researchers have been

attempting to harness the structuring techniques of object-oriented programming to the petri net

formalism. Some of these proposals are reviewed in §7.2, where we observe that they can, at

best, be described as partial solutions. One common criticism is that the petri net remains a

static, global control structure. This makes certain applications difficult if not impractical (as

considered in §7.2).

The language LOOPN++ (and its underlying formalism Object Petri Nets) embodies a more

extensive adoption of object-oriented structuring into petri nets. The intention is to increase

further the expressive comfort of petri nets and thus make feasible the modelling of even more

complex systems, and at the same time, reaping the other practical benefits of object-orientation

including clean interfaces, reusable software components, and extensible component libraries

[19]. Furthermore, OP-nets retain the important property of being transformable into behaviourally

equivalent CP-nets [15] so that the analysis techniques developed for CP-nets can be adapted.

7.2 Review of Object-Oriented Petri Net Proposals

In traditional petri net formalisms, the token types determine the kind of data that can be handled

by the net. In elementary nets and PT-nets, there is only one token type with only one value.

The only state information available is the number of tokens present in each place. In CP-nets,

tokens can assume arbitrary data types, such as integers, tuples, unions, records, arrays. These

token colours can be used to collapse repetitive net structures which are then differentiated by

the different token values (as was the case in the dining philosophers example of fig 7.1). Along

with the flexible token types comes flexible net inscriptions.

However, despite the range of token types provided in high-level petri net models, tokens are

still passive data items. Their life cycle or even acceptable operations is not encapsulated but is

external to the data and given by the petri net – by its connectivity and its inscriptions. In other

words, the net forms a global control structure in the same way that procedures form the global

control structure in traditional imperative programming languages.

This is contrary to the pattern displayed by object-oriented languages where classes encapsulate

both data and control, so that objects can constrain their response to messages. Furthermore,

there is a pleasing symmetry or balance between data and control. The data (or more specifically

the type of the data) can affect the choice of method for a particular message and hence can

affect the response to an operation. On the other hand, a method can modify the data stored in

an object. This symmetry is not present in traditional petri net formalisms and therefore complicates

or makes impractical the modelling of certain kinds of systems.

It would be desirable, for example, to have a simple way of modelling multi-level simulations,

where the components that flow through the system have their own internal life cycles. Real-life

situations generally have a number of layers of data and activity. For example, in modelling a

30

traffic intersection, the cars which move through the intersection can be considered as data

Page 33: Charles LaKoS loop n

objects. But cars also have internal activities such as consumption of petrol, mechanical failure,

etc. which may be of interest in the simulation. One could continue by considering the people in

the car as systems requiring modelling at a more detailed level.

As an illustrative example of such multi-level systems, we propose an extension of the dining

philosophers problem which we call the Russian philosophers problem. (The title is intended to

be reminiscent of the Russian matrioshka dolls which can be pulled apart to reveal other dolls

inside.) The Russian philosophers are like the dining philosophers, except that each Russian

philosopher is thinking about the dining philosophers problem. Only when such an imagined

dining philosophers problem deadlocks, will the corresponding philosopher stop thinking and try

to start eating. When such a Russian philosopher stops eating, he or she starts thinking about a

fresh dining philosophers problem. This fanciful problem is an example of a multi-level system

and typical of the situation where one part of a sytem is performing some task which is being

monitored by another.

Another desirable area of application is that of Object-Oriented Operating Systems, where the

entities handled by the system encapsulate their own protocols. For example, we might consider

a situation where an item of electronic mail encapsulates the logic to control interaction with its

contents. Thus, different parts of the document may be visible depending on the security

clearance of the viewer, or the display of the message may vary depending on the screen

technology available. Similar features are exhibited by Electronic Data Interchange, where a

message encapsulates not only the original message but also related documents. Thus, a bill of

lading might incorporate a request for a letter of credit, a request for insurance cover, etc.

A third desirable area of application is that of producing prototypes from Object-Oriented Design

methodologies. The designs produced by such methodologies include a set of objects, each of

which encapsulates its own data and interactions. For example, the Shlaer-Mellor methodology

[23], which can be integrated in a simulation design methodology [12], produces objects which

encapsulate their own life cycle, and which can be dynamically created and discarded. Without

the flexibility of intermixing data and control, the production of prototypes from such designs is

rather awkward [18].

With these sorts of application areas in mind, it is instructive to review some of the proposals

which have been made to incorporate object-oriented structuring into the petri net formalism.

One system, which was the precursor to this work, was the textual language for object-oriented

petri nets called LOOPN. LOOPN supported two class hierarchies – one for tokens and one for

subnets or modules. Inheritance, overriding and polymorphism for token types led to some

interesting results in the modelling of layered network protocols, so that one protocol layer could

be designed to pass any kind of message token [16]. Inheritance, overriding and polymorphism

of subnet or module types allowed the derivation of more complex modules from simpler ones,

thereby encouraging software reuse [17]. An unusual feature (in the context of petri net work)

was the provision of module access functions (which allowed access to some aspect of the state

of a subnet without modifying that state). These facilities proved to be extremely beneficial in

the development of clean module interfaces [17]. Nevertheless, LOOPN still retained a rigid

separation of token types and subnet types, with the petri net being a global control structure and

31

tokens being essentially passive data items. In other words, it is not a simple matter to apply

Page 34: Charles LaKoS loop n

LOOPN to the application areas identified above.

Researchers at the University of Aarhus, who have been intimately involved with the development

of the CP-net formalism and the associated Design/CPN tool [9, 11] have also been experimenting

with the integration of an object-oriented language BETA [13] into Design/CPN [1]. The BETA

language is used to declare the token types and to annotate the transitions with BETA code

segments. However, there are other contexts where BETA cannot be used and there is no

current intention of departing from the traditional approach where the petri net is a global control

structure.

Van Hee and Verkoulen [5, 29] have considered the modelling of (information) systems in petri

net models. In specifying such systems they identified the need for formalisms which integrated

three aspects of a system – the state space (described by data structures), the interaction structure

(between system components) and the operations (or local state transformations of the components).

In focussing on the petri net model, they observed: Petri Net formalisms do not support a real

data-oriented view of systems, such as is provided by modern object-oriented data models . In

response to this, they present a two-level data model consisting of simplexes or simple objects,

and complexes or container objects. This data model is integrated into a petri net formalism

where places hold particular complexes and where transitions transfer, modify, delete and generate

complexes, as specified in an object algebra. There are many attractive features to this system

including a greater facility in information modelling, the ability to specify class-dependent

methods and cardinality constraints, and the ability to verify some constraints automatically.

However, it seems to fall short of a complete integration of object-oriented ideas with data and

functions separated rather than encapsulated together, and with the life cycle of a complex being

external to the complex and not integrated within the object.

In summary, all the above proposals have, to a greater or lesser extent, focussed on the incorporation

of object-oriented structuring into the definition of token types. They have not addressed the

encapsulation of data and functions, but have retained the traditional petri net style with the

tokens as passive data items and their life cycles specified by the global control structure of the

net.

7.3 Example of the Russian philosophers

Having presented the definition of LOOPN++, we now consider solutions to two problems

illustrating the distinctive features of the language. First, we consider the Russian philosophers

problem posed in §7.2. We start with a solution to the traditional dining philsophers problem.

The class DPhil (fig 7.2) is assumed to be self-evident – it simply defines the data which

identifies each philosopher and provides functions to determine the related fork numbers.

The classes DgetRight and DretRight (fig 7.3) show how transition classes can be defined. The

fact that they are classes means that they can be instantiated many times. They are identified as

transition classes because they inherit from the pseudo class trans . Note that all the places which

the actions refer to are exported (and are bound when the classes are instantiated).

32

Page 35: Charles LaKoS loop n

Class DPhil -- dining philosophers token type

Export id, left, right, hungry;

integer n = 5; -- number of philosophers

integer id; -- identity of this philosopher

integer left() = (id - 1) mod n + 1; -- left fork number

integer right() = id mod n + 1; -- right fork number

boolean hungry() = true; -- function determines if hungry

End DPhil

Fig 7.2 Dining philosophers token type

class DgetRight : trans -- transition class to pick up right fork

export hasLeft, eating, fork;

DPhil* hasLeft, eating;

int_class* fork;

DPhil x <- hasLeft; -- get a philosopher who has their left fork

int_class f <- fork | f.i = x.right(); -- get their right fork

DPhil y -> eating | y = x; -- the philosopher can start eating

printf("*** %d picks up right \t(%d %7.2f)\n", x.id,

meminuse(), timeused());

end DgetRight

class DretRight : trans -- transition class to put down right fork

export hasRight, thinking, fork;

DPhil* hasRight = [], thinking = [];

int_class* fork = [];

DPhil x <- hasRight; -- get a philosopher who has their right fork

DPhil y -> thinking | y = x; -- the philosopher can start thinking

int_class f -> fork | f = [i:x.right()]; -- return the right fork

printf("*** %d puts down right \t(%d %7.2f)\n", x.id,

meminuse(), timeused());

end DretRight

Fig 7.3 Transition classes for handling the right fork

The class DTable (fig 7.4) defines transitions getLeft and retLeft , while instantiating the classes

DgetRight and DretRight . This serves to compare these two possible approaches of defining

transitions and emphasises that the notation for transitions is simply syntactic sugar.

The place thinking is initialised to hold the philosopher tokens, while the places hasLeft, hasRight,

eating are initially empty. The function deadlock uses the quantifier count to determine the

number of tokens in place hasLeft (satisfying the condition true). If this number is 5, then the

table of dining philosophers is deadlocked.

Note that the function setRetryLimit is called to ensure that only one transition is included in

each step. Further, the functions meminuse and timeused are called to report on the use of

machine resources.

33

Page 36: Charles LaKoS loop n

Class DTable -- table of dining philosophers

Export k, deadlock;

integer n = 5 ;

integer k = 0;

int_class* fork = [[i:1],[i:2],[i:3],[i:4],[i:5]];

DPhil* thinking = [[id:k+1],[id:k+2],[id:k+3],[id:k+4],[id:k+5]];

DPhil* hasLeft, hasRight, eating;

integer numwaiting() = count x -- hasLeft | true; end count

boolean deadlock() = numwaiting() = 5;

setRetryLimit(0.0); -- ensure one transition per step

trans getLeft

DPhil x <- thinking | x.hungry();

int_class f <- fork | f.i = x.left();

DPhil y -> hasLeft | y = x;

printf("*** %d picks up left \t(%d %7.2f)\n", x.id,

meminuse(), timeused());

end getLeft

DgetRight gr = [hasLeft:hasLeft, eating:eating, fork:fork];

trans retLeft

DPhil x <- eating;

DPhil y -> hasRight | y = x;

int_class f -> fork | f = [i: x.left()];

printf("*** %d puts down left \t(%d %7.2f)\n", x.id,

meminuse(), timeused());

end retLeft

DretRight rr = [hasRight:hasRight, thinking:thinking, fork:fork];

End DTable

Fig 7.4 Table of dining philosophers

It is now possible to extend the above classes to produce a solution to the Russian philosophers

problem. It is important to bear in mind how much of the above classes can be inherited without

change from the classes already defined. Firstly, we can define the class RPhil for Russian

philosophers (fig 7.5), as inheriting the features of the class DPhil, augmenting it with an image

of a table of dining philosophers, and overriding the definition of the function hungry .

Class RPhil : DPhil -- Russian philosophers token type

export k;

integer k = 10;

DTable image = [k:k]; -- image of the dining philosophers

boolean hungry() = image.deadlock(); -- hungry only if the image is deadlocked

End RPhil

Fig 7.5 Class for Russian philosophers

Next, we can define a modified form of the transition class for returning the right fork number,

called DretRight (fig 7.6). This transition needs to be modified since a Russian philosopher

returning to the thinking state, needs to start thinking about a fresh dining philosophers problem,

34

and not the old deadlocked version.

Page 37: Charles LaKoS loop n

class RretRight : DretRight -- transition class to return the right fork

RPhil* hasRight = [], thinking = [];

int_class* fork = [];

RPhil x <- hasRight;

RPhil y -> thinking | y = [id:x.id, k:x.k]; -- new Russian philosopher

int_class f -> fork | f = [i:x.right()];

printf("*** %d puts down Rright\n", x.id);

end RretRight

Fig 7.6 Transition class to return the right fork for a Russian philosopher

Finally, we can define the class RTable for a class of Russian philosophers (fig 7.7). Note that

this class inherits most of its fields, functions and transitions from its parent class (DTable). It

only needs to specify the place thinking with the new philosopher tokens. Since RPhil is a

subtype of DPhil , the inherited places of type DPhil will also be able to hold the RPhil tokens.

Most of the transitions can therefore be inherited without change, thus emphasising the power of

polymorphism in conjunction with object-orientation.

Class RTable : DTable –– table of Russian philosophers

RPhil* thinking = [[id:1, k:10],[id:2, k:20],[id:3, k:30],

[id:4, k:40],[id:5, k:50]];

RretRight rr = [hasRight:hasRight, thinking:thinking, fork:fork];

End RTable

Fig 7.7 LOOPN++ program for the Russian philosophers problem

7.4 Super places

As already noted in §3.9 LOOPN++ does not supply specific syntax for places. In the simplest

case, a place is simply declared using a multiset type, of the form type*. In order to define more

complex objects with place properties – here called super places – it is necessary to declare a

class which inherits from a multiset type. The multiset type serves to define the type of tokens

which can be passed to and from the place.

Using inheritance, it is possible in this way to define an enhanced place type which has the

features of a place type augmented by additional fields and functions. For example, fig 7.8

shows the definition of a super place called super1 which can hold integer tokens and which

supplies a function to return the number of tokens in the place.

class super1 : int_class*

export size;

boolean size() = count x -- self; end count

end super1

Fig 7.8 A super place class with a function to count the number of tokens

For more sophisticated use of super places, it is necessary to be able to modify the way the

35

object accepts and offers tokens to its environment. In this case, it is necessary to understand

Page 38: Charles LaKoS loop n

that multiset types are implemented as classes with pseudo transitions get, see, put, as shown in

fig 7.9. It is important to observe that this implies that a place accepts or offers one token at a

time, since the above pseudo transitions accept and offer one token at a time. Of course, a step

involving multiple activations of the above pseudo transitions can result in multiple tokens being

accepted or offered simultaneously by a place, but then these tokens are handled independently.

class T*

trans get

T token <- ... -- access the token to be returned

end get

trans see

T token -- ... -- access the token to be observed

end see

trans put

T token -> ... -- store the token

end put

end T*

Fig 7.9 Pseudo class definition for multiset types

In order to modify the behaviour of a place, it is necessary to override the above pseudo

transitions get, see and put. It is preferable to override them all in order to avoid inconsistent

behaviour. For example, fig 7.10 shows the definition of a capacity place, which will accept at

most limit tokens holding integer values.

class capacityplace : int_class*

export limit;

integer limit = 5; -- maximum number of tokens

int_class* buffer; -- place to hold the tokens

integer size() = count x -- buffer; end count

trans put : put

int_class y -> buffer | y = token; -- store a copy of the token in the buffer

integer d = 0 | size() < limit; -- check that the limit is not exceeded

end put

trans get : get

int_class token <- buffer; -- fetch the token from the buffer

end get

end capacityplace

Fig 7.10 Capacity place

It is important to observe that the transitions get and put (of fig 7.10) inherit from the pseudo

transitions of the same name of fig 7.9. Each of these require that the action which sets the token

token is the first action in the transition. (This may be more flexible when LOOPN++ has full

support for multiple inheritance.)

Another example of a super place is that of a place where tokens are delayed for a specified

period of time, as shown in fig 7.11.

36

Page 39: Charles LaKoS loop n

class delay_int_place : int_class*

export dly;

real dly = 0;

int_class* buffer;

trans put : put

int_class y -> buffer | y = token;

end put

trans get : get

int_class token <- buffer | token.delay(token.i);

end get

end delay_int_place

Fig 7.11 Delayed place

7.5 Example of the active philosophers

The solution to the dining philosophers (and the Russian philosophers) problem in §7.3 followed

the style of traditional petri net solutions. Thus, the actions for picking up forks are embedded in

the table (class DTable) rather than the more natural, object-oriented solution of embedding

them in the philosophers. Furthermore, it seems unnecessary for each philosopher to know the

number of philosophers at the table. In response to these criticisms, an alternative solution to the

dining philosophers problem is presented below.

To begin with, an extra class (OSetting) is introduced with the table setting (fig 7.12). This class

determines how many forks are available, as well as the identities of the left and right forks for

each philosopher. The table setting is defined as a super place which is prepared to offer and

accept the integer-valued forks. (Note that the table setting is, as we would expect, the same

whether we are dealing with the simpler dining philosophers, or the more complex Russian

philosophers.) Each dining philosopher is initialised with access to the (one) table setting.

Class OSetting : int_class*

export left, right, forks;

integer n = 5;

integer left(integer id) = id;

integer right(integer id) = id mod n + 1;

int_class* forks = [[i:1], [i:2], [i:3], [i:4], [i:5]];

trans get : get

int_class token <- forks;

int_class q -> self | q = token;

end get

trans put : put

int_class q -> forks | q = token;

end put

End OSetting

37

Fig 7.12 Table setting for the dining philosophers

Page 40: Charles LaKoS loop n

Now, each dining philosopher (class OPhil in fig 7.13) has places for the various possible states

and distinguishes between the states of the philosopher by the presence of a single colourless

token in the relevant place. Otherwise, the transitions of class OPhil are like those of class

DTable (in fig 7.4).

Class OPhil

export id, setting, hungry;

integer id;

OSetting setting;

null* thinking = [[]];

null* hasLeft, hasRight, eating;

boolean hungry() = true;

Trans getLeft

null x <- thinking | hungry();

int_class f <- setting | f.i = setting.left(id);

null y -> hasLeft;

printf("*** %d picks up left \t(%d %7.2f)\n", id,

meminuse(), timeused());

End getLeft

Trans getRight

null x <- hasLeft;

int_class f <- setting | f.i = setting.right(id);

null y -> eating;

printf("*** %d picks up right \t(%d %7.2f)\n", id,

meminuse(), timeused());

End getRight

...

End OPhil

Fig 7.13 Active philosopher, with encapsulated activity

Finally, the class OTable (in fig 7.14) allocates the table setting and makes it available to each

philosopher instance. All actions are now embedded within the philosopher instances.

Class OTable

OSetting setting;

OPhil* phils = [[id:1,setting:setting],

[id:2,setting:setting],

[id:3,setting:setting],

[id:4,setting:setting],

[id:5,setting:setting]];

End OTable

Fig 7.14 Table of active philosophers

It is possible to extend the above object-oriented solution to a solution to the Russian philosophers

problem. The above example demonstrates an important aspect of LOOPN++ – it gives the

modeller the choice as to the level at which to include the actions of the net.

38

Page 41: Charles LaKoS loop n

7.6 Example of Electronic Data Interchange

Our second example is taken from the field of Electronic Data Interchange and is motivated by

the presentation in [25]. We model a system where there are a number of mail subscribers and a

number of documents. Each subscriber generates, receives, reads and replies to mail documents.

Each document contains a number of information fields which we assume is supplied by a class

definition called Field . A document may also be associated with a number of related documents,

which are determined by the information fields of the original. A document may expect a

standard reply, or in a more complex example, a bill of lading might have a paper trail including

a request for a letter of credit, a request for insurance cover, etc.

Note that this example has not yet been run with the current loopn++ implementation, though it

should indicate how a reasonably complex problem can be addressed.

We start by defining a basic document. As well as data fields indicating the kind of document,

the specification of sender and receiver and the information fields, a document encapsulates

functions to generate the standard replies, to determine which information fields (from an incoming

reply) are not already included in the document, and to determine the subdocuments of the

current document. The appropriate class definition is given in fig 7.15.

Class Doc -- Class for basic documents

Export code, sender, receiver, info, newinfo, hasreply, reply, subdocs;

DocCode code; -- code for kind of document

Address sender, receiver; -- specification of sender and receiver

Field* info; -- current information in document

Doc* reply() = …; -- generate standard reply to this document

Field* newinfo(Field* incoming) = …; -- information fields in incoming not

-- already present in the document

Doc* subdocs() = …; -- subdocuments of this document

-- from available information fields

End Doc

Fig 7.15 LOOPN++ class definitions for a basic document

Class Umbrella : Doc*, Doc -- Class for umbrella documents

Export ref, receive, post;

UmbrID ref; -- unique identification for the umbrella

Doc* forposting, posted, expected; -- status of related subdocuments

Null* regenerate = [[]]; -- non-empty to regenerate subdocuments

Trans gensubdocs -- regenerate the subdocs from the info fields

Null x <- regenerate;

Doc* y -> forposting | y = subdocs()-forposting-posted;

-- only add new subdocuments

End gensubdocs

Trans put : put -- receive a response with its information

Doc y <- expected | y.ref=token.ref; -- reply must be expected

Field* f -> info | f=newinfo(token.info);-- determine new information from reply

Null g -> regenerate; -- need to regenerate subdocuments

End receive

39

Page 42: Charles LaKoS loop n

Trans get : get -- post a subdocument

Doc token <- forposting; -- a subdocument to be posted

Doc z -> posted | z=token; -- record the subdocument as posted

Doc* r -> expected | r=x.reply(); -- determine the replies expected

End;

End Umbrella

Fig 7.16 Class definition for umbrella documents

Since an EDI document is associated with a number of other subdocuments, it is important to

maintain the status of the paper trail associated with a particular document. This is modelled in

the class of umbrella documents, given by the class Umbrella . This class is a subclass of Doc

(because it gives the status information associated with a particular document). It is also a

subclass of Doc* since it can accept and offer subdocuments to its environment. It includes

places holding the subdocuments which are still to be posted, those which have already been

posted, and the expected replies. The class includes transitions to generate the subdocuments

associated with the current document, to receive an expected reply, and to post subdocuments.

The class definition is given in fig 7.16.

Note that the Umbrella class uses a predefined class Null . As in LOOPN, this is the class used

for colourless tokens, and is indirectly inherited by all other classes. While containing no data

fields, it includes functions for determining the status of a token in a place.

Next, we consider the class Subscriber for a user of the EDI system (fig 7.17). A subscriber is

also a super place which can accept and offer documents to its environment.

Class Subscriber : Doc* -- Class for an EDI subscriber

Export receivemail, sendmail, readmail, replymail;

Address own; -- address of the subscriber

Doc* mbox; -- mail box of messages for this subscriber

Doc* tosend; -- messages to be sent by this subscriber

Trans put : put -- receive mail and add it to the mail box

Address a = token.receiver | a = own; -- the document must be addressed to me

Doc y -> mbox | y=token; -- save the document in the mail box

End put

Trans get : get -- generate a new document and send it

Doc token <- tosend; -- new document generated

End get

Trans readmail -- examine a document without replying

Doc x <- mbox | x.reply()=[]; -- no reply expected

End readmail

Trans replymail; -- examine a document and reply

Doc x <- mbox | x.reply()≠[]; -- the document has an associated reply

Doc y -> tosend | y=x.reply(); -- send the reply

End replymail

End Subscriber

Fig 7.17 Class definition for an EDI subscriber

40

Page 43: Charles LaKoS loop n

Finally, the EDI system consists of subscribers and documents and the communication between

the two. The management of the EDI system can be modelled in many different ways. In fig

7.18, we model the system as some global mail-clearing house.

The ability to encapsulate data and actions in the one class and to dynamically generate instances

of such a class is fundamental to the above solution. For example, the transition receive in class

System accesses an umbrella u and then extracts the document from u. This multilevel access

makes the above solution very natural but it is not supported by traditional petri net formalisms.

Class System -- Root class for the EDI system

Subscriber* subs; -- the EDI subscribers

Umbrella* umb; -- the umbrella documents

Trans send -- subscriber-generated EDI document

Subscriber s <- subs;

Doc d <- s.sendmail; -- document from the subscriber

Umbrella u -> umb | u=d; -- form the umbrella from the document

Subscriber t -> subs | t=s; -- don't discard the subscriber

End send

Trans receive -- subscriber accepts an EDI document

Subscriber s <- subs;

Umbrella u <- umb;

Doc x <- u.post; -- the umbrella supplies the subdocument

Doc y -> s.receivemail | y=x; -- document must be acceptable

Subscriber t -> subs | t=s; -- retain the subscriber

Umbrella v -> umb | v=u; -- retain the umbrella

End receive

Trans reply -- a subscriber replies to an EDI document

Subscriber s <- subs;

Umbrella u <- umb;

Doc x <- s.replymail; -- reply sent by the subscriber

Doc y -> u.receive | y=x; -- the umbrella must accept the reply

Subscriber t -> subs | t=s; -- retain the subscriber

Umbrella v -> umb | v=u; -- retain the umbrella

End reply

End System

Fig 7.18 Class definitions for subscribers and the EDI system

There is a pleasing simplicity about both of the above solutions. Solutions in a traditional petri

net formalism are far from obvious. It is therefore strongly suggested that, as with CP-nets, the

mapping from the more descriptive formalism into a simpler one should be provided by an

implementation and not forced on the user of the formalism. As already observed, the added

expressive comfort could be the difference between a practitioner being able to propose a

solution to a problem or considering it impractical.

8 Conclusions

In this paper, we have presented a textual language which supports a complete integration of

object-oriented structuring into the petri net formalism. Classes encapsulate data, functions and

41

actions, and as a result, we are no longer constrained to have the actions in the form of the net as

Page 44: Charles LaKoS loop n

a global control structure. The language makes it convenient to model and simulate complex

systems with multiple levels of activity such as found in multi-level simulations, object-oriented

operating systems, and prototypes of object-oriented designs. It is thus possible to model

systems which would have been difficult or impractical before, while retaining the benefits of

object-orientation such as clean interfaces, reusable software components, and extensible

component libraries.

The language has a formal semantics which makes it possible to transform OP-nets into the

simpler formalism of CP-nets [15]. As a result, the automated analysis techniques available for

CP-nets can be adapted for use with OP-nets.

There are a number of projects arising out of this work. An implementation of LOOPN++ is

currently available and is being refined. A second project is considering the appropriate graphical

conventions for drawing OP-nets and the corresponding modifications to the LOOPN screen

editor. A third project requiring a significant ongoing effort is the adapting of standard net

analysis techniques to suit OP-nets.

References[1] S. Christensen and J. Toksvig DesignBeta V2.0.1 – BETA Code Segments in CP-nets

Lecture Notes OO&CPN - nr 5, Computer Science Department, Aarhus University(1993).

[2] J. Colville Demonstration of Data Communication Protocols Technical Report85.8, School of Computer Sciences, New South Wales Institute of Technology (1985).

[3] H.J. Genrich Predicate/Transition Nets Advances in Petri Nets 1986 – Part 1, W.Brauer, W. Reisig, and G. Rozenberg (eds.), Lecture Notes in Computer Science 254,Springer-Verlag (1987).

[4] K.M.v. Hee, P.M.P. Rambags, and P.A.C. Verkoulen Specification and Simulationwith ExSpect Functional Programming, Concurrency, Simulation and AutomatedReasoning, Lecture Notes in Computer Science 693, Springer-Verlag (1993).

[5] K.M.v. Hee and P.A.C. Verkoulen Integration of a Data Model and High-LevelPetri Nets Proceedings of 12th International Conference on the Application andTheory of Petri Nets, Lecture Notes in Computer Science , Gjern, Denmark, Springer(1991).

[6] H.P. Hillion Timed Petri Nets and Application to Multi-Stage Production SystemsAdvances in Petri Nets 1989, G. Rozenberg (ed.), Lecture Notes in Computer Science424, pp 281–305, Springer-Verlag (1990).

[7] K. Jensen Coloured Petri Nets Advances in Petri Nets 1986 – Part 1, W. Brauer, W.Reisig, and G. Rozenberg (eds.), Lecture Notes in Computer Science 254, pp 248–299,Springer-Verlag (1987).

[8] K. Jensen Coloured Petri Nets: A High Level Language for System Design andAnalysis Advances in Petri Nets 1990, G. Rozenberg (ed.), Lecture Notes in ComputerScience 483, Springer-Verlag (1990).

[9] K. Jensen Coloured Petri Nets: Basic Concepts, Analysis Methods and PracticalUse – Volume 1: Basic Concepts EATCS Monographs in Computer Science, Vol.26, Springer-Verlag (1992).

[10] K. Jensen Coloured Petri Nets: Basic Concepts, Analysis Methods and PracticalUse – Volume 2: Analysis Methods EATCS Monographs on Theoretical ComputerScience, Springer-Verlag (1994).

42

[11] K. Jensen, S. Christensen, P. Huber, and M. Holla Design/CPN™: A Reference

Page 45: Charles LaKoS loop n

Manual MetaSoftware Corporation (1992).

[12] C.D. Keen and C.A. Lakos A Methodology for the Construction of SimulationModels Using Object-oriented Petri Nets Proceedings of SCS Multiconference onSimulation Methodology and Practice – Modelling and Simulation, pp 267-271,Lyon, France (1993).

[13] B.B. Kristensen, O.L. Madsen, B. Møller-Pedersen, and K. Nygaard Object OrientedProgramming in the BETA Programming Language (1991).

[14] C. Lakos and C. Keen LOOPN++: A New Language for Object-Oriented Petri NetsProceedings of Modelling and Simulation (European Simulation Multiconference),pp 369-374, Barcelona, Society for Computer Simulation (1994).

[15] C.A. Lakos Object Petri Nets – Definition and Relationship to Coloured NetsTechnical Report TR94-3, Computer Science Department, University of Tasmania(1994).

[16] C.A. Lakos and C.D. Keen Modelling Layered Protocols in LOOPN Proceedings ofFourth International Workshop on Petri Nets and Performance Models, Melbourne,Australia (1991).

[17] C.A. Lakos and C.D. Keen Modelling a Door Controller Protocol in LOOPNProceedings of 10th European Conference on the Technology of Object-orientedLanguages and Systems, Versailles, Prentice-Hall (1993).

[18] A. Martin and B. Santanach Introducing Object-Oriented Concepts into the Frameworkof Coloured Petri Nets Thesis , Institut D'Informatique D'Entreprise, Evry (1993).

[19] B. Meyer Object-Oriented Software Construction Prentice Hall (1988).

[20] W. Reisig Petri nets : An Introduction EATCS Monographs on Theoretical ComputerScience, Vol. 4, Springer-Verlag (1985).

[21] W. Reisig Petri Nets in Software Engineering Advances in Petri Nets 1986 – Part 2,W. Brauer, W. Reisig, and G. Rozenberg (eds.)255, pp 63–96, Springer-Verlag (1987).

[22] W. Reisig A Primer in Petri Net Design Springer-Verlag (1992).

[23] S. Shlaer and S.J. Mellor Object Lifecycles – Modeling the World in States YourdonPress, Prentice Hall (1992).

[24] B. Stroustrup The C++ Programming Language (Second Edition) Addison-Wesley(1991).

[25] P.A. Swatman, P.M.C. Swatman, and R. Duke Electronic Data Interchange: AHigh-level Formal Specification in Object-Z Proceedings of 6th Australian SoftwareEngineering Conference, pp 341-354, Sydney, Australia, Springer-Verlag (1991).

[26] A.S. Tannenbaum (ed.) Computer Networks Second ed. Prentice-Hall, EnglewoodCliffs (1989).

[27] P.S. Thiagarajan Elementary Net Systems Advances in Petri Nets 1986, Part I, W.Brauer, W. Reisig, and G. Rozenberg (eds.), Lecture Notes in Computer Science 254,pp 26–59, Springer-Verlag (1987).

[28] A. Valmari Stubborn Sets for Coloured Petri Nets Proceedings of 12th InternationalConference on the Application and Theory of Petri Nets, Aarhus (1991).

[29] P.A.C. Verkoulen Integrated Information Systems Design: An Approach Based onObject-Oriented Concepts and Petri Nets PhD Thesis, Technical University of

43

Eindhoven, the Netherlands (1993).

Page 46: Charles LaKoS loop n

44

Page 47: Charles LaKoS loop n

R96-1

LOOPN++ User Manual

CA LakosNetworking Research Group

January 1996

Department of Computer ScienceUniversity of TasmaniaGPO Box 252CHobart Tasmania 7001

Page 48: Charles LaKoS loop n

TR96-1

LOOPN++ User Manual

CA Lakos

Networking Research Group

[email protected]

January 1996

Abstract

This technical report describes a language and simulator for specifying systems in terms of

Object Petri Nets. These petri nets are a variant of Coloured Petri Nets with the inclusion of

simulated time and a complete integration of object-oriented features. These features serve to

break away from the traditional approach of viewing the data as essentially passive and the petri

net as a global control structure. These features also encourage the convenient modularisation of

complex specifications and allow for the possibility of model reuse.

Kewords and phrases

Object-oriented languages, Object Petri Nets, Petri Net models, Concurrent systems,

Simulation

CR categories

B.4.4, F.1.1