• No results found

A Verified Implementation of Priority Monitors in Java

N/A
N/A
Protected

Academic year: 2021

Share "A Verified Implementation of Priority Monitors in Java"

Copied!
36
0
0

Loading.... (view fulltext now)

Full text

(1)

A Verified Implementation of Priority Monitors

in Java

´

Angel Herranz and Julio Mari˜no∗ Babel research group Universidad Polit´ecnica de Madrid

Abstract. Java monitors as implemented in thejava.util.concurrent. lockspackage provideno-priority nonblockingmonitors. That is, threads signalled after blocking on aconditionqueue do not proceed immediately, but they have to wait until both the signalling thread and possibly some of the others which have requested the lock release it. This can be a source of errors (if threads that get in the middle leave the monitor in a state incompatible with the signalled thread re-entry) or inefficiency (if repeated evaluation of preconditions is used to ensure safe re-entry). A concise implementation of priority nonblocking monitors in Java is pre-sented. Curiously, our monitors are implemented on top of the standard no-priority implementation. In order to verify the correctness of our so-lution, a formal transition model (that includes a formalisation of Java locks and conditions) has been defined and checked using Uppaal. This model has been adapted to PlusCal in order to obtain a formal proof in TLA independent of the number of threads.

Keywords:Monitors, Java, model checking, priority, nonblocking, TLA, PlusCal.

1

Introduction

A model-driven approach to the development of concurrent software [7] advo-cates the use of high-level, language-independent entities that can be subject to formal analysis (e.g., to early detect risks due to concurrency in reactive, critical systems) and that are later translated into a concrete programming language by means of safe transformations. This, rather than the unrestricted use of the concurrency mechanisms in the target language, aims at improving portability (always a must in embedded systems) and preventing hazards due to the use of error-prone or poorly documented primitives. We have used this approach for teaching concurrency for more than fifteen years at our university, first us-ing Ada95 [3], and now Java, which is certainly lackus-ing in many aspects when compared to the former.

Authors partly funded by grants DESAFIOS10 (TIN2009-14599-C03-00) from the Spanish Ministry of Science and Innovation, PROMETIDOS (P2009/TIC-1465) from the Madrid Regional Government and COST Action IC0701 on Formal Verifi-cation of Object Oriented Software.

(2)

CADTCounter

ACTIONInc:C Type[io] ACTIONDec:C Type[io] SEMANTICS TYPE:C Type=Z INVARIANT:∀c ∈C Type.c≥0 INITIAL(c):c= 0 CPRE:True Inc(c) POST:cout=cin+ 1 CPRE:c>0 Dec(c) POST:cout=cin1 class Counter {

final Lock mutex =

new ReentrantLock (true);

final Condition strictlyPos = mutex . newCondition ();

private int c = 0;

public void inc () {

mutex . lock (); c ++;

// signal some pending dec

strictlyPos . signal (); mutex . unlock ();

}

public void dec () {

mutex . lock ();

// check the CPRE now

if (c == 0) {

try {strictlyPos . await ();}

catch( Exception e) {} }

// if we are here , that means // that c > 0

c−−;

// it CAN be proved that // no more signals are needed

mutex . unlock ();

} }

Fig. 1.A minimal example showing the risks of Java’s no-priority locks and conditions.

Indeed, the early mechanisms for thread synchronization provided by Java were so limited that one of the pioneers of concurrent programming, Brinch Hansen, wrote a famous essay [6] alerting on the risks of their usage. As a response to these adverse reactions, more powerful concurrency mechanisms were introduced in later versions of Java, although without being explicitly presented as realizations of theclassical ones.

In this work we will focus on the locks & conditions library (java.util. concurrent.locks), that can be seen as an attempt to implement monitors (actually, Brinch Hansen’s contribution) in Java. Figure 1 shows an (apparently correct) implementation of a shared resource, specified in a formal notation, using the aforementioned library. On the left side of the figure, a shared counter is specified as a kind of abstract data type with implicit mutually exclusive access and an interface that specifies the only operations (Inc andDec) that can be used to modify its state. Moreover, these actions are pure and transactional. In this case, we also state the invariant that the counter cannot be negative at any time, and specify the conditional synchronization (CPRE, concurrency precondition) for Dec, that ensures the invariant after the execution of any of the two actions.

(3)

To the right, we see an implementation of the resource specification as a Java class using locks and conditions. ClassCounterencapsulates an integer variable

cto represent the counter. Also, a lock objectmutexis used to ensure execution of the actions in mutual exclusion. Finally, a condition object strictlyPos is used to implement the conditional synchronization of threads invoking dec(), i.e., to put them to sleep when the action is invoked with the counter set to 0.

The code has been written following the principle that only 0 or 1await()

calls are executed during an action, and the same applies to signal(). This coding guideline really pays off when applied to concurrency problems of greater significance. Basically, it reduces the context switching points inside the code for an action to (at most) one (the execution of await()), dividing it into two sequential fragments, which facilitates formal reasoning. It is the responsibility of the signalling thread (in this case, any thread executingInc) to ensure that the signalled thread wakes up in a safe state. In this case, there is nothing to check, asstrictlyPos.signal()is placed right after incrementing the counter and thus, if the signalled thread resumes execution right after the signalling one executesmutex.unlock()it is safe to decrement the counter. That justifies the comment above the line containing “c−−;”.

Unfortunately, this code is unsafe. Why? The reason is that Java’s imple-mentation is an example ofno-priority signalling [2]. That means that threads signalled after blocking on a condition queue do not proceed immediately, but they have to wait until both the signalling thread and also other threads that might have requested the lock release it. In other words, when the decrementer thread resumes execution it could be the case that other decrementer thread was already queued onmutexand set the counter to 0 right before. Moreover, given that the execution of the inc() anddec() methods takes very little time, the probability that this scenario happens is relatively low, which makes this the kind of error that can go unnoticed for thousands of runs.

The official documentation for theConditionclass leaves considerable room for the operational interpretation of theawait()andsignal()methods in fu-ture implementations and, in fact, the no-priority behaviour is not really implied by the natural language specification of the API. There is, however, a generic recommendation to enclose the calls toawait()inside invariant-checking loops, motivated by the possibility ofspuriouswakeups. Although the use of such loops can be seen as a defensive coding technique that can avoid hazards in general, we think that forcing the application programmer to use them can be quite unsat-isfactory in many situations as testing the concurrency precondition repeatedly can increase contention of certain concurrent algorithms intolerably.

The use of the 0-1 coding scheme allows for low-contention, deterministic implementations of shared resources. Also, in [2] the reader can find a classifi-cation and comparison of many monitor variations, concluding that, in general, priority implementations are less error-prone. With this in mind, we decided to reuse as much from the existing reference implementation of condition objects and turn them into a concise, priority implementation of monitors. Our proposal is described in the following section.

(4)

1 package es . upm . babel . cclib ;

2 import java . util . concurrent . locks . Lock ;

3 import java . util . concurrent . locks . ReentrantLock ;

4 import java . util . concurrent . locks . Condition ;

5

6 public class Monitor {

7 private Lock mutex = new ReentrantLock (true);

8 private Condition purgatory = mutex . newCondition ();

9 private int inPurgatory = 0;

10 private int pendingSignals = 0;

11

12 public Monitor () {

13 }

14

15 public void enter () {

16 mutex . lock ();

17 if ( pendingSignals > 0 | | inPurgatory > 0) {

18 inPurgatory ++;

19 try { purgatory . await (); }

20 catch ( InterruptedException e) { }

21 inPurgatory−−;

22 }

23 }

24

25 public void leave () {

26 if ( pendingSignals == 0 && inPurgatory > 0) {

27 purgatory . signal ();

28 }

29 mutex . unlock ();

30 }

31

32 public Cond newCond () { 33 return new Cond ();

34 }

inner classCondgoes here. 65 }

Fig. 2.Java source for priority monitors (classMonitor).

2

Implementing Priority Monitors with Java’s

No-priority Monitors

Figures 2 and 3 show a stripped-down version of the source code forMonitor.java, our implementation of priority monitors. Due to space limitations, comments and exception managers have been omitted. A full version, including comments and a couple of extra operations can be found at http://babel.ls.fi.upm.es/software/cclib.

In addition to theMonitorclass,Monitor.javadefines theCondclass. Both classes are intended to be a replacement for the original Lock and Condition

(5)

37 public class Cond {

38 private Condition condition ;

39 private int waiting ;

40 private Cond () {

41 condition = mutex . newCondition ();

42 waiting = 0;

43 }

44

45 public void await () {

46 waiting ++;

47 if ( pendingSignals == 0 && inPurgatory > 0 ) {

48 purgatory . signal ();

49 }

50 try { condition . await (); }

51 catch ( InterruptedException e) { }

52 pendingSignals−−;

53 }

54

55 public void signal () {

56 if ( waiting > 0) { 57 pendingSignals ++; 58 waiting−−; 59 condition . signal (); 60 } 61 } 62 }

Fig. 3.Java source for priority condition variables (classCond).

shown in Figure 2. However, this separation is a mere convenience for displaying the code: the implementation of classCondrequires access to the state variables in class Monitor.

The functionality provided by the class is similar to that of locks and con-ditions. Methodenter() provides exclusive access to the monitor, like method

lock()in classLock. If one thread got exclusive access to a monitor objectm,

susbsequent calls tom.enter()from other threads will force these to wait inm’s queue. The monitor is released by invokingm.leave()(analogous tounlock()). If there are threads waiting at m’s entry queue, executingm.leave()will give control to the first thread in the queue.

Condition queues associated withm are created by invoking m.newCond()

rather than calling the constructor of classCond. This is similar to the behaviour ofnewCondition()in classLock. Instancesc,c0. . . of classCondassociated with a given monitor object m are managed like condition variables. A thread that invokesc.await()afterm.enter()is blocked unconditionally and put to wait in a queue associated withc. Also, executingc.await()releasesm, so that other threads can get exclusive access tomby executingm.enter(). As in the case of

leave(), if there are threads waiting atm’s entry queue, executingm.await()

(6)

A thread that executesc.signal()after getting exclusive access to m will continue executing (signal-and-continue policy) but it is guaranteed that if there is a thread waiting on c, it will be awakened before any process waiting on m’s entry queue. This is where the behavior differs from that of signals in the standard locks and conditions package, where signalled threads are requeued at the tail of the lock’s entry queue.

The implementation is done on top of the existing implementation of locks and conditions, without reimplementing their functionality say, by using low-level concurrency mechanisms such as semaphores, nor accessing the Java run-time in order to “magically move” threads from one queue to another.

The technique used to simulate the effect of moving signalled threads from the head of the condition queue to the head (rather than the tail) of the moni-tor’s entry queue is toflush the contents of the latter until the signalled thread becomes the first. This is achieved by letting some threads get in the monitor, but only to make them await in a special condition queue devised for that pur-pose, and which we call “the purgatory”. Of course, these threads will have to be eventually awakened and allowed to get access to the monitor in the right sequence. As they have been moved to a condition variable, signalling them will bring them back to the lock’s entry queue, so a little care is needed to ensure that the whole system progresses appropriately. Two global counters are responsible for this.

VariableinPurgatorycounts the number of threads that have been sent to

thepurgatorycondition queue and have not been given access to the monitor

yet – even if they have already been signalled. VariablependingSignalscounts how many threads that have been signalled (in regular condition variables, not

purgatory) are yet waiting for re-entry.

Method enter() starts executing lock() on the monitor’s internal lock object, called mutex (line 16). However, if pendingSignals is not zero, this thread should give way to some signalled thread that was waiting for monitor re-entry later in the queue associated with mutex, and thus has to execute a

purgatory.await(), updating counterinPurgatorybefore and after (lines 17–

22). The same is done if other threads have been sent to the purgatory earlier and have not re-entered yet (inPurgatory > 0).

The implementation of method leave() is quite symmetric. It ends by in-vokingmutex.unlock()(line 29), but before, a chance to re-gain access to the monitor must be given to threads moved to the purgatory (line 27), only if there are no signalled threads queued for monitor re-entry (line 26).

MethodnewCondis implemented just by invoking the standard constructor of class Cond(line 33). Association to a given monitor object is just implicit in the visibility of the monitor’s state variables that theCond object has. A Cond

object is basically a pair of a Condition object associated with mutex and a counterwaitingthat keeps track of the number of threads waiting on it (lines 38–43).

The core of methodawait()is the corresponding call on its condition object (line 50) but, before, counter waitingis incremented (line 46) and the right to

(7)

execute inside the monitor is given to threads in the purgatory only if there are no pending signals (lines 47–49). Finally, if the thread that has invokedawait()

reaches line 52, the whole signalling procedure has been successful including monitor re-entry, sopendingSignalsmust be decremented accordingly.

Finally, method signal() checks whether the queue size is nonzero (line 56) and if so incrementspendingSignals (line 57), as monitor re-entry for the signalled thread will be incomplete, decrementswaiting(line 58) and executes

signal() on the condition object (line 59). Executingsignal() on an empty

queue has no effect.

Although the implementation of theMonitorandCond classes is quite con-cise, the interplay between the different threads and synchronization barriers, governed by the different counters is, admittedly, complex and hard to follow. After testing the class on a number of applications specifically devised to force errors related to incoming threads getting in the way of signalled ones, we still felt that a more convincing proof of the correctness of our implementation was necessary.

3

A Formal Model of Java Locks and Conditions

As a first step towards a formal argumentation of the correctness of our imple-mentation of priority monitors, we decided to define a transition model for the underlying implementation of locks and condition variables.

The model has been defined as a network of (parametric) automata in Up-paal [1]: one automaton for modelling the concurrency mechanism, i.e., the lock object and its associated conditions, and one automaton per thread that makes use of it. While we give a complete formalization of the concurrency mechanism, unrelated thread code is abstracted away by just showing the interaction with the lock and the conditions.

Each thread has its own thread identifier,pid, and so do condition variables (cid). All interaction between the different automata takes place via communi-cation channels:

– lock [pid]is the channel the threadpid uses when it needs to lock the lock.

– await[cid][pid] is the channel the thread pid uses when it needs to wait in conditioncid.

– signal [cid][pid]is the channel a thread uses to signal conditioncid.

– lock granted[pid]is the channel the concurrency mechanism uses to grant a lock to the threadpid.

– lock [pid]is the channel the threadpid uses to unlock the lock.

Let’s start with the abstract model for the threads, since, in our view, it is the most didactic way to present the whole model.

3.1 Modelling the Threads

Each thread has its own thread identifier,pid, and the model of a thread is the automaton (actually an Uppaal template parametrised by thread identifiers) shown in Figure 4.

(8)

Awaiting HasTheLock Locking Out cid : cid_t signal[cid]! lock_granted[pid]? cid : cid_t await[cid][pid]! unlock[pid]! lock_granted[pid]? lock[pid]!

Fig. 4.State model for a Java thread invoking operations on locks.

The automaton has four locations that represent the state of a thread with respect to a lock:

– Outis the location that represents that the thread neither has got the lock nor is waiting in a condition. It is an abstract representation of any program point of any arbitrary threadoutside of the monitor.

– HasTheLock is the location that represents that the thread has the lock. It is another abstract representation, this time, of any program point of any arbitrary threadin the monitor.

– Lockingis the location that represents that the thread is blocked while waiting for the lock to be granted to it. From the monitor perspective, it is trying to enter the monitor.

– Awaitingis the location that represents that the thread is blocked in a condi-tion. From the monitor perspective, it is waiting for a signal that allows it to re-enter the monitor.

Edges are extremely simple. The idea behind this simplicity is trying to con-taminate the thread models as little as possible. They do not involve access to variables, neither global nor local so adapting the model to any particular thread is immediate. Let us see the intended meaning of the actions at the edges:

– Out−Lockingwith actionlock [pid]!, a send-action that indicates that the thread needs the lock. As we will see, the co-action (lock [pid]?) is continuously en-abled in the model of the mechanism.

– Locking−HasTheLockwith actionlock granted[pid]?, a receive-action that indicates the thread is allowed to get the lock. The co-action will occur when the thread pid has thetop priority to lock the monitor.

– HasTheLock−Awaiting with action await[cid][pid]!, a send-action that indicates that the thread waits in the condition variablecid.1

– Awaiting−HasTheLock with action lock granted[pid]?, a receive-action that indi-cates the thread is allowed to re-take the lock (re-enter the monitor). 1 In Uppaal, expressions such as cid : cid t non-deterministically bind the identifier

(9)

Locked

Unlocked

cid : cid_t

signal[cid]?

requeue(cq[cid],lq)

cid : cid_t, pid : pid_t

await[cid][pid]? push(cq[cid],pid) pid : pid_t unlock[pid]? pid : pid_t lock[pid]? push(lq,pid) pid : pid_t lock[pid]? push(lq,pid) pid : pid_t

size(lq) > 0 & first(lq) == pid

lock_granted[pid]!

pop(lq)

Fig. 5.State model for a Java lock.

– HasTheLock−Out with actionunlock[pid]!, a send-action that indicates that the thread releases the lock (leave the monitor).

– HasTheLock−HasTheLock with action signal [cid]!, a send-action that indicates that the thread signals a thread on the condition variablecid.

3.2 Modelling Java Locks and Conditions

The model of a lock object and its associated condition variables is shown in Figure 5. The main task of the automaton is to keep track of threads bylistening for their actions (onlock,awaitandsignalchannels) and responding appropriately (on channellock granted).

To keep track of the threads, the automaton defines two queues:

– A bounded queue of thread identifiers, lq (lock queue), that represent the queue for getting the lock.

– A bounded queue per condition cid of thread identifiers, cq[cid] (condition queue of cid), that represent the condition variable queues.

The automaton has two locations, Locked and Unlocked, that represent that the lock is locked by a thread or unlocked, respectively.

Most logic is encoded in the edges. To explain this logic we will explore the actions that fire them. We have to take into account that edges areindexed by thread identifiers (pid) and condition variable identifiers (cid).

– Edges with the receive-actionlock [pid]?do not change locations, are always enabled and their assignments just push the thread identifier indexpid in the access queuelq.

– The edgeLocked−Unlocked with the receive-actionawait[cid][pid]?is always en-abled and the assignment just pushes the thread identifier indexpid in the condition variable queuecq[cid].

(10)

Locked Unlocked cid : cid_t signal[cid]? remember_signal(cq[cid]), requeue(cq[cid],lq)

cid : cid_t, pid : pid_t

await[cid][pid]? push(cq[cid],pid), just_entered := false, reset_signal() pid : pid_t unlock[pid]? just_entered := false, reset_signal() pid : pid_t lock[pid]? push(lq,pid) pid : pid_t lock[pid]? push(lq,pid), just_entered := false pid : pid_t

size(lq) > 0 & first(lq) == pid

lock_granted[pid]!

locking_pid := pop(lq), just_entered := true

Fig. 6.An instrumented version of the Lock automaton.

– The edge Locked−Lockedwith the receive-action signal [cid][pid]? is always en-abled and the assignment just moves the first thread identifier index pid from the condition queuecq[cid]to the access queue lq.

– The edge Unlocked−Locked with index pid is just enabled when the access queuelq is not empty and its first thread identifier ispid. The send-action of this edge islock granted[pid]!, granting the access to the thread with identifier pid.

– The edgeLocked−Unlockedwith the receive-actionunlock[pid]?is always enabled and does not modify any queue.

The full code for the declarations and auxiliary method definitions in this model can be found under http://babel.ls.fi.upm.es/software/cclib.

The natural step after defining this model is to check some properties on it. The first one is the correctness property: mutual exclusion. Encoding mutual exclusion in the Uppaal subset of CTL, the Computational Tree Logic [5], is easy:

A2(∀(pid1 : pid t) (

∀(pid2 : pid t ) (

(Thread(pid1).HasTheLock∧Thread(pid2).HasTheLock)⇒pid1=pid2))) That is, whether for all paths (A path quantifier), invariantly (2 modality), if two threads got the lock it is because they are the same thread. The tool finds the property correct for models of various sizes.

3.3 Instrumenting the Model

Nevertheless, for us, the most relevant property (correctness aside) is whether signalled threads resume their execution inside the monitor immediately after the signalling thread leaves. We will call this the characteristic property. As a

(11)

sanity check, we woud like to check that this property does not hold for the model just presented.

However, stating this property in the query language supported by Uppaal is not possible. Basically, the query language is a subset of CTL that only allows temporal modalities as the topmost symbol of formulae, but a query representing the characteristic property for our model would require to nest modal operators. A workaround for this limitation consists in instrumenting the model so that part of its recent history is somehowremembered. This way, we can encode certain temporal properties as state predicates, thus reducing the number of temporal operators required to express the characteristic property.

Again, we avoid contaminating the thread model and Figure 6 shows the resulting lock automaton (changes bold-faced). Basically, the system has new global variables to store:

– whether thenth thread that got access to the lock did execute an effective signal on some condition, and

– the identifier for the thread that received the signal (if any).

The first is done thanks to a new variable just signalled, and the second with variable thread just awakened. Both of them are set by operation remember signal while operationreset signalresets just signalled once the thread leaving the monitor coincides with thread just awakened. Also, the automata for threads are slightly constrained so that at most one signal is allowed per lock.

With these changes, the characteristic property is violated iff the (n+ 1)th thread in gaining access to the lock is different from the thread signalled by thenth thread. In order to avoid having generation counters in the model, we have added yet another boolean variable just entered to represent that the last transition to take place in the lock automaton is precisely the one that follows a lock grantedmessage. Violation of the characteristic property can then be encoded in the Uppaal query

E3(just entered∧just signalled∧locking pid6=thread just awakened).

That is, whether there is some path (Epath quantifier) that eventually leads (3 modality) to a state in which the aforementioned proposition holds. The tool finds an example for 3 threads almost immediately, as expected.

4

Verifying our Implementation

Since our implementation of priority monitors is based on the existing lock and conditions model:

– The lockmutex is represented by the lock model in Figure 5.

– The conditionpurgatory is represented by the condition identifierpurgacid (a symbolic name for referring to condition identifier 0).

– IntegersinPurgatory and pendingSignalsare represented by int Uppaal variables with the same name (inPurgatory andpendingSignals).

(12)

Actual_Signal Reentering Actual_Await Leave_End Leave_Begin In3 Signal_End Signal_Begin In2 Await_End Await_Begin ToHeaven Check_Priority Locking In1 Enter_End Enter_Begin Out unlock[pid]! pendingSignals > 0 || inPurgatory == 0 pendingSignals == 0 && inPurgatory > 0 signal[purgacid]! signal[thecid]! waiting[thecid] > 0 pendingSignals++, waiting[thecid]--waiting[thecid] == 0 cid : cid_t cid != purgacid thecid := cid lock_granted[pid]? await[thecid][pid]! pendingSignals == 0 && inPurgatory > 0 signal[purgacid]! pendingSignals > 0 || inPurgatory == 0 pendingSignals--cid : pendingSignals--cid_t cid != purgacid thecid := cid, waiting[thecid]++ pendingSignals == 0 && inPurgatory == 0 lock_granted[pid]? inPurgatory--await[purgacid][pid]! pendingSignals >0 || inPurgatory > 0 inPurgatory++ lock_granted[pid]? lock[pid]!

Fig. 7.State model for a thread using theMonitorclass operations.

– The implementation of the methodsenter,leave,await, andsignalhave beeninlined in the thread model (Figure 7).

The automaton in Figure 7 models a very general thread that enters the monitor, then executes several await calls (possibly 0), then executes one, at the most,signalcall, and finally leaves the monitor. To make the thread model easier to follow, most of the locations have been named according to fragments of the source code in Monitor.java. For example, execution of method enter()

(13)

2 3 4 5 6 1: 0.01 0.02 0.18 6.38 274.03 2: 0.01 0.03 1.14 87.62 3: 0.01 0.06 3.90 4: 0.01 0.10 9.78

locks & conditions

2 3 4 5 6 1: 0.00 0.03 0.50 17.06 442.26 2: 0.01 0.08 3.35 214.99 3: 0.02 0.18 12.06 4: 0.02 0.39 35.70 priority monitors Table 1.Times spent in checking the mutual exclusion property.

3 4 5 6

1: 0.00 0.02 0.08 0.40 2: 0.01 0.04 0.16 0.82 3: 0.01 0.08 0.32 1.48 4: 0.03 0.11 0.55 2.40 locks & conditions (property violated) 3 4 5 6 1: 0.03 0.36 11.18 450.73 2: 0.07 2.43 152.99 3: 0.16 9.42 4: 0.30 24.95 priority monitors Table 2.Times spent in checking the characteristic property.

possibility of a short excursion to the purgatory. The same scheme has been used to represent all the methods.

4.1 Experimental Results

The experimental results shown in this section have run on the instrumented models of the Java lock and conditions and of our priority monitor implementa-tion. The model of the threads are those presented in previous sections except for the elimination of the main loop2 (otherwise the state explosion makes the

tool useless).

We have checked the correctness property for both models (lock and condi-tions and monitor) with the expected result: the models satisfy mutual exclu-sion. Table 1 shows execution times (in seconds) given different numbers of client threads (indicated at the top) and condition queues (indicated on the left).3

The state model for priority monitors has been instrumented following the ideas in Sec. 3.3. Table 2 shows execution times for checking the characteristic property for both instrumented models.

In addition to the characteristic property, we have also checked the property that threads sent to the purgatory eventually enter the monitor, as an imple-mentation that would keep them blocked forever would satisfy the characteristic property trivially. The results are shown in Table 3.

2 Represented by edgeLeave End-Outin Figure 7. 3

Figures obtained on a Dual-Core AMD Opteron(tm) Processor 2218 @ 2.6GHz, RAM 8GB, running the Academic version of Uppaal 4.1.7 (rev. 4934) running a Debian distro with kernel Linux 2.6.39-bpo.2-amd64 SMP.

(14)

2 3 4 5 1: 0.01 0.02 0.42 14.04 2: 0.01 0.07 2.53 169.37 3: 0.01 0.15 9.49 4: 0.01 0.30 27.52 5: 0.02 0.58 64.18

Table 3.Times spent in checking that the stay in the purgatory is transitory.

5

Towards a Mathematical Proof

In order to prove our implementation of monitors correct for an unbounded number of threads we have encoded it in TLA, the temporal logic of actions [8], a logic for specifying and reasoning about concurrent systems. This has been done in two steps:

– A straightforward, manual, transformation of the Java code toPlusCal [10], an algorithm description language specially suited for concurrency.

– An automatic transformation of the PlusCal descriptions into TLA+ [9], a complete specification language based on TLA, using The TLA Toolbox [12], which allow both model-checking finite systems4 but also symbolic proof.

The PlusCal code is shown in figures 8–10. Figure 8 contains the specification of the original locks and conditions library as a set of four procedures which can be invoked by concurrent processes. Analogously to the Uppaal model, locks and conditions are modelled as FIFO queues (“lq” and “cq[cid]”). The await

statements (used at labelsjava lock blocked andjava await blocked) ensure the synchronization behaviour. Labels in PlusCal specifications are not only an aid to the reader, but also help structuring the resulting TLA spec, and will be used for reasoning about the system behaviour.

The direct translation of our monitors is shown in Figure 9. The only relevant change is that part of the code (see labels cclib leave resetlastsignal and cclib signal setlastsignal) manages an extra variable (lastSignal) used to ease stating and proving the characteristic property, similarly to the instrumentation in the Uppaal model. VariablelastSignalis a tuple with two process identifiers. Initially, and when no signal is in progress, lastSignal =h0,0i (0 is a non-valid process identifier). Otherwise, it records the pids of the signaler and signaled processes. Finally, a system of several processes to reason about the behaviour of our algorithm is defined in Figure 10. These clients enter the monitor (proc enter), execute 0 or 1 calls to await (proc await), execute 0 or 1 signal (proc signal), and then leave the monitor (proc leave).

Theorem 1 (Characteristic property). When a process performs a signal

on a nonempty condition (label cclib signal setlastsignal in Figure 9), the next 4 All theorems in this section have been checked with TLC, the TLA+

(15)

procedurejava lock(){ java lock begin: lq := Append(lq,self); java lock blocked: awaitself = Head(lq); java lock end: return;

}

procedurejava unlock(){ java unlock begin: lq := Tail(lq);

java unlock end: return; }

procedurejava await(wcid){ java await begin: cq[wcid] := Append(cq[wcid],self); java await unlocking: lq := Tail(lq);

java await blocked: awaitLen(lq)>0∧self = Head(lq); java await end: return;

}

procedurejava signal(scid){ java signal begin: if (Len(cq[scid])>0){

java signal requeue: lq := Append(lq,Head(cq[scid])); cq[scid] := Tail(cq[scid]); };

java signal end: return; }

Fig. 8.PlusCal specification of Java locks and conditions.

process to enter the monitor (label proc in in Figure 10) is the signaled one, and not an “opportunist” process:

∀pid ∈ProcSet :pc[pid] =“proc in”⇒lastSignal[2]∈ {0,pid}

Theorem 2 (Purgatory is transitory). Every process sent to the purgatory

(cq[0]) eventually enters the monitor (label proc in in Figure 10):

∀pid ∈ProcSet : (InPurgatory(pid);pc[pid] =“proc in”)

(The temporal formulaφ;ψmeans that every state satisfyingφwill eventually progress to another in whichψ holds.)

Theorem 3 (Enter call order is preserved). No process (pid2) entering the

monitor (label java lock blocked in Figure 8) can overtake5 any process (pid1) sent to the purgatory:

∀pid1,pid2∈ProcSet:(Member(cq[0],pid1)∧pc[pid2] =“java lock blocked”)

;(∧Member(order,pid1)∧Member(order,pid2)

∧Index(order,pid1)<Index(order,pid2)) The proof of the first theorem is lengthy due to the number of cases, but relatively straightforward, as is an invariant maintained by all transitions, and

5

(16)

procedurecclib enter(){ cclib enter begin: calljava lock();

cclib enter check priority: if (pendingSignals>0∨inPurgatory>0){ inPurgatory := inPurgatory + 1;

calljava await(0);

cclib enter toheaven: inPurgatory := inPurgatory - 1; };

cclib enter end: return; }

procedurecclib leave(){

cclib leave begin: if (pendingSignals = 0∧inPurgatory>0){ cclib leave saveit: calljava signal(0);

};

cclib leave resetlastsignal: if (lastSignal[1]6= self){lastSignal :=h0,0i;}; cclib leave unlocking: calljava unlock();

cclib leave end: return; }

procedurecclib await(cclwcid){ cclib await begin: waiting[cclwcid] := waiting[cclwcid] + 1;

if (pendingSignals = 0∧inPurgatory>0){ cclib await saveit calljava signal(0);

};

cclib await actualawait: calljava await(cclwcid);

cclib await reentering: pendingSignals := pendingSignals - 1; cclib await end: return;

}

procedurecclib signal(cclscid){ cclib signal begin: if (waiting[cclscid]>0){

cclib signal actualsignal: pendingSignals := pendingSignals + 1; waiting[cclscid] := waiting[cclscid] - 1; cclib signal setlastsignal: lastSignal :=hself, Head(cq[cclscid])i; cclib signal actualsignal: calljava signal(cclscid);

}; cclib signal end: return;

}

Fig. 9.Priority monitors encoded in PlusCal.

independent of the number of processes. The second property is proved by in-duction on the number of awaiting processes. The third property is not strictly necessary for the safety requirements of the Monitor class, but is nice anyway. The full TLA theory and an appendix detailing the formal proof (done by hand) of the first two theorems can be found with the rest of the cclib software, at http://babel.ls.fi.upm.es/software/cclib/doc.

(17)

process(procid ∈ ProcId){ proc enter: callcclib enter();

proc in: order := Append(order,self);\*to keep the order to gain access proc await: with(cid ∈ CId){if (cid # 0){callcclib await(cid);};}; proc signal: with(cid ∈ CId){if (cid # 0){callcclib signal(cid);};}; proc leave: callcclib leave();

}

Fig. 10.Process specification.

6

Conclusion

We have presented an implementation of nonblocking (signal-and-continue) pri-ority monitors in Java, implemented on top of the existing nonblocking, no-priority implementation in the standard locks & conditions package. Moreover, we have provided a state model for our solution (extending a model of the ex-isting Java mechanism) that givesformal evidence that the priority mechanism is actually implemented by our algorithm.

OurMonitorclass encourages the use of certain coding styles that cannot be

used with the standard Java implementation (i.e., the 0-1 await/signal coding idiom) and that result in cleaner, safer code, and we are actually using it in the classroom as a replacement of standard locks and conditions.

To our knowledge, there are just two other publicly available implementations of priority monitors in Java. The first one, by Chiao, Wu and Yuan [4], is really a language extension devised to overcome the limitations of synchronized methods and per-object waitsets – the paper is from 1999 and locks and conditions had not been added to Java yet. The implementation is based on a preprocessor which translates extended Java programs into standard Java code that invokes the methods of someEMonitorclass in the right sequence. In a more recent work, T.S. Norvell [11] already considers the limitations of Java monitors in the light of the provided locks and conditions library. However, his approach is different from ours, reimplementing the monitor functionality on top of lower level concurrency mechanisms (semaphores) and using explicit queues of threads. The fact that our code is conciser and based on higher-level methods has facilitated the use of model-checking as a validation tool, while his implementation is not verified.6

Some details of the reference implementation of Java locks have been omitted in our model, namely the possibility of spurious wakeups and reentrant locking. Regarding the first, it is fairly reasonable to assume the implementation of class ReentrantLockto be free of spurious wakeups as, in the most liberal interpretation of the API specification, they would make most attempts at formal reasoning useless. Regarding the second, it can be seen as a benefit of the model-driven approach: we do not consider features useless for the intended idioms.

6 To be fair, Norvell’s implementation is longer also because he extends the function-ality of Java monitors in other ways.

(18)

Methodologically speaking, the use of PlusCal/TLA as an intermediate stage between model checking/Uppaal and a deductive proof using some program ver-ification tool seems an adequate compromise. On one hand, the PlusCal model is closer to the actual Java code while containing all the information in the Up-paal one. Also, now that we have crafted a TLA proof of the key properties of our system by hand, we have a clearer idea of the techniques that should be supported by a program verification system in order to facilitate the task that remains future work.

Acknowledgments. We are very grateful to Bart Jacobs and the anonymous

reviewers for their comments on an earlier version of this paper.

References

1. Gerd Behrmann, Alexandre David, and Kim G. Larsen. A tutorial on Uppaal. In M. Bernardo and F. Corradini, editors,International School on Formal Methods for the Design of Computer, Communication, and Software Systems, SFM-RT 2004. Revised Lectures, volume 3185 ofLecture Notes in Computer Science, pages 200– 237. Springer Verlag, 2004.

2. Peter A. Buhr, Michel Fortier, and Michael H. Coffin. Monitor classification.ACM Computing Surveys, 27:63–107, 1995.

3. Manuel Carro, Julio Mari˜no, ´Angel Herranz, and Juan Jos´e Moreno-Navarro. Teaching how to derive correct concurrent programs (from state-based specifica-tions and code patterns). In C.N. Dean and R.T. Boute, editors,Teaching Formal Methods, CoLogNET/FME Symposium, TFM 2004, Ghent, Belgium, volume 3294 ofLNCS, pages 85–106. Springer, 2004. ISBN 3-540-23611-2.

4. Hsin-Ta Chiao, Chi-Houng Wu, and Shyan-Ming Yuan. A more expressive monitor for concurrent Java programming. In Arndt Bode, Thomas Ludwig, Wolfgang Karl, and Roland Wism¨uller, editors,Euro-Par 2000 Parallel Processing, volume 1900 of Lecture Notes in Computer Science, pages 1053–1060. Springer Berlin / Heidelberg, 2000.

5. E. Allen Emerson and Joseph Y. Halpern. ¨sometimes¨and ¨not never¨revisited: on branching versus linear time temporal logic.J. ACM, 33(1):151–178, January 1986. 6. Per Brinch Hansen. Java’s insecure parallelism.ACM SIGPLAN Notices, 34:38–45,

1999.

7. ´Angel Herranz, Julio Mari˜no, Manuel Carro, and Juan Jos´e Moreno-Navarro. Mod-eling concurrent systems with shared resources. InFormal Methods for Industrial Critical Systems, 14th International Workshop, FMICS 2009, Eindhoven, The Netherlands, November 2-3, 2009. Proceedings, volume 5825 of Lecture Notes in Computer Science, pages 102–116, 2009.

8. Leslie Lamport. The temporal logic of actions.ACM Trans. Program. Lang. Syst., 16(3):872–923, May 1994.

9. Leslie Lamport. Specifying Systems. Addison Wesley, 2004.

10. Leslie Lamport. The PlusCal algorithm language. In Martin Leucker and Carroll Morgan, editors,Theoretical Aspects of Computing (ICTAC2009), number 5684 in LNCS, pages 36–60. Springer Verlag, 2009.

11. Theodore S. Norvell. Better monitors for Java. Javaworld, October 2007. http://www.javaworld.com/javaworld/jw-10-2007/jw-10-monitors.html.

(19)

lq: 2 7 6 pq: 4 3 cq1: · · · 1 cqk:

Fig. 11.State of the process queues in the PlusCal simulation.

A

Proofs

A proof of the three properties stated in Section 5 follows. Section A.5 contains the whole specification of the algorithm in TLA+.

A.1 Facts of the Model

The model of our monitor implementation is based on the following facts that will be used in the proofs:

– For a process to gain access to the monitor, fromcclib enterorcclib await(cid), its process id must be the first in the “locking queue”lq(and just one process can be at the head).

– Processes ids are inserted in this queue in three situations:

1. When the process executejava lock: labeljava lock begin in Figure 8. 2. When the process is signalled by other process: the signaller moves

the signalled process id from a condition queue (cq(cid)) to lq: label java signal requeue in Figure 8. This requeue can be done from two different points:

(a) When the signalled process was in the purgatory: labelscclib leave saveit andjava await saveit in Figure 9.

(b) When the signalled process was in a user condition: labelcclib actual signal in Figure 9.

– The only process that can progress is the one at the head oflq, as this is precisely the guard that controls the execution of processes after perform-ing ajava lock (label java lock blocked in Figure 8) or ajava await (label java await blocked in Figure 8).

Several lemmata are assumed for all of them, which are stated first. Also, a graphical representation of a state of the PlusCal execution is shown in Figure 11. Queuelq is the “lock” or “enter” queue. Process identifiers are always en-queued inlq when trying to execute anenter. Moreover, a pid can be inserted in this queue in two more situations: right after receiving asignal and after being

(20)

sent to thepurgatory. It is important to note that apart from the processes that try to executeenter – which simply enqueue their pids inlq and get blocked – the only process that can progress is the one at the head oflq, as this is precisely the guard that controls the execution of processes after performing anenter, thus ensuring mutual exclusion inside the monitor.

Queuepq =cq0 is the purgatory queue. A process trying to enter “moves”

its own pid from the head oflq to the rear ofpqwhen there are pending signals – to give way to the signalled process – or when there are already some pid’s annotated in the purgatory – so as to prevent overtaking other processes in a similar situation.

Queuescq1. . .cqk are the “condition” queues. A process that executesawait

on condition i moves its pid from the head of lq to the rear of cqi. If, later,

another process executes a signal on condition i, it will move the pid at the front of cqi to the rear of lq, and pendingSignals is incremented. This is the

second “state” that a process annotated inlq can be in.

Aspq =cq0, i.e., it is one distinguished condition queue, processes blocked on

it are eventually signalled when no better option exists. That means that those pids will also be moved from the front of pq to the rear of lq. As this happens when the process is blocked at a different code point than a normal signal, we regard this as a third state. The colors in Figure 11 are used to distinguish the point of the code at which the process whose pid is at a given cell oflq blocked: red for processes visiting lq for the first time, blue for processes which come from the purgatory and yellow for processes signalled after being blocked on a (normal) condition.7

The aforementioned lemmata follow. Lemmas only needed for proving one of the main properties can be found in the corresponding sections.

Lemma 1 (Weak fairness).We assume that all processes have finite progress.

Proof. By definition, the system specification Spec in TLA+ directly specifies finite progress for every process: weak fairness is require for theNextaction and the Next action means “every process is eligible for execution” (by a explicit enumeration of their program points):

Spec =∆ ∧Init∧2[Next]vars ∧WFvars(Next)

Next = (∆ ∃ self ∈ProcSet: ∨java lock(self)∨java unlock(self)

∨java await(self)∨java signal(self)

∨cclib enter(self)∨cclib leave(self)

∨cclib await(self)∨cclib signal(self))

∨proc enter(self)∨proc in(self)

∨proc await(self)∨proc signal(self)

∨proc leave(self))

Lemma 2 (Mutual exclusion). The only process that can progress insidethe

monitor is the one whose pid is annotated at the front of lq .

7 Abusing a bit, we have displayed the cell for process 4 in blue, state that will be reached when it is moved back tolqand no yellow cells exist.

(21)

Proof. Only one process id can be the first element oflq.

Lemma 3. All queues have bounded size.

Lemma 4 (At most one pending signal). If processes are not allowed to

call signal twice before they execute leave, the value of pendingSignals is at most one.

A.2 Proof of Theorem 1

The characteristic property was expressed as an invariant of a slightly instru-mented model in which a pair of two auxiliary variables were used to recall the pids of the signaler and signaled processes after performing a signal:

∀pid ∈ProcSet : (InPurgatory(pid);pc[pid] =“proc in”)

Lemma 5. There can only be one yellow pid in lq at a given time.

We will consider several cases, depending on the execution state of the process whose pid is at the front of lq, which will be namedp:

Case 1: a signaled process. Then p is a yellow cell. Initially, when p reaches the front oflq, we know that pc[p] is not in any of the points required for the antecedent of the invariant. Then, the state evolves to a state in which the antecedent is true and (due to the previous lemma) the consequent is also true – lastSignal[1] =p – and later, p performs a leave, which resets the pair (that makes the invariant hold) and finally removespfrom the front oflq(which takes us to a different case).

Case 2: a locker. There are also several subcases here. If p is the signaler, an argument similar to the previous one shows that the system evolves only through states in which the invariant holds until p performs a leave – in this caselastSignal[0] =p right before theleave.

Ifpis a (red) locker, and there is no pending signal, thenlastSignal =h0,0i, XM Note:

Necesitamos un lema para esto?

and the invariant holds whenpc[p] =locker in. Otherwise, if there is one signal pending, then p blocks in the purgatory before removing itself from lq so the invariant holds in all the states in between.

The reasoning forbluelockers is analogous.

A.3 Proof of Theorem 2

The heart of this proof lies in showing the “liveness” ofpq, i.e. that it decreases eventually. This is so because pids cannot be in the purgatory an arbitrary number of times.

Lemma 6. A pid cannot be in lq more than three times.

Lemma 7 (Liveness of pq). The pid at the front of pq will be eventually

re-moved. XM Note: esta es la

parte que tiene seis casos.

(22)

A.4 Proof of Theorem 3

A.5 CCLib Monitor Specification in TLA+

module Monitor

extends Naturals,Sequences,TLC,FiniteSets,TLAPS

Auxiliary definitions General definitions

PositiveInteger =∆ Nat\ {0}

Operations on Last(q) =∆ q[Len(q)]

Front(q) = [i∆ ∈1. .(Len(q)−1)7→q[i]] not used:Insert as 2nd(q,x) =∆ hxi ◦q Member(q,e) =∆ ∃i ∈1. .Len(q) :q[i] =e Index(q,e) =∆ choose i ∈1. .Len(q) :q[i] =e

Process identifiers

constants N Procs

assumeAtLeastOne =∆ ∧N Procs ∈PositiveInteger First ProcId = 1∆

Last ProcId =∆ First ProcId+N Procs−1 ProcId =∆ First ProcId. .Last ProcId

Condition identifiers (condition number 0 will be implemented as purgatory)

constant N Conditions

assumeN Conditions ∈PositiveInteger

CId = 0∆ . .N Conditions –algorithmTestMonitor { variables \ ∗Problem variables cq= [cid∈CId7→ hi]; lq=hi; pendingSignals= 0; inPurgatory = 0;

waiting = [cid ∈CId 7→0]; \ ∗Instrumentation

lastSignal=h0,0i; \ ∗ hsignaller,signalledi entryOrder =hi;

(23)

\ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ \ ∗Specification of the Java implementations of Lock and Conditions

procedure java lock() {

java lock begin: lq:=Append(lq,self); java lock blocked:

await self =Head(lq); java lock end :

return; }

procedure java unlock() {

java unlock begin: lq:=Tail(lq); java unlock end:

return; }

procedure java await(wcid) {

java await begin:

cq[wcid] :=Append(cq[wcid],self); java await unlocking:

lq:=Tail(lq); java await blocked :

await Len(lq)>0∧self =Head(lq); java await end:

return; }

procedure java signal(scid) {

java signal begin: if(Len(cq[scid])>0){

lq:=Append(lq,Head(cq[scid])); cq[scid] :=Tail(cq[scid]); };

java signal end : return;

}

\ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ \ ∗Specification of the CCLib implementations of Monitors

procedure cclib enter() {

cclib enter begin: call java lock();

cclib enter check priority:

if(pendingSignals>0∨inPurgatory>0){ inPurgatory :=inPurgatory+ 1;

(24)

cclib enter to purgatory: call java await(0); cclib enter toheaven:

inPurgatory :=inPurgatory−1; };

cclib enter end: return;

}

procedure cclib leave() {

cclib leave begin:

if(pendingSignals= 0∧inPurgatory>0){ call java signal(0);

};

cclib leave resetlastsignal: if(lastSignal[1]6=self){

lastSignal:=h0,0i; };

cclib leave unlocking : call java unlock(); cclib leave end:

return; }

procedure cclib await(cclwcid) {

cclib await begin:

waiting[cclwcid] :=waiting[cclwcid] + 1; if(pendingSignals= 0∧inPurgatory>0){

call java signal(0); };

cclib await actualawait: call java await(cclwcid); cclib await reentering:

pendingSignals:=pendingSignals−1; cclib await end:

return; }

procedure cclib signal(cclscid) {

cclib signal begin: if(waiting[cclscid]>0){ cclib signal actualsignal:

pendingSignals:=pendingSignals+ 1; waiting[cclscid] :=waiting[cclscid]−1; cclib signal setlastsignal :

lastSignal:=hself,Head(cq[cclscid])i; call java signal(cclscid);

(25)

cclib signal end: return;

}

\ ∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗∗ \ ∗Process in the system

process(procid ∈ProcId) {

proc enter: call cclib enter(); proc in:

entryOrder:=Append(entryOrder,self); proc await:

with(cid ∈CId){ if(cid6= 0){

call cclib await(cid); };

};

proc signal : with(cid ∈CId){

if(cid6= 0){

call cclib signal(cid); };

};

proc leave: call cclib leave(); }

}

BEGIN TRANSLATION

constant defaultInitValue

variables cq,lq, pendingSignals,inPurgatory,waiting,lastSignal,

entryOrder,pc,stack,wcid,scid,cclwcid,cclscid vars =∆ hcq,lq,pendingSignals,inPurgatory, waiting,lastSignal,

entryOrder,pc,stack, wcid,scid,cclwcid,cclscidi

ProcSet = (ProcId∆ ) Init =∆ Global variables

∧cq= [cid ∈CId7→ hi]

∧lq =hi

∧pendingSignals = 0

(26)

∧waiting= [cid ∈CId7→0]

∧lastSignal =h0,0i ∧entryOrder=hi

Procedurejava await

∧wcid= [self ∈ProcSet7→defaultInitValue] Procedurejava signal

∧scid = [self ∈ProcSet7→defaultInitValue] Procedurecclib await

∧cclwcid= [self ∈ProcSet7→defaultInitValue] Procedurecclib signal

∧cclscid= [self ∈ProcSet7→defaultInitValue]

∧stack = [self ∈ProcSet 7→ hi]

∧pc= [self ∈ProcSet7→case self ∈ProcId→“proc enter”] java lock begin(self) =∆ ∧pc[self] =“java lock begin”

∧lq0 =Append(lq,self)

∧pc0= [pc except ![self] =“java lock blocked”]

unchanged hcq,pendingSignals,inPurgatory,

waiting, lastSignal,entryOrder,

stack,wcid,scid,cclwcid,

cclscidi

java lock blocked(self) =∆ ∧pc[self] =“java lock blocked”

∧self =Head(lq)

∧pc0 = [pc except ![self] =“java lock end”]

unchangedhcq, lq,pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder,stack,

wcid,scid,cclwcid, cclscidi

java lock end(self) =∆ ∧pc[self] =“java lock end”

∧pc0 = [pc except ![self] =Head(stack[self]).pc]

∧stack0= [stack except ![self] =Tail(stack[self])]

∧unchangedhcq,lq, pendingSignals,

inPurgatory,waiting, lastSignal,

entryOrder,wcid,scid,cclwcid,

cclscidi

java lock(self) =∆ java lock begin(self)∨java lock blocked(self)

∨java lock end(self)

java unlock begin(self) =∆ ∧pc[self] =“java unlock begin”

∧lq0 =Tail(lq)

∧pc0 = [pc except ![self] =“java unlock end”]

unchangedhcq,pendingSignals,

inPurgatory,waiting,

(27)

wcid,scid,cclwcid, cclscidi

java unlock end(self) =∆ ∧pc[self] =“java unlock end”

∧pc0= [pc

except ![self] =Head(stack[self]).pc]

∧stack0= [stack except ![self] =Tail(stack[self])]

unchanged hcq,lq,pendingSignals,

inPurgatory, waiting,

lastSignal,entryOrder,wcid,

scid,cclwcid,cclscidi

java unlock(self) =∆ java unlock begin(self)

∨java unlock end(self)

java await begin(self) =∆ ∧pc[self] =“java await begin”

∧cq0= [cq except ![wcid[self]] =Append(cq[wcid[self]],self)]

∧pc0= [pc except ![self] =“java await unlocking”]

unchangedhlq,pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder, stack,

wcid,scid,cclwcid,cclscidi

java await unlocking(self) =∆ ∧pc[self] =“java await unlocking”

∧lq0 =Tail(lq)

∧pc0= [pc except ![self] =“java await blocked”]

∧unchanged hcq,pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder,

stack,wcid,scid,cclwcid,

cclscidi

java await blocked(self) =∆ ∧pc[self] =“java await blocked”

∧Len(lq)>0∧self =Head(lq)

∧pc0= [pc except ![self] =“java await end”]

∧unchangedhcq,lq,pendingSignals,

inPurgatory,waiting,

lastSignal, entryOrder,

stack,wcid, scid,cclwcid,

cclscidi

java await end(self) =∆ ∧pc[self] =“java await end”

∧pc0 = [pc

except ![self] =Head(stack[self]).pc]

∧wcid0 = [wcid except ![self] =Head(stack[self]).wcid]

∧stack0= [stack except ![self] =Tail(stack[self])]

∧unchangedhcq, lq,pendingSignals,

inPurgatory,waiting,lastSignal,

entryOrder,scid,cclwcid,

(28)

java await(self) =∆ java await begin(self)

∨java await unlocking(self)

∨java await blocked(self)∨java await end(self) java signal begin(self) =∆ ∧pc[self] =“java signal begin”

∧ifLen(cq[scid[self]])>0

then ∧lq0 =Append(lq,Head(cq[scid[self]]))

∧cq0 = [cq except ![scid[self]] =Tail(cq[scid[self]])]

else ∧true

unchangedhcq,lqi

∧pc0= [pc except ![self] =“java signal end”]

∧unchanged hpendingSignals, inPurgatory,

waiting,lastSignal,

entryOrder,stack, wcid,scid,

cclwcid, cclscidi

java signal end(self) =∆ ∧pc[self] =“java signal end”

∧pc0= [pc except ![self] =Head(stack[self]).pc]

∧scid0 = [scid except ![self] =Head(stack[self]).scid]

∧stack0= [stack except ![self] =Tail(stack[self])]

unchangedhcq,lq, pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder, wcid,

cclwcid,cclscidi

java signal(self) =∆ java signal begin(self)

∨java signal end(self)

cclib enter begin(self) =∆ ∧pc[self] =“cclib enter begin”

∧stack0= [stack

except ![self] =h[procedure 7→ “java lock”,

pc 7→ “cclib enter check priority”]i ◦stack[self]]

∧pc0 = [pc except ![self] =“java lock begin”]

unchangedhcq, lq,pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder,wcid,

scid,cclwcid,cclscidi

cclib enter check priority(self) =∆ ∧pc[self] =“cclib enter check priority”

ifpendingSignals >0∨inPurgatory >0

then ∧inPurgatory0=inPurgatory+ 1

∧pc0= [pc except ![self] =“cclib enter to purgatory”]

else ∧pc0= [pc except ![self] =“cclib enter end”]

unchanged inPurgatory

unchangedhcq, lq,

pendingSignals,

(29)

entryOrder,stack,

wcid,scid,cclwcid,

cclscidi

cclib enter to purgatory(self) =∆ ∧pc[self] =“cclib enter to purgatory”

∧ ∧stack0 = [stack except ![self] =h[procedure 7→ “java await”,

pc 7→ “cclib enter toheaven”,

wcid 7→ wcid[self]]i ◦stack[self]]

∧wcid0= [wcid except ![self] = 0]

∧pc0= [pc except ![self] =“java await begin”]

∧unchanged hcq,lq,pendingSignals,

inPurgatory,waiting,

lastSignal, entryOrder,

scid, cclwcid,cclscidi

cclib enter toheaven(self) =∆ ∧pc[self] =“cclib enter toheaven”

∧inPurgatory0 =inPurgatory−1

∧pc0 = [pc except ![self] =“cclib enter end”]

unchangedhcq, lq,pendingSignals,

waiting,lastSignal,

entryOrder,stack,wcid,

scid,cclwcid,cclscidi

cclib enter end(self) =∆ ∧pc[self] =“cclib enter end”

∧pc0= [pc except ![self] =Head(stack[self]).pc]

∧stack0= [stack except ![self] =Tail(stack[self])]

unchanged hcq,lq,pendingSignals,

inPurgatory,waiting,

lastSignal, entryOrder,wcid,

scid, cclwcid,cclscidi

cclib enter(self) =∆ cclib enter begin(self)

∨cclib enter check priority(self)

∨cclib enter to purgatory(self)

∨cclib enter toheaven(self)

∨cclib enter end(self)

cclib leave begin(self) =∆ ∧pc[self] =“cclib leave begin”

ifpendingSignals = 0∧inPurgatory>0

then ∧ ∧scid0= [scid except ![self] = 0]

∧stack0 = [stack except ![self] =h[procedure 7→ “java signal”,

pc 7→ “cclib leave resetlastsignal”,

scid 7→ scid[self]]i ◦stack[self]]

∧pc0 = [pc except ![self] =“java signal begin”]

(30)

unchangedhstack,scidi ∧unchanged hcq,lq,pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder,wcid,

cclwcid,cclscidi

cclib leave resetlastsignal(self) =∆ ∧pc[self] =“cclib leave resetlastsignal”

iflastSignal[1]6=self

then ∧lastSignal0=h0,0i

else ∧true

∧unchanged lastSignal

∧pc0 = [pc except ![self] =“cclib leave unlocking”]

unchangedhcq, lq,

pendingSignals,

inPurgatory,

waiting,entryOrder,

stack,wcid,scid,

cclwcid,cclscidi

cclib leave unlocking(self) =∆ ∧pc[self] =“cclib leave unlocking”

∧stack0= [stack except ![self] =h[procedure7→ “java unlock”,

pc 7→ “cclib leave end”]i ◦stack[self]]

∧pc0= [pc except ![self] =“java unlock begin”]

unchanged hcq,lq,pendingSignals,

inPurgatory,waiting,

lastSignal, entryOrder,

wcid,scid,cclwcid,

cclscidi

cclib leave end(self) =∆ ∧pc[self] =“cclib leave end”

∧pc0= [pc except ![self] =Head(stack[self]).pc]

∧stack0 = [stack except ![self] =Tail(stack[self])]

∧unchanged hcq,lq,pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder,wcid,

scid,cclwcid,cclscidi

cclib leave(self) =∆ cclib leave begin(self)

∨cclib leave resetlastsignal(self)

∨cclib leave unlocking(self)

∨cclib leave end(self)

cclib await begin(self) =∆ ∧pc[self] =“cclib await begin”

∧waiting0= [waiting except ![cclwcid[self]] =waiting[cclwcid[self]] + 1]

ifpendingSignals = 0∧inPurgatory >0

(31)

∧stack0= [stack except ![self] =h[procedure7→ “java signal”,

pc 7→ “cclib await actualawait”,

scid 7→ scid[self]]i ◦stack[self]]

∧pc0= [pc except ![self] =“java signal begin”]

else ∧pc0= [pc except ![self] =“cclib await actualawait”]

unchanged hstack,scidi ∧unchangedhcq,lq,pendingSignals,

inPurgatory,lastSignal,

entryOrder,wcid, cclwcid,

cclscidi

cclib await actualawait(self) =∆ ∧pc[self] =“cclib await actualawait”

∧ ∧stack0= [stack except ![self] =h[procedure7→ “java await”,

pc 7→ “cclib await reentering”,

wcid 7→ wcid[self]]i ◦stack[self]]

∧wcid0= [wcid except ![self] =cclwcid[self]]

∧pc0 = [pc except ![self] =“java await begin”]

unchangedhcq, lq,pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder,

scid,cclwcid,cclscidi

cclib await reentering(self) =∆ ∧pc[self] =“cclib await reentering”

∧pendingSignals0=pendingSignals−1

∧pc0 = [pc except ![self] =“cclib await end”]

∧unchangedhcq,lq,inPurgatory,

waiting,lastSignal,

entryOrder,stack,wcid,

scid,cclwcid,cclscidi

cclib await end(self) =∆ ∧pc[self] =“cclib await end”

∧pc0= [pc

except ![self] =Head(stack[self]).pc]

∧cclwcid0 = [cclwcid except ![self] =Head(stack[self]).cclwcid]

∧stack0= [stack except ![self] =Tail(stack[self])]

unchanged hcq,lq,pendingSignals,

inPurgatory, waiting,

lastSignal,entryOrder,wcid,

scid,cclscidi

cclib await(self) =∆ cclib await begin(self)

∨cclib await actualawait(self)

∨cclib await reentering(self)

∨cclib await end(self)

(32)

ifwaiting[cclscid[self]]>0

then ∧pc0= [pc except ![self] =“cclib signal actualsignal”]

else ∧pc0= [pc except ![self] =“cclib signal end”]

unchangedhcq,lq, pendingSignals,

inPurgatory,waiting,

lastSignal,entryOrder,

stack, wcid,scid,cclwcid,

cclscidi

cclib signal actualsignal(self) =∆ ∧pc[self] =“cclib signal actualsignal”

∧pendingSignals0=pendingSignals+ 1

∧waiting0= [waiting except ![cclscid[self]] =waiting[cclscid[self]]−1]

∧pc0= [pc except ![self] =“cclib signal setlastsignal”]

∧unchanged hcq,lq,inPurgatory,

lastSignal,

entryOrder,stack,

wcid,scid,cclwcid,

cclscidi

cclib signal setlastsignal(self) =∆ ∧pc[self] =“cclib signal setlastsignal”

∧lastSignal0=hself,Head(cq[cclscid[self]])i ∧ ∧scid0= [scid except ![self] =cclscid[self]]

∧stack0= [stack

except ![self] =h[procedure 7→ “java signal”,

pc 7→ “cclib signal end”,

scid 7→ scid[self]]i ◦stack[self]]

∧pc0 = [pc except ![self] =“java signal begin”]

unchangedhcq,lq,

pendingSignals,

inPurgatory,waiting,

entryOrder,wcid,

cclwcid,cclscidi

cclib signal end(self) =∆ ∧pc[self] =“cclib signal end”

∧pc0 = [pc except ![self] =Head(stack[self]).pc]

∧cclscid0 = [cclscid except ![self] =Head(stack[self]).cclscid]

∧stack0= [stack except ![self] =Tail(stack[self])]

unchangedhcq,lq,pendingSignals,

inPurgatory,waiting,

lastSignal, entryOrder,wcid,

scid,cclwcidi

cclib signal(self) =∆ cclib signal begin(self)

∨cclib signal actualsignal(self)

∨cclib signal setlastsignal(self)

(33)

proc enter(self) =∆ ∧pc[self] =“proc enter”

∧stack0 = [stack except ![self] =h[procedure 7→ “cclib enter”,

pc 7→ “proc in”]i ◦stack[self]]

∧pc0= [pc except ![self] =“cclib enter begin”]

unchanged hcq,lq,pendingSignals,inPurgatory,

waiting,lastSignal,entryOrder,

wcid,scid, cclwcid,cclscidi

proc in(self) =∆ ∧pc[self] =“proc in”

∧entryOrder0=Append(entryOrder,self)

∧pc0= [pc except ![self] =“proc await”]

unchanged hcq,lq,pendingSignals,inPurgatory,

waiting, lastSignal,stack,wcid,scid,

cclwcid,cclscidi

proc await(self) =∆ ∧pc[self] =“proc await”

∧ ∃cid ∈CId :

ifcid 6= 0

then ∧ ∧cclwcid0= [cclwcid except ![self] =cid]

∧stack0= [stack except ![self] =h[procedure7→ “cclib await”,

pc 7→ “proc signal”,

cclwcid 7→ cclwcid[self]]i ◦stack[self]]

∧pc0= [pc except ![self] =“cclib await begin”]

else ∧pc0= [pc except ![self] =“proc signal”]

∧unchanged hstack,cclwcidi ∧unchanged hcq,lq,pendingSignals,inPurgatory,

waiting, lastSignal, entryOrder,

wcid,scid,cclscidi

proc signal(self) =∆ ∧pc[self] =“proc signal”

∧ ∃cid ∈CId:

ifcid 6= 0

then ∧ ∧cclscid0= [cclscid except ![self] =cid]

∧stack0= [stack except ![self] =h[procedure 7→ “cclib signal”,

pc 7→ “proc leave”,

cclscid 7→ cclscid[self]]i ◦stack[self]]

∧pc0= [pc except ![self] =“cclib signal begin”]

else ∧pc0= [pc except ![self] =“proc leave”]

∧unchanged hstack,cclscidi ∧unchanged hcq,lq,pendingSignals,inPurgatory,

waiting,lastSignal,entryOrder,

wcid,scid, cclwcidi

(34)

∧stack0= [stack except ![self] =h[procedure7→ “cclib leave”,

pc 7→ “Done”]i ◦stack[self]]

∧pc0 = [pc except ![self] =“cclib leave begin”]

unchangedhcq, lq,pendingSignals, inPurgatory,

waiting,lastSignal,entryOrder,

wcid,scid,cclwcid, cclscidi

procid(self) =∆ proc enter(self)∨proc in(self)

∨proc await(self)∨proc signal(self)

∨proc leave(self)

Next = (∆ ∃ self ∈ProcSet: ∨java lock(self)∨java unlock(self)

∨java await(self)

∨java signal(self)

∨cclib enter(self)

∨cclib leave(self)

∨cclib await(self)

∨cclib signal(self))

∨(∃self ∈ProcId :procid(self))

∨ Disjunct to prevent deadlock on termination

((∀self ∈ProcSet:pc[self] =“Done”)∧unchanged vars) Spec =∆ ∧Init∧2[Next]vars

∧WFvars(Next)

Termination =∆ 3(∀ self ∈ProcSet:pc[self] =“Done”) END TRANSLATION

Invariants and temporal properties (theorems) Types =∆

Global variables

∧cq ∈[CId →Seq(ProcSet)]

∧lq ∈Seq(ProcSet)

∧pendingSignals ∈Nat

∧inPurgatory ∈Nat

∧waiting ∈[CId →Nat]

∧lastSignal ∈(ProcSet∪ {0})×(ProcSet∪ {0}) Procedurejava await

∧wcid ∈[ProcSet→CId∪ {defaultInitValue}] Procedurejava signal

∧scid ∈[ProcSet →CId∪ {defaultInitValue}] Procedurecclib await

(35)

Procedurecclib signal

∧cclscid ∈[ProcSet→CId∪ {defaultInitValue}]

theorem TypePreservation =∆ 2Types

0.−Mutex Mutex =∆

∀pid1∈ProcSet:

∀pid2 ∈ProcSet:

pc[pid1] =“proc in”∧pc[pid2] =“proc in”⇒pid1 =pid2

1.− Characteristic property:La que hemos chequeado conUPPAAL, que si se hace un signal sobre una condition no vac´ıa, el siguiente thread en tomar acceso del monitor es el primero de dicha condition, y no alguno de los que estaban en la cola del lock mutex.

As an invariant with the instrumentation: CharacteristicAsInv =∆

∀pid ∈ProcSet:pc[pid] =“proc in”⇒lastSignal[2]∈ {0,pid}

2.−ToHeaven: se acaba saliendo del purgatorio InPurgatory(pid) =∆ Member(cq[0],pid)

ToHeaven =∆ ∀pid ∈ProcSet: (InPurgatory(pid);pc[pid] =“proc in”) by Auto

theorem ToHeavenThm =∆ InPurgatory(1); pc[1] =“proc in”

h1i1.assumeInPurgatory(1)∧[Next]vars ⇒(InPurgatory(1)0∨pc0[1] =“proc in”0),

InPurgatory(1)∧ hNextivars ⇒pc0[1] =“proc in”,

InPurgatory(1)⇒enabled hNextivars

prove 2[Next]vars ∧WFvars(Next)⇒(InPurgatory(1);pc[1] =“proc in”)

byRuleWF1 h1i.qed 3.−OrderPreservation: NoOrderPreservation =∆ ∀pid1∈ProcSet: ∀pid2∈ProcSet: (∧Member(cq[0],pid1)

∧pc[pid2] =“java lock blocked”)

;

(∧Member(entryOrder, pid1)

(36)

∧Index(entryOrder,pid2)<Index(entryOrder,pid1)) OrderPreservation =∆ ¬NoOrderPreservation

References

Related documents

1. Gonzalez-Diaz, H., Vilar, S., Santana, L., Uriarte, E., 2007, &#34; Medicinal chemistry and bioinformatics--current trends in drugs discovery with networks topological

abdominal mass continued to enlarge. Radia- tion therapy was given to the left side of the.. Roentgenograms of chest of SB., 21)j2-year-old girl with sarcoma botryoides and

Field experiments were conducted at Ebonyi State University Research Farm during 2009 and 2010 farming seasons to evaluate the effect of intercropping maize with

Administration of Citrullus lanatus fruit extract did not affect serum sodium, chloride, bicarbonate, calcium and creatinine levels of Wistar rats.. However, a

In general, this equation, with or without some modification, applies to all other type of osmotic systems. Several simplifications in Rose-Nelson pump were made by Alza

As α increases, the formed cycles are slowly shifted, on the whole, in the phase space in the single direction. It is seen in Fig. 2, a from inclined displacements of the

Deployment Models Service Models Essential Charateristics Common Charateristics Software as a Service (SaaS) Platform as a Service (PaaS) Infrastructure as a Service

aOR: adjusted odds ratio; BMI: body mass index; CVD: cardiovascular disease; CIMT: carotid intima‑media thickness; CI: confidence interval; eGFR: estimated glomerular filtration