Towards Runtime Adaptation of Actor
Systems

arXiv:1709.02346v1 [] 31 Aug 2017

Ian Cassar
Supervisor: Dr. Adrian Francalanza

Faculty of ICT
University of Malta

29th May 2015

Submitted in partial fulfillment of the requirements for the degree of
M.Sc. I.C.T. (Computer Science)

Faculty of ICT
Declaration

I, the undersigned, declare that the dissertation entitled:
Towards Runtime Adaptation of Actor Systems

submitted is my work, except where acknowledged and referenced.

Ian Cassar
29th May 2015

ii

Acknowledgements

I would honestly like to express my gratitude towards my supervisor Dr. Adrian Francalanza, whom despite being abroad and busy with his own research he always found
enough time and patience to provide me with essential guidance and support. His excellent guidance, patience and tutoring made it possible for me to understand a lot of
complex material that helped me make further progress in my work. I have learnt a lot
from him and I must say that it has been quite a privilege working alongside him for the
second time.
I would also like to thank my parents for always offering me their full support during
my tough times. Even when I started to have doubts in my abilities, they never stopped
believing in me, and always provided me with essential moral support. Moreover I would
like to thank my loving girlfriend Melanie whom despite being busy with her final year
in medical school, she was always by my side when I needed her most. The support
of my loved ones kept me persevering and believing in myself even when I was facing
tough situations. I therefore dedicate this dissertation to all of them.
Finally I would like to acknowledge that the research work disclosed in this dissertation was partially funded by the Master It! scholarship scheme (Malta).

iii

“When there’s a will, there’s a way.”
– An Old English Proverb

iv

The research work disclosed in this publication is partially funded by the Master it!
Scholarship Scheme (Malta). This Scholarship is part-financed by the European Union
–

European Social Fund (ESF) under Operational Programme II

– Cohesion

Policy 2007-2013, “Empowering People for More Jobs and a Better Quality Of Life.

Operational Programme II – Cohesion Policy 2007-2013
Empowering People for More Jobs and a Better Quality of
Life
Scholarship part-financed by the European Union
European Social Fund (ESF)
Co-financing rate: 85% EU Funds;15% National Funds
Investing in your future

v

Abstract
Runtime Adaptation (RA) is a technique prevalent to long-running, highly available software systems, whereby system characteristics are altered dynamically in response to
runtime events, while causing limited disruption to the execution of the system. Actorbased, concurrent systems are often used to build such long-running, reactive systems
which require suffering from limited downtime. This makes runtime adaptation an appealing technique for mitigating erroneous behaviour in actor-systems, since mitigation
is carried out while the system executes.
In this dissertation we focus on providing effective adaptations that can be localised
and applied to specific concurrent actors, thereby only causing a temporary disruption to
the parts of the system requiring mitigation, while leaving the rest of the system intact.
We make the application of localised adaptations efficient through incremental synchronisation, whereby the specifier can strategically suspend specific parts of the system,
whenever this is strictly required for ensuring that adaptations are effectively applied.
We also study static analysis techniques to determine whether the specified incremental
synchronisation is in some sense adequate for local adaptations to be carried out.
We thus identify a number of generic adaptations that can be applied to any actor system, regardless of its design and the code that it executes. We implement the identified
adaptations as an extension of an existing Runtime Verification tool for actor-systems,
thereby creating a RA framework for monitoring and mitigating actor systems. In parallel to our implementation we also develop a formal model of our RA framework that
further serves to guide our implementation. This model also enables us to better understand the subtle errors that erroneously specified adaptation scripts may introduce.
We thus develop a static type system for detecting and rejecting erroneous adaptation
scripts prior to deployment, thereby providing the specifier with assistance for writing
valid scripts. Although the static typesystem analyses scripts with respect to certain assumptions, we do not assume that the monitored system abides by these assumptions.
We therefore augment our RA framework with dynamic checks for halting monitoring
whenever the system deviates from our assumption. Based on this dynamically checked
model of our RA framework, we prove type soundness for our static type system.
As a result of this dissertation we thus implement and formalise a novel a Runtime
Adaptation framework for actor systems as extension to an existing Runtime verification tool. Furthermore, exploring the mixture of static and dynamic typechecking, in the
context of runtime verification, for the purpose of adaptation is also quite novel. This exploration lead to the developing a novel type analysis technique for detecting erroneously
specified runtime adaptation scripts.

vi

Contents
1. Introduction
1.1 Aims and Objectives . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.2 Document Outline . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

1
2
5

2. Background
2.1 Runtime Verification, Adaptation and Enforcement . . . . .
2.2 Synchronous and Asynchronous monitoring . . . . . . . . .
2.2.1 Offline and Online monitoring . . . . . . . . . . . .
2.2.2 Varying definitions of Online Monitoring approaches
2.2.3 The Spectrum of Online Monitoring Approaches . .
2.3 The Actor-Model and Erlang . . . . . . . . . . . . . . . . .
2.4 detectEr − A concurrent RV tool . . . . . . . . . . . . . . .
2.4.1 The Syntax . . . . . . . . . . . . . . . . . . . . . .
2.4.2 The Model . . . . . . . . . . . . . . . . . . . . . .
2.4.3 The Semantics . . . . . . . . . . . . . . . . . . . .
2.5 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

8
9
12
12
14
16
22
25
25
27
28
29

3. Assessing the Impact of Increasing Monitor Synchronisation
3.1 Introducing Synchronous Monitoring . . . . . . . . . . . .
3.1.1 Instrumentation through Aspect-Oriented Weaving
3.1.2 The Synchronous Instrumentation Protocol . . . .
3.2 A Hybrid Instrumentation for Timely Detections . . . . . .
3.2.1 Logic Extensions . . . . . . . . . . . . . . . . . .
3.2.2 The Hybrid Monitoring Protocol . . . . . . . . . .
3.3 Evaluation . . . . . . . . . . . . . . . . . . . . . . . . . .
3.3.1 The Yaws Webserver . . . . . . . . . . . . . . . .
3.3.2 Monitoring for safety properties in Yaws . . . . .
3.3.3 The Performance Results . . . . . . . . . . . . . .
3.4 Conclusion . . . . . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

31
32
34
35
37
39
41
42
43
44
47
52

4. Language Design, Implementation and Formalisation
4.1 Identifying Adaptation Actions within the Constraints of Actor Systems
4.1.1 Identifying ideal Runtime Adaptation Actions . . . . . . . . . .
4.1.2 The Implementation Constraints of Adaptation Actions . . . . .

54
55
57
60

vii

.
.
.
.
.
.
.
.
.
.
.

4.2
4.3
4.4
4.5

4.6

Designing the Runtime Adaptation Mechanism . . . . . . . . . . .
Implementing the Runtime Adaptation Protocol . . . . . . . . . . .
4.3.1 Implementing Pre-instrumented Synchronous Adaptations .
Strengthening Yaws through Runtime Adaptation . . . . . . . . . .
A Formal Model for Runtime Adaptation . . . . . . . . . . . . . .
4.5.1 An Operational Model for detectEr . . . . . . . . . . . . .
4.5.2 Extending the Operational Model with Runtime Adaptation
Conclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5. Filtering Out Invalid Adaptation Scripts
5.1 Static Type Checking . . . . . . . . . . . . . .
5.1.1 The Type Annotations . . . . . . . . .
5.1.2 The Type Environments . . . . . . . .
5.1.3 The Typing Rules . . . . . . . . . . . .
5.1.4 Introducing Mutual Exclusion . . . . .
5.2 Dynamic Analysis of Typed Scripts . . . . . .
5.3 Proving Type Soundness . . . . . . . . . . . .
5.3.1 Preliminaries . . . . . . . . . . . . . .
5.3.2 The Top Level Proof . . . . . . . . . .
5.3.3 Proving Type Safety . . . . . . . . . .
5.3.4 Proving Subject Reduction . . . . . . .
5.3.5 Proving the Monitor Reduction Lemma
5.4 Conclusion . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

64
67
68
71
73
74
83
91

.
.
.
.
.
.
.
.
.
.
.
.
.

92
93
95
97
99
104
113
123
123
124
126
130
134
136

6. Related Work
6.1 Synchronous and Asynchronous Monitoring . .
6.2 Employing Runtime Mitigation . . . . . . . . .
6.3 Extending a Logic with a Notion of Adaptation
6.4 Static Analysis of Monitoring scripts . . . . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

138
138
140
142
142

7. Conclusion
7.1 Future Work . . . . . . . . . . . . . . .
7.1.1 Implementing More Adaptations
7.1.2 An Algorithmic Type System .
7.1.3 Type inference . . . . . . . . .
7.1.4 Static Detection of Other Errors

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

145
148
148
148
148
149

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

A. Monitor Optimisations in detectEr
150
A.1 Synthesising Unoptimised Monitors . . . . . . . . . . . . . . . . . . . 150
A.2 Monitor Optimisations . . . . . . . . . . . . . . . . . . . . . . . . . . 153
B. Additional Properties and Tabulated Results
155
B.1 Additional Runtime Verification Properties . . . . . . . . . . . . . . . . 155
B.1.1 Five or more Headers Required Property . . . . . . . . . . . . 155
viii

B.1.2 Valid HTTP Request Format Property . . . .
B.2 Detailed Result Tables . . . . . . . . . . . . . . . .
B.2.1 Results for Unoptimised Monitors . . . . . .
B.2.2 Results for Statically Optimised Monitors . .
B.2.3 Results for Dynamically Optimised Monitors

.
.
.
.
.

158
159
159
160
161

C. Proofs for Auxiliary Lemmas
C.1 Proving The Correspondence Auxiliary Lemma . . . . . . . . . . . . .
C.2 Proving The Primary Soundness Auxiliary Lemmas . . . . . . . . . . .
C.3 Proving The Secondary Soundness Auxiliary Lemmas . . . . . . . . .

163
165
167
194

References

222

ix

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

List of Figures
1.1

An example server actor implementation . . . . . . . . . . . . . . . . .

2.1
2.2
2.3
2.4
2.5
2.6
2.7
2.8
2.9
2.10

Runtime Verification . . . . . . . . . . . . . . . . . . . . . . . . .
Runtime Adaptation . . . . . . . . . . . . . . . . . . . . . . . . . .
Runtime Enforcement . . . . . . . . . . . . . . . . . . . . . . . . .
The Online monitoring Spectrum. . . . . . . . . . . . . . . . . . .
Completely-synchronous Monitoring (CS) . . . . . . . . . . . . . .
Synchronous Monitoring with Synchronous Instrumentation (SMSI)
Asynchronous Monitoring with Checkpoints (AMC) . . . . . . . .
Asynchronous Monitoring with Synchronous Detection (AMSD) . .
Completely-asynchronous Monitoring (CA) . . . . . . . . . . . . .
The Logic and its Semantics . . . . . . . . . . . . . . . . . . . . .

.
.
.
.
.
.
.
.
.
.

10
11
11
16
17
18
19
20
21
28

3.1
3.2
3.3
3.4
3.5
3.6
3.7
3.8

A High-level illustration of Synchronous Instrumentation Monitoring. .
A High-level depiction of the Synchronous Event Monitoring protocol. .
A High-level illustration of Synchronous Detection (Hybrid) monitoring.
A High-level depiction of the Hybrid Monitoring Protocol. . . . . . . .
Yaws client connection protocol . . . . . . . . . . . . . . . . . . . . .
Performance impact analysis (using unoptimised monitors) . . . . . . .
Performance impact analysis (using statically optimised monitors) . . .
Performance impact analysis (using fully optimised monitors) . . . . .

33
36
37
42
43
49
50
51

4.1
4.2
4.3
4.4
4.5
4.6
4.7

Triggering the self-adaptive functionality of a system. . . . . . . . .
Applying adaptations using interrupts. . . . . . . . . . . . . . . . .
Applying adaptations encoded as messages. . . . . . . . . . . . . .
A conceptual protocol for applying synchronous adaptations. . . . .
A high-level depiction of the Runtime Adaptation protocol. . . . .
The Logic and its LTS Semantics . . . . . . . . . . . . . . . . . . .
A Runtime Semantics for Instrumented Properties with Adaptations

56
61
61
63
68
75
84

5.1
5.2
5.3

A Type System for Adaptation sHML scripts . . . . . . . . . . . . . . 96
Dynamically Typed Adaptation-Script Rules . . . . . . . . . . . . . . . 119
The Instrumentation Transition Rules . . . . . . . . . . . . . . . . . . 120

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.

.
.
.
.
.
.
.

3

A.1 Monitor Combinator Generation for Formula (A.1) . . . . . . . . . . . 151
A.2 Optimised Synthesis for Formula (A.1). . . . . . . . . . . . . . . . . . 154
x

1. Introduction

Runtime Adaptation (RA) [51, 50, 49] is a technique prevalent to long-running, highly
available software systems, whereby system characteristics (e.g., its structure, locality
etc.) are altered dynamically in response to runtime events (e.g., detected hardware faults
or software bugs, changes in system loads), while causing limited disruption to the execution of the system. Numerous examples can be found in service-oriented architectures
[63, 48] (e.g., cloud-services, web-services, etc.) for self-configuring, self-optimising
and self-healing purposes [2]; the inherent component-based, decoupled organisation of
such systems facilitates the implementation of adaptive actions affecting a subset of the
system while allowing other parts to continue executing normally.
Actor systems [4, 43, 17] consist of independently-executing components called actors. Every actor is uniquely-identifiable with a unique process id, has its own local
memory, and can also spawn other actors and interact with them through asynchronous
messaging. Actors systems are often used to build the aforementioned service-oriented
systems with limited downtime [7, 52, 43]. Actor-oriented coding practices such as
fail-fast design-patterns [17, 43] already advocate for a degree of RA that contribute towards building robust, fault-tolerant systems. Such systems are developed using mechanisms such as process linking and supervision trees, in which supervisor actors can
detect crashed actors and respond through adaptations such as restarting the actors, reverting them through a previous (or different) implementation version, or else killing
further actors that may potentially be affected by the crash.
1

1. Introduction

This dissertation studies ways how runtime adaptation for actor systems can be extended to be able to respond to sets of runtime events that go beyond actor crashes.
More specifically, we would like to observe sequences of events that allow us to invoke
an adaptation action to mitigate the effects of the detected misbehaviour. This technique,
however, can also be used to observe positive (liveness) events that allow us to adapt the
system to execute more efficiently (e.g., by switching off unused parts). More generally,
we intend to develop a framework for extending actor-system functionality through RA,
so as to improve aspects such as resilience and resource management.
Due to the inherent concurrency of actor-systems, adaptation actions can be achieved
using generic, actor-level manipulations (e.g., restart misbehaving actors) to mitigate
only the concurrent components (actors) that contributed to the detected misbehaviour,
thus leaving the other actors unaffected. Although similar adaptations are already being
employed by using certain system design conventions (e.g., fail-fast, supervision trees,
etc.), it can be quite challenging to implement effective adaptations that can be applied on
any system without requiring it to be developed in a particular manner. This is because
effective adaptations require the monitor to temporarily synchronise with the components in need of adaptation, so as to achieve a tighter level of control upon them. This
allows for the adaptations to apply the required mitigation in a timely manner thereby
increasing the effectiveness of the the applied mitigation. However, actor systems do not
natively support synchronisation. Implementing such an RA framework is even more
challenging as to our knowledge, runtime adaptation wrt. actor systems has not yet been
explored, and is therefore quite novel.

1.1

Aims and Objectives

We aim to introduce Runtime Adaptation for actor systems by extending existing Runtime Verification (RV) tools, such as [68, 37, 23, 39], with RA functionality. The appeal
of such an approach is that RV tools already provide mechanisms for specifying the
system behaviour needed to be observed, together with instrumentation mechanisms for

2

1. Introduction

j

Incrementor
(2) {inc, 3, cli}
i

External View
(3) {res, 4}

k

Decrementor

(2) {inc, 3, cli}

Common-Interface
(1) {inc, 3, cli}

(3) err

Figure 1.1: An example server actor implementation offering integer increment and
decrement services.
observing such behaviour. We also aim to introduce adaptations that can be localised to
individual components, thereby allowing for specific actors to be mitigated while leaving
the other actors unaffected.
As a proof-of-concept, we aim to focus on one of these actor-based RV tools called
detectEr [39] — an RV tool for monitoring invariant (safety) properties about long-

running (reactive) actor-based systems written in Erlang [7]; presently this tool is only
able to perform detections that are exclusively asynchronous. Using this tool we want
to investigate ways how to insert a degree of synchrony so as to allow for detections to
be replaced by effective adaptation actions that respond to behaviours detected, while
reusing as many elements as possible from the existing technology.
Example 1.1.1. In Fig. 1.1 we present a simple running example system to which we
refer in different parts throughout this dissertation. This figure depicts an actor-based
server consisting of:
• a front-end common-interface actor with identifier i, receiving client requests;
• a back-end incrementor actor with identifier j, handling integer increment requests;
• and a back-end decrementor actor k, handling decrement requests.
A client sends service requests to actor i of the form {tag, arg, ret} where: tag selects the
type of service, arg carries the service arguments and ret specifies the return address for
the result (typically the client actor ID). The interface actor forwards the request to one
of its back-end servers (depending on the tag) whereas the back-end servers process the
requests, sending results (or error messages) to ret. With respect to this simple system
we can define several safety (invariant) RV properties including Property1.
3

1. Introduction

Property 1. For every increment request, {inc, n, cli}, that is received by the commoninterface i, the requesting client, cli, should never be sent an error message err.
With an RA framework developed as extension of an RV setup, RV properties can easily be augmented with adaptation actions that the monitor can apply upon detecting a
violation. For instance, RV property1 can thus be extended into RA property2 below.
Property 2. For every increment request, {inc, n, cli}, that is received by the commoninterface i, should the requesting client, cli, be sent an error message err, this should be
mitigated by: restarting the common-interface i, and purging the mailbox of the system
actor that sent the error message to the client.



To fulfill our aim of developing such a runtime adaptation framework for actor systems,
we divide our work into the following three objectives:
(i) As a minor objective, we first want to carry out a preliminary study wrt. the different
techniques by which synchronous monitoring can be introduced in asynchronous
actor systems, and thus integrate them within the detectEr RV tool. Although it is
generally accepted that synchrony increases monitoring overheads, we are not aware
of any studies that attempt to quantify and assess by how much, especially wrt. actor
systems. Hence we also want to carry out an impact assessment based on the studied
synchronous monitoring techniques (i.e., the ones we integrate in detectEr). This
would aid us in identifying the ideal synchronisation mechanism for introducing
effective adaptation actions within detectEr in an efficient way.
(ii) More importantly, we want to identify and implement generic adaptation actions
that are relevant in the context actor systems. To be able to effectively apply these
adaptations we want to build upon the efficient synchronous monitoring candidate
technique identified in (i), so as to permit the monitor to temporarily achieve tighter
control over the system (or parts of it) thereby being able to effectively execute the
required mitigation. Using this synchronisation technique and the identified adaptations, we want to extend detectEr along with its specification language resulting
4

1. Introduction

in an effective RA framework for actor systems. This framework should allow the
specifier to apply the necessary adaptations on any actor-based system (written in
Erlang) regardless of the code it executes or the way it is designed and implemented.
Providing the monitor with more control can however lead to introducing errors in
the system. To be able to better identify and study these errors we want to develop a
formal model wrt. the implementation constraints of our RA framework.
(iii) Formalising our RA framework puts us in a position to identify and study in more
depth the errors that our monitors may introduce, without having to deal with the
complexities of the implementation. We conjecture that some of the identified errors
can be statically detected by thoroughly inspecting the adaptation scripts prior to
deployment. We therefore want to look into static analysis techniques that can assist
the specifier into writing error-free adaptation scripts.
Each objective contributes in its own way towards developing a more refined Runtime
Adaptation framework for actor systems. For instance in (i) we address performance
aspects related to developing an efficient (yet effective) RA framework; while in (ii)
we identify the relevant adaptation actions and address their implementability issues.
This contributes to having a more effective RA framework that provides the specifier
with an adequate number of generic adaptations that can be applied to any actor-based
(Erlang) system. Furthermore, formalisation also contributes towards developing a more
understandable RA framework. In fact in (iii) we plan to exploit the understandability
of the formal model to address certain incorrectness that the RA monitors may now
introduce. This contributes towards a more conducive RA framework which provides
guidance to the specifier for writing error-free adaptation scripts.

1.2

Document Outline

In this thesis we address our three objective in Chapter 3, 4 and 5. Each of these chapters
presents and evaluates the contributions required for fulfilling the respective objective.
More specifically, we structure our document in the following manner:
5

1. Introduction

• In Chapter 2 we provide the reader with the necessary background material for better
understanding our work. We define and differentiate between Runtime Verification,
Adaptation and Enforcement. We also identify a spectrum of online monitoring approaches ranging from complete synchronous and complete asynchronous monitoring
on opposite ends of the spectrum. Finally we present the actor model as implemented
by a host language called Erlang. This is followed by an overview of the specification language used by our target RV tool, detectEr, for specifying RV properties for
Erlang systems.
• In Chapter 3 we explore the design space for synchronisation mechanisms and assess
the respective overheads induced as a result of increasing synchrony when monitoring actor systems. Based on the results of this impact assessment, we identify an
efficient synchronisation technique that guides the implementation of our RA adaptation framework presented in Chapter 4.
• In Chapter 4 we identify relevant adaptations and build on the efficient synchronisation technique identified in Chapter 3. This allows for the implementation of more
effective adaptations which require the monitor to achieve tighter control over the
system. We then formalise the behaviour of our runtime adaptation scripts as operational semantics. This allows for better understanding the core concepts of our
runtime adaptation framework without having to deal with the complexities of the
implementation.
• As erroneous adaptation scripts can introduce errors, in Chapter 5 we present a static
type system for identifying potential potential problems, that may arise from erroneous scripts, prior to deployment. Finally we evaluate the type system by proving
type soundness for typed monitors.
• Finally, in Chapter 6 we present other work related to different aspects of our research.
We then conclude this document and provide future work suggestions in Chapter 7.
• The appendix chapters A, B and C respectively provide: information about detectEr’s
6

1. Introduction

monitor optimisations; additional RV properties used in our impact assessment along
with detailed result tables; and the proofs for auxiliary lemmas required when proving
theorems introduced in Chapters 4 and 5.

7

2. Background

In this chapter we provide the reader with the necessary background information for
better understanding our work. More importantly we aim to disambiguate terms and
techniques that may sometimes be confused for one another, while we also want to
establish what we truly mean when we refer to certain terms during the course of this
dissertation.
For instance, in Sec. 2.1 we define and disambiguate between three monitoring techniques, namely Runtime Verification, Runtime Adaptation and Runtime Enforcement.
This is intended to clarify what we mean by these three terms, as the distinction between
them is not always made clear in the current literature.
Similarly, in Sec. 2.2 we disambiguate between two different classes of monitoring techniques found in the current literature, namely Offline and Online monitors;
whereby we expand on the latter by looking into different definitions of online monitoring. Based on these definitions we devise a spectrum of online monitoring techniques
wrt. component-based systems. Furthermore, we also define our own terms to identify
and distinguish between the techniques in the spectrum. This is intended to help the
reader understand the design space that we need to explore for augmenting the detectEr
RV tool with synchrony in Chapter 3.
Following this, in Sec. 2.3 we present an overview of the actor model with respect
to Erlang − a programming language that implements this model. This is intended to
aid the reader in understanding the core concepts of actor-systems, along with technical
8

2. Background

(implementation-oriented) issues regarding the Erlang programming language.
Finally, in Sec. 2.4 we present a detailed overview of the detectEr RV tool, particularly about its specification logic. This is aimed to aid the reader in understanding the
syntax and semantics of the logic used by this tool, upon which we build and extend in
this dissertation.

2.1

Runtime Verification, Adaptation and Enforcement

Ensuring formal correctness for actor-based, concurrent systems is a difficult task, primarily because exhaustive, static analysis verification techniques such as model-checking
quickly run into state-explosion problems. This is typically caused by multiple thread
interleavings of the system being analysed, and by the range of data the system can input
and react to.
We therefore look into three different (yet related) dynamic monitoring techniques
that overcome the issues of static analysis techniques by verifying only a single execution, normally the currently executing one. In literature the boundaries between these
techniques are often blurred; we therefore want to disambiguate between these monitoring techniques and establish upfront what we mean by the terms Runtime Verification
(RV), Runtime Adaptation (RA) and Runtime Enforcement (RE).
The first technique that we consider is Runtime verification (RV) [10, 55]. This
is a lightweight dynamic verification technique that provides an appealing compromise
towards ensuring a degree of formal correctness without running into such scalability
issues. These issues are avoid by only verifying that a specific execution of a system
(usually the currently executing one) satisfies or violates a specific correctness property.
In most runtime verification settings [10, 38, 39, 33] a correctness property ϕ is generally specified in terms of a logic with precise formal semantics, and then automatically
synthesised into an executable monitor M(ϕ). This monitor is essentially the executable
implementation of the abstract property, which actually checks whether a given execution trace of a specific system, violates (or satisfies) the property it was derived from. As

9

2. Background

illustrated in Fig. 2.1, the monitors in runtime verification usually have a passive role
[10, 39]. In fact, they are generally concerned exclusively with receiving system events,
analysing them and detecting (flagging) violations (or satisfactions) of their respective
correctness properties. Hence RV monitors refrain from directly modifying the system’s
behaviour in any way.

Figure 2.1: Runtime Verification
Monitors in Runtime Adaptation [63, 51, 49, 67] break the passivity of RV monitors.
In fact, RA monitors are generally provided with more control over the system, enabling
them to automatically execute adaptation actions after analysing a particular sequence
of system events. For instance as shown in Fig. 2.2, rather than flagging violations, RA
monitors can therefore execute adaptation actions upon detecting an event sequence that
denotes incorrect behaviour. In this way they aim to mitigate and reduce the effects of the
detected violation. The adaptation actions executed by the monitor, do not necessarily
correct or revert the detected misbehaviour [59, 51]; instead they change certain aspects
of the system as it executes, with the aim of preventing either future occurrences of the
same error, or of other errors that may potentially occur as a side-effect of the detected
violation.
However, the use of runtime adaptation goes beyond self-healing [2] i.e., detecting
and mitigating misbehaviour. In fact RA can also be used to optimise [2, 51] the system’s
behaviour based on the information collected by the monitor, e.g., switch off redundant
processes when under a small load, or increase processes and load balancing when under
a heavy load. These type of adaptations contribute towards making the monitored system
10

2. Background

Figure 2.2: Runtime Adaptation
more efficient.
Runtime Enforcement (RE) [36, 57, 56] is the strictest monitoring technique, whereby
correct behaviour is kept in line by anticipating incorrect behaviour and countering it
before it actually happens. In fact as depicted in Fig. 2.3, in runtime enforcement techniques the monitor and the system are so tightly coupled to the extent that the monitor
acts as a proxy which wraps around the system and analyses its external interactions (see
the dotted-line in Fig. 2.3). In this way the monitor is thus able to either drop incorrect
events by preventing certain system actions from occurring; or else add system events by
executing actions on behalf of the system, that should have been executed by the system
but where not [57, 56].

Figure 2.3: Runtime Enforcement
The distinction between RV, RA and RE is however often blurred in the current liter11

2. Background

ature. For instance, we find a number of RV tools such as [25, 24, 23, 18] that allow for
user defined actions to be executed whenever certain conditions are met. This distinction
is further blurred as Runtime Adaptation may also be used to achieve Runtime Enforcement by anticipating system misbehaviour and executing stricter and more corrective
adaptations. This may require adaptations to be tailor-made for the respective system, to
totally prevent incorrect behaviour from manifesting. However, unlike RE, RA typically
allows for violations to occur, but then executes remedial actions to mitigate the effects
[51] of the committed violations. During the course of this thesis we focus on Runtime
Adaptation as defined in Fig. 2.2.

2.2

Synchronous and Asynchronous monitoring

As developing a runtime adaptation framework on top of a runtime verification setup
requires introducing a degree of synchrony in our monitoring, we look into the different
types of monitoring techniques found in the current literature. Furthermore, as monitors
are usually derived from high-level properties, the same property may be converted into
different types of monitors which may apply different instrumentation techniques for
checking whether the property was satisfied or not.
In Sec. 2.2.1 we therefore conjecture that there are two main classes of monitoring
approaches, namely, Online and Offline. Particularly, we look into the latter class (i.e.,
offline) as this is often ambiguated with asynchronous monitoring, whereas for the former class (i.e., online) in Sec. 2.2.2 we discuss the different definitions that one can find
in the current RV literature. Based on these definitions, in Sec. 2.2.3 we present a spectrum of online monitoring approaches wrt. to component-based systems, that vary in the
level of coupling and control that they posses over the system’s components.

2.2.1

Offline and Online monitoring

We divide runtime monitoring into the following two main classes: (i) Online monitoring
and (ii) Offline monitoring. Although our work deals exclusively with online monitoring,
12

2. Background

we opt to define offline monitoring such that we are able to distinguish it from other
monitoring techniques.
Online Monitoring
In an online monitoring setup [55, 10, 31, 44], an executing system is dynamically monitored for violations (or satisfactions) during the course of its execution. Online monitors
are therefore developed in such a way that they execute alongside the system by verifying its execution in an incremental fashion [55, 10]. This means that the monitor must
be able to receive notifications about relevant events occurring in the executing system
and make a decision based on the current information collected so far.
As online monitors are developed to verify currently executing systems, they are also
capable of making early detections. This is sometimes exploited by RV tools (such as
in [25, 24]) to allow for the execution of user defined actions that may potentially be
used to mitigate the corruption suffered by the system as a result of a property violation.
The primary disadvantage of online monitoring is that it imposes an inevitable runtime
overhead on the system given that additional monitoring code is added to the system.
As runtime overheads are a very undesirable side effect of online monitors, a good deal
of effort is usually devoted to create highly efficient monitors that keep this overhead as
low as possible.
Offline Monitoring
Unlike online monitoring, in offline monitoring [55, 10, 19, 35, 61] the system is not
directly monitored as it executes, instead the relevant system events are recorded as an
execution trace inside a data store. Once the monitored system terminates (or whenever
a satisfactory number of events have been recorded) the collected execution trace is
forwarded to the offline monitor. The offline monitor, which is entirely independent
from the system, then proceeds by inspecting the system events recorded in the trace,
and given that the trace provides enough information, it deduces whether the property it
represents was satisfied or violated.
13

2. Background

Offline monitors are particularly suitable for properties that can only be verified by
globally analysing the complete execution trace which is generated once the system
stops executing. In fact some properties may require to be globally analysed using a
backward traversal [61, 66] of the trace. This monitoring mode is less intrusive than
online monitoring, as it does not interfere with the system except for the logging of
events, thus imposing much less overheads. This, however, comes at the cost of late
detections, given that violations are detected either after the system stops or else after
the system is done executing a considerably large amount of actions which are recorded
and then inspected by the monitor.

2.2.2

Varying definitions of Online Monitoring approaches

In the current literature we often encounter different definitions of online monitoring
which usually vary in two aspects: (i) the level of coupling between the monitor and the
system, and (ii) the level of control that the monitor has over the system.
The definition of synchronous online monitoring presented in [33, 31, 9], requires
the system to work in lockstep with the monitoring code, whereby the entire monitored
system is bound to block and wait until the necessary monitoring code is done handling
an event generated by a system component. These type of online monitors are therefore
very tightly coupled to the system and have a very tight level of control over the system
to the extent that they cause all system components to block even when only a single
component generates an event. As this extreme synchronous monitoring technique uses
a high amount of synchronisation is generally appealing for inherently synchronous systems [31, 9] such as circuits and embedded systems.
The work presented in [19, 21], implement a less stringent definition of synchronous
online monitoring in which the monitors are only allowed to block the concurrent component which generates the event. In this way other concurrent components may carry
on with their normal execution, even while the component which generated the event
is interrupted until the required monitoring is carried out. Although these monitors still
posses a relatively tight level of coupling and control over the system, they are able to
14

2. Background

provide timely detections without resorting to a high use of synchronisation as opposed
to the definitions presented in [33, 31, 9].
Yet another online monitoring approach is proposed in [27, 22], allowing the user to
manually specify synchronisation checkpoints where the system (or certain parts of it)
should temporarily stop and wait for the monitor to finish handling monitoring requests.
In this way the system is able to asynchronously send monitoring events to the monitor,
without waiting for the monitoring code to complete. Explicit checkpoints can then
be employed to temporarily block certain parts of the system thereby increasing the
monitor’s level of control over the system which allows the lagging monitor to catch up
with the system execution. The use of checkpoints are especially useful when monitoring
for the occurrence of certain safety-critical events, in which late detection is not feasible.
Checkpoints therefore permit the user to optimize the synthesised monitors by manually
determining the level coupling and control that the monitor possesses over the system.
In [66] synchronous monitoring is defined as a monitoring approach which provides
timely detections. This definition describes an even less stringent monitoring approach
in which the monitors are loosely-coupled from the system yet retain enough control over
the system so that they can provide timely detections. We conjecture that implementing
synchronous monitoring wrt. this definition requires monitoring most of the system
events asynchronously, yet utilise synchronisation at strategic points which might lead
to a violation. In this way violations are still detected in a timely manner.
Finally, in this thesis we follow the definitions for asynchronous online monitoring
given in [27, 66, 22, 23]. These definitions state that although the synthesised monitors
still execute alongside the system, they are however loosely-coupled from the system to
the extent that they have barely any control over the monitored system and may suffer
from late detections. In fact whenever a specified event is performed by the system, an
event notification is asynchronously forwarded to the monitor without suspending any
part of the system. The monitor is then able to verify the received event notifications at
its own pace independently from the system. Importantly note that unlike offline monitors (defined in Sec. 2.2.1), asynchronous monitors still execute alongside the system

15

2. Background

and still have to analyse the systems trace of events in an incremental fashion. Due to
this lack of synchrony and control, asynchronous monitors are presumed to impose considerably lower overheads compared to other approaches. However, most of the time an
asynchronous monitor is unable to detect violations immediately and carry out effective
mitigation actions.

2.2.3

The Spectrum of Online Monitoring Approaches

Based on the range of definitions presented in Sec. 2.2.2, we devise a spectrum of online monitoring approaches which we explain wrt. component-based systems, whereby
components represent concurrent entities (e.g., node, thread, actor, . . . ) that can be in
either two states, namely blocked or running. As depicted in Figure 2.4, this spectrum
ranges from a tightly coupled completely-synchronous monitoring approach on one end,
to a loosely-coupled completely-asynchronous monitoring approach on the other end.
The spectrum also presents currently known online monitoring approaches that lie in
between these two extremes. These middle way approaches provide a tradeoff between
the level of coupling and control that the monitor has upon the system. It is generally assumed that monitoring approaches that are closer to the synchronous end of the
spectrum tend to posses a higher level of control over the system, which comes at the
expense of higher the overheads. Conversely, approaches which are closer to the opposite end are generally presumed to be more efficient. However, so far we are not aware
of any publications that conducted a thorough investigation to confirm (or reject) this
general assumption.
CS

SMSI

AMC

AMSD

Tightly Coupled

CA
Loosely Coupled

Figure 2.4: The Online monitoring Spectrum.

16

2. Background

Completely-synchronous Monitoring (CS)
As depicted in Fig. 2.5, Completely-synchronous monitoring [33, 31, 9] refers to the
synchronous extreme of the spectrum. This implies that the system and the monitors
are extremely tightly coupled together to the extent that whenever an event occurs in
one component of the system, such as C1, the entire system execution is interrupted (as
shown by (1) in Fig. 2.5). The system components remain blocked until the necessary
monitoring checks are performed, in which case the monitor unblocks the components
(as shown by (2)).

Figure 2.5: Completely-synchronous Monitoring (CS)
Since completely-synchronous monitoring generally provides immediate detections,
it can therefore be used to effectively mitigate incorrect behaviour. This approach is
however highly intrusive as it introduces an unnecessarily high level of synchronisation that might lead to an infeasible performance degradation on the monitored system.
Completely-synchronous monitoring is generally applied on small synchronous systems
[31, 9] such as embedded systems and circuits.
Synchronous Monitoring with Synchronous Instrumentation (SMSI)
Synchronous Monitoring with Synchronous Instrumentation [21, 19] is a monitoring approach which is closer to a completely-synchronous approach yet is less intrusive. In
fact as shown in Fig. 2.6, this approach assumes that whenever a single component of
17

2. Background

the monitored system, such as C1, executes a specified event, then only the execution
of this component is interrupted (as shown by (1) in Fig. 2.6). This component remains
blocked until monitoring completes, in which case the component is reset to a running
state (as shown by (2)).

Figure 2.6: Synchronous Monitoring with Synchronous Instrumentation (SMSI)
Hence synchronous instrumentation monitoring reduces the level of overall synchronisation by relinquishing control over the other components, thereby allowing them to
keep executing whenever a certain component generates an event. However, this monitoring technique still posses a relatively high control over the system as it still needs to
synchronously inspect each and every event, even though it only interrupts the normal
execution of the component which generated the event until monitoring completes.
Asynchronous Monitoring with Checkpoints (AMC)
This approach [27, 22, 33] allows for an asynchronous decoupling between the system
(or parts of it) and monitor executions yet also provides the user with the ability to specify checkpoints where the decoupled system and monitor executions should synchronise.
We conjecture that in terms of component-based systems, checkpoints may also be associated with specific system components. For instance as shown by (1) and (2) in Fig. 2.7,
an RV user may use a checkpoint specify that certain system components such as C1 and
C2 should temporarily block their execution and synchronise. Such synchronisation can
18

2. Background

be done during periods where the system is not required to be responsive, and therefore
affords to wait for the monitor to catch-up with the system execution.

Figure 2.7: Asynchronous Monitoring with Checkpoints (AMC)
As shown by (3), once the monitor synchronises with the system, it unblocks the
respective system components and allows them to continue generating further event notifications. An appealing feature of this monitoring approach is that it enables the user to
manually determine the level of synchrony and control that the monitor can posses over
the system. The user may also use this mechanism to specify that the system should
stop and wait for the monitor’s analysis to complete when certain safety-critical events
occur during execution [27]. This ensures that safety-critical violations are detected in a
timely fashion thus permitting the monitor to effectively react to the violation and possibly invoke mitigating actions. To further reduce the level of monitor intrusion, the user
may specify that the system should keep on executing whenever it generates non-safety
critical events.

19

2. Background

Figure 2.8: Asynchronous Monitoring with Synchronous Detection (AMSD)
Asynchronous Monitoring with Synchronous Detection (AMSD)
Asynchronous Monitoring with synchronous detection (based on the definition given in
[66]) is yet another monitoring approach that lies closely to completely-asynchronous
monitoring in our spectrum. This approach uses minimal synchronisation to ensure synchronous (timely) detections, by synchronising only for system events that might directly
contribute to a violation. In Fig. 2.8 we illustrate this concept wrt. an example that assumes a simple transaction system which must satisfy the following invariant property:
Property 3. A user cannot make a transaction before a login.
Property 3 is thus only violated whenever a transaction event (trans) is perceived without a preceeding login event. This means that to timely detect violations of property3 it
suffices that the monitor synchronises only with a system component whenever this performs a transaction event. As shown by (1) in Fig. 2.8, only component C2 is blocked,
as this produced a transaction event that may lead to a property violation. By contrast C1
kept on executing even though it produced a login event. In (2) we see that the monitor

20

2. Background

managed to detect that C2 violated property3, as it did not login prior to performing the
transaction. The detection was made in a timely fashion as C2 was not allowed (in (1))
to execute any further after committing the transaction event that lead to the violation.
Furthermore in (2) one can notice that C1 blocked waiting for the monitor’s verdict as
a result of producing a transaction event, in which case the monitor detects that C1 had
already performed a login event meaning that it did not violate property3. Hence in (3)
the monitor allows C1 to proceed.
Completely-asynchronous Monitoring (CA)
Finally, in Completely-asynchronous monitoring [27, 39, 22, 23] the monitors are designed to be as loosely coupled as possible from the system they are monitoring. In fact
completely-asynchronous monitors are designed to listen for system events and handle
them in the “background” without interfering in any way with the system execution.
Hence, as shown in Fig. 2.9, in completely-asynchronous monitoring, the system is allowed to proceed immediately after placing an event notification (e.g., C1 event 1) in the
monitor’s buffer. The monitor can then independently read the event notifications from
its buffer and carry out the necessary checks at its own pace.

Figure 2.9: Completely-asynchronous Monitoring (CA)
Although this approach imposes minimal intrusion over the monitored system, it
suffers from late detection which makes it highly-unlikely for an asynchronous monitor
to be able to effectively mitigate the detected misbehaviour. As offline monitoring [33,
66], is inherently asynchronous, it is sometimes classified as being an asynchronous
monitoring approach. However, we conjecture that offline monitoring generally requires
21

2. Background

prerecorded (generally complete) traces, while CA monitoring can incrementally analyse
partial traces that can be extended as the system proceeds with its execution.
Conclusion
We have introduced the concept of a spectrum of online monitoring approaches, and
explained each approach wrt. component based systems. The spectrum consists in:
• Completely-synchronous monitoring on one end — this achieves an extremely
high level of control over the system that could potentially deter monitoring efficiency; and
• Completely-asynchronous monitoring on the opposite end — this is presumed to
provide efficient monitors with very low intrusion over the monitored system, by
sacrificing the level of control over the monitored system, including timely detections.
We have also identified a number of middle-way approaches that lie in between these
two online monitoring extremes in our spectrum. We also conjecture that there may
be other middle-way approaches apart from the ones that we identified which still need
to be explored. The identified approaches however allow us to better understand the
design space that we need to explore and assess in Chapter 3 in order to achieve our first
objective (see objective (i) in Sec. 1.1), i.e., that of increasing the level of synchrony in
the detectEr RV tool while preserving monitoring efficiency.

2.3

The Actor-Model and Erlang

The component based systems that we consider in this thesis are actor-based [5] systems
written in a programming language called Erlang [7, 58]. In Erlang processes (i.e.,
actors) are threads of execution that are uniquely identified by a process identifier and
posses their own local memory. Erlang processes execute asynchronously to one another,
interacting through asynchronous messages instead of sharing data.
22

2. Background

In fact, processes can only communicate by explicitly sending a copy of their data
to the destination process (using the unique identifier as address). These messages are
received at a process mailbox (a form of message buffer) and can be exclusively read at a
later stage by the process owning the mailbox. Since asynchronous messages may reach
a mailbox in a different order than the one intended, the (mailbox) read construct uses
pattern matching to allow a process to retrieve the first message in the mailbox matching
a pattern, possibly reading messages out of order. Whenever a retrieved pattern is not
matched, the executing process blocks, waiting for a message matching this pattern to
reach its mailbox. Apart from the mailbox, actors possess their own private state known
as the process dictionary, in which they can associate data entries with a unique key,
which can then be used in different points during the actor’s execution to retrieve or
modify the stored data entry.
Processes may also be registered under a specific name. This allows other actors
to send messages to a registered process by using its name rather then its process id.
This is useful to avoid having to communicate the process id of an (important) actor,
to the other actors. Furthermore, processes may spawn other processes at runtime, and
communicate locally-known (i.e., private) process identifiers as messages. A Process
may not only use the known process identifiers to communicate messages, but also to
forcibly terminate or link processes [58]. Implementation wise, Erlang processes are
relatively lightweight [7], and language coding practices [58] recommend the use of
concurrent processes wherever possible. These factors ultimately lead to systems made
up of independently-executing components (i.e., processes) that are more scalable, maintainable, and use the underlying parallel architectures more efficiently [17].
At its core, Erlang is dynamically-typed, and function calls with mismatching parameters are typically detected at runtime. This implies that Erlang provides very few
static guarantees thereby making it prone to more dynamic errors; this makes Runtime
Adaptation an appealing approach for detecting and mitigating dynamic errors. Function definitions are named and organised in uniquely-named modules. Within a particular module, there can be more than one function with the same name, as long as these

23

2. Background

names have different arities (i.e., number of parameters). Thus, in Erlang, every function
is uniquely identified by the triple module name:function name/arity e.g., math:mul/2
identifies a function named mul which takes two arguments and is defined in the math
module.
Erlang also offers a number of fault-tolerance mechanisms including process linking
[7, 58], whereby whenever a process fails i.e., terminates abnormally, all the other actors
linked to it are terminated as well. However, Erlang also provides a trapping mechanism
whereby rather then failing, the linked processes are sent a message in their mailbox
notifying them that a linked process has failed. Furthermore, linking may be either bidirectional or uni-directional [7]. This means that if an actor A links with B using a
bi-directional link, then should either one of the linked actors crash, then the other actor
crashes as well (or is notified in case of trapping). Whereas if A links with B using a
uni-directional link, then only A gets terminated (or notified) in case the linked actor B
crashes, i.e., if A crashes, B remains unaffected.
Erlang systems are often structured by the supervisor pattern which is built on linking and trapping, in which processes at the system fringes are encouraged to fail-fast
when an error occurs (as opposed to handling the error locally). In this way the linked
supervisor processes [17] is kept responsible to carry out the necessary adaptations for
detecting the crash and handling the error. However, misbehaviour does not necessarily
lead to crashing an actor, for instance if an invalid computation is saved to a database,
then none of the actors are likely to crash. Therefore, a more refined adaptation mechanism built over a runtime verification setup provides a good alternative for detecting and
mitigating such misbehaviour.
As actors in Erlang are inherently processes that interact using asynchronous message passing, they can be formally represented as Labelled Transition Systems (LTS)
[70, 40, 14, 39] where at an abstract level, computation can be viewed as a graph of
states connected by labelled arcs denoting the communicated messages.

24

2. Background

2.4

detectEr − A concurrent RV tool

In [39], the authors present detectEr − a provably correct, actor-based runtime verification tool written in Erlang. This tool uses a syntactic subset of the modal µ-calculus
called Safe Hennessy-Milner logic (sHML) [3] to specify safety properties over Labelled Transition System (LTS). As Erlang programs can be formalised in terms of an
LTS, sHML can therefore be used to specify properties for Erlang systems as well.
Moreover, the monitors synthesised by this tool, employ a completely asynchronous
(CA) monitoring approach for detecting the runtime violations of the respective formulas. This implies that the synthesised monitors barely have any control over the monitored system. Asynchronous monitoring is in fact achieved using the tracing mechanism
offered by the EVM OTP platform libraries [58] which do not require instrumentation
at source-code level. Instead VM directives generate trace messages for specified execution events that are sent to a specially-designated tracer actor i.e., to the synthesised
monitor.
Hence to introduce synchrony in detectEr it is likely that we need to completely
redesign the mechanism for forwarding trace messages to the monitor, such that the
system and the monitor are allowed to synchronise whenever a trace event is exchanged.

2.4.1

The Syntax

The tool’s sHML syntax [39, 3] is defined inductively using the BNF in Fig. 2.10. It is
parametrised by a set of boolean expressions, b, c ∈ Bool, equipped with a decidable
evaluation function, b ⇓ v where v ∈ {true, false}, and a set of system actions α, β ∈ Act.
It assumes two distinct denumerable sets of variables:
• Term variables, x, y, . . . ∈ Var — used in action patterns to quantify over data
values, and to evaluate boolean conditions defined in if-statements; and
• Formula variables, X, Y, . . . ∈ LVar — used to define recursive (logical) formulas.
Note that even though we work up-to α-conversion (for both variable sets), detectEr
accordingly renames duplicate variables during a pre-processing phase.
25

2. Background

Formulas ϕ, ψ ∈ Frm include: truth tt, and falsity ff, conjunctions, ϕ&ψ, modal necessities, [e]ϕ for specifying particular actions that the system may perform, maximal
fixpoints to describe recursive properties, max X. ϕ, and if-statements to analyse system
data, if b then ϕ else ψ. A necessity formula, [e]ϕ specifies a patten e denoting the following system actions:
• message input: e.g., i?3 − process i must receive the value 3;
• message output: e.g., i. j!3 − process i must send j the value 3;
• function call: e.g., i . call(mod, fun, [1, test]) − process i must call a function
fun, which is defined in module mod, using arguments [1, test]; and
• function return: e.g., i . ret(mod, fun, 2, {res, 5}) − process i must return from
function fun (which takes 2 arguments) with return value {res, 5}.
Remark 1. Although from a theoretical standpoint sHML specifications should limit
themselves to input and output actions so as to treat individual components as blackboxes, monitoring for function calls and returns is essential. This is because certain
output and input actions in actual Erlang implementations may be abstracted away inside the function calls of system libraries, making them (directly) unobservable to the
instrumentation mechanism. However, these actions (and the data associated with them)
can still be observed indirectly, through the calls and returns of the functions that abstract


them.

A pattern e in a necessity [e]ϕ may also contain term variables that pattern-match
with actual (closed) system actions, thus acting as a binder for these variables in the
continuation subformula ϕ; to improve readability, we sometimes denote term variables
that are not used in the continuation subformula as underscores, . Similarly a recursive
definition max X. ϕ, serves as a binder for recursive variable X in ϕ.
Example 2.4.1. Recall Ex. 1.1.1 in which we define safety Property 1 for monitoring
invariant behaviour in the actor-based server illustrated in Fig. 1.1. Using detectEr’s

26

2. Background

syntax we can now formally represent this property as the following formula:


ϕ = max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [ .y!err] ff)
def


(2.1)

The tool detectEr allows us to specify the recursive safety property (2.1) (above) as
follows: it requires that, from an external viewpoint, every increment request received
by i, i.e., action i?{inc, x, y}, is followed by a service answer from j to the client address
bound to variable y, with value x + 1 as specified by action j.y!{res, x + 1} (and then
recurs through variable Y). However, increment requests followed by an error message
sent from any actor back to y (as defined by action .y!err), represent a violation (i.e.,
ff). The detectEr compiler is then used to automatically synthesise a concurrent actor-

based monitor corresponding to property (2.1). It also adds directives to Erlang VM to


forward system execution events to the synthesised monitor [39].

Ex. 2.4.1 is just a pathological example showing that sHML is ideal for specifying
infinite computation (using maximal fixpoints) in the form of invariant, safety properties
such as formula (2.1). In the case of actor-based systems, this essential as the actor
model is usually used to develop non-terminating, reactive systems [4, 43, 7].

2.4.2

The Model

The semantics [39] for sHML, given in Fig. 2.10, is defined over a formal Labelled
Transition System (LTS) representation of an arbitrary Erlang program in the style of
the operational models (for Erlang programs) presented in [70, 40, 14, 39]. In these
operational models, the runtime semantics for Erlang programs is formally represented
in the form of a set of actors A, B ∈ Actr composed in parallel. These actors can reduce
τ

either by using internal (unobservable) τ-actions, A −→ B, or else by using external
α

observable α actions, A −→ B, i.e., actions that an external actor such as the monitor can
τ

α

τ

perceive. Weak transitions [39] for modeled Erlang systems i.e., A (−→)∗−→ (−→)∗ B, are
α

often represented as A =⇒ B.

27

2. Background

BNF Syntax.
ϕ, ψ ∈ Frm ::= tt | ff | ϕ & ψ | [α]ϕ | X | max X. ϕ | if b then ϕ else ψ
Satisfaction Semantics.
(A, tt) ∈ R
(A, ff) ∈ R
(A, if b then ϕ else ψ) ∈ R
(A, ϕ & ψ) ∈ R
(A, [e]ϕ) ∈ R
(A, max X. ϕ) ∈ R

always
never
if (b ⇓ true and (A, ϕ) ∈ R) or (b ⇓ false and (A, ϕ) ∈ R)
if (A, ϕ) ∈ R and (A, ψ) ∈ R
α

if A ==⇒ B and mtch(e, α) = σ then (B, ϕσ) ∈ R
if (A, ϕ[(max X. ϕ)/X] ∈ R)

Violation Semantics.
A, t v tt

never

A, t v ff

always

A, t v if b then ϕ else ψ if (b ⇓ true and A, t v ϕ) or (b ⇓ false and A, t v ψ)
A, t v ϕ & ψ if A, t v ϕ or A, t v ψ
A, αt v [e]ϕ
A, t v max X. ϕ

α

if A ==⇒ B and mtch(e, α) = σ and B, t v ϕσ
if A, t v ϕ[(max X. ϕ)/X]

Figure 2.10: The Logic and its Semantics

2.4.3

The Semantics

In [39] the authors define satisfaction semantics for the sHML logic over arbitrary Erlang
programs modelled as an LTS (as discussed in Sec. 2.4.2), to denote how an sHML
formula ϕ is satisfied by an Erlang program A.
The satisfaction semantics state that a satisfaction relation R ∈ (Actr × Frm) is always satisfied by tt but never satisfied by ff, and in the case of conjunction formulas,
(ϕ & ψ), both branches must be satisfied. Conditionals, if b then ϕ else ψ, are only satisfied if the taken branch is satisfied. Modal necessities, [e]ϕ, are satisfied in two ways:
either trivially i.e., when necessity pattern e does not pattern match action α; or else
since the resulting actors B satisfy ϕσ after performing a pattern-matchable event action
α that yields substitution environment σ :: Var * Val.
Finally recursive definitions, max X. ϕ, are satisfied by a set of actors A, if the unfolded formula ϕ[(max X. ϕ)/X] (where every occurrence of formula variable X in ϕ is
substituted by max X. ϕ) is also satisfied by A. This implies that recursive formulas may
28

2. Background

potentially be satisfied by infinite computation.
In [39] the authors also argue that even though sHML can specify infinite behaviour,
the execution traces that violate safety properties (expressible through sHML) are always
finite. This implies that since runtime monitors can only work with finite traces [10, 55],
they are therefore able to detect violations from finite violating traces. Based on this
fact the authors formulate the violation semantics (presented in Fig. 2.10) and prove that
these correspond to their satisfaction semantics.
The violation relation, denoted as v , is the least relation of the form (Actr × Act ∗
×Frm) satisfying the rules defined in Fig. 2.10 (follows from the semantics given in
[39]). Regardless of the contents of a trace t, all actor systems A can never violate
tt, yet always violate ff. Conjunction formulas, ϕ & ψ, cause a violation if either ϕ or

ψ lead to a violation. Conditionals, if b then ϕ else ψ, only cause a violation in case
the taken branch (ϕ or ψ) causes a violation. All actor systems A violate a necessity
formulas [e]ϕ whenever a pattern-matchable action α is performed (yielding substitution
σ :: Var * Val), and the resulting actors B that are transitioned violate ϕσ. Note that
actors that do not perform any pattern-matchable actions trivially satisfy [e]ϕ, and hence
cannot lead to a violation. A recursive formula max X. ϕ is violated whenever the
unfolded formula ϕ[(max X. ϕ)/X] is also violated.
The violation relation is only satisfied by finite traces, as opposed to the satisfaction
relation which can also be satisfied by infinite traces. Hence the violation semantics
makes it easier for us to understand how the synthesised runtime monitors flag a violation
wrt. a finite trace. In this dissertation we therefore focus on the violation semantics.

2.5

Conclusion

We have provided the reader with the necessary background information for better understanding our work. We have also established what the terms Runtime Verification,
Runtime Adaptation and Runtime Enforcement refer to in the context of this dissertation, given that the distinction between these techniques is not always made clear in the

29

2. Background

current literature.
We have also clarified how we distinguish between Offline and Online monitors,
and expanded on the latter by looking into different definitions of online monitoring,
upon which we presented a spectrum of online monitoring techniques wrt. componentbased systems. Furthermore, we also define our own terms to identify and distinguish
between the techniques in the spectrum. By identifying this spectrum we are now in a
better position to understand the design space that we need to explore for introducing
synchrony within the detectEr RV tool in Chapter 3.
Finally we provided an overview of the actor model as implemented by a host language Erlang, followed by a detailed overview of our target RV tool, detectEr, on which
we build our runtime adaptation framework for actor systems in Chapter 4.

30

3. Assessing the Impact of Increasing
Monitor Synchronisation

Effective runtime adaptation requires achieving a tighter level of control over the system
so as to provide timely detections of violations, thereby allowing the monitor to mitigate
erroneous actors before these are allowed to proceed. In this chapter we therefore conduct preliminary investigations for implementing synchronous monitoring mechanisms
over an inherently asynchronous architecture (i.e., the actor model).
We conduct this investigation by exploring the design space of monitoring mechanisms identified in Sec. 2.2.3, and assess the repercussions that a monitored actor system
may suffer due to synchrony. Although one might easily infer that synchronous monitoring incurs higher overheads as opposed to its asynchronous counterpart, we set out to
investigate and quantify the overhead increase in an inherently asynchronous setting such
as ours. This is important as in the case of asynchronous platforms where synchrony is
not natively supported (such as the actor-model), introducing synchronous monitoring in
a naive way might highly affect the performance of the monitored asynchronous system.
This opposes inherently synchronous settings [31, 9] in which the impact of using synchronous monitoring might not be as significant. Quantifying the impact of synchronous
monitoring on asynchronous actor-systems will then guide our design decisions for implementing adequate adaptation mechanisms in Chapter 4.
We start our investigations by first introducing tight, coarse-grained synchronisation
31

3. Assessing the Impact of Increasing Monitor Synchronisation

in Sec. 3.1, as this is more straightforward to understand and implement. Following
this in Sec. 3.2, we explore techniques for reducing the level of synchrony to a more
fine-grained level, while still being able to achieve timely detections. Since synchronous
communication is not native to asynchronous platforms (such as the actor model), introducing synchrony requires instrumenting the system to encode a synchronisation protocol that forces the system to wait until the monitor is done verifying a reported event.
Encoding such a protocol may therefore introduce overheads that must be minimised as
much as possible.
Hence in Sec. 3.3, we finally assess and quantify the monitoring overheads when
using coarse-grained synchrony, fine-grained synchrony and asynchronous monitoring.
We conduct our experiments wrt. Yaws − a third-party, actor-based webserver written
in Erlang, by monitoring it for several safety properties expressed in detectEr’s logic,
using these three different monitoring techniques.

3.1

Introducing Synchronous Monitoring

There are a number of ways how one can layer synchronous monitoring atop of an inherently asynchronous computational model, such as the actor model. For instance, one can
insert actual monitoring functionality within the sequential thread of execution of each
actor (in the style of [68, 19]) and then have the monitoring code (scattered across independently executing actors) synchronise, as required, in a choreographed setup [68, 38].
Instead, we opt for an orchestrated solution as shown in Fig. 3.1 (below).
In this setup, monitored actors are instrumented to synchronously report monitored
actions as event notifications to a (conceptually) centralised monitoring setup that receives all reported events and performs the necessary checking. Note that although
conceptually centralised, the orchestrator monitor is not monolithic [39] as it consists
of independent, concurrent sub-monitors that execute independent of one another.
Furthermore, it is imperative that such a synchronous event reporting mechanism
must prevent a system actor from progressing until the monitor is done checking its syn-

32

3. Assessing the Impact of Increasing Monitor Synchronisation

Figure 3.1: A High-level illustration of Synchronous Instrumentation Monitoring.
chronously reported event. For instance in Fig. 3.1, system actor A must only be allowed
to progress up to event A:evt2 after the monitor completes checking the previously reported event, i.e., A:evt1. However, while A is waiting for the monitor to finish verifying
its reported event (i.e., A:evt1), the other concurrent actors B and C are still allowed to
progress independently until they synchronously report their own events.
There are various reasons for opting for such an orchestrated setup. The main reason
being that it allows us to perform a like-with-like comparison with the existing asynchronous setup present in detectEr, thus obtaining a more precise comparison between
the relative overheads of synchronous and asynchronous monitoring. Furthermore, this
setup conforms to the ideology of the actor oriented paradigm [7, 29], which strongly
encourages the developer to divide large tasks into smaller subtasks that can execute
concurrently whenever this is possible. This is therefore reflected in this setup, as the
monitor and the system are kept as two completely separate entities. Also, by using this
approach the instrumentation code at the system side is kept minimal, thus leaving the
instrumented code close to the original. Moreover, monitoring is consolidated into a
group of concurrent actors that are separate from the monitored system, thereby improving manageability (e.g., parts of the system may crash leaving the monitor unaffected).

33

3. Assessing the Impact of Increasing Monitor Synchronisation

3.1.1

Instrumentation through Aspect-Oriented Weaving

We consider Aspect Oriented Programming (AOP) as an ideal candidate for implementing synchronous event reporting. The instrumentation is carried out using an AOP
Framework for Erlang [54, 41] (in the style of standard AOP tools such as [28]) to add
the necessary instrumentation in the system code.
As detectEr supports the monitoring of output and input actions (i.e., message sends
and receives) along with function calls and returns, we needed an AOP framework that
supports instrumenting all of these actions. The AOP presented in [54] was not ideal
as this only supports function calls and returns; however this was later augmented with
aspects for instrumenting the required input and output actions as a preliminary investigation in [41]. However this AOP framework [41] required further refinements, which
we conducted during the course of this research, as it was still unstable and too coarsegrained1 .
The extended aspect-based instrumentation requires an aspect file that specifies the
actions requiring instrumentation, along with a purpose built module called advices.erl
containing three types of advices used by the AOP injections, namely before advice,
after advice and upon advice advices. For the function call events specified in the
aspect file, the AOP weaves before advice advices reporting the event data to the
monitor before the function is invoked. For message sends and function returns, the
AOP weaves after advice advices to report the committed action to the monitor once
the action is performed. In the case of message sends, this allows for the system to
actually send the required message prior to reporting the event to the monitor. In the
case of function returns, after advice advices are required since return values are
only known after the function actually returns. The weaving concerning mailbox reading events (performed using the recieve construct) was not as straightforward. As the
Erlang recieve construct may contain multiple pattern-matching guarded clauses i.e.,
recieve guard1 -> expression1 ; . . . ; guardn -> expressionn end.
1

The aspects for instrumenting input and output actions did not support for selectively instrumenting
specific send and receive operations which match a specific pattern. This was therefore adding unnecessary
instrumentation.

34

3. Assessing the Impact of Increasing Monitor Synchronisation

The AOP weaves upon advice advice for each guarded expression that matches the
message patten defined by the receive aspects, as specified in the aspect file. For instance if we assume that only guard1 and guard2 match the receive patterns specified in
the aspect file, we get
recieve guard1
guard2
guard3
guardn
end.

->
->
->
->

upon advice(..), expression1 ;
upon advice(..), expression2 ;
expression3 ; . . . ;
expressionn

as opposed to the coarse-grained instrumentation in [41] which adds upon advice to
every guarded expression thereby introducing unnecessary instrumentation. Also note
that at runtime only one recieve guarded expression is triggered. In the case if the
triggered expression is an instrumented one, the necessary pattern-matched data of the
event is extracted and thus can be reported to the monitor by the advice as required.

3.1.2

The Synchronous Instrumentation Protocol

Fig. 3.2 depicts a global implementation overview of the synchronous instrumentation
monitoring (SMSI) protocol for two monitored events, i.e., events e1 and e2. This
protocol denotes the messages exchanged between the instrumentation code injected
in the asynchronous actor system and the monitor in order to achieve a synchronous
event reporting mechanism. In fact this mechanism is achieved through handshakes over
asynchronous messages between the two parties; this technique is a common practice
for encoding synchronous message passing on asynchronous models such as the actor
model [7].
This protocol starts with monitoring loop sending an initial acknowledgement message to the system, signalling it to execute until it produces the next monitored event.
To synchronously report a monitored event, the instrumentation code at the system
side extracts the necessary data associated with the event, sends it as a trace message to the monitor, and then pauses by issuing a receive statement that waits for an
unpause-acknowledgement message from the monitor. Since, the (acknowledgement)
35

3. Assessing the Impact of Increasing Monitor Synchronisation

asynchronous messages may get reordered in transit (potentially interfering with this
protocol) the instrumentation code at the system side generates a unique nonce for every
monitored event and sends it along with the event data. In return, when the monitor sends
back the acknowledgement message, it includes this unique nonce. This allows the instrumentation code to pattern-match for (and possibly read out-of-order) mailbox inputs
containing this nonce, and unblock based on this corresponding acknowledgement.
System
Monitor
ack(init nonce)
...
Actor i commits event e1;
loop(Nonce) →
i gets data d1 from e1;
evt(d1,{i,nonce1})
send ack(Nonce),
i blocks on nonce(e1);
{Evt,Nonce2} = recv event(),
i unblocks with nonce1;
ack(nonce1)
PtrnMatch = handle(Evt),
...
if(PtrnMatch) →
Actor j commits event e2; evt(d2,{ j,nonce2})
loop(Nonce2);
j gets data d2 from e2;
else →
j blocks on nonce(e2);
ack(nonce2)
send ack(Nonce2)
j unblocks with nonce2;
end.
...
Figure 3.2: A High-level depiction of the Synchronous Event Monitoring protocol.
The monitoring loop outlined in Fig. 3.2 corresponds to the pattern-matching functionality required by a necessity formula [e]ϕ. For instance, if the pattern is not matched
(i.e., the necessity is trivially satisfied), it acknowledges immediately to the system to
continue executing and terminates monitoring by issuing a send ack(Nonce2) command to release the blocked system actor. However, if the pattern matches there is still
a chance that a violation can be detected and therefore proceeds by handling the event.
Event handling involves executing internal (non-necessity) actions specified after the
modal necessity (i.e., corresponding to ϕ in [e]ϕ) such as: unfolding recursive definitions,
evaluating if-statement conditions, spawning conjunction submonitors, etc. Crucially, if
ϕ reduces into ff (e.g., after evaluating an if-statement), the monitor refrains from sending back the acknowledgement, thus blocking the offending system indefinitely while
flagging the violation. In a runtime adaptation setup, this allows for effectively executing adaptation actions instead of flagging the violation. Otherwise, the monitor issues
36

3. Assessing the Impact of Increasing Monitor Synchronisation

command loop(Nonce2) to reiterate and release the blocked actor using the delegated
nonce (i.e., Nonce2).

3.2

A Hybrid Instrumentation for Timely Detections

Since synchronous instrumentation monitoring (SMSI) is too coarse-grained (as it synchronises for every system event), we therefore devise an alternative fine-grained synchronisation strategy which achieves timely detections with less synchronisation. The
main idea is to move away from synchronous instrumentation monitoring, towards using
incremental synchronisation to obtain synchronous detection monitoring (AMSD) for
actor systems. This is possible as in order to attain timely detections, the instrumentation need not require certain system components to execute in lockstep with the monitor
for every monitored event leading to the violation. Instead, (expensive) synchronous
event monitoring can be limited to critical actions, (i.e., system actions contributing directly towards a violation), thereby letting the system execute in a decoupled fashion
otherwise.

Figure 3.3: A High-level illustration of Synchronous Detection (Hybrid) monitoring.
For instance in Fig. 3.3 if we assume that events A:evt3 and B:evt2 are the only
37

3. Assessing the Impact of Increasing Monitor Synchronisation

system events that may lead to a violation, we can limit synchronous event monitoring
to these two events only. Note that for now we make a simplifying assumption that for
the logic used by detectEr (given in Fig. 2.10) these critical actions are necessity actions
preceeding (directly or indirectly) a ff forumla as explained in Ex. 3.2.1 below.
Example 3.2.1. Recall Property 1 which we formalised as formula (2.1) in Ex. 2.4.1
(restated below):


ϕ = max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [ .y!err] ff)
def


(2.1)

Based on our simplifying assumption, to synchronously detect a violation in this property, only action [ . y!err] needs to be synchronously monitored, as it precedes a ff
forumla. The other actions (i.e., [i?{inc, x, y}] and [ j . y!{res, x + 1}]) can however be
monitored for asynchronously, without affecting the timeliness of detections.



Remark 2. With this simplifying assumption we assume that the actor that commits the
last action preceeding a violation (ff) is solely responsible for the detected violation,
however this is not always the case.
For instance in the example system given in Fig. 1.1, despite the incrementor being the one to send the error message err to the connected client thus violating (2.1),
this error was caused by an invalid interaction made by the common-interface in some
previous step. Hence strictly speaking to truly achieve a timely detection, the commoninterface should also be forbidden from progressing after performing an interaction that
potentially contributes to the violation, until the monitor performs the necessary checks.
Our assumption however permits us to get a clear indication of the associated performance impacts without introducing too much complexities at this stage. Remember that
for now our main objective is to identify an efficient synchronous monitoring mechanism
upon which we can build in order to introduce runtime adaptation in Chapter 4.

38



3. Assessing the Impact of Increasing Monitor Synchronisation

3.2.1

Logic Extensions

To achieve synchronous detection monitoring we explore a hybrid technique that is capable of switching between synchronous and asynchronous event monitoring, and which
limits synchronous monitoring to critical system actions. To achieve this we extend the
syntax of detectEr’s logic [39] (outlined in Sec. 2.4) by two constructs: a synchronous
false formula and a synchronous necessity formula, with violation semantics analogous
to that of ff and [e]ϕ respectively.
ϕ, ψ ∈ Frm ::= . . . | sff (synchronous false) | [|e|]ϕ

(synchronous necessity)

In the extended logic, formulas carry additional instrumentation information relating to
how they need to be runtime-monitored. By default, all the monitoring is asynchronous,
unless one specifies that a violation is to be synchronously detected, sff, or that a particular system event needs to be synchronously monitored, [|e|]ϕ. Although synchronous
necessities can be used in specification scripts to engineer synchronisation checkpoints
(i.e., to encode AMC; see Sec. 2.2.3) similar to what is presented in [26], in this study
we limit their use to achieving timely-detections. In fact, rather then using synchronous
necessities explicitly we encode synchronous falsities, sff, in terms of synchronous necessities as explained in Ex. 3.2.2.
Example 3.2.2. Recall the system presented in Fig. 1.1 for which we extend and refine
formula (2.1) (restated in Ex. 3.2.1) as (3.1) (shown below). We now distinguish between
two kinds of violations in which: result values w that are less than x +1 are defined to
be asynchronously detected (i.e., if w < x + 1 then ff), while values greater than x+1 are
specified to be synchronously detected (i.e., if w > x + 1 then sff). We also specify that
cases where client y is sent an error (i.e., .y!err) are to be synchronously detected as
well.



 if w = x + 1 then Y

max Y. [i?{inc, x, y}]  ( [ j.y!{res, w}]  & if w > x + 1 then sff
& if w < x + 1 then ff



 ) & ( [ .y!err] sff)
(3.1)

39





3. Assessing the Impact of Increasing Monitor Synchronisation

The new monitor synthesis algorithm requires a pre-processing phase to determine
which events are to be synchronously monitored in order to implement a synchronous
fail. For instance, formulas [ .y!err]sff and [| .y!err|]ff are both monitored in the same
way, in fact the pre-processing function syn enc, presented in Def. 3.2.1, encodes the
former into the latter. As shown by Def. 3.2.1, generally determining the critical actions
to synchronously monitor for implementing a synchronous fail is not as straightforward,
since the first necessity formula preceding a sff may be interposed by intermediate formulas such as conjunctions and if-statements (as in the case of formula (3.1)).



Def. 3.2.1. syn enc : Frm 7→ (Bool × Frm)



h f alse, [|e|]ψ0 i







h f alse, [e]ψ0 i








hb1 ∨ b2 , ψ01 & ψ02 i












hb1 ∨ b2 , if c then ψ01 else ψ02 i
syn enc(ϕ) = 















hb, max X. ψ0 i







htrue, ffi






h f alse, ϕi
def

if ϕ = [e]ψ ∧ syn enc(ψ) = htrue, ψ0 i
if ϕ = [e]ψ ∧ syn enc(ψ) = h f alse, ψ0 i
if ϕ = ψ1 & ψ2 ∧ syn enc(ψ1 ) = hb1 , ψ01 i
∧ syn enc(ψ2 ) = hb2 , ψ02 i
if ϕ = if b then ψ1 else ψ2
∧ syn enc(ψ1 ) = hb1 , ψ01 i
∧ syn enc(ψ2 ) = hb2 , ψ02 i
if ϕ = max X. ψ ∧ syn enc(ψ) = hb, ψ0 i
if ϕ = sff
otherwise

The pre-processing function syn enc recursively inspects a formula ϕ and returns
a more implementable version in which the necessities specifying critical actions are
identified and converted into synchronous necessities. For cases where ϕ reduces to sff,
this function returns a pair containing boolean value true and ff. The boolean value true
indicates that a synchronous falsity sff was found and converted to ff, which is more
implementable. This truth value then allows for the first necessity action declared prior
to sff to be converted into a synchronous necessity. This guarantees that the falsity is
detected on-time. In fact when inspecting necessity actions, [e]ψ, the function recursively inspects the subsequent formula ψ (i.e., it reapplies itself as syn enc(ψ)) and if
the recursive application returns the pair htrue, ψ0 i, it converts the necessity [e]ψ into a
synchronous necessity [|e|]ψ0 . This is possible as the truth value true in the returned pair,
signifies that at least one sff was found somewhere along ψ and converted into ff, thus
40

3. Assessing the Impact of Increasing Monitor Synchronisation

changing ψ into ψ0 which is more implementable. After encoding a normal necessity
into a synchronous necessity, the syn enc function then returns a pair containing the
encoded formula, i.e., [|e|]ψ0 , and truth value f alse, where the latter denotes that the synchronous falsity has already been encoded. This prevents prior asynchronous necessities
from being unnecessarily converted into synchronous ones.
To conduct our experiments we therefore augment the syn enc function as a preprocessing phase in our compiler to encode formulas containing synchronous falsities
into a more implementable versions as shown in Ex. 3.2.3.
Example 3.2.3. In the case of property (3.1) the compiler uses the syn enc function
to check whether there exists at least one path that leads directly to a synchronous fail.
Since action [ j.y!{res, w}] is followed by a conjunction of three if-statements in which
one of them leads to a synchronous falsity, i.e., if w > x + 1 then sff, then this necessity
is converted into a synchronous necessity. More obviously action [ . y!err] is also
converted as it is immediately followed by sff. The compiler therefore converts property
(3.1) into (3.2):



 if w = x + 1 then Y

max Y. [i?{inc, x, y}]  ( [| j.y!{res, w}|]  & if w > x + 1 then ff
& if w < x + 1 then ff



 ) & ( [| .y!err|] ff)
(3.2)

3.2.2







The Hybrid Monitoring Protocol

In hybrid monitoring, both synchronous and asynchronous event monitoring require
code instrumentation, as shown in Fig. 3.4 (below). Asynchronous necessity actions
inject advice functions that send a monitoring message, e.g., event e1, to the monitor
containing: the event details, e.g., d1, and a null nonce; this message is reported without
blocking the system. Upon receiving the null nonce, the monitor determines that it does
not need to send an acknowledgment back to the system. Furthermore, Synchronous
necessity actions are implemented as per synchronous instrumentation (see Sec. 3.2),
where the system attaches a fresh nonce along with the event data to the monitoring
message thereby notifying the monitor that it requires to be acknowledged back.
41

3. Assessing the Impact of Increasing Monitor Synchronisation

System
...
Actor i commits event e1;
i gets data d1 from e1;
...
Actor j commits event e2;
j gets data d2 from e2;
...
Actor i commits event e3;
i gets data d3 from e3;
i blocks on nonce(e3);
i unblocks with nonce1;
...

Monitor
evt(d1,null)

evt(d2,null)

evt(d3,{i,nonce1})
ack(nonce1)

loop(Nonce) →
if(Nonce , null) →
send ack(Nonce)
end,
{Evt,Nonce2} = recv event(),
PtrnMatch = handle(Evt),
if(PtrnMatch) →
loop(Nonce2);
else if(Nonce , null) →
send ack(Nonce2)
end.

Figure 3.4: A High-level depiction of the Hybrid Monitoring Protocol.

3.3

Evaluation

By now the extended version of the detectEr RV tool supports three methods for monitoring actor-based systems, namely:
• Asynchronous Monitoring (CA), which was the only monitoring mechanism originally supported by detectEr;
• Synchronous Instrumentation Monitoring (SMSI), introduced in Sec. 3.1;
• Hybrid Monitoring (AMSD), introduced in Sec. 3.2.
Using these three monitoring approaches, we want to carry out experiments in which we
monitor for several safety properties written specifically for the Yaws webserver. With
these experiments we aim to obtain performance results for each approach in order to be
able to compare and contrast the performance overheads imposed by these monitoring
techniques. The results of these experiments will then serve as guidance for choosing
the appropriate instrumentation mechanism on which we can build our RA framework
in Chapter 4.

42

3. Assessing the Impact of Increasing Monitor Synchronisation

(2) {HandlerPid, next, ClientPort}

(1) connect

Acceptor

Current Handler

Client

(4) HTTP requests

(3)spawn
New Handler

Figure 3.5: Yaws client connection protocol

3.3.1

The Yaws Webserver

Yaws [73, 52] is a high-performance, actor-based HTTP webserver written in Erlang.
For every client connection, this server assigns a dedicated handler actor that listens
for and services HTTP client requests. At its core, its implementation fundamentally
relies on the lightweight nature of Erlang processes to be able to efficiently handle a vast
amount of client connections.
The Yaws protocol for establishing client connections is depicted in Fig. 3.5. This
protocol relies on an acceptor component which upon creation, it spawns a connection
handler to be assigned to the next client connection. Subsequently, the acceptor blocks
waiting for messages in its mailbox, while the unassigned handler waits for the next
TCP connection request. Clients send connection requests through standard TCP ports
(1), which are received as messages in the handler’s mailbox. The current handler accepts these requests by reading the respective message from its mailbox and (2) sending
a message containing its own pid and the port of the connected client to the acceptor.
This acts as a notification that the handler is now engaged in handling the connection of
a specific client. Upon receiving the message, the acceptor unblocks, records the information sent by the handler and (3) spawns a new handler listening for future connection
requests from other clients.
Once it is assigned a handler, the connected client then engages directly with it by
using (4) standard HTTP requests; these normally consist of six (or more) HTTP headers
containing information such as the client’s User Agent, Accept-Encoding and the KeepAlive flag status. It is worth highlighting that the HTTP request information is not sent in
one go, but follows a protocol of messages: it starts by sending the http req, followed
43

3. Assessing the Impact of Increasing Monitor Synchronisation

by six (or more) http header messages containing client information, terminated by
a final http eoh message. The dedicated connection handler inspects the request and
the client information received in the headers, and services the respective HTTP request
accordingly.
Earlier versions of the Yaws webserver were found to be suffering from a Directory
Traversal Vulnerability [47] which was also reported on the reputable exploit-db website
[46]. This vulnerability permits for attackers to perform a Dot-dot slash attack in which
a malicious client provides the webserver with an invalid, malformed URL, containing
a sequence of dots and slashes, in an attempt to exploit the vulnerability. This allows
external clients to navigate to sensitive directories, pertaining to the server, such as the
System32 directory.

3.3.2

Monitoring for safety properties in Yaws

Despite the simplicity and elegance of detectEr’s sHML specification language, it turns
out that it provides enough expressivity to specify numerous safety properties for the
Yaws webserver. For brevity in this section we only present two of these properties, and
in appendix Sec. B.1 we present another two properties.
Detecting Malicious Requests
In the first property we assume the existence of the isMalicious(..) predicate to define
Property 4 (below). This (decidable) predicate can determine whether the client will
engage in a security-breaching activity by analysing the 6 HTTP headers sent to the
handler.
Property 4. Every time a client connection is established (determined from message
(2) of Fig. 3.5), and the assigned handler receives an HTTP-Get request followed by 6
HTTP headers (h1 to h6) and terminated by the end-of-headers notification, then the
communicated headers must not amount to a potentially security-breaching request as
determined by the predicate isMalicious(..).
44

3. Assessing the Impact of Increasing Monitor Synchronisation

Using detectEr’s formal logic presented in Fig. 2.10 (see Sec. 2.4.1), we can now formalise Property4 as formula (3.3).
max X. (
[acceptor?{hPid, next, }]
[hPid . ret({yaws, do recv, 3, {ok, {http req, GET, , }}})]
[hPid . ret({yaws, do recv, 3, {ok, h1}})] [hPid . ret({yaws, do recv, 3, {ok, h2}})]
[hPid . ret({yaws, do recv, 3, {ok, h3}})] [hPid . ret({yaws, do recv, 3, {ok, h4}})]
[hPid . ret({yaws, do recv, 3, {ok, h5}})] [hPid . ret({yaws, do recv, 3, {ok, h6}})]
if (isMalicious(h1, h2, h3, h4, h5, h6)) then ff
else [hPid . ret({yaws, do recv, 3, {ok, http eoh}})] X

)
(3.3)

The logical formula stated in (3.3) specifies this property as a recursive formula
which pattern matches the assigned handler to term variable hPid. This value is then
used to pattern match with the header term variables, h1 to h6, for every iteration of
the HTTP request protocol. An if-statement is then used to evaluate the isMalicious(..)
predicate to determine whether the received HTTP headers pose a security threat or not.
We note that, whereas the handler messages to the acceptor are observed directly (i.e.,
by using input action [acceptor?{hPid, next, }]), the client HTTP messages received
by the handler have to be observed indirectly through the return values (of the form
{ok, header}) of the invoked function do recv, which is defined in module yaws with
arity 3. Instrumentation allowing a direct observation of these actions is complicated by
the fact that the client TCP messages are sent through functions from the inet Erlang
library, which is part of the Erlang Virtual Machine kernel [58, 7].
Detecting Directory Traversal Exploitation
In a similar manner we specify yet another property for detecting behaviour that may
exploit the Directory Traversal Vulnerability, found in earlier versions of the software,
as described in Sec. 3.3.1. For the intents and purposes of this case study, we aim to
detect such possible attacks by monitoring for external client requests and compare the
requested urls to a white-list. We assume that external clients are only allowed to request
for two files, namely “pig.png” and “site.html” as specified in safety property5.
45

3. Assessing the Impact of Increasing Monitor Synchronisation

Property 5. Every time a client connects, and the assigned handler receives an HTTPGet request for a specific file stored on our server, followed by 6 HTTP headers (h1 to
h6) and the end-of-headers notification, then the requested file can only refer to either
for “pig.png” or “site.html”.
We formalise this property (i.e., Property5) as recursive formula (3.4) which binds
the assigned handler with term variable hPid, and then uses this value to pattern match
with the HTTP-GET request, the 6 headers, and the ending header http_eoh. The file
requested in the HTTP-GET request is pattern matched and stored in the path term variable. The matched path value is then inspected by an if-statement that checks whether
the requested path points either to “pic.png” or “site.html”. If the path is found to
be pointing to any of these two files, the property immediately checks that the server actually sends the requested file to the client and recurs on formula variable X; otherwise,
a property violation is flagged.
max X.
[acceptor!{hPid, next, }]
[hPid . ret({yaws, do recv, 3, {ok, {http req, ’GET’, {abs path, path}, }}})]
[hPid . ret({yaws, do recv, 3, {ok, }})]

..
.
[hPid . ret({yaws, do recv, 3, {ok, http_eoh}})]
if path == “/pic.png00 orelse path == “/site.html00 then
[hPid . call({yaws sendfile, send, [ , path, , ]})] X

(3.4)

else ff




Adapting these Properties for Hybrid Monitoring
To be able to monitor for these properties using our hybrid approach, we use the extended syntax (introduced in Sec. 3.2.1) to reformulate security properties (3.3) and (3.4)
whereby we require violation detections to be synchronous i.e., using sff instead of ff.
For instance, from formula (3.4) we obtain the hybrid formula (3.5) stated below.

46

3. Assessing the Impact of Increasing Monitor Synchronisation

max X.
[acceptor!{hId, next, }]
[hId . ret({yaws, do recv, 3, {ok, {http req, ’GET’, {abs path, path}, }}})]
[hId . ret({yaws, do recv, 3, {ok, h1}})]

..
.
[hId . ret({yaws, do recv, 3, {ok, h6}})]
[hId . ret({yaws, do recv, 3, {ok, http_eoh}})]
if path == “/pic.png00 orelse path == “/site.html00 then
[hId . call({yaws sendfile, send, [ , path, , ]})]X

(3.5)

else sff



All of the other properties used in our experiments where reformulated in the same
manner as per hybrid formula (3.5).

3.3.3

The Performance Results

We measure the respective overheads resulting from the three monitor instrumentation
techniques that are now supported by detectEr, over Yaws for varying client loads, in
terms of:
• the average number of CPU cycles required per Yaws client request;
• the average memory utilisation for the Yaws webserver to respond to batches of
client requests; and
• the average response time of the (monitored) Yaws server, i.e., the time taken for
the server to respond to a client request.
Remark 3. In our experiments we utilise a version of detectEr, presented in [15], that
provides two monitoring optimisations — a static optimisation and a dynamic optimisation, along with the original unoptimised monitor synthesis technique given in [39].
These techniques only optimise the way that the concurrent monitors are synthesised and
the way they operate at runtime, but they are independent from the system instrumentation used for event reporting. This means that they can be applied to every monitoring
instrumentation technique explored so far (i.e., asynchronous, synchronous and hybrid
47

3. Assessing the Impact of Increasing Monitor Synchronisation

instrumentation). This enables us to ensure that the performance discrepancies obtained
in our experiments persist regardless of how the synthesised concurrent monitors are
optimised. This allows us to rule out that the discrepancies are caused due to unoptimised monitors. We also provide a brief overview of these optimisations in Appendix


Chapter A.

The experiments were carried out on an Intel Core 2 Duo T6600 processor with 4GB
of RAM, running Microsoft Windows 7 and EVM version R16B03. To further confirm
the discrepancies between synchronous, hybrid and asynchronous monitoring we run
our test-suite and take separate batches of results using (i) unoptimised monitors, (ii)
statically optimised monitors and (iii) dynamically (fully) optimised monitors.
For each property (in our test-suite) and each client load, we take five sets of readings
and then average them out. Since results pertaining to a specific monitor optimisation
do not show substantial variations when using different properties, we again average the
results across all properties and compile them as graphs for each optimisation. We thus
obtain the graphs shown in Figures 3.6 to 3.8 — the same results are also compiled as
tables in appendix Sec. B.2.
From the graphs in Figures 3.6 to 3.8 we note that regardless of how the monitors are
optimised [15], memory and CPU utilisation suffer from a marginal difference in which
the hybrid monitoring overheads generally lie midway between synchronous monitoring
− which scored the worst; and asynchronous monitoring − which gave the best performance results. A clearer, and more substantial difference is seen in the average time
that the server takes to respond2 to each client request when it is under a certain load.
Fig. 3.6 shows that unoptimised monitors suffer from a substantial response difference of
about 46ms when a synchronously monitored instance of Yaws is placed under the load
of 2000 client requests, as compared to its asynchronous counterpart. By contrast hybrid
monitoring provided timely detections by suffering only from a 9ms response overhead
increase (for the same load) when compared to asynchronous monitoring.
2

Providing low average response times [47] is imperative in reactive systems (such as webservers) to
make sure that the client is served as soon as possible.

48

Avg. Response Time per req. (ms)

Avg. Memory Util. (MB)

Avg. CPU Cycles (per req)

3. Assessing the Impact of Increasing Monitor Synchronisation

108

50

100

200
500
No. of Client Requests

1000

2000

50

100

200
500
No. of Client Requests

1000

2000

50

100

200
500
No. of Client Requests

1000

2000

50

40

30

120
100
80
60
40
20

Async

Hybrid

Sync

Baseline

Figure 3.6: Performance impact analysis (using unoptimised monitors)
49

Avg. Memory Util. (MB)

Avg. CPU Cycles (per req)

3. Assessing the Impact of Increasing Monitor Synchronisation

108

107.5

50

100

200
500
No. of Client Requests

1000

2000

50

100

200
500
No. of Client Requests

1000

2000

50

100

200
500
No. of Client Requests

1000

2000

40

35

30

Avg. Response Times per req. (ms)

25

60

40

20

Async

Hybrid

Sync

Baseline

Figure 3.7: Performance impact analysis (using statically optimised monitors)
50

Avg. Response Times per req. (ms)

Avg. Memory Util. (MB)

Avg. CPU Cycles (per req)

3. Assessing the Impact of Increasing Monitor Synchronisation

107.5

50

100

200
500
No. of Client Requests

1000

2000

50

100

200
500
No. of Client Requests

1000

2000

50

100

200
500
No. of Client Requests

1000

2000

32
30
28
26

30
25
20
15
10

Async

Hybrid

Sync

Baseline

Figure 3.8: Performance impact analysis (using fully optimised monitors)
51

3. Assessing the Impact of Increasing Monitor Synchronisation

Furthermore, Figures 3.7 and 3.8 demonstrate that even though the monitoring optimisations [15] help in reducing the overall overheads, synchronous instrumentation
monitoring still fared the worst when compared to the rest. In fact this suffers from a
19ms difference (compared to asynchronous) when using statically optimised monitors
to synchronously monitor a Yaws instance placed under the load of 2000 client requests,
and a 9ms difference with dynamically optimised monitors. Contrasting this, the hybrid approach only suffers from a 4ms and 1.5ms response time difference for statically
optimised and dynamically optimised monitors respectively.
We therefore conclude that regardless of any optimisation used by the synthesised
monitors, synchronous instrumentation monitoring incurs substantial overheads when
compared to its asynchronous counterpart. By contrast, the hybrid instrumentation results show that timely detections can still be achieved with overheads that are higher yet
closer to that of asynchronous monitoring.
The reason for this performance degradation is apparent from Fig. 3.2 and Fig. 3.4,
where one can notice that for every synchronous monitored event, the system execution
incurs a responsiveness penalty waiting for the monitor to acknowledge back. A further
penalty is also incurred since synchronous event reporting requires executing additional
code (e.g., to generate nonces) when compared to its asynchronous counterpart. Hence
hybrid monitoring performs better than synchronous instrumentation monitoring as the
former performs less synchronisations. For the same reason, hybrid monitoring still
imposes a slightly higher degree of overheads than asynchronous monitoring as it still
needs to apply a small level of synchrony in order to achieve timely detections.

3.4

Conclusion

In this chapter we have analysed the design space for introducing synchrony on top of
an inherently asynchronous platform. The primary objective of this investigation was to
identify a synchronisation technique that allows for a tighter monitoring control to be
achieved efficiently. This contributes towards developing an effective, yet efficient RA

52

3. Assessing the Impact of Increasing Monitor Synchronisation

framework.
We started by devising a handshaking protocol for achieving synchronous event monitoring on actor-based Erlang systems which required refining an AOP framework [41]
for Erlang systems to instrument the protocol in existing systems. Using this handshaking protocol we introduced a coarse-grained synchronous instrumentation monitoring
approach which synchronised for every reported event. Following this we reduced the
level of synchronisation by limiting it to critical actions i.e., system actions that can
directly contribute to a violation. This lead to creating a hybrid monitoring approach
capable of switching between synchronous and asynchronous event reporting, while still
ensuring timely detections when required. This however required us to extend the detectEr’s specification language.

After integrating the synchronous and hybrid monitoring approaches in detectEr3 ,
we carried out a systematic assessment wrt. Yaws, regarding the relative overheads
incurred by different instrumentation techniques within an actor setting. The results of
this impact assessment show that the hybrid technique yielded response times that are
lower than those of synchronous instrumentation approach.
Therefore, the conducted impact analysis enables us to conclude that since synchrony
is not natively supported by asynchronous, actor-based systems, this induces inevitable
synchronisation overheads. However, our analysis also allows us to conclude that although the incremental synchronisation methodology used in our hybrid technique still
introduces a certain degree of overheads, these can be minimised and kept to a feasible
standard if synchronisation is utilised on a by-need basis; i.e., only when it is strictly
required (e.g., to timely achieve detections or to effectively apply adaptations).
In the next chapter we therefore build upon the incremental synchronisation methodology that we have identified by this impact assessment, in order to implement an efficient yet effective Runtime Adaptation framework for actor systems.

3

A stable release of this version called
https://bitbucket.org/casian/detecter2.0

53

detectEr

2.0

is

also

accessible

from

4. Language Design, Implementation
and Formalisation

In this chapter we present the design and implementation details about how we develop
our runtime adaptation framework on top of the detectEr RV tool. Based on the implementation constraints we then develop a formal operational model which provides
a higher level description of how our RA scripts behave at runtime. We divide this
chapter into two parts: in the first part (Sec. 4.1 to 4.4) we analyse the implementation
challenges, while in the in the second part (Sec. 4.5) we explain how we developed the
formal model for our RA scripts.
More specifically, in Sec. 4.1 we identify a number of adaptation actions that should
be applicable on any arbitrary actor system, regardless of the design and coding practices used in its implementation. We also articulate the constraints that the actor model
imposes which makes it more challenging to implement such adaptation actions. We
therefore provide a preliminary investigation of how we can overcome these challenges.
In Sec. 4.2 we present our design decisions for integrating the identified adaptations
within the logic used by detectEr, thereby enabling users to specify runtime adaptation
monitors for mitigating property violations. This is followed by Sec. 4.3 in which we expand on the preliminary investigation given in Sec. 4.1 and provide an overview of how
we implement the synchronisation protocol required by our runtime adaptation framework. We conclude the first part by showing how one of the RV scripts written for Yaws
54

4. Language Design, Implementation and Formalisation

(used in the evaluation of Chapter 3), can easily be converted into an RA script for our
new framework.
Finally in Sec. 4.5 we focus on developing a formal model that allows for predicting
the runtime behaviour of an RA script without actually running it. This allows us to
analyse the behaviour of our scripts without having to deal with the complexities of
the implementation, thereby enabling us to further understand the possible errors that
erroneous RA scripts may introduce.

4.1

Identifying Adaptation Actions within the Constraints
of Actor Systems

Actor design practices such as the fail-fast design pattern and systems structured using
supervision-trees, permit for the creation of self-adaptive actor systems whereby supervisor actors are purposely developed to intervene and mitigate cases when an actor
crashes abnormally. As misbehaviour does not necessarily cause an actor to crash, these
design strategies are limited in the type of misbehaviour that they can detect (i.e., detections only happen when an actor crashes). One way how to to overcome this limitation
is to employ an RV monitor which is able to deliberately interfere by crashing certain
system actors whenever it detects erroneous behaviour. This would in turn trigger the
system’s built-in self-adaptive functionality which mitigates the crashed actors.
Example 4.1.1. Assume a simple self-adaptive actor system illustrated in Fig. 4.1 (below). This system consists in four components, namely A, B and C which are the actual
worker components, along with component S which is their supervisor. The sole purpose of this supervisor is to apply the necessary mitigation when either one (or more) of
the worker components crash.
As explained by Fig. 4.1, this simple RA monitor (1) receives and analyses the system events, and upon detecting a violation it (2) deliberately terminates one of the system’s components (i.e., component C) instead of flagging the violation. Upon crashing,

55

4. Language Design, Implementation and Formalisation

Figure 4.1: Triggering the self-adaptive functionality of a system through Runtime Monitors.
(3) this component sends a link message to its supervisor, S , which in turn (4) applies


the necessary mitigation.

Although this straightforward RA mechanism is already quite useful and effective,
it relies on the assumption that the monitored actor system already contains a certain
degree of self-adaptive capabilities. In cases where a monitored actor system lacks (or
has limited) self-adaptive functionality, killing an actor would not trigger any type of
mitigation mechanism. At best this could be used to permanently switch-off redundant
or useless parts of the system such as orphaned actors [69] i.e., actors that are no longer
in use but for some reason did not terminate. At worst, however, killing actors in a
non-adaptive system might cause it to become unresponsive.
We must therefore look into other adaptation techniques that could enable us to mitigate erroneous actors though high-level actor-manipulation techniques, that can be applied regardless of the design and implementation of the system under scrutiny. In other
words our adaptations should be applicable to any system actor irrespective of the code
that it executes, and of the way that it is linked with other actors. However, implementing such complex adaptations which are agnostic of the actor’s code, is quite challenging
due to certain restrictions imposed by the actor model and the host language (in our case
Erlang).

56

4. Language Design, Implementation and Formalisation

4.1.1

Identifying ideal Runtime Adaptation Actions

An RA framework for actor systems should provide generic adaptations that allow the
specifier to manipulate a number of architectural aspects pertaining to actor systems. To
identify what these aspects really are, we recall the basics of the actor model (presented
in Sec. 2.3), which we summarise in the following three points:
(i) An actor is a concurrent process.
(ii) An actor has a mailbox for asynchronous interaction (through message passing).
(iii) Actors can be structured (i.e., related through linking).
As stated in (i), since actors are essentially processes and given that we want to keep
our mitigation techniques as generic as possible, the only two types of generic process
manipulations that can be applied upon a black-box process (i.e., without knowing what
it executes) are (a) Process Terminations and (b) Process Restarts.
(a) Process Terminations: These operations should be carefully used to terminate noncrucial misbehaving actors, such that the actor system is able to carry on free of
the erroneous actors, even though some of its functionality might be unavailable.
Example 4.1.2. Recall the system illustrated in Fig. 1.1, for which we have already
formalised RV Property 1, in terms of detectEr’s logic, as formula (2.1) in Ex. 2.4.1
(restated below).


ϕ = max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [ .y!err] ff)
def


(2.1)

Based on (a) we reformulate (2.1) as (4.1) whereby the script now includes a the kill
adaptation which terminates the actor that sends the error message err to the connected
client. Note that we now map this erroneous actor to term variable z so that we are able
to apply the kill adaptation upon it.


max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [z .y!err] kill(z) Y)

(4.1)

As in our example system (given in Fig. 1.1) we do not assume any sort of built-in selfadaptive functionality (i.e., no linking, trapping, supervisors, etc.), in formula (4.1) we
57

4. Language Design, Implementation and Formalisation

eliminate future occurrences of this error from being made by the same erroneous actor,
by permanently switching off this actor. Although this termination restricts the overall
functionality of our system, we still retain the rest of the system’s functionality as the


other concurrent actors are not effected by the applied adaptation.

(b) Process Restarts: These actions should be used to restart more crucial components
in an attempt to remove/reduce any side-effects (that may cause further errors)
incurred due to the detected misbehaviour, by reinitialising them afresh.
Example 4.1.3. For the example system in Fig. 1.1 we can now extend formula (2.1)
(restated in Ex. 4.1.2) with the restart adaptation in (4.2).


max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [z .y!err] restr(i, z)tt)



(4.2)

We use the restart adaptation to reinitialise both the common-interface actor i, and also
the actor mapped to term variable z, in order to mitigate the invalid behaviour which
lead to producing error err. In this way we still retain the full functionality of the system
while we reduced the possible side-effects inflicted on the mitigated actors, by restarting


them from their point of initialization.

Furthermore from architectural aspect (ii), we know that actors are not just concurrent process, but they are also interactive processes. In fact actors are able to interact
asynchronously by depositing messages in the private mailbox of other actors. Due to
this method of interaction we identify another generic adaptation, namely:
(c) Message Interception: These adaptations should be used to intercept and manipulate messages that might have been erroneously exchanged due to the detected
violation, thereby preventing them from causing further problems.
Example 4.1.4. Assuming that we can use adaptation prg to purge all the messages in
an actor’s mailbox, we now develop (4.3) as an extension of (2.1).


max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [z .y!err] prg(z)Y)

58

(4.3)

4. Language Design, Implementation and Formalisation

In (4.3) we use the prg adaptation to flush the mailbox contents of the actor mapped to
z, with the aim of removing other conflicting messages that the erroneous actor (mapped


to z) might have received prior to sending the error message to the client.

Furthermore, as by (iii) we know that these communicating actors can also be structured and related, we identify yet another generic adaptation that allows us to manipulate
this aspect of actor systems:
(c) Process Restructuring: There are various ways how actor systems can be structured
including: how the actors are linked together e.g., actor A is linked to actor B but
not to C; the type of linking between actors e.g., unidirectional or bidirectional,
trapped or untrapped; and the way actors are identified e.g., some actors are registered with a unique name while others are not. Restructuring should therefore
allow us to manipulate these structural aspects.
Example 4.1.5. Recall from Sec. 2.3 that when a process dies, every process linked to
it dies as well (unless trapping is used). Assuming that the common-interface (from
Fig. 1.1) is linked to the decrementor and incrementor components, in (4.4) we use the
unlink restructuring adaptation to remove the link between the interface, i, and the actor

mapped to z.


max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [z .y!err] unlink(i, z)Y)


(4.4)

In this way, if the actor mapped to z crashes as a result of producing error err, at least
the other linked components can still operate. Hence rather than losing all the system’s
functionality, we allow for the system to remain partially operational.



The identified adaptations (a), (b), (c) and (d) are essentially categories of adaptations rather than actual adaptation actions. We also conjecture that instances of these
categories can be implemented in various ways, but ultimately provide the same overall
effect e.g., one instance of the restructuring category is used to add links between actors,
while another instance is used to remove links instead; these reside in the same category
as they ultimately both manipulate the structure of an actor system, even if they do it in
a different way.
59

4. Language Design, Implementation and Formalisation

Remark 4. The overall effects of different categories can also be combined to obtain
even complex adaptations. A case in point is the silent-kill adaptation which despite
being an instance of category (a) (i.e., process terminations), it is also able to manipulate
the structural aspects of an actor system. In fact whenever the silent-kill is applied to
an actor i, it removes any link related to i prior to terminating it. It is therefore able to
suppress the system’s built-in self-adaptive functionality (if any) by removing the links
that alert the other actors about the terminated actor, and thus prevent the system from


attempting to apply its own mitigation.

Example 4.1.6. With the identified adaptation categories we are now in a position to
formally specify RA property2 (see Ex. 1.1.1) as an extension of RV property1 which we
have already formalised as formula (2.1) in Ex. 2.4.1. We thus obtain formula (4.5):


max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [z .y!err] restr(i) prg(z) Y)
(4.5)
The specifier presumes that the error (which may arise after a number of correct interactions) is caused by the interface actor i (as shown in Fig. 1.1, where an inc request
is erroneously forwarded to the decrementor actor k) — the specifier may, for instance,
have prior knowledge that actor i is a newly-installed, untested component. The monitor
thus restarts actor i by using adaptation restr(i), and empties the mailbox of the backend
server through adaptation prg(z), as this may contain more erroneously forwarded messages. Note that the actor to be purged is determined at runtime, where term variable z
is bound to identifier k from the previous action [z .y!err]). Importantly, note that in the
above execution (where k is the actor sending the error message), actor j’s execution is
not affected by any adaptation action taken.

4.1.2



The Implementation Constraints of Adaptation Actions

Although some adaptations are supported by the host language, other adaptations need
to be implemented. Implementing adaptations pertaining to some of the identified categories is not always straight forward, as the actor-model and the host language restrict
the way that actors can externally effect other actors. In the case of Erlang, as illustrated
60

4. Language Design, Implementation and Formalisation

in Fig. 4.2, an actor A is not allowed to externally interrupt the operation of another
actor B, thereby forcing it to execute interrupt service routine (ISR) which invokes the
required adaptation function. Although interrupts and ISRs are commonly used in concurrent and multithreaded systems [69], they are strictly abolished by the actor-model
and Erlang.

Figure 4.2: Applying adaptations using interrupts.
Instead, as shown in Fig. 4.3, in the actor-model the adapter actor A must encode and
adaptation request as a message and deposit it in the mailbox of adaptee B, requesting
it to execute the required adaptation function. The recipient actor, B, must then read the
message from its private mailbox, interpret it and willingly execute the required function
(it might choose not to).

Figure 4.3: Applying adaptations encoded as messages.
The only type of external actions that are natively supported by the Erlang OTP
libraries [58] are actor killing and linking. This allows us to augment detectEr’s RV
61

4. Language Design, Implementation and Formalisation

monitors with flavours of adaptation categories (a) and (d) by enabling them to execute
the respective OTP functions over the necessary system actors.
Remark 5. Although these natively supported adaptations can also be used with asynchronous monitoring, recall that this suffers from late detections. This means that even
though we are able to apply these adaptations, it does not necessarily mean that they
can be effectively applied to prevent or properly mitigate any possible side-effects. For
instance, recall that in formula (4.1) we use the kill operation to terminate the actor that
sends the err message to the client. However, if this actor is not terminated in a timely
manner it would still be able to progress and perform further operations, that may lead to
more serious consequences, prior to being killed. Similarly, in the case of formula (4.4),
if the interface actor i and the erroneous actor mapped to z are not unlinked on-time, actor z might progress and crash (thereby causing the linked actors to crash as well) before


the monitor manages to remove the links.

The Erlang OTP libraries, however, still do not provide native mechanisms for restarting actors (i.e., implementing (b)) or for reading and modifying the mailbox contents of
the of another actor (i.e., to implement (c); in fact this is strictly abolished). Hence as
outlined in Fig. 4.3, to implement these adaptations we have to somehow ‘trick ’ the
adaptee into providing the monitor with enough control to willingly perform these more
complex operations.
It turns our that synchronisation not only enables us to address the issue of applying adaptations effectively, outlined in Remark 5 (as explored in Chapter 3), but also
provides the monitor with a tighter control over the system actors. This allows for the
implementation of more complex adaptations such as those pertaining to categories (b)
and (c). The general idea for implementing these adaptations is to use synchronisation
as a form of local anesthetic which places the necessary system actors in a state of controlled dormancy that allows the monitor to operate upon them.
More specifically, as illustrated in Fig. 4.4 whenever a system actor is blocked by
the instrumented synchronisation protocol, this does not only wait for the monitor’s ac62

4. Language Design, Implementation and Formalisation

Figure 4.4: A conceptual protocol for applying synchronous adaptations.
knowledgement message, but also waits for adaptation requests encoded as messages.
Upon receiving an adaptation request, e.g., restr, the code instrumented in the system
actor can then analyse the request and carry out the necessary procedure for applying
the requested pre-instrumented adaptations. As the adaptations are executed within the
system actor’s own thread of execution, these adaptation functions would also have full
access to the actor’s private mailbox and state (i.e., its process dictionary), thereby allowing for more intrusive operations, such as message interception, to be performed.
The required adaptations can therefore be implemented as regular functions which are
then instrumented in the system such that they can be invoked by the instrumented synchronisation mechanism.
However, if an adaptation message is sent to an active system component (i.e., an
actor which was not blocked by our instrumentation), the actor might suffer the following
consequences:
• it might not even read the request message, hence the adaptation is not conveyed;
• it might read the message while it is performing other operations and ignore it, as
it would not know what it means;
• the message might be read during the actor’s normal execution and cause conflicts
leading to a crash.
It is therefore imperative to make sure that the respective actors are suspended by our
63

4. Language Design, Implementation and Formalisation

instrumentation prior to applying these adaptations.
Hence, due to these implementation constraints, we classify the implementable adaptations into the following two classes:
• Asynchronous Adaptations: these can be administered on the respective actors while
they are executing, typically these include natively supported actions.
• Synchronous Adaptations: the actors requiring these adaptations must be suspended
by our instrumentation prior to applying them.
Based on the adaptation categories identified in Sec. 4.1.1 and the adaptation classes
presented above we create Table 4.1. This presents a matrix of adaptations denoting the
categories that can be applied synchronously, asynchronously or both. In general, asyn-

Classes

chronous adaptations e.g., actor linking and killing, can also be applied synchronously.

Synchronous

(a) Terminations
X

Asynchronous

X

Categories
(b) Restarts (c) Interceptions
X
X

(d) Restructuring
X
X

Table 4.1: A classification for Synchronous and Asynchronous Adaptation Categories.

4.2

Designing the Runtime Adaptation Mechanism

As synchronous adaptations require suspending the adaptee prior to applying the adaption upon it, we employ an incremental synchronisation mechanism for gradually suspending only the actor executions of interest as the property is being monitored for, i.e.,
without suspending any of the other actors. We opt for incremental synchronisation
based on the results we obtained from our impact assessment carried out in Chapter 3.
This demonstrated that incremental synchronisation is able to keep synchronisation overheads under control by applying synchrony on a by-need basis, thereby also allowing for
feasible runtime adaptation. Hence, inspired from synchronous necessities (i.e., [|e|]) in
our hybrid monitoring scripts (see Sec. 3.2.1) we extend necessity formulas with a synchronisation modality, [e] ρ ϕ, where ρ ranges over either b (blocking): stating that the
64

4. Language Design, Implementation and Formalisation

subject of the action (i.e., the actor that committed the action) is suspended if its pattern
eis matched; or a (asynchronous): stating that the action subject is allowed to continue
executing asynchronously, even when the pattern e is matched.
We also recall that if the necessity formula [e]ϕ mismatches with a trace action,
its observation terminates due to the trivial satisfaction of the formula. In our case
this also means that the synchronous adaptations contained the continuation ϕ are never
administered. Hence, any actor executions that were previously blocked so as to make
these synchronous adaptations applicable are no longer required to remain blocked. We
thus provide a mechanism for releasing the actors blocked so far by further extending
necessity formulas with a list of actor references, RL. This list denotes the (blocked)
actors to be released in case the necessity pattern e mismatches, [e] ρRL ϕ.
Example 4.2.1. Recall formula (4.5) from Ex. 4.1.6 (restated below).


max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [z .y!err] restr(i) prg(z) Y)

(4.5)

We augment the necessity operations in (4.5) with the required synchronisation modalities and release lists so to obtain (4.6).


(4.6)
max Y. [i?{inc, x, y}] bε ([ j.y!{res, x + 1}] aε Y) & ([z .y!err] bi restr(i) prg(z) Y)
Formula (4.6) states that since actor i might require being adapted (by restr(i)), then this
must be blocked by setting the modality of necessity declared prior to the adaptation
to blocking. In this case we block actor i whenever it receives an increment request 1 ,
such as {inc, 4, h}, i.e., by using blocking necessity [i?{inc, x, y}] bε . In case a result, e.g.,
{res, 5}, is sent to the client mapped to y instead of an error message err, this means
that this event would match the pattern of necessity [ j .y!{res, x+1}] aε .
However, this also implies that necessity [z .y!err] bi would become trivially satisfied and hence would not proceed to adapting actor i with restr(i), as this is no longer
necessary. Hence, our formula must release actor i upon its trivial satisfaction thereby
allowing it to receive further increment requests. We therefore add i to the release list of
1

Note that this might cause the entire system to deadlock; we however explain this later on in Remark 6
and Ex. 4.2.2.

65

4. Language Design, Implementation and Formalisation

this necessity, i.e., [z .y!err] ib . Moreover we set the synchronisation modality of this
necessity to blocking, i.e., [z .y!err] bi in order to suspend the actor mapped to variable


z prior to purging its mailbox.

Since adaptations in a script may be followed by further observations, we also require
a similar release mechanism for adaptation actions, i.e., adaptation(AL)RL , where the
actors defined in the release list, RL, are unblocked after the adaptation is administered
to the actors defined in the adaptation list, AL.
Remark 6. Although minimally intrusive, the expressivity of our mechanism for incremental synchronisation relies on what system actions can be observed (i.e., the level of
abstraction at which the system is monitored). For instance, recall the server system
depicted in Fig. 1.1. If monitored from an external viewpoint, the communications sent
from the interface actor, i, to the backend incrementor and decrementor actors, j and k,
are not visible (according to [39], they are seen as internal τ-actions). However, for observations required by properties such as (4.5), we would need to block actor i only after
it sends a message to either of the backend actors—otherwise the entire system blocks
(such as in (4.6)). This requires observing the system at a lower level of abstraction,
where certain τ-actions are converted into visible ones e.g., the instrumentation used by
detectEr allows us to observe internal actions such as function calls or internal messages

sent between actors as discussed for Fig. 1.1.
Example 4.2.2. In (4.7) we extend (4.6) with the observation of an internal communication action (i.e., i. !{inc, x, y}) for blocking purposes (as explained in Remark 6), and
also with release lists for adaptations restr and prg.
ϕ0 = max Y. [i?{inc, x, y}] a
def

[i. !{inc,

x, y}] bε

( [ j.y!{res, x + 1}] a Y) &
( [z .y!err] bi restr(i)ε prg(z)i,z Y)

!

(4.7)

After asynchronously observing i ?{inc, v, h} (for some v, h), the respective monitor
synchronously listens for an internal communication action (i.e., [i . !{inc, x, y}] bε ) from
i to some actor with the same data, {inc, v, h}; if this action is observed, the subject of the
action (i.e., i) is blocked. If the subsequently observed action is an error reply, z .h!err
66

4. Language Design, Implementation and Formalisation

originating from an actor bound at runtime to z, we block this and start applying the
necessary synchronous adaptation actions, i.e., restr(i)ε and prg(z)i;z . Note that the last
adaptation action releases the two blocked actors i and z before recurring; similarly the
necessity formula for the error reply releases the blocked actor i if the respective action,
[z .y!err] bi , is not matched.

4.3



Implementing the Runtime Adaptation Protocol

We implement our runtime adaptation protocol (illustrated in Fig. 4.5 below) as an extension of the incremental synchronisation technique introduced in our hybrid monitoring
protocol (see Sec. 3.2). The system instrumentation for the new protocol remains more
or less the same as in the hybrid protocol. In fact asynchronous actions (i.e., [e] aRL ) inject
advice functions that send monitoring messages, containing the event details and a null
nonce, without blocking the action’s subject. By contrast, blocking actions (i.e., [e] bRL )
generate and send a fresh nonce along with the event details to the monitor, and subsequently cause the instrumented actor to block waiting for a releasing acknowledgement
message from the monitor. While waiting, an instrumented blocked actor can now also
receive synchronous adaptation requests.
Upon receiving an adaptation request message containing: a valid nonce, and an
adaptation tag denoting a valid pre-instrumented adaptation function (e.g., restr to denote the restart function), the system side instrumentation sets the requested adaptation
as next-in-line for execution. The requested adaptations are only applied if and when the
blocked system actor receives the releasing acknowledgement.
To allow for the monitor to adapt and release system actors, even after receiving
further event messages, the monitoring loop now maintains a map of nonces. Whenever
the monitor receives an event message containing a non-null nonce and the subject’s
process id, it adds the received nonce as a new entry to its map by using the subject’s
id as key; it then proceeds by handling the event. As event handling now also includes
applying adaptations (i.e., adaptation(AL)RL ), the monitor may proceed by issuing the

67

4. Language Design, Implementation and Formalisation

System
Monitor
...
Actor i commits event e1;
loop(Map) →
evt(d1,{i,nonce1})
i gets data d1 from e1;
{Evt,Id,Nonce} = recv event(),
i blocks on nonce(e1);
if Nonce , null,
...
Map2 = Updt(Id,Nonce,Map);
Actor j commits event e2;
else
evt(d2,{j,nonce2})
j gets data d2 from e2;
Map2 = Map;
j blocks on nonce(e3);
end,
...
{PtrnMtch,AL,RL} = handle(Evt),
Actor k commits event e3;
if(PtrnMtch) →
evt(d3,null)
k gets data d3 from e3;
adapt(AL,Map2),
...
release(RL,Map2),
adpt(restr,nonce1)
i sets restart;
loop(Map2);
j sets purge;
adpt(prg,nonce2)
end.
i unblocks with nonce1;
j unblocks with nonce2;
...

ack(nonce1)
ack(nonce2)

Figure 4.5: A high-level depiction of the Runtime Adaptation protocol.
necessary adaptation actions denoted in the adaptation list AL (if any), and then releases
the actors enlisted in the release-list RL. To issue synchronous adaptations and release
acknowledgements, the monitor uses the process ids enlisted in AL and RL as keys for
retrieving the associated nonces from its map. After applying the necessary adaptions
and releases, the monitor removes the map entries associated with the released actors. It
finally reiterates and waits for the next system event.

4.3.1

Implementing Pre-instrumented Synchronous Adaptations

Although Erlang already natively implements some of the adaptations that we categorised in Table 4.1, as part of our prototype we implement more complex and effective
synchronous adaptation actions. More specifically, we tried to implement a representative adaptation action for each synchronous adaptation category presented in Table 4.1.
These implementations can be applied over instrumented system actors using the RA
protocol implementation outlined in Fig. 4.5. We therefore implement the following
68

4. Language Design, Implementation and Formalisation

pre-instrumented synchronous adaptation functions:
• silent-kill (Terminations category − slnt kill(AL)RL ): As outlined in Remark 4 (in the
beginning of Sec. 4.1.1), this action instructs a blocked actor (using an adaptation request message) to willingly relinquish its supervision links prior to terminating itself.
By doing so it refrains from alerting any of its supervisors or linked actors about its
termination. This enables the monitor to terminate actors without possibly triggering
any of the system’s built-in self-adaptation mechanisms. Unlike asynchronous killing,
since the silent-kill requires the recipient actor to be blocked, it ensures that actors are
terminated on-time, as blocking forbids an actor from progressing after committing
an action.
• purge (Interception category − prg(AL)RL ): Upon interpreting a purge request, the
blocked actor uses a loop of non-blocking receives [7] to continuously read and ignore the messages in its mailbox. The loop stops when no more messages are left,
i.e., the mailbox is emptied. In this case, synchronisation was essential given that the
actor model strictly prohibits external actors (like our monitor) from accessing and
modifying the mailbox contents of another actor. A variant of this adaptation is intercept, in which only the messages matching a specific pattern are purged from the
mailbox, while the rest remain intact.
• sync-link (Interception category − sync-link(AL)RL ): The monitor sends a link request
message to an instrumented blocked system actor containing a list of process ids.
When the instrumented code interprets this request, it links the host actor with all the
process ids enlisted in the request message (if not already linked) in a timely manner.
The same mechanism applies for the reverse function sync-unlink, which unlinks the
instrumented actor from all the enlisted actors if a link exists.
• restart (Restarts category − restr(AL)RL ): Restarting a process in Erlang is normally
performed by first killing the process and then respawning it. However, since the
adaptee may be linked to other actors, killing it might also terminate (or inform) other
linked actors as a side-effect which could cause further complications. Moreover,
69

4. Language Design, Implementation and Formalisation

even if the actor is terminated silently with a silent-kill action, we still run into the
following two problems:
– the links removed by the silent-kill must be relinked to the newly respawned
process, in the same way as they were prior to killing the original actor;
– as respawning an adaptee actor changes its process id, we need to somehow
inform the other actors about this change, otherwise they would not be able to
contact the restarted process.
To avoid these problems with our restart implementation, a monitor restart request causes
the blocked actor to: empty its mailbox (same as in purge), refresh its internal state (i.e.,
it removes any non-instrumentation data residing in its process dictionary [7]) and finally
restarts the actor by calling the original initial function used by the spawn operation
that created the actor in question. In other words, the adaptee is not terminated and
respawned; instead we call its initialisation function from within the actor’s own thread
of control. This is better explained in Ex. 4.3.1.
Example 4.3.1. Recall the system given in Fig. 1.1, and assume that the commoninterface i was at some point during system initialisation spawned by a parent process
which called the function spawn(interface:start()). This spawn operation therefore creates a new actor process which internally calls the function interface:start(), i.e., the
function which starts the common-interface from the very beginning. Whenever i is sent
a restart request by our monitor, it empties its mailbox, refreshes its internal state and
finally calls interface:start() to start afresh.



By using this mechanism, a process is able to retain its process identifier even after
restarting. This is crucial to avoid having to inform other system actors about the new
process id; failing to do so would lead to having other system actors unable to contact the
restarted actor. Implementing this adaptation was not as straight forward as the previous
ones, as this required a further extension to the AOP instrumentation framework. The
extended AOP is now also able to save the details (module, function and arguments) of
70

4. Language Design, Implementation and Formalisation

the initial function used in spawn operation, within the internal state (i.e., its process
dictionary) of the newly spawned actor. This information is then retrieved if and when
the instrumented actor receives a restart adaptation request while it is blocked.

4.4

Strengthening Yaws through Runtime Adaptation

To provide a better insight on how runtime verification properties can easily be converted
into runtime adaptation properties, we incrementally extend Yaws property (3.4) (given
in Sec. 3.3.2). More specifically we strengthen the Yaws webserver with runtime adaptations for mitigating invalid HTTP requests that might contribute to a dot-dot-slash attack
which exploits the directory traversal vulnerability. Hence we show that by introducing
minimal constructs in the currently available RV scripts we can use our RA framework
to mitigate third-party applications such as the Yaws webserver.
max X.
[acceptor?{hId, next, }]
[hId . ret({yaws, do recv, 3, {ok, {http req, ’GET’, {abs path, path}, }}})]
[hId . ret({yaws, do recv, 3, {ok, h1}})]

..
.
[hId . ret({yaws, do recv, 3, {ok, h6}})]
[hId . ret({yaws, do recv, 3, {ok, http_eoh}})]
if path == “/pic.png00 orelse path == “/site.html00 then
[hId . call({yaws sendfile, send, [ , path, , ]})]X

(4.8)

else
slnt kill(hId) prg(acceptor) X



Contrasting to (3.4), instead of issuing a violation (ff), property (4.8) applies a silentkill action (i.e., slnt kill(..)) to immediately isolate and terminate the handler actor assigned to the requesting client, thus preventing the termination of the handler from possibly affecting other linked actors. Note that we are able to apply a silent-kill mitigation
over the assigned handler actor (bound to hId), as our RA framework also supports applying adaptations over actors which are only known at runtime. This adaptation is then
followed by a purge adaptation (i.e., prg(..)) that empties the mailbox of the acceptor.
71

4. Language Design, Implementation and Formalisation

As both the silent-kill and purge adaptations are synchronous adaptations (as explained in Sec. 4.3.1) we need to specify the necessary synchronisation operations. As
these adaptations are applied on system actors hId and acceptor, we must identify a set
of necessities that must be converted into blocking necessities to ensure that the adaptations are applied correctly. The rule of thumb for identifying such candidates is that
whenever we have an adaptation which applies on a system actor i, e.g., prg(i), then a
candidate necessity should:
(i) be defined prior to the adaptation;
(ii) have i defined as its subject; and
(iii) be the closest candidate necessity lying in the path leading to the adaptation − this
is important so that an actor i is not blocked earlier than required.
For instance, to correctly apply the first slnt kill(hId) action declared in (4.8), we convert the end-of-headers necessity (i.e., [hId . ret({yaws, do recv, 3, {ok, http_eoh}})]
into a blocking one. This candidate was chosen as it satisfies rules (i), (ii) and (iii). Using the same argument we add the blocking attribute to the first necessity, allowing the
prg(acceptor) adaptation to be applied. The rest of the necessities are converted into

asynchronous necessities. Hence by using these 3 rules we augment (4.8) with blocking
necessities to obtain (4.9).
max X.
[acceptor?{hId, next, }] b
[hId . ret({yaws, do recv, 3, {ok, {http req, ’GET’, {abs path, path}, }}})] a
[hId . ret({yaws, do recv, 3, {ok, h1}})] a

..
.
[hId . ret({yaws, do recv, 3, {ok, h6}})] a
[hId . ret({yaws, do recv, 3, {ok, http_eoh}})] b
if path = “/pic.png00 orelse path = “/site.html00 then
[hId . call({yaws sendfile, send, [ , path, , ]})] a X

(4.9)

else
slnt kill(hId) prg(acceptor) X



Thus far in (4.9), we have only added synchronisation to block the adaptees without
ever releasing them. We must therefore release these blocked actors by specifying them
72

4. Language Design, Implementation and Formalisation

in the appropriate release-lists. As shown by (4.10) we add the acceptor to the release
list of every necessity operation that lies between the blocking necessity which blocks the
acceptor actor ([acceptor?{hId, next, }] b ), and the adaptation (i.e., prg(acceptor)).
This ensures that the acceptor is released in case the property is trivially satisfied before
reaching the point where the adaptation needs to be applied. Furthermore, we also add
the handler and the acceptor to the release list of the last adaptation; this ensures that the
actors are released after applying the adaptations.
max X.
[acceptor!{hId, next, }] bε
[hId . ret({yaws, do recv, 3, {ok, {http req, ’GET’, {abs path, path}, }}})] aacceptor
[hId . ret({yaws, do recv, 3, {ok, h1}})] aacceptor

..
.
[hId . ret({yaws, do recv, 3, {ok, h6}})] aacceptor
[hId . ret({yaws, do recv, 3, {ok, http_eoh}})] bacceptor
if path == “/pic.png” orelse path == “/site.html” then
[hId . call({yaws sendfile, send, [ , path, , ]})] aacceptor X
else
slnt kill(hId)ε prg(acceptor)hId,acceptor X



(4.10)
Therefore the rule of thumb for releasing a blocked actor is to add its identifier to the
release list of:
(i) every necessity lying between the point where it was blocked and its adaptation
action; and to
(ii) the release list of the adaptation which is applied to it (or to those defined after it
within the same path).

4.5

A Formal Model for Runtime Adaptation

In this section we develop a formal operational model of our runtime adaptation framework. The primary reasons for developing this formal model are:
• A formal model enables us to understand how the monitors specified by an arbitrary
script are to behave at runtime without having to actually compile the script and run
73

4. Language Design, Implementation and Formalisation

the synthesised monitors. This also relieves us from having to deal with the complexities of the implementation.
• As during the course of this research we developed the model in parallel to the implementation, the model also served to guide the implementation (and vice-versa).
• The model allows us to prove that the operational behaviour of our framework corresponds to the semantics defined in earlier work [39].
• Formalisation also enables us to better understand the kind of errors that our RA
scripts may introduce, thereby allowing us to look into static analysis techniques in
Chapter 5 for detecting erroneous scripts prior to deployment.
We develop this model in two subsequent phases. As part of the first phase, in
Sec. 4.5.1 we develop and present a preliminary operational model for detectEr’s original (unextended) logic (as presented in Fig. 2.10 in Sec. 2.4), given that currently such
a model does not exist. We then prove that our operational model corresponds to detectEr’s semantics developed in previous work [39].

For the second phase, in Sec. 4.5.2, we formalise the mechanisms for applying adaptation and incremental synchronisation, in a way which reflects the implementation constraints (explained Sec. 4.1). We then augment these formalised mechanisms in our
preliminary formal model.

4.5.1

An Operational Model for detectEr

A semantics [66] for the closed logic formulas (i.e., no free term or formula variables)
of the core logic presented in Sec. 2.4.3, is given as a Labelled Transition System (LTS).
This is defined by the transition rules in Fig. 4.6. Note that for convenience we restate
the logic’s syntax in Fig. 4.6 as well.
The Preliminaries
The LTS semantics models abstractly the monitoring of the respective property, and
assumes: a set of (visible) actions α, β ∈ Act, and a distinguished silent action, τ; we let
74

4. Language Design, Implementation and Formalisation

Syntax
ϕ, ψ ∈ Frm ::= tt | ff | ϕ & ψ | [e]ϕ | X | max X. ϕ | if b then ϕ else ψ
Structural Equivalence Semantics
ϕ1 & ϕ2 ≡ ϕ2 & ϕ1

ϕ1 & (ϕ2 & ϕ3 ) ≡ (ϕ1 & ϕ2 ) & ϕ3

tt & ϕ ≡ ϕ

ff & ϕ ≡ ff

Reduction Semantics
rIdem1

rIdem2

α

ff −−→ ff

b ⇓ true
rTru

τ

α

rCn1

rFls

τ

if b then ϕ else ψ −
−→ ψ

α

ϕ & ψ −−→ ϕ0 & ψ0

rStr

τ

ψ −−→ ψ0
α

γ

ϕ ≡ ϕ0 −−→ ψ0 ≡ ψ

b ⇓ false

if b then ϕ else ψ −
−→ ϕ

ϕ −−→ ϕ0

α

tt −−→ tt

τ

ϕ→
− ϕ0
rCn2

ψ→
− ψ0

τ

ϕ & ψ −−→ ϕ0 & ψ

rCn3

mtch(e, α) = σ
rMax

τ

max X. ϕ −
−→ ϕ[max X. ϕ/X]

rNc1

γ

ϕ −−→ ψ

α

[e]ϕ −−→ ϕσ

τ

ϕ & ψ −−→ ϕ & ψ0
mtch(e, α) = ⊥

rNc2

α

[e]ϕ −−→ tt

Figure 4.6: The Logic and its LTS Semantics
γ range over Act ∪ {τ}. Visible actions represent system operations which may contain
values v, u ∈ Val, that range over either actor identifiers, i, j, h ∈ Pid, or generic data
p ∈ Data such as integers n, lists p1 : . . . : pn , and tuples {p1 , . . . , pn }. We also use
metavariable l ∈ (Pid ∪ Var) to represent either a process identifier or a term variable.
In this semantics we work up-to structural equivalence of formulas ϕ ≡ ψ (see Equivalence Semantics in Fig. 4.6) for commutativity, associativity etc. The semantics also assumes a partial function mtch(e, α), defined in Def. 4.5.1, for matching action patterns,
e, with visible system actions, α. When a match is successful, the function returns a
substitution from the term variables found in the pattern, to the corresponding values of
the matched action, σ :: Var * Val.
Def. 4.5.1 (Pattern Matching). Function mtch(e, d) defines how a pattern e can be
compared to another pattern d. When a match occurs this function returns a mapping
from term variables to actual values, while it returns ⊥ when the patterns do not match.

75

4. Language Design, Implementation and Formalisation




pmtch(l1s , l2s ) ⊕ pmtch(l1r , l2r )






⊕ pmtch( p1 , p2 )
if e = l1s .l1r !p1 ∧ d = l2s .l2r !p2








pmtch(l1 , l2 ) ⊕ pmtch( p1 , p2 ) if e = l1 ?p1 ∧ d = l2 ?p2





pmtch(l , l ) ⊕ pmtch( p , p ) if e = l . call(p ) ∧ d = l . call(p )
1 2
1 2
1
1
2
2
mtch(e, d) = 






pmtch(l1 , l2 ) ⊕ pmtch( p1 , p2 ) if e = l1 . ret(p1 ) ∧ d = l2 . ret(p2 )








{}
if e = or d =






⊥
otherwise



{}
if p0 = n ∧ p00 = n








{}
if p0 = x ∧ p00 = y








{x 7→ v}
if (p0 = x and p00 = n) or (p0 = n and p00 = x)



 Ln

00
pmtch( p0 , p00 ) = 
if p0 = {p01 , . . . , p0n }, p00 = {p00

i=1 σl2
1 , . . . , pn }






where pmtch( p0i , p00


i ) = σi




0
00
0
0
0
00
00
00


pmtch( p01 , p00


1 ) ⊕ pmtch( p2 , p2 ) if p = p1 ; p2 ∧ p = p1 : p2




⊥
otherwise




σ1 ∪ σ2 if ∀p ∈ dom(σ1 ) ∩ dom(σ2 ) · σ1 (p) = σ2 (p)





σ1 ⊕ σ2 = 
⊥
if σ1 = ⊥ or σ2 = ⊥






⊥
otherwise
def

def

def

The mtch(e, d) function starts by checking whether the patterns define the same kind
action e.g., whether both patterns define an output action. The mtch function also supports the universally matching pattern “ ”, this matches any action pattern. If the action
types match, this function uses the pmtch( p0 , p00 ) function to inspect the actions’ subjects
and the data, where the latter includes values v, term variables x, tuples {p01 , . . . , p0n } and
lists p01 ; p02 . The pmtch( p0 , p00 ) function checks that the data patterns correspond in both
patterns e.g., a tuple of three matching entries are present in both patterns. If the data
patterns match it returns a substitution environment σ which maps the term variables
declared in one pattern, to the values of the other pattern.
Note that although the mtch function is defined for comparing necessity patterns i.e.,
mtch(e, d), it can still be used to compare a necessity pattern e with a system action α

i.e., mtch(e, α), as well as to compare two system actions mtch(α, β). This is possible
as system actions are equivalent to closed patterns i.e., patterns that do not define any
76

4. Language Design, Implementation and Formalisation

term variables. This pattern matching mechanism is further explained in the following
example.
Example 4.5.1. We consider a number of scenarios where two patterns are pattern
matched using the mtch function.
mtch(srv.clnt!{x, ack, y} , srv.clnt!{5, ack, z}) = {x 7→ 5}

(4.11)

mtch(srv.clnt!{5, ack, z} , srv.clnt!{x, ack, y}) = {x 7→ 5}

(4.12)

For instance in (4.11) the output (open) necessity pattern srv . clnt!{x, ack, y} is successfully matched with another (open) output pattern srv . clnt!{5, ack, z}. Note that
term variable x is pattern matched with the value 5 and thus returned in the resultant
substitution environment. Although variable y is also matched to variable z, the match
function does not return an entry in the resultant substitution environment (remember
that the substitution only maps variables to values). Also note that the match returns the
same substitution environment if the arguments are inverted as shown in (4.12).
mtch(srv.clnt!{x, ack, y}, srv.clnt!{5, ack, joe}) = {x 7→ 5, y 7→ joe}

(4.13)

mtch(srv.clnt!{5, ack, joe}, srv.clnt!{5, ack, joe}) = {}

(4.14)

In (4.13) the necessity pattern srv.clnt!{x, ack, y} is now matched with a closed patten
i.e., a system action srv.clnt!{5, ack, joe}, where x and y are pattern matched with the
values 5 and joe respectively. Moreover, in (4.14) the two closed actions are matched
(exactly), thereby returning the empty substitution.
mtch(clnt.srv!{x, ack, y}, srv.clnt!{5, ack, joe}) = ⊥

(4.15)

mtch(clnt?{x, ack, y}, srv.clnt!{5, ack, joe}) = ⊥

(4.16)

The mismatch in (4.15) is caused by the mismatching subjects of the output actions
i.e., srv versus clnt, whereas the mismatch in (4.16) is caused since the different type
of actions cannot pattern match, i.e., input action clnt?{x, ack, y} and output action


srv!clnt{5, ack, joe} do not match.

77

4. Language Design, Implementation and Formalisation

The Modelled Reductions
In Fig. 4.6, we give two kinds of reductions namely α-reductions and τ-reductions, which
α

τ

are respectively represented using judgements ϕ −→ ϕ0 and ϕ −→ ϕ0 . The first judgeα

ment, i.e., ϕ −→ ϕ0 , represents cases where a monitor ϕ evolves (progresses) into a
different monitor ϕ0 after observing an external system event α. This models the implementation mechanism where a monitor reads a system event from its mailbox and
τ

progresses accordingly. The second judgement, i.e., ϕ −→ ϕ0 , models an internal (silent)
action that a monitor ϕ can perform so to evolve into ϕ0 . Internal actions include operations such as unfolding recursive definitions and evaluating if-statement conditions.
Using these judgements we can represent a monitor execution as a sequence of internal
(τ) and external (α) reductions as shown in Ex. 4.5.2.
Example 4.5.2. Reduction sequence (4.17) models the execution of a monitor ψ wrt. the
following sequence of observable system events: α1 ; . . . ; αn .
τ

α1

τ

αn

τ

τ

ψ (−→)∗ −−→ (−→)∗ · · · (−→)∗ −−→ (−→)∗ ψ0

(4.17)

As shown by (4.17), a monitoring reduction over an observation αi may be interposed
αi

τ

τ

by internal (silent) τ-reductions, i.e., ψ00 (−→)∗ −−→ (−→)∗ ψ000 .



More specifically, in Fig. 4.6, formulas tt and ff are idempotent wrt. external transitions and interpreted as final states (verdicts). Conditional formulas silently (internally)
branch to the respective subformula depending on the evaluation of a decidable boolean
expression (rTru and rFls), whereas rule rMax internally unfolds a recursive formula.
Necessity formulas, [e]ϕ, transition only with a visible action, α. If the action α matches
the necessity’s pattern (i.e., mtch(e, α) = σ), it transitions to the continuation subformula
ϕ where the variables bound by the matched pattern are substituted with the respective
matched values obtained from the action, i.e., it reduces into ϕσ where σ contains the
data mappings. Otherwise, the necessity formula transitions to tt in case of a mismatch
(rNc2).
The rules for conjunction formulas model the parallel execution of subformulas as
described in [39], whereby subformulas are allowed to perform independent silent τ78

4. Language Design, Implementation and Formalisation

transitions (rCn2 and rCn3) but transition together for external (α) actions depending on
their individual transitions (rCn1). Finally, rStr allows us to abstract over structurally
γ

τ

γ

τ

equivalent formulas. We write ϕ =
⇒ ψ in lieu of ϕ(−→)∗ −→ (−→)∗ ψ. We let t ∈ Act∗
t

αn

α1

range over lists of visible actions and write ϕ ⇒
= ψ to denote ϕ ==⇒ . . . ==⇒ ψ where
t = α1 . . . αn .
Example 4.5.3. Recall property ϕ from (2.1) of Ex. 2.4.1 (restated below).


def
ϕ = max Y. [i?{inc, x, y}] ( [ j.y!{res, x + 1}] Y) & ( [ .y!err] ff)

(2.1)

Using the semantics of Fig. 4.6, we can express how formula ϕ reduces to a violation
when executed wrt. to the following trace:
i?{inc, 5, h}; j.h!{res, 6}; i?{inc, 3, h0 }; k.h0 !err
The runtime derivation is given as follows:
i?{inc,5,h}

ϕ ========⇒ (( [ j.y!{res, x + 1}] ϕ) & ( [ .y!err] ff)) {x 7→ 5, y 7→ h}
≡ (( [ j.h!{res, 5 + 1}] ϕ) & ( [ .h!err] ff))
(ϕ reduced with rules rMax and rNc1, where mtch(i?{inc, x, y}, i?{inc, 5, h}) = {x 7→ 5, y 7→ h})
j . h!{res,6}

k . h0 !err

i?{inc,3,h0 }

========⇒ ϕ ========⇒ (( [ j.h0 !{res, 3 + 1}] ϕ) & ( [ .h0 !err] ff)) =======⇒ ff



The Correspondence Evaluation
Although we have developed LTS semantics which describe the runtime behaviour of
the formulas expressed in detectEr’s logic, we still do not have any guarantees that this
semantics corresponds to other semantics already available for detectEr [39].
As outlined in Sec. 2.4, in [39] the authors provide satisfaction semantics describing
how an actor system satisfies a formal property, along with violation semantics which
describe how a finite trace of system events, violates a formal property expressed in
detectEr’s logic.

Similar to the way that the authors of [39] prove semantic correspondence between
the satisfaction and violation semantics, in Thm. 4.5.1 we prove that our LTS semantics
79

4. Language Design, Implementation and Formalisation

corresponds to the violation semantics. The primary reason for proving correspondence
wrt. the violations semantics (and not the satisfaction semantics), is that violations are
defined wrt. a finite trace t, which in some sense is analogous to the way a formula ϕ
t

reduces into ϕ’ over a trace t, i.e., ϕ ⇒
= ϕ0 .
Theorem 4.5.1 (Semantic Correspondence).
t

t

A, t v ϕ iff (ϕ ⇒
= ff and A ⇒)
=
In Thm. 4.5.1 we prove that if an arbitrary actor system A generates a trace t which
violates ϕ, i.e., A, t v ϕ, then for the same trace t, the formula ϕ should reduce to ff (i.e.,
to a violation) with our LTS semantics. We also prove the converse, i.e., if ϕ reduces to
ff wrt. some trace t with our LTS semantics, then the respective violation relation should

also hold.
We here give an outline of the main cases of the proof for Thm. 4.5.1. The reader
may safely skip the details of the proof and proceed with Sec. 4.5.2.
Proof. During this proof we make reference to Lemma C.1.1 (stated below).
Lemma C.1.1 .
γ

γ

ϕ −→ ϕ0 and B, t v ϕ0 and A =
⇒ B implies A, γt v ϕ
γ

This Lemma is proved by rule induction on ϕ −→ ϕ0 . We provide a proof outline of this
lemma in Appendix Sec. C.1.
We prove the only-if case for Thm. 4.5.1 by rule induction on A, t v ϕ. We here outline
the main cases.
Case A, t v [e]ϕ: We know
t = αt0

(4.18)

A =⇒ B
mtch(e, α) = σ
B, t0 v ϕσ

(4.19)
(4.20)
(4.21)

α

By (4.21) and IH we know
t0

ϕσ ⇒
= ff
t0

B⇒
=
80

(4.22)
(4.23)

4. Language Design, Implementation and Formalisation
αt0

t

Thus, by (4.19) and (4.23) we obtain A =⇒, i.e., A ⇒
= by (4.18), and by (4.20),
αt0

t

(4.22) and rNc1 we derive [e]ϕ =⇒ ff, and by (4.18) this means [e]ϕ ⇒
= ff as required.
Case A, t v max X. ϕ: We know
A, t v ϕ[X/max X. ϕ]

(4.24)

By (4.24) and IH we know
t

ϕ[X/max X. ϕ] ⇒
= ff
t

A⇒
=

(4.25)
(4.26)

τ

By rMax we know max X. ϕ →
− ϕ[X/max X. ϕ], and hence, by(4.25), we obtain
t
max X. ϕ ⇒
= ff as required.
Case A, t v ϕ & ψ: We know
A, t v ϕ
or A, t v ψ
We must consider the following cases:
Subcase t = ε: By (4.27), (4.28) and since t = ε we know
ϕ = ff
or ψ = ff

(4.27)
(4.28)

(4.29)
(4.30)

ε

By (4.29) and since ff & ψ ≡ ff we know ff & ψ ⇒
= ff as required. Similarly, by (4.30)
ε
and since ϕ & ff ≡ ff we know ϕ & ff ⇒
= ff.
Subcase t = αt: Since t = αt, by (4.27) and IH we know
αt

ϕ⇒
= ff
αt

A⇒
=
Similarly, since t = αt, by (4.28) and IH we know
αt

ϕ⇒
= ff
αt

A⇒
=

(4.31)
(4.32)

(4.33)
(4.34)

From (4.31) and (4.33) we can deduce
α

ϕ −→ ϕ0
0 t

(4.35)

ϕ ⇒
= ff

(4.36)

ψ −→ ψ0

(4.37)

α

0 t

ψ ⇒
= ff

81

(4.38)

4. Language Design, Implementation and Formalisation

By (4.35), (4.37) and rCn1 we know
α

ϕ & ψ −→ ϕ0 & ψ0
By (4.36), (4.38) and (4.39) we know
α

t

ϕ & ψ −→ ϕ0 & ψ0 ⇒
= ff & ff ≡
Hence this subcase holds by (4.34) and (4.40).

(4.39)
αt

ϕ&ψ ⇒
= ff

(4.40)

Subcase t = τt (ϕ makes the τ transition): Since t = τt, by (4.28) and IH we know
τt

ϕ⇒
= ff
τt

A⇒
=

(4.41)
t

A⇒
=

≡

(4.42)

From (4.41) we can deduce
τ

ϕ −→ ϕ0

(4.43)

0 t

ϕ ⇒
= ff

(4.44)

By (4.43) and rCn2 we know
τ

ϕ & ψ −→ ϕ0 & ψ
(4.45)
0
We know that for trace t, ψ reduces to some ψ such that by (4.45) we can conclude
τ

t

ϕ & ψ −→ ϕ0 & ψ ⇒
= ff & ψ0 ≡
Hence this subcase holds by (4.42) and (4.46).

t

ϕ&ψ⇒
= ff

(4.46)

Subcase t = τt (ψ makes the τ transition):
The proof for this subcase is synonymous to that of the previous subcase.

t

We prove the if case by numerical induction on the number of transitions used in ϕ ⇒
= ff.
n = 0: We know that ϕ = ff and t = ε and we trivially have A, ε v ff for arbitrary A.
n = k + 1: For some ϕ0 we know t = γt0 such that
γ

ϕ −→ ϕ0
0
0 t

ϕ ⇒
= ff

(4.47)
t0

where | =⇒ |= k.

(4.48)

Similarly we also know
γ

A=
⇒B
t0

B⇒
=

(4.49)
(4.50)

By (4.48), (4.50) and IH we know
B, t0 v ϕ0
(4.51)
0
0
By (4.47), (4.49), (4.51) and Lemma C.1.1 we know A, γt v ϕ as required. 
82

4. Language Design, Implementation and Formalisation

4.5.2

Extending the Operational Model with Runtime Adaptation

Fig. 4.7 (below) describes a semantics for the extended logic with adaptations as discussed in Sec. 4.1 to 4.3. Recall that in Table 4.1 (given in Sec. 4.1.2) we distinguished
between synchronous and asynchronous classes of adaptations. Given that adaptations
pertaining to the same class are applied (to the adaptee) in the same manner, in this
extended logic we work at this level of abstraction.
We therefore represent asynchronous and synchronous classes of adaptations as aA(w)r
and sA(w)r respectively: they both take two lists of actor references as argument —
w, r ∈ (Pid ∪ Var)∗ — which are synonymous to the adaptation and release lists. Furthermore, the extended logic also includes the extended necessity, [e] ρw , where in this
case w is analogous to the release list, RL, of the necessity.
The extended logic also uses two additional constructs, blk(r) ϕ and rel(r) ϕ; these
are not meant to be part of the specification scripts, but are used as part of the runtime
syntax. Since the extended logic can also affect the system being monitored, through
adaptations and synchronisations, the operational semantics is given in terms of configurations, s . ϕ, where s is an abstract representation of the system and ϕ is a closed
formula. Note that in the previous operational model (given in Fig. 4.6), giving an abstract representation of the system was not necessary as the formulas were passive, i.e.,
unable to effect the system. By contrast in the extended logic we now include adaptations
and synchronisation mechanisms that can effect the system; an abstract representation s
therefore allows us to model some of these effects at a high level of abstraction.
In addition to closed formulas ϕ, configurations thus include the monitored system
represented abstractly as a partial map, s :: Pid * {•, ◦}, describing whether an actor
(through its unique identifier) is currently blocked (suspended), •, or executing, ◦. We
occasionally write w : • to denote the list of mappings i1 : •, . . . , in : • where w = i1 , . . . , in
(similarly for w : ◦).
In the operational model given in Fig. 4.7, we also distinguish between two kinds of
reductions that can be carried out by a configuration, namely α-reductions, formalised
α

τ

as s . ϕ −→ s0 . ϕ0 , and τ-reductions i.e., s . ϕ −→ s0 . ϕ0 . The former defines cases where
83

4. Language Design, Implementation and Formalisation
Extended Logic Syntax with Adaptations and Synchronisations
ϕ, ψ ∈ Frm ::= . . .

ρ

[e] w ϕ

|

|

aA(r)w ϕ

sA(r)w ϕ

|

|

blk(r) ϕ

rel(r) ϕ

|

Monitor Transition Rules
mtch(e, α) = σ
rNc1

subj(α) = i

mtch(e, α) = σ
rNc2

α

[e] br ϕ −−→ blk(i) (ϕσ)

α

[e] ar ϕ −−→ ϕσ

mtch(e, α) = ⊥
rNc3

rAdA

α

ρ

[e] r ϕ −−→ rel(r) tt

rAdS

a(w )

aA(w)r ϕ −−−−→ rel(r) ϕ

s(w )
sA(w)r ϕ −−−−→ rel(r) ϕ
µ

ϕ −−→ ϕ0
rRel

rBlk

r(r )

rel(r) ϕ −−−→ ϕ

rCn4

b(r )

blk(r) ϕ −−−−→ ϕ

µ

ϕ & ψ −−→ ϕ0 & ψ

System Transition Rules
subj(α) = i
sNew

τ

w ⊆ dom(s)
sAdA

α

s −−→ s, i : ◦
sBlk

ids(α) ⊆ dom(s)

sAct

s, i : ◦ −−→ s, i : ◦
sRel

b(w )

s, w : ◦ −−−−→ s, w : •

a(w )

s −−−−→ s
sAdS

r(w )

s(w )

s, w : • −−−−→ s, w : ◦

s, w : • −−−−→ s, w : •

Instrumentation Transition Rules
µ

µ

ϕ −−→ ϕ0
iAda
µ

ϕ −−6 →
iTrm

µ

ϕ −−6 →

s −−→ s0
τ

s . ϕ −−→ s0 . ϕ0
α

s −−→ s0

iAct

α

α

α

ϕ −−→ ϕ0

α

s . ϕ −−→ s0 . ϕ0
τ

τ

ϕ −−6 →

s . ϕ −−→ s0 . tt

α

s −−→ s0

iSys

s −−→ s
τ

s . ϕ −−→ s0 . ϕ

ϕ −−→ ϕ0
iMon

τ

s . ϕ −−→ s . ϕ0

Figure 4.7: A Runtime Semantics for Instrumented Properties with Adaptations
a system s commits an external monitorable event α which is perceived by monitor ϕ;
due to this event, both the system and the monitor may evolve together thus forming a
new configuration s0 . ϕ0 . The latter (i.e., τ-reductions) formalise cases where the whole
configuration s.ϕ performs an internal transition into another configuration s0 .ϕ0 where
s0 and ϕ0 are the evolved versions of s and ϕ respectively. These internal transitions can
be caused in the following cases:
• System s performs an internal operation which is unobservable by ϕ, and thus s
84

4. Language Design, Implementation and Formalisation

evolves into some s0 independently of formula ϕ.
• Formula ϕ performs the internal transition thereby reducing into some formula ϕ0 ,
independently from the system s.
• System s and formula ϕ interact over a synchronisation operation or an adaptation
action − this causes both s and ϕ to evolve together into some new s0 and ϕ0 ,
according to the exchanged interaction.
More specifically, to describe adaptation interactions between the monitor and the
system, the LTS semantics of Fig. 4.7 employs four additional labels, ranged over by the
variable µ. These include the asynchronous and synchronous adaptation labels, a(w ) and
s(w ), to denote respectively that an asynchronous and synchronous action affecting ac-

tors w, has been executed. They also include a blocking action, b(w ), and an unblocking
(release) action, r(w ), affecting the execution of actors with identifiers in the list w.
The semantics is defined in terms of three LTSs: one for logical formulas (i.e., monitors), one for systems, and one for configurations which is based on the other two LTSs.
The Monitor Transition Rules
These extend the rules in Fig. 4.6 with the exception of those for the necessity formulas,
which are replaced by rules rNc1, rNc2 and rNc3. Whereas the new rNc2 follows
the same format as that of rNc1 from Fig. 4.6, the rule for blocking necessity formulas,
rNc1, transitions into a blocking construct, blk(i) ϕ, for blocking the subject of the action
(i.e., actor i) in case a pattern match is successful. Note that the action’s subject is
retrieved using the subj(e) function as defined in Def. 4.5.2 (below). Furthermore, in
case an action mismatches a necessity pattern, rNc3 transitions the necessity formula to
a release construct, rel(r) ϕ, with the specified release list of actors, r.
Def. 4.5.2 (The subject Function).



l1






l
subj(γ) = 


l





l
def

if e = l1 .l2 !p
if e = l?p
if e = l . call(p)
if e = l . ret(p)

85

4. Language Design, Implementation and Formalisation

This function inspects a necessity pattern e and extracts the process identifier of the
actor that committed the action. In particular note that in output actions e.g., i. j!3, the
subject is i, as this actor is sending the message to j, and hence it is the one performing


the output action.

Asynchronous and synchronous adaptation formulas, aA(w)r ϕ and sA(w)r ϕ, transition with the respective labels (i.e., a(w ) and s(w )), using rules rAdA and rAdS, to a
release construct, rel(r) , thereby communicating the respective adaptations to w. Similarly the block and release constructs reduce over labels (i.e., b(r ) and r(r ) with rules
rRel and rBlk respectively. Finally, rule rCn4 allows monitor actions affecting the
system, i.e., µ, to be carried out under a conjunction formula, independent of the other
branch; we elide the obvious symmetric rule rCn5.
The System Transition Rules
These rules allow further actor spawning (rule sNew in Fig. 4.7) but restrict external
actions (α) to those whose subject is currently active, i.e., unblocked i : ◦ (rule sAct);
external actions model the system events that can be reported to the monitor. Whereas
asynchronous adaptations can be applied to any actor list, irrespective of their status i.e.,
can be either w:◦ or w:• (rule sAdA), synchronous ones require the adaptees to be blocked
i.e., must be w:• (rule sAdS). This transpires from our implementation constraints where
synchronous adaptations require the respective system actors to be blocked by our instrumented code in some prior stage, so that the monitor can later on communicate
synchronous adaptations as messages, which are then received and interpreted by the
instrumented actor. By contrast, this is not required by asynchronous adaptations.
Finally rules sBlk and sRel model the blocking and releasing mechanism used by
the RA protocol code instrumented in the system. In fact, blocking is modelled in terms
of a transition which changes the status of the respective adaptees from active i : ◦, to
blocked i : •; the converse applies for releasing.

86

4. Language Design, Implementation and Formalisation

The Instrumentation Rules
The instrumentation rules for configurations describe how system (visible) actions, α,
affect the monitors, and dually, how the monitor adaptation and synchronisation actions,
µ, affect the system. For instance, if the monitor instigates action µ and the system allows
it, they both transition together as a silent (internal) transition (rule iAda). Dually, if the
system generates external action α and the monitor can observe it, they also transition
together (rule iAct); this essentially models the event reporting mechanism whereby
monitorable events are forwarded to the monitor by the system.
If the monitor cannot observe an external α-action (rule iTrm), it terminates as formula tt. Importantly note that both rules iAct and iTrm require the monitor not to be in a
µ

position to perform an adaptation/synchronisation action, i.e., premise ϕ −→;
6
this gives
precedence to monitor actions over system ones in our instrumentation. This precedence
models the fact that in our RA protocol implementation (refer to Sec. 4.3), the monitoring loop only perceives a system event by reading it from its mailbox after it is done
applying the necessary handling actions, which may include µ-actions e.g., issuing an
adaptation. Rules iSys and iMon allow systems and monitors to perform internal transitions (wrt. τ-actions) independently of each other.
Remark 7. We point out the subtle difference between rules iAct and iAda. Rule iAct
states that whenever the system s performs an external α-action which is perceived by
formula ϕ, then the entire configuration s . ϕ transitions over the external action, i.e.,
α

s . ϕ −→ s0 . ϕ0 . This opposes rule iAda in which whenever the system s and formula ϕ
interact over a synchronisation or adaptation action µ, then the configuration transitions
µ

τ

silently, i.e., s . ϕ −→ s0 . ϕ0 instead of s . ϕ −→ s0 . ϕ0 . This transpires since external αactions may be observable by other external systems which interact with the monitored
system, and hence these actions must be kept observable. Contrasting this, µ-actions
model interactions that are only relevant (i.e., internal) to the system and monitor, and
hence they are not be made perceivable to external systems.

87



4. Language Design, Implementation and Formalisation

Formalising Synchronisation Errors
In the following example we use our extended operational model to demonstrate how
adaption formulas reduce at runtime when executed wrt. a system. More importantly
we show the subtle errors that erroneously specified synchronisation operations may
introduce at runtime.
Example 4.5.4. Recall the adaptation formula ϕ0 defined in (4.7) of Ex. 4.2.2 (restated
below).
ϕ0 = max Y. [i?{inc, x, y}] a
def

[i. !{inc,

x, y}] bε

( [ j.y!{res, x + 1}] a Y) &
( [z .y!err] bi restr(i)ε prg(z)i,z Y)

!
(4.7)

Now, consider the following system s = ((i, j, k, h) : ◦) where all of the system actors (i.e.,
i, j, k and h) are unblocked. We can therefore model the runtime execution of formula ϕ0
wrt. system s with adaptations as follows:


 [ j.h!{res, 2}] a ϕ0 &

ε


s . ϕ0 ========⇒ · ==========⇒ s . blk(i) 
(4.52)
 [z .h!err] b restr(i) prg(z) ϕ0 
ε
i,z
i


τ
−→ (( j, h, k) : ◦, i : •) . [ j.h !{res, 2}] aε ϕ0 & [z .h!err] bi restr(i)ε prg(z)i,z ϕ0
i?{inc,1,h}

i . k!{inc,1,h}

(4.53)
k . h!err

−−−−−−→ (( j, h, k) : ◦, i : •) . blk(k) restr(i)ε prg(k)i,k ϕ0
τ

−→ (( j, h) : ◦, i : •, k : •) . restr(i)ε prg(k)i,k ϕ0
τ

τ

=
⇒ (( j, h) : ◦, i : •, k : •) . rel(i, k) ϕ0 −→ s . ϕ0

(4.54)
(4.55)
(4.56)

In particular, the blocking necessities matched in (4.52) and (4.54) yield the actor blocking constructs which block actors i and k in (4.53) and (4.55) respectively. This allows
for the synchronous adaptations in (4.56) to be performed, thereby proceeding to release
of the respective actors.
Erroneous blocking directives however may lead to stuck synchronous adaptations
i.e., rule sAdS is inapplicable on the system. For instance, if we change the first blocking
88

4. Language Design, Implementation and Formalisation

necessity in ϕ0 of (4.7) to an asynchronous one, [i. !{inc, x, y}] aε , it yields the execution
below, where ϕ00 is the erroneous formula:
ϕ

00

= max Y. [i?{inc,

def

i ?{inc,1,h}

x, y}] a

i . k!{inc,1,h}

x, y}] aε

[i. !{inc,

( [ j.y!{res, x + 1}] a Y) &
( [z .y!err] bi restr(i)ε prg(z)i,z Y)

!

k . h!err

s . ϕ00 ========⇒ · =========⇒ · ======⇒ ((i, j, h) : ◦, k : •) . restr(i)ε prg(k)i,k ϕ00 (4.57)
The final configuration in (4.57) (i.e., ((i, j, h) : ◦, k : •) . restr(i)ε prg(k)i,k ϕ00 ) is stuck
because the synchronous adaptation on i (i.e., restr(i)) cannot be carried out since i is
not blocked (i.e., since i : ◦). A similar situation is reached if a blocked actor is released prematurely by a concurrent branch. For instance, if we erroneously change the
release list of the necessity subformula [ j . y!{res, x + 1}] aε from ε to i, this releases i
upon mismatch, interfering with adaptation actions along the other branch of the conjunction. Hence this erroneous formula (ϕ000 ) exhibits a race condition whereby it can
non-deterministically yield a number of executions, some of which may lead to stuck
configurations. Here we present two possible executions, where one of them applies the
restr(i) adaptation erroneously:

ϕ

000

( [ j.y!{res, x + 1}] ia Y) &
( [z .y!err] bi restr(i)ε prg(z)i,z Y)

!


 [ j.h!{res, 2}] a ϕ0 &
i

========⇒ · =========⇒ (( j, h, k) : ◦, i : •) . 
 [z .h!err] b restr(i) prg(z) ϕ000
ε
i,z
i






= max Y. [i?{inc,

def

s . ϕ000

i?{inc,1,h}

x, y}] a

[i. !{inc,

x, y}] bε

i . k!{inc,1,h}

k . h!err

−−−−−−→ (( j, h, k) : ◦, i : •) . (rel(i) tt) & (blk(k) restr(i)ε prg(k)i,k ϕ000 )

(4.58)

Execution 1: The adaptation is erroneously applied.
The concurrent branch rel(i) tt in (4.58) is evaluated first, thereby releasing actor i prior
to applying the restart action restr(i) defined in the opposing branch (i.e., blk(k) restr(i)ε . . .).
This leads to the stuck configuration shown in (4.59) where adaptation restr(i)ε cannot
be applied (with rule iAda) as actor i is active, i.e., i : ◦.
τ

=
⇒ ((i, j, h) : ◦, k : •) . restr(i)ε prg(k)i,k ϕ000
89

(4.59)

4. Language Design, Implementation and Formalisation

Execution 2: The adaptation is validly applied.
The adaptation can however be validly applied in case the concurrent branch in (4.58)
containing the restart adaptation (i.e., branch blk(k) restr(i)ε . . .) is evaluated before the
releasing branch as shown in (4.60) below.
τ

−→ (( j, h) : ◦, (i, k) : •) . (rel(i) tt) & (restr(i)ε prg(k)i,k ϕ000 )
τ

−→ (( j, h) : ◦, (i, k) : •) . (rel(i) tt) & (prg(k)i,k ϕ000 )

(4.60)

Synchronisation errors caused due to concurrency can be more difficult for the specifier
to notice without any assistance. Similarly, synchronisation errors can also be introduced
in cases where the monitor attempts to release an unblocked actor. For instance, if we
continue with the second execution (i.e., from (4.60)), the configuration progresses to a
point where both concurrent branches attempt to release actor i. In the derivation below
we consider two possible continuations, starting from (4.60), both of which lead to a
stuck configuration.
τ

Continuation 1:

−→ ((i, j, h) : ◦, k : •) . tt & (prg(k)i,k ϕ000 )
τ

−→ ((i, j, h) : ◦, k : •) . (rel(i, k) ϕ000 )
τ

−→ ((i, j, h, k) : ◦) . (rel(i) ϕ000 )
Continuation 2:

(4.61)

τ

−→ (( j, h) : ◦, (i, k) : •) . (rel(i) tt) & (rel(i, k) ϕ000 )
τ

=
⇒ ((i, j, h, k) : ◦) . (rel(i) tt) & ϕ000

(4.62)

Once again in both (4.61) and (4.62) we have reached a stuck configuration because a
second release action on i could not be carried out as this was already released.



The semantics of Fig. 4.7 allows us to formalise configurations in an erroneous state
(as identified in Ex. 4.5.4) i.e., when a monitor wants to release or apply synchronous
adaptations that the system prohibits, as follows:
s(w )
s(w ) 
r(w )
r(w ) 
def
Def. 4.5.3. error(s . ϕ) = ϕ −−−−→ and s −−−6 −→ or ϕ −−−→ and s −−6−→

for some w ∈ dom(s)
This definition states that a synchronisation error can be caused in two ways. The first
90

4. Language Design, Implementation and Formalisation

s(w )

way occurs when a formula ϕ communicates a synchronous adaptation action ϕ −−−−→
to the system actors defined in w, but one (or more) of these actors are active (i.e., there
exists some i ∈ w such that i : ◦), and is thus not in a position to correctly react to the
reported adaptation. Similarly, the second way occurs when a formula ϕ communicates
r(w )

a release action instead, i.e., ϕ −−−→.

4.6

Conclusion

In this chapter we addressed our second objective (i.e., objective (ii) in Sec. 1.1) by designing and implementing an effective Runtime Adaptation framework for actor systems
as an extension of an existing RV setup. In fact we have identified two classes of adaptation actions, namely asynchronous and synchronous adaptations, where the latter require
a degree of synchronisation in order to be applied over the respective adaptees, as opposed to the former. The required synchronisation was therefore introduced by building
upon the efficient incremental synchronous (Hybrid) monitoring approach explored in
Chapter 3. In fact the prototype implementation of our Runtime Adaptation framework
was developed as an extension of detectEr 2.0 i.e., the extended version of detectEr that
we created in Chapter 3. This RA framework for actor systems is now called adaptEr2 .
Furthermore, to be able to better understand the subtle errors that our RA synchronisation protocol may introduce, we developed a formal operational model. This allowed
us to identify situations where our runtime adaptation framework may introduce errors
in the monitored system, if synchronisation is not properly defined. This model thus enabled us to identify a notion of synchronisation errors which we formalised as Def. 4.5.3.
This therefore enables us to address our third objective by showing that these errors can
be detected using static analysis techniques, particularly by developing a type system. In
Chapter 5 we therefore investigate ways how to develop a type system for assisting the
specifier into writing valid scripts, by rejecting scripts containing invalid synchronisation
commands that lead to the identified error.
2

The prototype tool implementation is accessible from https://bitbucket.org/casian/adapter.

91

5. Filtering Out Invalid Adaptation
Scripts

As we seen in the previous chapter, by contrast to RV scripts, our runtime adaptation
scripts are no longer passive. In fact our RA framework permits the specifier to define
blocking necessities and synchronous adaptations that could possibly introduce synchronisation errors. We conjecture that there are various types of errors that erroneously
defined scripts may introduce. Such errors include:
(1) Applying a synchronous adaptation or a release operation on an actor which was
not priorly suspended by a blocking necessity.
(2) Blocking an actor earlier then required, therefore disabling subsequent events from
occurring; a case in point is shown in Ex. 4.2.2 where we had to explicitly specify
the internal communication action i . !{inc, x, y} to be able to block actor i after
it forwards the increment request to some other internal component.
(3) Adaptations may be applied to external systems interacting with the system which
might not be have been instrumented by our RA framework. For instance, we
could specify an RA script for our running example system in Fig. 1.1 in which
we apply an adaptation to the connected external client mapped to term variable y.
A static analysis phase can be employed to detect errors in our RA scripts and reject
them at compile time, thereby serving as a tool for assisting the specifier into creating
92

5. Filtering Out Invalid Adaptation Scripts

valid adaptation scripts. As a proof of concept, in this chapter we focus on developing
a static type system for ruling out scripts that are erroneous wrt. to (1) which we have
already formalised as Def. 4.5.3 in Sec. 4.5.2. However, we conjecture that similar static
analysis techniques may be employed to detect other errors such as (2) and (3).
This chapter is structured in three sections. In Sec. 5.1 we develop a static type system for ruling out scripts that are erroneous wrt. error (1), prior to deployment. Since
static typechecking is performed wrt. certain assumptions, that might not always be honoured by a system execution, in Sec. 5.2 we augment our runtime monitors with dynamic
checks that halt monitoring in case the system deviates from our assumptions. This further prevents synchronisation errors from being introduced by our RA monitors. Finally,
in Sec. 5.3 we evaluate our typing strategies by proving type soundness, i.e., we prove
that at no point during execution can an accepted typed script introduce synchronisation
errors in the monitored system.

5.1

Static Type Checking

Recall that in Ex. 4.5.4 we presented cases where invalid synchronisation leads to erroneous (stuck) configurations in which synchronous adaptations or release operations are
incapable of being validly applied on an active system actor. Particularly, we showed
how RA scripts containing concurrent conjunction branches can introduce errors which
can be difficult for the specifier to notice prior to deployment, without any form of assistance.
In the previous chapter we formalised these synchronisation errors as Def. 4.5.3 (restated below). In this section we therefore focus on developing a static type system that
assists the specifier into writing valid scripts by rejecting the scripts which they may
violate this formalised notion of synchronisation errors, and are thus unsafe.
s(w )
s(w ) 
r(w )
r(w ) 
def
Def. 4.5.3. error(s . ϕ) = ϕ −−−−→ and s −−−6 −→ or ϕ −−−→ and s −−6−→

for some w ∈ dom(s)
From this definition note that these synchronisation errors are caused due to (one or
93

5. Filtering Out Invalid Adaptation Scripts

more) invalid entries specified in the list of actor references, used for applying a synchronous adaptation or release operations. We note that an invalid entry i, defined in a
list of actor references w, can have the following three forms:
• it can either refer to an actor which is currently active, i.e., an actor which is not
blocked by the instrumented code, i : ◦;
• it might not be referring to a process identifier pertaining to the system, i.e., it might
refer to some generic data, i < s; or else
• it might be referring to some generic data, i = 3 < s.
Hence to begin with, our type system must be able to make a distinction between generic
data and actual process identifiers. Moreover, the type system must also be able to
distinguish between two generic classes of process identifiers specified within the RA
script it is analysing.
The first class includes the process identifiers that are used in the synchronous adaptations and synchronisation operations (i.e., blocking and releasing) of the RA script
being analysed. As these operations may potentially contribute to synchronisation errors, the type system must ensure that the identifiers pertaining to this class are carefully
used within the script. Hence our type system must employ a mechanism for keeping
track of how these identifiers are being blocked and released at runtime. For instance the
type system must be able to statically track that these identifiers are being blocked by a
blocking necessity that specified prior to the application of a synchronous adaptation or
release operation.
More importantly, the type system must ensure that these identifiers are carefully
specified within concurrent branches, thereby being able to reject cases where two (or
more) branches exhibit a race condition. Race conditions such as those exhibited by ϕ000
in Ex. 4.5.4, include for instance cases where a branch may only apply a synchronous
adaptation correctly over an actor i if it (non-deterministically) executes before another
concurrent branch which releases i. Ensuring careful use of these identifiers requires
introducing an element of linearity, whereby the use of a process id (pertaining to this
94

5. Filtering Out Invalid Adaptation Scripts

class) must be restricted to a single monitoring branch. We thus refer to process identifiers pertaining to this class as linear.
The second class includes other process identifiers which are not involved with any
kind of operation that requires synchronisation, i.e., these typically used as the subjects
of asynchronous necessities and thus never used as subjects of blocking necessities or as
part of release lists or adaptation lists. As these process identifiers are not used in such
sensitive operations, they can safely be specified amongst concurrent branches (unlike
linear process ids). Hence we refer to the process ids pertaining to this class as unrestricted. The type system must however make sure that processes that are assumed to
be unrestricted are in fact not being used in synchronisation operations and synchronous
adaptations.
We present our static type system in the four subsections: in Sec. 5.1.1 we explain
the type annotations introduced by our type system; followed by Sec. 5.1.2 in which we
explain the type environments containing the required assumptions for type checking
a script. Finally in Sec. 5.1.3 and 5.1.4 we present details explaining the typing rules
forming our type system.

5.1.1

The Type Annotations

The type system relies on the type structure defined in Fig. 5.1 (below) where values are
partitioned into either generic data, dat, or actor identifiers, where the latter are further
subdivided into unrestricted, uid, and linear, lid. The type system is substructural [64],
in the sense that it uses linear types to statically track how the linear actor identifiers
used for synchronous adaptations, are blocked and released by the parallel branches
(i.e., conjunctions) of the respective script.
To be able to statically keep track of how the linear identifiers specified in a given
script, are blocked and released, type checking (internally) uses a sub-category for linear
types, lbid, to denote a blocked linear identifier. The general idea behind this tracking
mechanism is that the type system uses a look-up table to statically approximate how a
system actor is specified to blocked and released at runtime; this is achieved by switching
95

5. Filtering Out Invalid Adaptation Scripts

Type Structure
T, U ∈ Typ ::= dat

(generic) |

(unrestricted) |

uid

lid

(linear) |

lbid

(blocked)

Type Environment Splitting
sE

sU

∅+∅=∅

Γ1 + Γ2 = Γ3
T ∈ {lid, lbid}
(Γ1 , l : T) + Γ2 = (Γ3 , l : T)

sL

Γ1 + Γ2 = Γ3
T ∈ {dat, uid}
(Γ1 , l : T) + (Γ2 , l : T) = (Γ3 , l : T)
sR

Γ1 + Γ2 = Γ3
T ∈ Γ1 + {lid, lbid}
(Γ2 , l : T) = (Γ3 , l : T)

Adaptation-Script Typing Rules
Σ; (Γ, bnd(e)) ` ϕ
tNcA

Σ; Γ ` rel(r) tt

tFls

Σ; Γ ` [e] ar ϕ

subj(e) = l

Σ; (Γ, bnd(e)) ` blk(l) ϕ

tNcB

Σ; Γ ` ff

Σ; Γ ` ϕ

Σ; Γ ` rel(r) tt
tIf

Σ; Γ ` [e] br ϕ
Γ = Γ0 , w : lid
Σ; (Γ0 , w : lbid) ` ϕ
Σ; Γ ` blk(w) ϕ

tBlk

Γ = Γ0 , w : lid
Σ; Γ ` rel(r) ϕ
Σ; Γ ` aA(w)r ϕ

tAdA

excl(ϕ, ψ) = ⊥
tCn1

Σ; Γ1 ` ϕ
Σ; (Γ1 + Γ2 ) ` ϕ & ψ

excl(ϕ, ψ) = hrϕ , rψ i
tCn2

tTru

Σ; Γ ` ψ

Σ; Γ ` if b then ϕ else ψ

tRel

Γ = Γ0 , w : lbid
Σ; (Γ0 , w : lid) ` ϕ
Σ; Γ ` rel(w) ϕ

tAdS

Γ = Γ0 , w : lbid
Σ; Γ ` rel(r) ϕ
Σ; Γ ` sA(w)r ϕ

Σ; Γ2 ` ψ
tMax

(Σ, X 7→ Γ); Γ ` ϕ
Σ; Γ ` max X. ϕ

Σ; eff(Γ, rψ ) ` ϕ Σ; eff(Γ, rϕ ) ` ψ
tVar

Σ; Γ ` ϕ & ψ

Σ; Γ ` tt

Σ(X) ⊆ Γ
Σ; Γ ` X

Figure 5.1: A Type System for Adaptation sHML scripts
between types lid and lbid. In fact, whenever the type system analyses an identifier i
that is used in a blocking operation (i.e., a blocking necessity), it consults its look-up
table and checks that i is linear typed, i.e., i : lid; if this is true, then it changes the
type associated with the identifier in its look-up table, from lid to lbid. In this way the
type system can detect whether a synchronous adaptation or a release operation is being
correctly applied to i, by consulting its look-up table and checking that the associated
type is lbid. This signifies that at runtime i would have been blocked in some prior
96

5. Filtering Out Invalid Adaptation Scripts

operation as required.
Type checking works on typed scripts, where the syntax of Fig. 4.7 is extended so
that the binding term variables used in action patterns are annotated by the types dat,
uid or lid. Note that the type lbid is not part of the external syntax, i.e., this cannot be

specified as a type annotation in a script, because this type is reserved for internal use by
the type system, for tracking purposes.
Example 5.1.1. The adaptation-script (4.7) of Ex. 4.2.2 would be annotated as follows:
ϕ0 = max Y. [i?{inc, x : dat, y : uid}] a
def

[i. !{inc,

x, y}] bε

( [ j.y!{res, x + 1}] a Y) &
( [z : lid .y!err] bi restr(i)ε prg(z)i,z Y)

!

(5.1)

In (5.1) above, pattern variables x, y and z are associated to types dat, uid and lid respec

tively.

Note that explicit type annotations are only required to be specified for binding term
variables, i.e., they are only used within necessities that first bind a variable. For example, in (5.1), we do not add the type annotations for variables x and y in the second
necessity, i.e., [i. !{inc, x, y}] bε , as these variables are bound (and were thus annotated)
in the prior necessity, i.e., [i?{inc, x : dat, y : uid}] a .
In addition, note that we do not include explicit type annotations for statically known
process ids such as i and j; these typically represent process identifiers that are registered and can thus be know at compile time. The necessary type annotations for these
registered processes are passed over to the type system as type assumptions represented
in the form of a type environment.

5.1.2

The Type Environments

Our type system for (typed) adaptation-scripts is defined as the least relation satisfying
the rules in Fig. 5.1. The type system judgements take the form Σ; Γ ` ϕ where
• Value environments, Γ ∈ Env :: (Pid ∪ Var) * Typ, map identifiers and variables to
types — these are used as a look-up table containing the necessary type assumptions,
97

5. Filtering Out Invalid Adaptation Scripts

which the system consults and updates, while it compositionally analyses a given
typed script.
• Formula environments, Σ ∈ LVar * Env, map formula variables to value environments, e.g., (Σ,{X 7→ Γ}) — these are used to analyse recursive formulas in a compositional way. (The use of this environment will be clarified when we discuss rules tMax
and tVar in Sec. 5.1.3).
Note that we sometimes write Γ ` ϕ in lieu of ∅; Γ ` ϕ. We also write w : T to denote the list
of mappings l1 : T, . . . , ln : T where w = l1 , . . . , ln (recall that metavariable l ∈ (Pid∪Var)
ranges over identifiers and variables).
Extending the Type Environments
The rules in Fig. 5.1 assume standard environment extensions, (Γ, Γ0 ). This allows the
type system to add new type assumptions by extending its current value environment
with new information. Since value environments are maps, they are therefore implicitly
restricted such that a domain value of the map (e.g., i or x) can only map to one type,
e.g., value environment Γ = {i : uid, i : lid} is invalid as i is mapped to two different types.
This restriction also reflects in the way a value environment is extended.
For instance, if a valid value environment Γ = {i : uid, j : uid} is extended by Γ0 = {i :
uid, z : lid}, we get the resultant environment (Γ, Γ0 ) = {i : uid, j : uid, z : lid}. Although

identifier i is present in both Γ and Γ0 , the resultant extended environment (Γ, Γ0 ) is
still valid as both environments map i to the same type, i.e., to uid. Hence the implicit
mapping restriction is not breached by the extension. By contrast if Γ is extended by
Γ00 = {i : lid, z : lid}, the resultant environment (Γ, Γ00 ) would not constitute a valid map.
A similar environment extension is also used for formula environments which map
formula variables, e.g., X, onto a value environment e.g., Γ.

98

5. Filtering Out Invalid Adaptation Scripts

Splitting Value Environments
The rules in Fig. 5.1 also use environment splitting, Γ1 + Γ2 , as defined by the environment splitting rules sE, sU, sL and sR. These rules are used to distribute linearly the
mappings amongst two (possibly) smaller environments. Rule sE states that an empty
value environment Γ = ∅ can be divided into two empty environments i.e., ∅ + ∅. Rule sU
dictates that generic data and unrestricted typed references, e.g., x : uid, can be divided
amongst the two resultant value environments. For instance, if x: uid ∈ Γ and Γ is split
into Γ1 and Γ2 , i.e., Γ = Γ1 + Γ2 , then x : uid is distributed to both Γ1 and Γ2 , such that
x : uid ∈ Γ1 and x : uid ∈ Γ2 .
This contrasts rules sL and sR which state that linear typed references, e.g., i : lid
or i : lbid, can only be given to one of the two resultant environments. For example, if
z : lid ∈ Γ and this is split into Γ1 and Γ2 then x : lid can only be distributed to either Γ1
(with rule sL), i.e., z : lid ∈ Γ1 but z : lid < Γ2 , or else to Γ2 (with rule sR).
This splitting mechanism is essential for the type system to make sure that linear
typed references are carefully used amongst the concurrent conjunction branches, by
restricting their use to only one branch.

5.1.3

The Typing Rules

The typing rules presented in Fig. 5.1 serve to statically analyse a typed script in a compositional manner. Hence these rules regularly consult and update the type assumptions,
stored in the type environments, such that subsequent type judgements are made wrt. the
information gained by previous judgements.
For instance in formula (5.1) (stated in Ex. 5.1.1), necessity [i . !{inc, x, y}] bε and
other subsequent constructs are typechecked wrt. the information gained from analysing
prior constructs. In fact, from the prior necessity [i?{inc, x : dat, y : uid}] a the type
system can deduce that x and y were bound and were given the types dat and uid respectively.
Rules tTru, tFls and tIf are quite straightforward, i.e., truth and falsities always

99

5. Filtering Out Invalid Adaptation Scripts

typecheck, while the analysis of if-statements is approximated by requiring both branches
to typecheck. Furthermore, typing rules that update the type environments of subsequent
judgements include rules tNcA, for analysing asynchronous necessities, and rules tNcB
for blocking necessities.
These rules are very similar: tNcA updates the value environment Γ with the process
bindings (i.e., variables annotated with either uid or lid) introduced by the necessity
pattern e. This permits for the continuation formula ϕ (e.g., ϕ is the continuation formula
of [e] ρr ϕ) to be typechecked wrt. these new process bindings (i.e., Σ; (Γ, Γ0 ) ` ϕ, where Γ0
contains the new process bindings).
It also checks that in case of action mismatch, the resultant actor releases, defined
the release list r of the respective necessity, also typecheck (i.e., Σ; Γ ` rel(r) tt). Rule
tNcB performs similar checks, but the continuation formula typechecking is prefixed by
the blocking of the subject of the pattern, (i.e., Σ; (Γ, Γ0 ) ` blk(l) ϕ).
Remark 8. Both tNcA and tNcB utilise the auxiliary function bnd(e) that extracts a
map of process bindings from a necessity pattern as shown below:
bnd(i?{inc, x : dat, y : uid}) = {y : uid}

(5.2)

bnd( x : lid .y : uid!{res, z}) = {x : lid, y : uid}

(5.3)

From (5.2) note that this function only returns term variable bindings that are only annotated with a process type i.e., lid or uid. The reason for not considering generic data
types is that the type system does not track the use of these variables. In fact the main
focus of our type system is to detect cases where a synchronous adaptation or a release
operation is applied to an invalid actor id, and not about how the data variables are being
used and inspected by if-statements.
Furthermore, from (5.3) note that variable z was not included in the resultant map
as it does not have a type annotation. As our analyses assumes closed formulas, by the
point the type system inspects necessity pattern x : lid .y : uid!{res, z}, variable z would
have already been bound and added to the respective value environment.
Rule tNcA then uses the map extracted by this function from a pattern e, to typecheck
100

5. Filtering Out Invalid Adaptation Scripts

the continuation formula, e.g., ϕ, wrt. this extended type environment, i.e., Σ; (Γ, bnd(e)) `
ϕ. The same applies for rule tNcB, with the difference that the continuation formula is
prefixed by a block of the pattern’s subject, i.e., Σ; (Γ, bnd(e)) ` blk(l) ϕ).



The typing rules tBlk and tRel for actor blocking (i.e., blk(w) ) and releasing (i.e.,
rel(w) ), enable the system to track which actor references are marked as blocked or

not. For instance, whenever rule tBlk is used to analyse script blk(w) ϕ wrt. a value
environment Γ, this rule consults Γ and checks that all actor references in w are present
in Γ and are typed as linear, i.e., Γ = Γ00, w : lid. If this condition is satisfied, it updates
the Γ environment by changing the type of the identifiers enlisted in w to lbid, thereby
creating a new environment Γ0 = Γ00, w:lbid. Rule tBlk then typechecks the continuation
formula ϕ wrt. Γ0 . In this way the typesystem is able to check whether the synchronous
adaptations or release operations declared in ϕ, are being applied to actor references that
were priorly blocked.
In fact, rule tRel uses a mechanism similar to that of tBlk, whereby it checks that
all actor references, defined in a release list w, have the type lbid in Γ. Hence the type
system is able to reject cases where release operations are applied to active processes
by consulting the value environment and checking whether their type was changed to
lbid by a previously defined blocking operation. If all actor references enlisted in w are

typed as linear blocked (lbid) in the Γ value environment, then rule tRel changes their
type back to lid, thus creating a new value environment which is used to typecheck the
continuation formula.
Typechecking asynchronous adaptations, aA(w)r , with tAdA, requires the adaptees
specified in w, to be linearly typed, i.e., w : lid, whereas typechecking synchronous
adaptations, sA(w)r , with rule tAdS, requires the adaptees to be linearly blocked, i.e.,
w : lbid. In this way the type system ensures that synchronous adaptations are only
applied on actor references which were blocked by a prior blocking operation (i.e., their
type was changed from lid to lbid). Both rules consider the respective released actors
when typechecking the continuations, (i.e., Σ; Γ ` rel(r) ϕ), thus also checking that the
actors defined in the release list r are also applied in a valid way, prior to checking the
101

5. Filtering Out Invalid Adaptation Scripts

continuation formula ϕ.
We have two rules for typechecking conjunction formulas, ϕ & ψ, namely rules tCn1
and tCn2; for now we only consider the former, as the latter in covered in more detail
in Sec. 5.1.4. Recall that since conjunction subformulas may be executing in parallel (as
defined by the reduction rules rCn1, rCn2, rCn3 and rCn4 from Fig. 4.6 and Fig. 4.7),
synchronous adaptations and release actions may lead to race conditions as explained
in Ex. 4.5.4 (see (4.59) and (4.60)). Rule tCn1 eliminates such cases by typechecking
each subformula ϕ and ψ wrt. a split environment, Γ1 + Γ2 , as is standard in linear type
systems [64] (refer to Sec. 5.1.2). This ensures that linear typed actor references (e.g.,
l : lid or l : lbid) are only distributed to a single concurrent branch, thereby eliminating
cases where multiple branches attempt to release or apply a synchronous adaptation on
the same actor references. Note that conjunction formulas are inspected with rule tCn1,
if the side-condition excl(ϕ, ψ) = ⊥ holds (otherwise tCn2 applies); this side-condition
is explained in more depth in Sec. 5.1.4.
While analysing recursive definitions, max X. ϕ, formula environment Σ also plays a
crucial role in preserving linearity. In fact this environment permits rule tMax to save
a copy of the value environment Γ at the point where a formula variable X is bound by
max X. ϕ (i.e., Γ becomes (Σ,{X 7→ Γ})). This then enables rule tVar to compare the saved

copy to the contents of the (possibly different) value environment Γ0 at the point where X
is used (i.e., when the formula recurs), by checking that Σ(X) ⊆ Γ0 . Note that we check
whether the saved copy Σ(X) is a subset of (or equal to) the current value environment
Γ0 , as during the type derivation the value environment might be extended with new
bindings while inspecting necessity operations with rules tNcA and tNcB. Furthermore,
this condition together with environment splitting, allow the type system to eliminate
cases where a formula that applies synchronous adaptations (or releases), recurs along
two (or more) concurrent branches which may eventually lead to race conditions.
Hence, checking formula variables is imperative to eliminate cases in which synchronisation errors may appear at a later stage during runtime due to concurrency and
recursion. For instance consider formula (5.4) which may appear to be free of potential

102

5. Filtering Out Invalid Adaptation Scripts

synchronisation errors, yet Ex. 5.1.2 proves otherwise.

ϕerr = max X. [i?3] bε (sA(i)i X) & (X)

(5.4)

Example 5.1.2. Assume that when formula ϕerr is used to verify trace t = i?3; i?3; . . .
wrt. s = (i : ◦), their configuration yields the following execution (there may be others).
τ

s . ϕerr −−−→ (i : ◦) . [i?3] bε (sA(i)i ϕerr ) & ϕerr

i?3
−−−→ (i : ◦) . blk(i) (sA(i)i ϕerr ) & ϕerr
τ

−−−→ (i : •) . (sA(i)i ϕerr ) & ϕerr



(5.5)
(5.6)
(5.7)

τ

−−−→ (i : •) . (rel(i) ϕerr ) & ϕerr

(5.8)

τ


===⇒ (i : ◦) . [i?3] bε ((sA(i)i ϕerr ) & ϕerr ) & [i?3] bε ((sA(i)i ϕerr ) & ϕerr )
(5.9)
i?3


==⇒ (i : •) . (sA(i)i ϕerr ) & ϕerr & (sA(i)i ϕerr ) & ϕerr
(5.10)
τ


===⇒ (i : ◦) . ϕerr & ϕerr & (sA(i)i ϕerr ) & ϕerr
(5.11)

The above derivation shows that although formula ϕerr does not immediately yield
a stuck configuration, it still manages to get stuck after unfolding formula variable X
twice as a result of receiving system event i?3 twice in a row. More specifically, in the
above derivation notice that after unfolding the recursive definition in (5.5), the resultant
formula perceives system action i?3 which matches with necessity [i?3] bε thus causing
the subject actor i to block in (5.6). In (5.7) we see that a synchronous adaptation could
validly be applied on actor i as it was already blocked (i.e., i : •) in the previous step. This
actor is then released in (5.8) as it was specified in the release list of the synchronous
adaptation applied in (5.7). In (5.9) we see that after i was released, the recursive definitions of the resultant conjunction formula (i.e., ϕerr & ϕerr ) were unfolded. Once again
in (5.10), action i?3 matches blocking necessities [i?3] bε thus causing actor i to block.
Finally from (5.10) to (5.11) we see that the synchronous adaptation of the left branch
was applied on i, and then i was released, i.e., i : ◦. Hence the last configuration in (5.11)
is stuck since the synchronous adaptation in the right branch is unable to apply on i.
This shows how concurrency and recursion can induce synchronisation errors that
are very hard to notice without assistance. Therefore, as shown in Derivation 1, our
103

5. Filtering Out Invalid Adaptation Scripts

typesystem uses the linearity imposed by type environment splitting in tCn1, together
with the formula environment, Σ, to rule out such cases by using rules tMax and tVar.
Derivation 1.
(Σ0 (X) = {i : lid}) ⊆ (i : lid)
tVar
tRel
tAdS

Σ0 ; (i : lid) ` X

Rejected!!

Σ0 ; Γ0 ` rel(i) X

(Σ0 (X) = {i : lid}) * ∅

Σ0 ; Γ0 ` (sA(i)i X)

Σ0 ; ∅ ` X

tCn1
tBlk

Σ0 ; Γ0 = {i : lbid} ` ((sA(i)i X) & (X))
Σ ; Γ = {i : lbid} ` blk(i) ((sA(i)i X) & (X))
0

0

tNcB
tMax

tVar
Σ; Γ ` tt

tTru

Σ; Γ ` rel(ε) tt

tRel

(Σ0 = {X 7→Γ}); Γ ` [i?3] bε ((sA(i)i X) & (X))
(Σ = ∅); (Γ = {i : lid}) ` max X. [i?3] bε ((sA(i)i X) & (X))

The above type derivation rejects formula ϕerr by combining the use of environment
splitting with formula environment Σ. In particular, when the typesystem analyses the
recursive binder max X. with rule tMax, it saves a copy of Γ = {i : lid}, identified by X,
in Σ. Then while checking conjunction formula ((sA(i)i X) & (X)) with rule tCn11 , the
value environment Γ0 = {i : lbid} is split as Γ0 + ∅. The left branch2 is thus typechecked
wrt. Γ0 , while the right branch is typechecked wrt. ∅. The right branch then gets rejected
when rule tVar is applied to verify that the saved copy i.e., Γ0 = {i : lbid}, is a subset of
(or equal to) the assigned value environment i.e., ∅; however this is not the case. Hence


the formula gets rejected.

5.1.4

Introducing Mutual Exclusion

Unfortunately, ensuring that linearity is preserved by using environment splitting (in rule
tCn1) is however too coarse of an analysis and rejects useful adaptation-scripts such as
This rule was chosen over tCn2 as the side-condition excl((sA(i)i X), (X)) = ⊥ holds; this is discussed
in Sec. 5.1.4.
2
Note that the formula still gets rejected even if the right branch is typechecked wrt. Γ0 while the left
branch is typechecked wrt. ∅.
1

104

5. Filtering Out Invalid Adaptation Scripts

(5.1) from Ex. 5.1.1 as shown below.
Example 5.1.3. The conjunction formula used in (5.1) from Ex. 5.1.1 has the form:
( [ j.y!{res, x + 1}] a Y) & ( [z : lid .y!err] bi restr(i)ε prg(z)i,z Y)
where the subformulas are necessity formulas with mutually exclusive patterns i.e., there
is no action satisfying both patterns j.y!{res, x + 1} and z : lid .y!err. In such cases, a
conjunction formula operates more like an external choice construct rather than a parallel
composition [62], where only one branch continues monitoring. Hence these branches
can never exhibit a race condition not even due to recursion (as opposed to (5.4) in
Ex. 5.1.2). However, if the type system uses rule tCn1 to analyse this conjunction formula, this gets unnecessarily rejected when rule tVar is applied to check the formula
variable Y which is used in both branches (i.e., in a similar manner as in Ex. 5.1.2).



To refine our analysis, in Def. 5.1.2 (below), we define an approximating function
excl(ϕ, ψ) that syntactically analyses subformulas to determine whether they are mutu-

ally exclusive or not. When this can be determined statically (as in the case of (5.1)), it
means that at runtime at most one branch will continue, whereas the others will terminate due to trivial satisfaction, thereby releasing the actors specified by the respective
necessity formulas (recall rNc3 from 4.7). We refer to these releases as side-effects
(formally defined in Def. 5.1.1 below).
Accordingly, excl(ϕ, ψ) denotes that mutual exclusion can be determined by returning a tuple consisting of two release sets, hrϕ , rψ i, containing the actors released by the
respective subformulas when an action is mismatched. Rule tCn2 then typechecks each
subformula wrt. the entire value environment Γ, adjusted to take into consideration the
side-effects imposed on one branch by the other branch, i.e., it considers actors released
by the other (defunct) branch, e.g., Σ; eff(Γ, rψ ) ` ϕ, where rψ signifies the actors that are
released in case the other branch ψ gets trivially satisfied.

105

5. Filtering Out Invalid Adaptation Scripts

Defining Side-effects
At runtime, whenever a mutually exclusive branch ψ terminates trivially due to mismatching necessities, as a side-effect, it releases a number of actors specified in the
release-lists of the terminated necessities. These side-effects may therefore effect the
matching branch ϕ by for instance releasing a specific linear actor on which this branch
applies a synchronous adaptation, i.e., in a similar way to what we have previously shown
in Ex. 4.5.4 (see (4.59) and (4.60)).
The eff(Γ, r ) function defined in Def. 5.1.1, therefore permits the type system to take
into consideration these side-effect releases while typecheck a branch ϕ. This means
that a branch ϕ is typechecked as if the other exclusive branch ψ has terminated and thus
released the respective actors.
Def. 5.1.1 (Side-effects).
eff(Γ, r ) = {l : lid | l ∈ r ∧ l : lbid ∈ Γ} ∪ {l : t | l < r ∧ l : t ∈ Γ}
def

This function therefore takes as input the value environment Γ associated with one
branch ϕ, together with the release set derived from the opposing branch, i.e., rψ . For
each process reference l residing in release set rψ , this function searches for l in Γ and
changes its type from lbid to lid. Each branch (e.g., ϕ) can thus be analysed separately
with the assumption that the processes defined in the release set of the opposing branch
(e.g., rψ ) will actually be released at runtime in case a system event causes this opposing
branch (i.e., ψ) to terminate trivially.
Defining Mutual Exclusion
The excl(ϕ, ψ) function, defined in Def. 5.1.2, returns tuple hrϕ , rψ i in case both functions
vexcl(ϕ, ψ) and vexcl(ψ, ϕ) respectively return release sets rϕ and rψ . These release sets

denote the actors released by branches ϕ and ψ respectively, in case these get trivially
satisfied at runtime. If either one of these two vexcl functions return ⊥, then the excl
function also returns ⊥, thus denoting that branches ϕ and ψ are not mutually exclusive.

106

5. Filtering Out Invalid Adaptation Scripts

Def. 5.1.2 (Mutual Exclusion).




hrϕ , rψ i if (vexcl(ϕ, ψ) = rϕ , ⊥) ∧ (vexcl(ψ, ϕ) = rψ , ⊥)
excl(ϕ, ψ) = 


⊥
otherwise



vexcl(ϕ1 , ψ) ∪ vexcl(ϕ2 , ψ) if ϕ = ϕ1 & ϕ2





k
ρ


vexcl(rel(r) tt, ψ)
if ϕ = [e] r ϕ0 ∧ ψ ,&n=1 (rel(r)tt)n







∧ ∀e1 ∈ fps(ψ) · mtch(e, e1 ) = ⊥





k


r
if ϕ = if c then ϕ1 else ϕ2 ∧ ψ ,&n=1 (rel(r)tt)n





vexcl(ϕ, ψ) = 
∧ vexcl(ϕ1 , ψ) = vexcl(ϕ2 , ψ) = r




k


vexcl(ϕ0 , ψ)
if ϕ = max X. ϕ0 ∧ ψ ,&n=1 (rel(r)tt)n








r
if ϕ = rel(r) tt





k


ε
if ϕ , rel(r) tt ∧ ψ =&n=1 (rel(r)tt)n





⊥
otherwise

ρ


e
if ϕ = [e] r ϕ0







fps(ϕ1 ), fps(ϕ2 ) if ϕ ∈ {if c then ϕ else ψ, ϕ & ψ}
fps(ϕ) = 



fps(ϕ0 )
if ϕ = max X. ϕ0






otherwise
def

def

def

The vexcl(ϕ, ψ) function incrementally inspects the contents of a branch ϕ wrt. the
opposing branch ψ which is always kept constant during analysis. This function returns
the release set of ϕ only if this is mutually exclusive to ψ. We now highlight the three
main cases of this function.
The first case we consider is when ϕ is also a conjunction ϕ1 & ϕ2 . In this case the
vexcl is recursively applied for both ϕ1 and ϕ2 wrt. the same opposing branch ψ. The

idea is that if ϕ1 and ϕ2 are both mutually exclusive to ψ then their conjunction, i.e.,
ϕ1 & ϕ2 is also mutually exclusive to ψ. In this case the function returns a union of the
release sets of both ϕ1 and ϕ2 , returned by the respective recursive applications. Note
however that the inner branches ϕ1 and ϕ2 need not be mutually exclusive to each other,
as long as they are mutually exclusive to ψ (we explain this in more detail in Ex. 5.1.4).
The second case we consider is when ϕ starts with a necessity, i.e., [e] ρr ϕ0 . In this case
the function internally calls the fps(ψ) function to retrieve all the top patterns defined in
the opposing branch ψ. To better understand what these top patterns are, as an example
assume that we use the fps function to analyse ψ = ([e1 ] ρr1 ψ1 ) & ([e2 ] ρr2 ψ2 ) & (sA(w3 )r3 ψ3 ).
107

5. Filtering Out Invalid Adaptation Scripts

In this case, fps(ψ) returns the pattern set {e1 , e2 , }, where e1 and e2 refer to the patterns
of the guarding necessities of branches ([e1 ] ρr1 ψ1 ) and ([e2 ] ρr2 ψ2 ), while “ ”is a universally matching pattern which is returned due to the branch starting with sA(w3 )r3 — we
sometimes refer to these branches as unguarded.
The vexcl function then uses standard pattern matching (as defined by the mtch function in Def. 4.5.1) to check whether the pattern e of the necessity being analysed matches
any of the top patterns defined in the opposing branch ψ. If it does not match with any of
them (i.e., mtch returns ⊥ for each top pattern), then this means that branch ϕ is mutually
exclusive to branch ψ. This function is therefore recursively applied for rel(r) tt (i.e., the
function internally calls vexcl(rel(r) tt, ψ)), thereby denoting that whenever a necessity
[e] ρr gets trivially satisfied at runtime, it releases the actors enlisted in r prior to reducing

into tt; this recursive application thus returns r.
k

Until now we have ignored the side condition ψ ,&n=1 (rel(r)tt)n , which states that for
this case to apply, the opposing branch ψ must not form a collection of trivially satisfied
branches e.g., ϕ ≡ (rel(r1 ) tt) & · · · & (rel(rn ) tt). This condition is used throughout most
of the cases in the vexcl function to navigate its inspection to the penultimate case.
This case states that if the formula being inspected, ϕ, is not a release operation and the
k

opposing branch ψ forms a collection of trivially satisfied branches, i.e., ψ =&n=1 (rel(r)tt)n ,
then the function returns an empty release set, ε. This condition is thus used to signify
that any branch ϕ is mutually exclusive to a collection of trivially satisfied branches. For
k

instance, if ϕ = sA(w)r ϕ0 and ψ =&n=1 (rel(r)tt)n , then vexcl(ϕ, ψ) returns ε and not ⊥.
Remark 9. These side-conditions therefore allow us to show that two mutually exclusive


branches (e.g., [i?3] bε sA(i)i tt & [i?4] bε sA(i)i ff ), remain mutually exclusive even when
one of the branches gets trivially satisfied at runtime. For instance, Lemma C.3.4 (stated
below) proves that if two mutually exclusive branches ϕ and ψ reduce over a τ-action,
then the resultant branches ϕ0 and ψ remain mutually exclusive to each other.
τ

Lemma C.3.4 excl(ϕ, ψ) = (rϕ , rψ ) and ϕ →
− ϕ0 implies excl(ϕ0 , ψ) = (rϕ , rψ )
τ

This Lemma is proved by rule induction on ϕ →
− ϕ0 in Appendix Sec. C.3, along with two

108

5. Filtering Out Invalid Adaptation Scripts

other similar lemmas, i.e., Lemma C.3.5 and Lemma C.3.6 which respectively prove
that mutual exclusion is also preserved in the case of α and µ reductions.



Example 5.1.4. To better understand how the excl function works, consider the following partial script having 4 branches.

([i?3] aj ϕ1 & [ x : uid?3] ak ϕ2 ) & ([i?4] aε ψ1 & [y : uid?5] al ψ2 )
|
{z
}
|
{z
}
ϕ
ψ
As shown by the excl function derivation below, if we check for mutual exclusion between the topmost two branches, ϕ and ψ we can notice that the patterns in the right
topmost branch (i.e., i?3 and x : uid?3) clearly do not match any pattern in the left topmost branch (i.e., i?4 and x : uid?5). Hence this function returns tuple h( j;k), li denoting
that if ϕ gets trivially satisfied at runtime, then the actors contained in release set ( j;k)
would be released; similarly actor l is released in case ψ gets trivially satisfied.

excl(

ψ
ϕ
}|
{
z
}|
{ z
[i?3] aj ϕ1 & [ x : uid?3] ak ϕ2 , [i?4] aε ψ1 & [y : uid?5] al ψ2

=

vexcl([i?3] aj ϕ1 & [ x : uid?3] ak ϕ2 , ψ) , vexcl([i?4] aε ψ1 & [y : uid?5] al ψ2 , ϕ)

= vexcl([i?3] aj ϕ1 , ψ) ∪ vexcl([ x : uid?3] ak ϕ2 , ψ) , vexcl([i?4] aε ψ1 , ϕ) ∪ vexcl([y : uid?5] al ψ2 , ϕ)

(since i?3 and x : uid?3 do not match wrt. i?4 and y : uid?5)
=

vexcl(rel( j) tt, ψ) ∪ vexcl(rel(k) tt, ψ) , vexcl(rel(ε) tt, ϕ) ∪ vexcl(rel(k) tt, ϕ)

=

{ j} ∪ {k} , ε ∪ {l}

=

( j;k) , l

Therefore with this mechanism we can statically infer that if an event matches either
i?3 or x : uid?3, it would certainly not match patterns i?4 and x : uid?5 (and vice-versa).
However, this does not mean that the internal branches are exclusive amongst themselves
as well. In fact excl([i?3] aj ϕ1 , [ x : uid?3] ak ϕ2 ) = ⊥ as both patterns may match system
action i?3.
Therefore, as shown in the type derivation (below), while typechecking this partial
formula the type system can be less restrictive about the topmost branches ϕ and ψ by
using type rule tCn2 (as they are mutually exclusive). However, in the case of inner
109

)

5. Filtering Out Invalid Adaptation Scripts

branches [i?3] aj ϕ1 and [ x : uid?3] ak ϕ2 , the type system takes a more stringent approach
and preserves linearity by splitting the value environment with rule tCn1.
Derivation 2.
···

tNcA
tCn1

···

Σ; Γ1 ` [i?3] aj ϕ1

Σ; Γ2 ` [ x : uid?3] ak ϕ2 )

tNcA
···

Σ; eff(Γ, l) ` ([i?3] aj ϕ1 & [ x : uid?3] ak ϕ2 )

tCn2

Σ; eff(Γ, ( j; k)) ` ([i?4] aε ψ1 & [y : uid?5] al ψ2 )

tCn2

Σ; Γ ` ([i?3] aj ϕ1 & [ x : uid?3] ak ϕ2 ) & ([i?4] aε ψ1 & [y : uid?5] al ψ2 )

(where eff(Γ, l) = Γ1 + Γ2 )



Example Typechecking Derivations
The better understand how our type system accepts valid scripts and rejects invalid ones,
we present a number of example type derivations for some of the properties that we have
seen so far. For instance in Derivation 3 (below) we show how (5.1) typechecks (i.e., is
accepted by the typesystem) wrt. value environment Γ0 = {i : lid, j : uid}.
Derivation 3.
Formula Environments: Σ = (Y 7→ Γ0 )
Value Environments: Γ0 = {i : lid, j : uid}; Γ1 = Γ0 , {y : uid}; Γ2 = {i : lbid, j : uid, y : uid}; Γ3 = eff(Γ2 , i) = Γ1 ;
Γ4 = eff(Γ2 , ε) = Γ2 ;

Γ5 = Γ4 , {z : lid};

Γ6 = Γ4 , {z : lbid};

Γ7 = {i : lid, j : uid, y : uid, z : lid}.

Accepted
(Σ(Y) = Γ0 ) ⊆ Γ7

tVar
tRel
Accepted

tAdS

(Σ(Y) = Γ0 ) ⊂ Γ3

tVar
tNcA
tCn2
tBlk

Σ; Γ3 ` Y

tBlk

Σ; Γ3 ` [ j.y!{res, x + 1}] aε Y

Σ; Γ7 ` Y
Σ; Γ6 ` rel(i; z) Y

Accepted

Σ; Γ6 ` restr(i)ε prg(z)i;z Y

Σ; Γ4 ` tt

tTru

Σ; Γ5 ` blk(z) restr(i)ε prg(z)i;z Y Σ; Γ4 ` rel(i; z) tt
Σ; Γ4 ` [z : lid .y!err] bi restr(i)ε prg(z)i;z Y

tRel
tNcB

Σ; Γ2 ` (([ j.y!{res, x + 1}] aε Y) & ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y))
Σ; Γ1 ` blk(i) (([ j.y!{res, x + 1}] aε Y) & ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y))

tNcB

Σ; Γ1 ` [i. !{inc, x, y}] bε (([ j.y!{res, x + 1}] aε Y) & ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y))
tNcA


 ([ j.y!{res, x + 1}] aε Y)

a
b 


Σ; Γ0 ` [i?{inc, x : dat, y : uid}] ε [i. !{inc, x, y}] ε 
b
& ([z : lid .y!err] i restr(i)ε prg(z)i;z Y)
tMax

 ([ j.y!{res, x + 1}] aε Y)
a
b 
∅; Γ0 ` max Y. [i?{inc, x : dat, y : uid}] ε [i. !{inc, x, y}] ε 
& ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y)

110







5. Filtering Out Invalid Adaptation Scripts

Formula (5.1) is accepted by the type system since all possible three branches in Derivation 3 managed to reach (and satisfy) a base case typing rule (i.e., tVar and tTru).
By contrast, for any value environment Γ, the typesystem is unable to typecheck
erroneous script ϕ00 (augmented with the necessary type annotations) from Ex. 4.5.4,
and thus rejects it. The derivation below shows how this erroneous script is rejected
when typechecked wrt. Γ0 :
Derivation 4.
Formula Environments:
Value Environments:

Σ = (Y 7→ Γ0 )
Γ0 = {i : lid, j : uid};

Γ3 = eff(Γ1 , ε) = Γ1 ;

Γ1 = Γ0 , {y : uid};
Γ4 = Γ3 , {z : lid};

Γ2 = eff(Γ1 , i) = Γ1 ;
Γ5 = {i : lid, j : uid, y : uid, z : lbid}.

Rejected!!
i : lbid < Γ5

tAdS

(Σ(Y) = Γ0 ) ⊂ Γ2

tVar
tNcA
tCn2

Σ; Γ2 ` Y

tBlk

Σ; Γ2 ` [ j.y!{res, x + 1}] aε Y

Σ; Γ5 ` restr(i)ε prg(z)i;z Y

Σ; Γ3 ` tt

tTru

Σ; Γ4 ` blk(z) restr(i)ε prg(z)i;z Y Σ; Γ3 ` rel(i; z) tt
Σ; Γ3 ` [z : lid .y!err] bi restr(i)ε prg(z)i;z Y

tRel
tNcB

Σ; Γ1 ` (([ j.y!{res, x + 1}] aε Y) & ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y))

tNcA

Σ; Γ1 ` [i. !{inc, x, y}] aε (([ j.y!{res, x + 1}] aε Y) & ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y))



 ([ j.y!{res, x + 1}] aε Y)
a
a 

Σ; Γ0 ` [i?{inc, x : dat, y : uid}] ε [i. !{inc, x, y}] ε 
b
& ([z : lid .y!err] i restr(i)ε prg(z)i;z Y)
tMax

 ([ j.y!{res, x + 1}] aε Y)
a
a 
∅; Γ0 ` max Y. [i?{inc, x : dat, y : uid}] ε [i. !{inc, x, y}] ε 
& ([z : lid .y!err] ai restr(i)ε prg(z)i;z Y)

tNcA

Our type system thus rejects formula ϕ00 as it detects that actor i is not being blocked by
some blocking necessity prior to applying restr(i)ε . In fact, this error is detected, and
thus rejected, by rule tAds which detects that the adaptee i had the type lid in the value
environment Γ5 ; if i had been blocked by a prior construct, then its type would have been
changed to lbid and thus the script would have been accepted (as we have previously
shown in Derivation 3 for formula (5.1)).
Similarly, the typesystem also rejects scripts that introduce synchronisation errors
due to concurrency such as ϕ000 from Ex. 4.5.4. Recall that ϕ000 suffers from a race condition whereby necessity [ j.y!{res, x + 1}] ai erroneously releases actor i if it mismatches
111







5. Filtering Out Invalid Adaptation Scripts

a system action. As discussed in Ex. 4.5.4, this results in a premature release of actor
i, which may interfere with the synchronous adaptation restr(i)ε along the other branch.
However, as shown in Derivation 5, rule tCn2 allows for this interference to be statically
detected by considering the side-effects of the respective mutually exclusive branches.
Derivation 5.
Formula Environments: Σ = (Y 7→ Γ0 )
Value Environments: Γ0 = {i : lid, j : uid};
Γ3 = eff(Γ2 , i) = Γ1 ;

Γ1 = Γ0 , {y : uid};

Γ4 = eff(Γ2 , i) = Γ1 ;

Γ5 = Γ4 , {z : lid};

Γ2 = {i : lbid, j : uid, y : uid};
Γ6 = {i : lid, j : uid, y : uid, z : lbid}.

Rejected!!
i : lbid < Γ6

tAdS

(Σ(Y) = Γ0 ) ⊂ Γ3

tVar
tNcA
tCn2
tBlk

Σ; Γ3 ` Y

tBlk

Σ; Γ3 ` [ j.y!{res, x + 1}] ia Y

Σ; Γ6 ` restr(i)ε prg(z)i;z Y

Σ; Γ4 ` tt

tTru

Σ; Γ5 ` blk(z) restr(i)ε prg(z)i;z Y Σ; Γ4 ` rel(i; z) tt
Σ; Γ4 ` [z : lid .y!err] bi restr(i)ε prg(z)i;z Y

tRel
tNcB

Σ; Γ2 ` (([ j.y!{res, x + 1}] ia Y) & ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y))
Σ; Γ1 ` blk(i) (([ j.y!{res, x + 1}] ia Y) & ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y))

tNcB

Σ; Γ1 ` [i. !{inc, x, y}] bε (([ j.y!{res, x + 1}] ia Y) & ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y))


 ([ j.y!{res, x + 1}] ia Y)

a
b 


Σ; Γ0 ` [i?{inc, x : dat, y : uid}] ε [i. !{inc, x, y}] ε 
b
& ([z : lid .y!err] i restr(i)ε prg(z)i;z Y)
tMax

 ([ j.y!{res, x + 1}] ia Y)
a
b 
∅; Γ0 ` max Y. [i?{inc, x : dat, y : uid}] ε [i. !{inc, x, y}] ε 
& ([z : lid .y!err] bi restr(i)ε prg(z)i;z Y)

tNcA

In fact, when rule tCn2 is applied in Derivation 5, it uses the excl function to analyse the
respective branches. Since these branches are mutually exclusive, the function returns
the tuple hi, ii, i.e., the release lists of the top necessities [ j . y!{res, x + 1}] ia and [z :
lid .y!err] bi . The release sets in this tuple are then used to construct environments Γ3 and

Γ4 , from Γ2 , wrt. the side-effects imposed by one branch on the other branch in case of
trivial satisfaction. Since both branches release i upon being trivially satisfied, then both
Γ3 and Γ4 are constructed in the same way, i.e., Γ3 = eff(Γ2 , i) = {i : lid, j : uid, y : uid} = Γ4 .
Note that i : lbid in Γ2 was changed to i : lid in Γ3 and Γ4 . This allows for the type
system to detect that if at runtime the synchronous adaptation restr(i)ε is applied, then the
trivially satisfied opposing branch (i.e., [ j.y!{res, x + 1}] ia ) could have already released
actor i, thereby leading to a synchronisation error.
112







5. Filtering Out Invalid Adaptation Scripts

5.2

Dynamic Analysis of Typed Scripts

The typed adaptation-scripts of Sec. 5.1 need to execute wrt. the systems described in
Sec. 4.5. Crucially, however, we cannot expect that a monitored system observes the
type discipline assumed by the script. This, in turn, may create type incompatibilities
that need to be detected and handled at runtime by the monitor.
Example 5.2.1. Recall that in Derivation 3, the typed script (5.1) (restated below) was
accepted by the type system when typechecked wrt. value environment Γ0 = {i : lid, j :
uid}.

ϕ0 = max Y. [i?{inc, x : dat, y : uid}] aε
def

[i. !{inc,

x, y}] bε

( [ j.y!{res, x + 1}] aε Y) &
( [z : lid .y!err] bi restr(i)ε prg(z)i,z Y)

!
(5.1)

There are two classes of type incompatibilities that may arise during runtime monitoring:
• When listening for a pattern, e.g., i?{inc, x : dat, y : uid}, the system may generate
the action i?{inc, 5, i}; pattern matching the two would incorrectly map the identifier
variable y (of type uid) to the linear identifier value i (which is already typed as lid);
we call this a type mismatch incompatibility, as an identifier i can only be assigned
one type.
• When listening for pattern z:lid .y!err, the system may generate a matching action
i.h!err mapping variable z to i. Aliasing z with i violates the linearity assumption associated with z, (i.e., denoted by its type lid), which assumes it to be distinct from any
other identifier mentioned in the script [64]; we call this an aliasing incompatibility.
Such incompatibilities would not occur if a script is used to monitor a typed system that
typechecks wrt. the same value environment used for typechecking the script. For instance, in the case of formula (5.1) which typechecks wrt. Γ0 , the system in Fig. 1.1
would also need to typecheck wrt. to the same environment. By employing static typechecking for both the monitor and the system, we would therefore be able to statically
detect cases where a system event, such as i : lid?3, may erroneously match a necessity pattern such as x : uid?3, thereby detecting a type mismatch. A similar technique
113

5. Filtering Out Invalid Adaptation Scripts

can also be used to statically detect that a linear process identifier i, may at runtime be
mapped to other linear typed variables in the adaptation formula; thereby detecting cases


of aliasing.

In the absence of system typing, our monitors need to perform dynamic type checks
(at runtime) and abort monitoring as soon as a type incompatibility is detected. This is
important since any violations to the type discipline assumed by the script, may potentially render the adaptations specified in the script unsafe, and thus these should not be
administered on the system.
In order to perform dynamic type checks, the monitor should be able to start with an
initial set of type assumptions and then extend these assumptions from the information
that it learns about the system while handling system events. Hence, the operational
semantics of typed scripts is defined wrt. the value type environment, Γ, with which they
are statically typechecked. This environment is then extended at runtime with new type
assumptions, that the monitor infers whenever a value received from a system event,
is bound to a typed variable defined in a matching necessity pattern. The new type
assumption would thus consist in the matched value mapped to the type of the variable
it was bound to.
For instance, if a monitor which executes wrt. environment Γ, receives a system
event i?3 which matches necessity pattern x : uid?3, it would therefore infer a new type
assumption in which it associates identifier i to type uid (which is the type of variable x
to which i was bound). The monitor therefore extends Γ with this new assumption, i.e.,
i : uid, thereby creating environment (Γ, i : uid).
Example 5.2.2. As shown in the derivation below, the execution of the typed script ϕ0 in
(5.1) wrt. system s = (i, j, k, h) : ◦, would use the type environment Γ = {i : lid, j : uid} from
Ex. 5.1.1 as a look-up table which its consults in order to detect type incompatibilities.
For instance the typed script is able determine that an action such as i?{inc, 5, i} cannot
be pattern matched with i?{inc, x : dat, y : uid}, as this would lead to a type mismatch
when y : uid is mapped as i : uid (note the mismatching types, i.e., y : uid 7→ i conflicts
with the assumption i : lid which is already present in Γ).
114

5. Filtering Out Invalid Adaptation Scripts

i?{inc,5,i}

s . hΓ, ϕ0 i ========⇒ s . h{Γ, y : uid}, [i. !{inc, x, y}] bε · · ·i {x 7→ 5, y 7→ i}
(By applying substitution {x 7→ 5, y 7→ i}, environment {Γ, y : uid} becomes {i : lid, j : uid, i : uid})
⇒ Type mismatch!!

The above reduction leads to a stuck configuration caused by the type mismatch of i, as
this yields an invalid extension to the value environment i.e., the resultant environment
is no longer a map (as previously explained in Sec. 5.1.2).
Conversely as shown in (5.12) of the below derivation, matching pattern i?{inc, x :
dat, y : uid} with action i?{inc, 5, h} would not only constitute a valid match, but also al-

low monitoring to extend the assumed knowledge of the system from Γ to Γ0 = {Γ, h : uid},
where h is associated to the type of the matched pattern variable y. The extended environment Γ0 would however allow the monitor to detect a type mismatch between pattern
z : lid .h!err and action h .h!err as shown in (5.14).


 ([ j.h!{res, 5 + 1}] aε ϕ0 ) &


 i
s . hΓ, ϕ i ========⇒ s . h{Γ, h : uid}, [i.
b
0
([z : lid .h!err] i restr(i)ε prg(z)i,z ϕ )
(5.12)


 ([ j.h!{res, 5 + 1}] aε ϕ0 ) &

i.h!{inc,5,h}
==========⇒ (( j, k, h) : ◦, i : •) . h{Γ, h : uid}, 
 i
b
0
([z : lid .h!err] i restr(i)ε prg(z)i,z ϕ )
(5.13)
0

i?{inc,5,h}

!{inc, 5, h}] bε

h.h!err

======⇒ (( j, k, h) : ◦, i : •) . h{Γ, h : uid, z : lid}, restr(i)ε prg(z)i,z ϕ0 i {z 7→ h}

(5.14)

(If we apply substitution {z 7→ h} we get environment {Γ, h : uid, h : lid})
⇒ Type mismatch!!

Importantly, the derivation below shows that the monitor is also enabled to detect
an aliasing violation between the same pattern, z : lid . h!err, and action i . h!err,
since variable z cannot be mapped to i, as this linear identifier is already present in the
respective value environment.
i?{inc,5,h}

i.h!{inc,5,h}

i.h!err

s . hΓ, ϕ0 i ========⇒ · ==========⇒ · ======⇒
(( j, k, h) : ◦, i : •) . h{Γ, h : uid, z : lid}, restr(i)ε prg(z)i,z ϕ0 i {z 7→ i}
(If we apply substitution {z 7→ i} we get environment Γ0 = {i : lid, j : uid, h : uid, i : lid})
Linear identifier i : lid ∈ Γ was remapped to z : lid ⇒ Aliasing!!

115

5. Filtering Out Invalid Adaptation Scripts

It would however allow z : lid .h!err to be matched to action k .h!err, which would
(again) extend the current type environment to (Γ0 , k : lid) using the script type association


z : lid.
The Preliminaries for Dynamic Typechecking

Although safe, the mechanism discussed in Ex. 5.2.2 turns out to be rather restrictive for
recursive properties (using maximal fixpoints). By this we mean that we end up unnecessarily aborting monitoring for the fear of unsafety. Note that, by alpha-conversion, the
variable bindings made by a necessity formula under a recursive formula is different for
every unfolding of that formula: e.g., unfolding script (5.1) twice yields



[i?{inc, x : dat, y : uid}] aε . . . [z : lid .y!err] bi . . .



 [i?{inc, x0 : dat, y0 : uid}] a
ε


0
(. . . [z : lid .y!err] bi . . . ϕ0 )






where the outer bindings x, y, z are distinct from the inner bindings x0 , y0 , z0 . More importantly, however, the scope of these bindings extends until the next fixpoint unfolding,
and are not used again beyond that point. For instance, x, y, z above are not used beyond
the respective adaptations of the first unfolding. Thus, one possible method for allowing
a finer dynamic analysis for adaptation-scripts, especially relating to linearity and aliasing violations, is to employ a mechanism that keeps track of which linear bindings are
still in use. In the example above, this would allow us to bind k twice — once with z
during the first iteration, and another time with z0 . This is safe as we know that by the
time the second binding occurs (z0 ), the first binding (z) is not in use anymore, i.e., there
is no aliasing.
To implement this mechanism, our formalisation uses three additional components.
(1) The operational semantics for adaptation-scripts uses an extra scoping environment,
∆ ∈ Pid * P(LVar), keeping track of the recursion variables under which an
identifier binding is introduced, by associating that identifier to a set of formula
variables, κ ∈ P(LVar). Environment ∆ keeps track of the linear identifiers that
116

5. Filtering Out Invalid Adaptation Scripts

are currently in use only. For instance, if Γ = {i : uid}, ∆ = ∅, ϕ = max Y. ([i?x :
lid] aε [ x?3] bε sA(x) x Y), the script would reduce as follows for event i? j:
i? j

s . hΓ, ∆, ϕi ===⇒ s . h{Γ, j : lid}, ∆0 = { j : {Y}}, [ j?3] bε sA( j) j ϕi

(5.15)

Hence as identifier j was bound to x : lid, the monitor extended its type assumptions
in Γ with the assumption that i is a linear process id i.e., i : lid. It also added an
entry in the ∆0 environment showing that linear identifier j is scoped under formula
variable Y and is thus currently in use. Therefore, if i is rebound to a linear variable
while it is still in use, we can detect aliasing by consulting the scoping environment.
(2) To facilitate updates to environment ∆, the patterns in necessity formulas are decorated by sets of formula variables, denoting their respective recursion scope. For
instance, if we apply the decoration process on max X. ([e] ρr . . . max Y. ([e0 ] ρw ff)) we
0

get max X. ([e{X} ] ρr . . . max Y. ([e0{X,Y} ] ρw ff)). Decoration can easily be performed us0

ing a linear scan of the script before executing it.
(3) The runtime syntax uses an additional construct, clr(Y )ϕ, when unfolding a recursive
formula. This is required: to mark the end of an unfolding; and upon execution, to
remove all identifier entries in ∆ that have the recursion variable Y declared in their
respective set of formula variables, κ, so as to record that they are not in use anymore.
For instance, when using this additional construct in (5.15) we obtain the following
derivation:
i? j

s . hΓ, ∆, ϕi ===⇒ s . h{Γ, j : lid}, { j : {Y}}, [ j?3{Y} ] bε sA( j) j clr(Y )ϕi
j?3

===⇒ s . h{Γ, j : lid}, { j : {Y}}, clr(Y )ϕi
τ

−−→ s . h{Γ, j : lid}, ∅, ϕi

(5.16)

Note that in (5.16) the clr(Y ) was used to clear the contents in ∆0 which were associated with formula variable Y.
Moreover, the ∆ environment is first initialised by associating every statically know lin-

117

5. Filtering Out Invalid Adaptation Scripts

ear process identifier i : lid residing in Γ to κ = ∅, in the following manner:
∆ = {i : ∅ | Γ(i) = lid}
For example, if a script typechecks statically wrt. Γ = {i : lid, j : uid}, the ∆ environment
is initialised as ∆ = {i : ∅}. These linear identifiers are therefore universally scoped and
thus cannot be cleared.
Remark 10. In this scoping mechanism, the identifiers bound to linear variables from
different concurrent branches are added to a conceptually global scoping environment
∆, in which they are associated to the formula variable X under which they are currently
scoped i.e., in use. The bindings associated to formula variable X are then cleared from ∆
whenever clr(X ) is called. This approach may appear to be problematic in the following
situations:
(i) A formula multiply defines the same recursive variable, e.g., (max X. ϕ1 ) & (max X. ϕ2 ).
Our clearing mechanism may erroneously clear the bindings used in branch max X. ϕ2
when branch max X. ϕ1 recurs (and vice-versa).
(ii) A formula recurs on the same recursive variable in two (or more) coexisting concur
rent branches, e.g., max X. [i?3] bε (sA(i)i X) & X . In such cases the clearing mechanism could clear the bindings or one recurring branch e.g., (sA(i)i X) by using clr(X ),
which could erroneously clear the bindings scoped under the other branch.
We solve issue (i) as we work up to α-conversion i.e., we rename one of the variables by
conducting a linear scan of the script, e.g., the renamed script becomes:
(max X. ϕ1 ) & (max Y. ϕ2 [X/Y])
In the case of issue (ii), recall that in ∆ we only track linear identifiers, e.g., i : lid, which
means that this issue is problematic whenever a formula that uses a linear identifier
i : lid, recurs over the same formula variable in multiple non-mutually exclusive branches.
However from Ex. 5.1.2 in Sec. 5.1.3, recall that these cases are statically rejected by
our typing rules. Hence, a well-typed script can never introduce such an issue.

118



5. Filtering Out Invalid Adaptation Scripts

rIdem1

rMax

rClr

τ

τ

hΓ, ∆, clr(X )ϕi −−→ hΓ, {i : κ | i : κ ∈ ∆ ∧ X < κ}, ϕi

dom(∆) ∩

dom(Γ0 )

=∅

subj(α) = i
Γ0 = bnd(e)σ
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ

α

hΓ, ∆, [eκ ] bw ϕi −−→ h(Γ, Γ0 ) , (∆ ∪ {i : κ | Γ0 (i) = lid}) , blk(i) ϕσi
mtch(e, α) = σ

dom(∆) ∩
rNc2

dom(Γ0 )

=∅

Γ0 = bnd(e)σ
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ

α

hΓ, ∆, [eκ ] aw ϕi −−→ h(Γ, Γ0 ) , (∆ ∪ {i : κ | Γ0 (i) = lid}) , ϕσi
γ

mtch(e, β) = ⊥
rNc3

hΓ, ∆, ϕ ≡ ϕ0 i −−→ hΓ, ∆, ψ0 ≡ ψi
rStr

α

ρ

hΓ, ∆, [eκ ] w ϕi −−→ hΓ, ∆, rel(w) tti

γ

hΓ, ∆, ϕi −−→ hΓ, ∆, ψi
c ⇓ f alse

c ⇓ true
rTru

rFls

τ

hΓ, ∆, if c then ϕ else ψi −−→ hΓ, ∆, ϕi
α

hΓ, ∆, ϕi −−→ hΓ0 , ∆0 , ϕ0 i
rCn1

τ

hΓ, ∆, if c then ϕ else ψi −−→ hΓ, ∆, ψi

α

hΓ, ∆, ψi −−→ hΓ00 , ∆00 , ψ0 i

dom(∆) = (dom(∆0 )∩dom(∆00 ))

α

hΓ, ∆, ϕ & ψi −−→ h(Γ0 , Γ00 ), ∆0 ∪∆00 , ϕ0 & ψ0 i
µ

τ

hΓ, ∆, ϕi −−→ hΓ, ∆0 , ϕ0 i
rCn2

α

hΓ, ∆, ffi −−→ hΓ, ∆, ffi

hΓ, ∆, max X. ϕi −−→ hΓ, ∆, ϕ[(clr(X )max X. ϕ)/X]i

mtch(e, α) = σ
rNc1

rIdem2

α

hΓ, ∆, tti −−→ hΓ, ∆, tti

hΓ, ∆, ϕi −−→ hΓ, ∆, ϕ0 i
rCn3

τ

hΓ, ∆, ϕ & ψi −−→ hΓ, ∆0 , ϕ0 & ψi
rAdA

µ

hΓ, ∆, ϕ & ψi −−→ hΓ, ∆, ϕ0 & ψi

a(r )

hΓ, ∆, aA(r)w ϕi −−−−→ hΓ, ∆, rel(w) ϕi
rAdS

s(r )

hΓ, ∆, sA(r)w ϕi −−−−→ hΓ, ∆, rel(w) ϕi
rRel

r(w )

h(Γ, w : lbid), ∆, rel(w) ϕi −−−−→ h(Γ, w : lid), ∆, ϕi
rBlk

b(w )

h(Γ, w : lid), ∆, blk(w) ϕi −−−−→ h(Γ, w : lbid), ∆, ϕi

Figure 5.2: Dynamically Typed Adaptation-Script Rules

119

5. Filtering Out Invalid Adaptation Scripts

µ

hΓ, ∆, ϕi −−6 →
iAct

α

α

s . hΓ, ∆, ϕi −−→ s0 . hΓ0 , ∆0 , ϕ0 i
µ

α

hΓ, ∆, ϕi −−6 →
iTrm

s −−→ s0

α

hΓ, ∆, ϕi −−6 →

α

s . hΓ, ∆, ϕi −−→ s0 . tt

µ

µ

hΓ, ∆, ϕi −−→ hΓ0 , ∆0 , ϕ0 i
iAda

α

hΓ, ∆, ϕi −−→ hΓ0 , ∆0 , ϕ0 i

s −−→ s0

τ

s −−→ s0
iSys

τ

s . hΓ, ∆, ϕi −−→ s0 . hΓ0 , ∆0 , ϕ0 i

s −−→ s
τ

s . hΓ, ∆, ϕi −−→ s0 . hΓ, ∆, ϕi

τ

hΓ, ∆, ϕi −−→ hΓ0 , ∆0 , hΓ0 , ∆0 , ϕ0 ii
iMon

τ

s . hΓ, ∆, ϕi −−→ s . hΓ0 , ∆0 , ϕ0 i

Figure 5.3: The Instrumentation Transition Rules (adapted from Fig. 4.7)
The Dynamically Typed Model
Fig. 5.2 describes the transition rules for typed adaptation-scripts, defined over triples
hΓ, ∆, ϕi. Along with the system rules of Fig. 4.7 and the instrumentation rules (adapted
to triples hΓ, ∆, ϕi from Fig. 4.7) of Fig. 5.3, they form the complete operational semantics. By contrast to Fig. 4.6, rMax in Fig. 5.2 unfolds a recursive formula to one prefixed
by a clear construct, (i.e., clr(X )max X. ϕ), where rule rClr is then used to remove all
entries in ∆ associated with X.
The new version of rNc1 in Fig. 5.2 implicitly checks for type mismatch incompatibilities by requiring that the environment extension, (Γ, Γ0 ), is still a map, i.e., conflicting entries e.g., i : uid, i : lid would violate this condition. It also uses the predicate
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ, to check that the substitution environment, σ,
derived from matching a necessity pattern e to a system event, does not map the same
process identifier, i, to two or more distinct linear typed variables such as x : lid and y : lid.
For instance, if a system event {id, i, i} is matched with necessity pattern {id, x : lid, y : lid}
this predicate detects that x , y but xσ = yσ = i, and thus detects aliasing. Furthermore,
this new version of rNc1 checks that the new bindings, i.e., dom(Γ0 ), are distinct from
the linear identifiers that are currently in use, i.e., dom(∆), as these would constitute

120

5. Filtering Out Invalid Adaptation Scripts

aliasing incompatibilities. If no type incompatibilities are detected, the rule transitions
by updating ∆ accordingly. Rule rNc2 is analogous to rNc1.
Rule rCn1 performs similar checks by ensuring that linear aliasing introduced along
separate branches do not overlap. In fact this rule uses predicate dom(∆) = (dom(∆0 ) ∩
dom(∆00 )) to detect cases where the same identifier is simultaneously mapped to two
distinct linear typed variables pertaining to the two concurrent branches ϕ and ψ (e.g.,
identifier i is simultaneously mapped to x : lid defined in ϕ, and also to y : lid in ψ).
If any of the conditions for rNc1, rNc2 and rCn1 are not satisfied, the adaptationscript blocks and is terminated in an instrumented setup using rule iTrm from Fig. 5.3,
i.e., it aborts as soon as type incompatibilities are detected.
An example driven explanation for Detecting Aliasing
As shown by our newly introduced dynamic checks in rules rNc1, rNc2 and rCn1,
aliasing can be introduced in two ways: (i) either by remapping a linear process id while
it is already in use, or else (ii) by having two or more branches that simultaneously map
the same linear process id to different linear typed variables.
Example 5.2.3. For the first case consider the following derivation for formula (5.1)
(restated below), wrt. system s = ((i, j, k, h) : ◦), Γ = {i : lid, j : uid}, ∆ = {i : ∅}. Note that
decorations were added accordingly while deriving (5.1).
ϕ0 = max Y. [i?{inc, x : dat, y : uid}] aε
[i. !{inc, x, y}] bε
def

( [ j.y!{res, x + 1}] aε Y) &
( [z : lid .y!err] bi restr(i)ε prg(z)i,z Y)

!
(5.1)



 ( [( j.y!{res, x + 1}){Y} ] aε clr(Y )ϕ0 ) & 

 i
s . hΓ, ∆, ϕ i −−→ s . hΓ, ∆,
· · · 
( · · · restr(i)ε prg(z)i,z clr(Y )ϕ0 )


 ( [( j.h!{res, 3 + 1}){Y} ] aε clr(Y )ϕ0 ) & 
i?{inc,3,h}
b 
 i

−−−−−−−−−→ s . hΓ, ∆, [(i. !{inc, 3, h}){Y} ] ε 
( · · · restr(i)ε prg(z)i,z clr(Y )ϕ0 )


 ( [( j.h!{res, 3 + 1}){Y} ] aε clr(Y )ϕ0 ) & 
i.i!{inc,3,h}
−−−−−−−−−−→ (i : •, ( j, k, h) : ◦) . hΓ, ∆, 
 i
( [(z : lid .h!err){Y} ] bi restr(i)ε . . .)


i.h!err
−−−−−−−→ (i : •, ( j, k, h) : ◦) . h(Γ, Γ0 ), ∆0 , restr(i)ε prg(i)i,i clr(Y )ϕ0 i
0

τ

[(i?{inc, x : dat, y : uid}){Y} ] aε

where Γ0 = {i : lid} and ∆0 = {i : ∅, i : Y}
But dom(∆) ∩ dom(Γ0 ) = {i} , ∅ ⇒ Aliasing Detected!

121

5. Filtering Out Invalid Adaptation Scripts

Aliasing is therefore detected since in the penultimate reduction of the above derivation,
linear process id i was remapped by necessity [z : lid .y!err] bi to linear typed variable
z : lid, while this was still in use as stated by the entry {i : ∅} in ∆. More specifically,
aliasing was detected by the dynamic check dom(∆) ∩ dom(Γ0 ) = ∅ performed by rNc2.
For the second case, consider the following script:

 ([ x : lid?3] b sA(x) tt) &
x
def
ε

ϕalias = 
 ([y : lid?z : dat] b if z > 0 then sA(x) tt)
x
ε






Although ϕalias observes the linearity imposed by the static typing rules given in Fig. 5.1,
aliasing across concurrent branches may however lead to unsafety, particularly in the
case a system actor e.g., i, commits an input operation of 3, i.e., upon event i?3. The
monitor reduction below shows how the global dynamic check made by rule rCn1 is
used to detect aliasing amongst the two concurrent branches (note that we have added
the necessary decorations to ϕalias in the reductions).
(dom(∆0 )∩dom(∆00 ) = {i} , dom(∆)) implies Aliasing!!
i?3
h∅, ∆ = ∅, [(x : lid?3)∅ ] bε sA(x) x tti −−−→ h{i : lid}, ∆0 = {i : ∅}, sA(i)i tti
i?3
hΓ, ∆ = ∅, [y : lid?z : dat∅ ] bε if z > 0 then sA(x) x tti −−−→ h{i : lid}, ∆00= {i : ∅}, if 3 > 0 then sA(i)i tti
rCn1

i?3
h∅, ∆ = ∅, ϕalias i −−−→ Aliasing Detected!!

Aliasing is thus detected by rule rCn1 since (dom(∆0 ) ∩ dom(∆00 )) = {i} , dom(∆),
meaning that both branches have simultaneously mapped the same linear process id i to


two different linear typed variables, i.e., x : lid and y : lid.
Defining Type Soundness

Using a straightforward extension we redefine the definition of synchronisation errors
(given in Def. 4.5.3), wrt. our dynamically typed operational model given in Fig. 5.2, as:
Def. 5.2.1 (Synchronisation Error).
def

s(w )

s(w )

r(w )

r(w )

error(s . hΓ, ∆, ϕi) = hΓ, ∆, ϕi −−−−→ and s −−−6 −→ or hΓ, ∆, ϕi −−−−→ and s −−−6 −→





for some w ∈ dom(s)
Based on Def. 5.2.1, we prove Type Soundness wrt. the semantics of typed adaptation
122

5. Filtering Out Invalid Adaptation Scripts

scripts.
Theorem 5.2.1 (Type Soundness). Whenever Γ ` ϕ then:
t

s . hΓ, {i : ∅ | Γ(i) = lid} , ϕi =⇒ s0 . hΓ0, ∆0, ϕ0 i implies

¬error(s0 . hΓ0, ∆0, ϕ0 i)

This theorem states that if a dynamically checked monitor hΓ, ∆, ϕi (where ∆ is initialised as ∆ = {i : ∅ | Γ(i) = lid}), is executed wrt. a system s forming configuration
s . hΓ, ∆, ϕi, then no matter how this configuration evolves at runtime over some trace
t, it can never reach an unsafe configuration, i.e., a configuration that satisfies the error
definition given in Def. 5.2.1.

5.3

Proving Type Soundness

So far we have relied on examples to show that our typing mechanisms are able to accept
valid scripts while rejecting erroneous ones (either statically or dynamically). However,
since there are an infinite number of possible scripts and executions that one can express,
we want to provide soundness guarantees. We want to show that a synchronisation error
can never be introduced by any arbitrary script ϕ which is: statically accepted by our type
system, and which executes wrt. a system s and the respective Γ and ∆ environments,
i.e., as configuration s . hΓ, ∆, ϕi. We therefore prove type soundness for dynamically
typed scripts to ensure that at no point during execution can an accepted script yield a
synchronisation error (as defined by Def. 5.2.1). A few comments are in order prior to
discussing the proofs.

5.3.1

Preliminaries

When proving type soundness (and the supporting lemmas) we assume closed formulas.
This is required as our type system is compositional, i.e., it decomposes a formula into
subparts and analyses them separately. For this reason, certain parts of the formula may
be taken out context and become open formulas as shown in the example below:

123

5. Filtering Out Invalid Adaptation Scripts

Open Formula?

?

z
}|
{
(Σ,{X 7→ Γ}); Γ ` [e1 ] ar X & [e2 ] aw ff
tMax

But at runtime, X is substituted by
a

max X. ([e1 ] r

Σ; Γ ` max X. [e1 ] ar X & [e2 ] aw ff
|
{z
}

X & [e2 ] aw ff), thus keeping the

formula closed.

Closed Formula

(The same argument applies for Data Variables.)

Furthermore, as dynamic typechecking introduces a new runtime construct, i.e., clr(X ),
we make the following additions:
• We extend our static typesystem (see Fig. 5.1) with type rule tClr that we define
as follows:

Σ; Γ ` ϕ
tClr

Σ; Γ ` clr(X )ϕ

• We also extend function vexcl (defined in Def. 5.1.2) with an extra case which
handles the clear construct in the same way as per the maximal fixed points, i.e.,
we modify function vexcl as follows:


...




k
vexcl(ϕ, ψ) = 
vexcl(ϕ0 , ψ) if ϕ ∈ {max X. ϕ0 , clr(X )ϕ0 } ∧ ψ ,&n=1 (rel(r)tt)n



 ...
def

Furthermore, our proof of type soundness makes use of a well-formedness judgement
for systems wrt. type environments Γ. It that states that when a linear actor identifier is
annotated as blocked in Γ, then it must indeed be blocked in s for it to be well-formed.
Def. 5.3.1 (System Well-formedness). wf(s, Γ) = Γ(i) = lbid implies s(i) = •
def

Note that Def. 5.3.1 is a technical device used to decompose soundness into the respective individual transitions. However, it does not affect our premise that the system itself
may not adhere to the typing discipline of the typing environment. Recall also that linear
actors annotated as lbid are not allowed in initial environments. Thus, by Def. 5.3.1, any
system is well-formed wrt. an environment Γ where lbid < cod(Γ).

5.3.2

The Top Level Proof

As is standard, type soundness relies on the following two properties, namely Type
Safety (Lemma 5.3.1) and Subject Reduction (Lemma 5.3.2).
124

5. Filtering Out Invalid Adaptation Scripts

Lemma 5.3.1 (Type Safety).
For all ∆ · wf(Γ, s), Σ; Γ ` ϕ implies ¬error(s . hΓ, ∆, ϕi)
Type Safety (Lemma 5.3.1) ensures that any typed script cannot immediately be in error
wrt. well-formed systems, as defined in Def. 5.2.1. This however does not imply that it
will not reach an error after some number of transitions.
For this reason, we also need Subject Reduction (Lemma 5.3.2), which states that
a typed script and a well-formed system remain typed and well-formed after a single
transition.
Lemma 5.3.2 (Subject Reduction).
γ

wf(Γ, s) and Γ ` ϕ, and s. hΓ, ∆, ϕi −→ s0 . hΓ0, ∆0, ϕ0 i implies
wf(Γ0 , s0 ) and Γ0 ` ϕ0
Thus by repeatedly applying Lemma 5.3.2 over a sequence of transitions we know that
starting from a typed script and a well-formed system will always lead to an eventual typed script and well-formed system. From this latter fact, we can then apply
Lemma 5.3.1 to deduce that the resulting configuration cannot be in error.
To prove Theorem 5.2.1 (Type Soundness). Whenever Γ ` ϕ then:
t

s . hΓ, {i : ∅ | Γ(i) = lid}, ϕi =⇒ s0 . hΓ0, ∆0, ϕ0 i

implies ¬error(s0 . hΓ0, ∆0, ϕ0 i)

Proof. We prove the following statement from which the theorem follows:


wf(Γ, s)
and






∅; Γ ` ϕ
and
implies ¬error(s0 . hΓ0, ∆0, ϕ0 i)





t

s . hΓ, ∆, ϕi =⇒ s0 . hΓ0, ∆0, ϕ0 i 
t

t

This is proved by numerical induction on the number of transitions in ⇒
= , i.e., | ⇒
= |= n
using Type Safety and Subject Reduction as follows:
Case n = 0: Since n = 0 implies that no transitions were performed, we know
wf(Γ, s)
Σ; Γ ` ϕ
s0 = s and Γ0 = Γ and ∆0 = ∆ and ϕ0 = ϕ,
125

(5.17)
(5.18)
(5.19)

5. Filtering Out Invalid Adaptation Scripts

By (5.17), (5.18) and Lemma 5.3.1 (Type Safety) we know that for all ∆
¬error(s . hΓ, ∆, ϕi)

(5.20)

By (5.19) and (5.20) we can deduce
¬error(s0 . hΓ0 , ∆0 , ϕ0 i)

(5.21)

∴ Case holds by (5.21).
Case n = k+1: Since n = k + 1, and the rule premises we know
wf(Γ, s)
Σ; Γ ` ϕ
γ

t0

(5.22)
(5.23)
t0

s . hΓ, ∆, ϕi −→ · =⇒ s0 . hΓ0 , ∆0 , ϕ0 i where | =⇒ | = k

(5.24)

By (5.24) and defn of t we know
γ

s . hΓ, ∆, ϕi −→ s00 . hΓ00 , ∆00 , ϕ00 i
t0

s00 . hΓ00 , ∆00 , ϕ00 i =⇒ s0 . hΓ0 , ∆0 , ϕ0 i

(5.25)
(5.26)

By (5.22), (5.23), (5.25) and Lemma 5.3.2 (Subject Reduction) we know
wf(Γ00 , s00 )
Σ00 ; Γ00 ` ϕ00

(5.27)
(5.28)

By (5.26), (5.27), (5.28) and IH we know
¬error(s0 . hΓ0 , ∆0 , ϕ0 i)
∴ Case holds by (5.29).

5.3.3

(5.29)


Proving Type Safety

We now consider proving type safety; here we detail the proof for Lemma 5.3.1 .
To Prove Lemma 5.3.1 (Type Safety).
wf(Γ, s) and Σ; Γ ` ϕ implies ¬(error(s . hΓ, ∆, ϕi))
Proof. By Rule induction on Σ; Γ ` ϕ.

126

5. Filtering Out Invalid Adaptation Scripts

Case tFls: From the rule premises we know that for all ∆
Σ; Γ ` ff
wf(Γ, s)

(5.30)
(5.31)
s(w )

From the monitor reduction rules in Fig. 5.2 we know that hΓ, ∆, ffi −−6−→ and also
r(w )

hΓ, ∆, ffi −−6 →. Hence, this case holds as by Def. 5.2.1 (Synchronisation Error) we know
¬(error(s . hΓ, ∆, ffi)) as required.
Note: The proofs for cases tTru, tVar, tIf, tAdA, tMax, tClr, tNcA and tNcB are
analogous to that of case tFls.
Case tCN1: From the rule premises we know that for all ∆
Σ; Γ ` ϕ & ψ

(5.32)

Γ = (Γ1 + Γ2 )
Σ; Γ1 ` ϕ
Σ; Γ2 ` ψ

(5.33)
(5.34)
(5.35)

wf(Γ, s)

(5.36)

because

and

By (5.36) and system well-formedness (Def. 5.3.1) we know
Γ( j) = lbid implies s( j) = •

(5.37)

As by (5.33) we know that Γ1 ⊆ Γ and Γ2 ⊆ Γ, then from (5.37) we can deduce
Γ1 ( j) = lbid implies s( j) = •
Γ2 ( j) = lbid implies s( j) = •

(5.38)
(5.39)

By applying the defn of well-formedness (Def. 5.3.1) on both (5.38) and (5.39) we know
wf(Γ1 , s)
wf(Γ2 , s)

(5.40)
(5.41)

¬(error(s . hΓ1 , ∆, ϕi))

(5.42)

By (5.34), (5.40) and IH we know

By (5.35), (5.41) and IH we know
¬(error(s . hΓ2 , ∆, ψi))
127

(5.43)

5. Filtering Out Invalid Adaptation Scripts

By (5.42) and (5.43) we know that both branches do not yield errors. Hence we can
conclude
¬(error(s . hΓ, ∆, ϕ & ψi))

(5.44)

∴ Case holds by (5.44).
Case tCN2: From the rule premises we know that for all ∆
Σ; Γ ` ϕ & ψ

(5.45)

excl(ϕ, ψ) = (rϕ , rψ )

Σ; eff(Γ, rψ ) ` ϕ
Σ; eff(Γ, rϕ ) ` ψ

(5.46)
(5.47)
(5.48)

wf(Γ, s)

(5.49)

because

and

By (5.49) and system well-formedness (Def. 5.3.1) we know
Γ( j) = lbid implies s( j) = •

(5.50)

By defn of eff we know that eff(Γ, r ) contains less processes of type lbid then Γ, since eff
sets every process reference l ∈ r to lid and then produces a new type environment from
Γ. This allows us to conclude
∀ j : lbid ∈ eff(Γ, rψ ) ⇒ j : lbid ∈ Γ
∀ j : lbid ∈ eff(Γ, rϕ ) ⇒ j : lbid ∈ Γ

(5.51)
(5.52)

By (5.50), (5.51), (5.52) and transitivity we can deduce
(eff(Γ, rψ ))( j) = lbid implies s( j) = •
(eff(Γ, rϕ ))( j) = lbid implies s( j) = •

(5.53)
(5.54)

By applying the defn of well-formedness (Def. 5.3.1) on both (5.53) and (5.54) we know
wf(eff(Γ, rψ ), s)
wf(eff(Γ, rϕ ), s)

(5.55)
(5.56)

¬(error(s . h(eff(Γ, rψ )), ∆, ϕi))

(5.57)

By (5.47), (5.55) and IH we know

By (5.48), (5.56) and IH we know
¬(error(s . h(eff(Γ, rϕ )), ∆, ψi))
128

(5.58)

5. Filtering Out Invalid Adaptation Scripts

By (5.57) and (5.58) we know that both branches do not yield errors. Hence we can
conclude
¬(error(s . hΓ, ∆, ϕ & ψi))

(5.59)

∴ Case holds by (5.59).
Case tAdS: From the rule premises we know that for all ∆
Σ; Γ ` sA(w)r ϕ

(5.60)

Γ = Γ0 , w : lbid
Σ; Γ ` rel(r) ϕ

(5.61)
(5.62)

wf(Γ, s)

(5.63)

because

and

By (5.63) and system well-formedness (Def. 5.3.1) we know
Γ( j) = lbid implies s( j) = •

(5.64)

From (5.61) and (5.64) we can deduce
s(w) = •

(5.65)

By (5.61) and rule rAdS we know
s(w )

hΓ, ∆, sA(w)r ϕi −−−→ hΓ, ∆, rel(r) ϕi

(5.66)

By (5.65) and rule sAdS we know
s(w )

s −−−→ s

(5.67)

Hence, by (5.66), (5.67) and Def. 5.2.1 (Synchronisation Error) we know
¬(error(s . hΓ, ∆, sA(w)r ϕi))

(5.68)

∴ Case holds by (5.68).
Case tRel: From the rule premises we know that for all ∆
Σ; Γ ` rel(w) ϕ

129

(5.69)

5. Filtering Out Invalid Adaptation Scripts

because
Γ = Γ0 , w : lbid
Σ; Γ0 , w : lid ` ϕ

(5.70)
(5.71)

wf(Γ, s)

(5.72)

and

By (5.72) and system well-formedness (Def. 5.3.1) we know
Γ( j) = lbid implies s( j) = •

(5.73)

From (5.70) and (5.73) we can deduce
s(r) = •

(5.74)

By (5.70) and rule rRel we know
r(r )

hΓ0 , w : lbid, ∆, rel(w) ϕi −−→ hΓ0 , w : lid, ∆, ϕi

(5.75)

By (5.74) and rule sRel we know
r(r )

(s, r : •) −−→ (s, r : ◦)

(5.76)

Hence, by (5.75), (5.76) and Def. 5.2.1 (Synchronisation Error) we know
¬(error(s . hΓ, ∆, rel(w) ϕi))

(5.77)

∴ Case holds by (5.77).

5.3.4



Proving Subject Reduction

Before proving subject reduction, in Def. 5.3.2 we define the after function − this denotes
how a value type environment Γ changes based on the execution of a system action γ.
Def. 5.3.2 (The after function).



Γ0 , w : lbid






Γ0 , w : lid
after(Γ, γ) = 


Γ0





Γ
def

if γ = b(w ) ∧ Γ = Γ0 , w : lid
if γ = r(w ) ∧ Γ = Γ0 , w : lbid
if γ = α ∧ ∃Γ0 · Γ ⊆ Γ0
otherwise

This function enables us to make a more precise deduction of how the runtime reductions, over some arbitrary action γ, effect and evolve a value environment Γ into a more
130

5. Filtering Out Invalid Adaptation Scripts

updated environment Γ0 as defined by the function after(Γ, γ). This opposes proving that
a runtime reduction reduces Γ into some arbitrary value environment.
The proof for Subject Reduction relies on the following auxiliary lemmas:
Lemma C.2.1 (System Reduction).
γ

Γ0 = after(Γ, γ) and wf(Γ, s) and s→
− s0 implies wf(Γ0 , s0 )
— This Lemma proves that well-formedness is preserved upon any action γ. This is
γ

proved by rule induction on s→
− s0 in Sec. C.2.
Lemma C.2.2 (Monitor Reduction).
γ

Σ; Γ ` ϕ and hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i implies Σ; Γ0 ` ϕ0 and Γ0 = after(Γ, γ)
— This Lemma proves that if a well-typed script performs a dynamically checked runtime reduction, it always reduces into a new well-typed script. This is proved by rule
γ

induction on hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i; we show two main cases in Sec. 5.3.5 while the
rest are proved in Sec. C.2.
To Prove Lemma 5.3.2 (Subject Reduction).
γ

wf(Γ, s) and ∅; Γ ` ϕ and s.hΓ, ∆, ϕi→
− s0 .hΓ0 , ∆0 , ϕ0 i implies wf(Γ0 , s0 ) and ∅; Γ0 ` ϕ0 .
γ

Proof. By Rule induction on s . hΓ, ∆, ϕi→
− s0 . hΓ0 , ∆0 , ϕ0 i.
Case iAct: From the rule premises we know
α

s . hΓ, ∆, ϕi →
− s . hΓ0 , ∆0 , ϕ0 i

(5.78)

because
α

s→
− s
α

(5.79)

hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i

(5.80)

wf(Γ, s)
∅; Γ ` ϕ

(5.81)
(5.82)

and

By (5.80), (5.82) and Lemma C.2.2 (Monitor Reduction) we know
∅; Γ0 ` ϕ0
Γ0 = after(Γ, α)
131

(5.83)
(5.84)

5. Filtering Out Invalid Adaptation Scripts

By (5.79), (5.81), (5.84) and Lemma C.2.1 (System Reduction) we know
wf(Γ0 , s)

(5.85)

∴ Case holds by (5.83) and (5.85).
Case iSys: From the rule premises we know
τ

s . hΓ, ∆, ϕi→
− s0 . hΓ, ∆, ϕi

(5.86)

because
τ

s→
− s0

(5.87)

wf(Γ, s)
∅; Γ ` ϕ

(5.88)
(5.89)

and

By defn of after (Def. 5.3.2) and since γ = τ we know that
after(Γ, τ) = Γ

(5.90)

By (5.87), (5.88), (5.90) and Lemma C.2.1 (System Reduction) we know
wf(Γ, s0 )

(5.91)

∴ Case holds by (5.89) and (5.91).
Case iMon: From the rule premises we know
τ

s . hΓ, ∆, ϕi→
− s . hΓ0 , ∆0 , ϕ0 i

(5.92)

because
τ

hΓ, ∆, ϕi→
− hΓ0 , ∆0 , ϕ0 i

(5.93)

wf(Γ, s)
∅; Γ ` ϕ

(5.94)
(5.95)

and

By (5.93), (5.95) and Lemma C.2.2 (Monitor Reduction) we know
∅; Γ0 ` ϕ0
Γ0 = after(Γ, τ)

132

(5.96)
(5.97)

5. Filtering Out Invalid Adaptation Scripts

By (5.97) and the defn of after (Def. 5.3.2) we know
Γ0 = Γ

(5.98)

By (5.94) and system well-formedness (Def. 5.3.1) we know
Γ( j) = lbid implies s( j) = •

(5.99)

By (5.98) and (5.99) we know
Γ0 ( j) = lbid implies s( j) = •

(5.100)

By (5.100) and system well-formedness (Def. 5.3.1) we know
wf(Γ0 , s)

(5.101)

∴ Case holds by (5.96) and (5.101).
Case iAda: From the rule premises we know
τ

s . hΓ, ∆, ϕi→
− s0 . hΓ0 , ∆0 , ϕ0 i

(5.102)

because
µ

s→
− s0
µ

(5.103)

hΓ, ∆, ϕi→
− hΓ0 , ∆0 , ϕ0 i

(5.104)

wf(Γ, s)
∅; Γ ` ϕ

(5.105)
(5.106)

and

By (5.104), (5.106) and Lemma C.2.2 (Monitor Reduction) we know
∅; Γ0 ` ϕ0
Γ0 = after(Γ, µ)

(5.107)
(5.108)

By (5.103), (5.105), (5.108) and Lemma C.2.1 (System Reduction) we know
wf(Γ0 , s0 )

(5.109)

∴ Case holds by (5.107) and (5.109).
Case iTrm: From the rule premises we know
α

s . hΓ, ∆, ϕi →
− hΓ, ∆, tti

133

(5.110)

5. Filtering Out Invalid Adaptation Scripts

because
α

s→
− s

(5.111)
α

hΓ, ∆, ϕi →
−6

(5.112)

Given that by (5.112) we know that the monitoring stops applying, then we know that the
monitor enters an idempotent state (i.e., it remains infinitely true) such that it is unable
reduce into a script which is unsound.


Case holds trivially.

5.3.5

Proving the Monitor Reduction Lemma

In this section we highlight two main cases pertaining to the proof for Lemma C.2.2 (Monitor Reduction); the other cases are proved in Sec. C.2. The proof for this lemma is
conducted by rule induction on the monitor reductions. Particularly, here we show the
proofs for cases rNc1 and rMax as these cases rely on the following three auxiliary
lemmas:
Lemma C.2.3 (Formula Substitution).
Σ; Γ0 ` max X. ϕ and Σ; Γ ` ϕ implies Σ; Γ ` ϕ[(clr(X )max X. ϕ)/X]
— The Formula Substitution Lemma proves that whenever a formula variable X is substituted in a well-typed formula, then the resultant formula is also well-typed formula.
Lemma C.2.4 (Weakening).
(Σ,{X 7→ Γ0 }); Γ ` ϕ, X < fv(ϕ) implies Σ; Γ ` ϕ
— The Weakening lemma proves that if a formula ϕ which typechecks wrt. formula
environment (Σ,{X 7→ Γ0 }) has no free occurrences of formula variable X (i.e., X < fv(ϕ)),
then we can weaken the formula environment by removing the unnecessary entry X 7→ Γ0 .
Lemma C.2.5 (Term Substitution).
Σ; (Γ, bnd(e)) ` ϕ and mtch(e, α) = σ and dom(∆) ∩ dom(bnd(e)σ) = ∅
and ∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ implies Σ; (Γ, bnd(e)σ) ` ϕσ
134

5. Filtering Out Invalid Adaptation Scripts

— The Term Substitution Lemma is used to prove that if a system event α matches a
necessity pattern e without introducing any type inconsistencies, then if the substitution
environment σ, created by the match operation, is applied onto a well-typed formula,
then the resultant formula is also well-typed.
Note that these three lemmas are proved in Sec. C.2.
To prove Lemma C.2.2 (Monitor Reduction)
γ

Σ; Γ ` ϕ and hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i implies Σ; Γ0 ` ϕ0 ∧ Γ0 = after(Γ, γ).
γ

Proof. By Rule induction on hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i
Case rMax: From our rule premises we know
τ

hΓ, ∆, max X. ϕi →
− hΓ, ∆, ϕ[(clr(X )max X. ϕ)/X]i
Σ; Γ ` max X. ϕ

(5.113)
(5.114)

By (5.114) and tMax we know
(Σ,{X 7→ Γ}); Γ ` ϕ

(5.115)

By (5.114), (5.115) and Lemma C.2.3 (Formula Substitution) we know
(Σ,{X 7→ Γ}); Γ ` ϕ[(clr(X )max X. ϕ)/X]

(5.116)

By (5.116), due to substitution we know that every free formula variable X in ϕ is substituted by (clr(X )max X. ϕ), and hence we know
X < fv(ϕ[(clr(X )max X. ϕ)/X])

(5.117)

By (5.116), (5.117) and Lemma C.2.4 (Weakening) we know
Σ; Γ ` ϕ[(clr(X )max X. ϕ)/X]

(5.118)

By the defn of after (Def. 5.3.2) (Def. 5.3.2) and since γ = τ we know
Γ = after(Γ, τ)

(5.119)

∴ Case holds by (5.118) and (5.119).
Case rNc1: From our rule premises we know
α

hΓ, ∆, [eκ ] br ϕi →
− hΓ, bnd(e)σ, ∆0 , blk(i) ϕσi

135

(5.120)

5. Filtering Out Invalid Adaptation Scripts

because
mtch(e, α) = σ

dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ
subj(α) = i

(5.121)
(5.122)
(5.123)
(5.124)

Σ; Γ ` [e] bi ϕ

(5.125)

subj(e) = l

(5.126)
(5.127)
(5.128)

and

By (5.125) and tNcB we know
Σ; (Γ, bnd(e)) ` blk(l) ϕ
Σ; Γ ` rel(r) tt

By (5.121), (5.122), (5.123), (5.127) and Lemma C.2.5 (Term Substitution) we know
Σ; (Γ, bnd(e)σ) ` blk(lσ) ϕσ

(5.129)

By (5.121), (5.124) and (5.126) we know
lσ = i

(5.130)

Σ; (Γ, bnd(e)σ) ` blk(i) ϕσ

(5.131)

By (5.129) and (5.130) we know

Since Γ ⊆ (Γ, bnd(e)σ), by defn of after (Def. 5.3.2) (Def. 5.3.2) we know
after(Γ, α) = (Γ, bnd(e)σ)

(5.132)

∴ Case holds by (5.131) and (5.132).
Case rNc2: The proof for this case is analogous to that of case rNc1.
The rest of the cases are proved in Sec. C.2.

5.4



Conclusion

In this chapter we have addressed our third objective (i.e., objective (iii) in Sec. 1.1),
whereby we developed a static type system for assisting the specifier into writing valid
136

5. Filtering Out Invalid Adaptation Scripts

runtime adaptation scripts that are free from synchronisation errors (as stated in Def. 4.5.3).
This type system distinguishes between identifiers which are used in synchronisation
mechanisms and synchronous adaptations i.e., linear identifiers, and those which are
not i.e., unrestricted identifiers. By making this distinction, the type system is able to
restrict the use of linear identifiers such that errors are not introduce due to race conditions. However it can be less stringent with how the unrestricted identifiers can be used,
as these are incapable of introducing synchronisation errors given that they cannot be
used in synchronisation operations (if they are the script gets rejected).
The static type system uses a tracking mechanism, in its analysis, to make sure that
linear identifiers are always blocked prior to being used in synchronous adaptations.
Furthermore, the typesystem detects erroneous scripts that suffer from race conditions
by employing a degree of linearity. In this way, unless two (or more) branches are
mutually exclusive, the type system allows only one branch to operate on the same linear
identifier. This therefore eliminates cases where one concurrent monitoring branch may
for instance release a blocked system actor before another branch manages to apply a
synchronous adaptation. Implementing this tracking mechanism required augmenting
RA scripts with the necessary type annotations, thereby also allowing the type system to
distinguish between term variables used for binding generic data, and variables that bind
linear and unrestricted process identifiers.
As we monitor untyped systems, process identifiers that are bound to term variables
may introduce type inconsistencies such as type mismatches and aliasing. We thus augmented the monitor’s operational LTS semantics with dynamic typechecking so as to
eliminate introducing synchronisation errors, due to these type inconsistencies, by aborting monitoring.
Finally we provided a formal proof guaranteeing that our type system is sound wrt.
to typed adaptation scripts. We thus guarantee that any typed script that typechecks
statically, generates dynamically typed monitors that are safe, i.e., at no point during
their execution can they introduce synchronisation errors.

137

6. Related Work

In this chapter we consider other work available in the current literature which is related
to different aspects of this dissertation; we thus structure this chapter in four sections. In
Sec. 6.1 we discuss work related to the synchronous and asynchronous monitoring aspects discussed and explored in Chapter 3. Moreover, in relation to the work presented in
Chapter 4, in Sec. 6.2 we present other work which employs a form of mitigation mechanisms that relate to our runtime adaptation methodology. In conjunction, in Sec. 6.3
we also discuss work which dealt with extending a formal Logic with a notion of adaptations. Finally, in Sec. 6.4 we present work related to the static and dynamic analysis
techniques that we presented in Chapter 5.

6.1

Synchronous and Asynchronous Monitoring

By and large, most widely used online RV tools employ synchronous instrumentation
[53, 25, 19, 32, 8]. There is also a substantial body of work commonly referred to as
asynchronous RV [31, 30, 6]. However, the latter tools and algorithms assume completed
traces, generated by complete program executions and stored in a database or a log file.
As explained in Sec. 2.2.1, we term these bodies of work as offline, and their aims are
considerably different from the work presented in Chapter 3.
There exist a few tools offering both synchronous and asynchronous monitoring,
such as MOP [18, 19], JPAX [45, 66] and DB-Rover [34, 33], whereby the speci138

6. Related Work

fier can choose whether a property should be monitored for either synchronously or
asynchronously. Crucially, however, unlike our newly developed hybrid monitoring
approach, these tools do not provide the fine grained facility of supporting both synchronous and asynchronous monitoring at the same time, i.e., by switching between the
two modes at runtime. Apart from detectEr [39], eLarva [23] is another Erlang monitoring tool that uses the EVM tracing mechanism to perform asynchronous monitoring.
Rosu et al. [66] make a similar distinction to ours in their definitions synchronous
and asynchronous online monitoring, based on which they develop formula rewriting
algorithms to implement efficient centralised monitors. However, their definitions of
synchrony and asynchrony deals with the timeliness of detections made by their monitors i.e., that the monitors are efficient enough not to lag behind; however, they provide
minimal details on how the system is instrumented. By contrast, we focus on showing
how instrumentation is carried out over actor-based systems, and show how hybrid instrumentation can be used to obtain timely detections for certain properties, by blocking
strategic parts of the monitored system until the monitor performs the necessary checks.
This amounts to the definition of synchronous detection monitoring given in [66].
A closer work to ours (wrt. this area) is [26], whereby the authors allow a decoupling between the system and monitor executions but provide explicit mechanisms for
pausing the system while the lagging (asynchronous) monitor execution catches up. In
our case this mechanism is handled in a more implicit manner as we weave it in a more
natural way within our logic. In fact we implement similar mechanisms at a higher level
of abstraction by integrating them seamlessly in our logic when switching from asynchronous to synchronous monitoring in our hybrid monitoring approach. Furthermore,
in [26] the authors dealt with introducing asynchrony on top of a setting which supported
synchronous monitoring, while in our case we had to do the converse. In fact in Chapter 3 we studied ways on how to introduce synchrony in a tool and a setting, which only
provide support for asynchrony.
Talcott et al. in [71] compare and contrast three coordination models for actors which
cover a wide spectrum of communication mechanisms and coordination strategies. The

139

6. Related Work

comparison focusses on the level of expressivity of each model, the level of maturity,
the level of abstraction of the model, and the way user definable coordination behavior
is provided. One of the analysed coordination models, i.e., the Reo model, is a channel
based language in which channels may be either synchronous or asynchronous. This
model resembles the way our hybrid monitoring protocol interacts with the monitored
system. In fact our monitoring language constructs [α] and [|α|], seem analogous
to the asynchronous and synchronous channels in the Reo model. The major difference
is that these coordination models are used to develop a concurrent system by specifying
protocols of communication between its, while our constructs are used for monitoring an
existing actor-based system by defining the way that the system and the monitor interact.

6.2

Employing Runtime Mitigation

In [65], Rinard et al. introduce failure-oblivious-computing, a tailor made runtime adaptation technique that enables reinforced systems to survive memory errors without memory corruption. The solution consists in the creation of a safe C compiler which inserts
bounds checks that dynamically detect invalid memory accesses. Upon detection, instrumented systems execute recovery code which mitigates the invalid memory access, thus
allowing the system to continue with its normal execution instead of crashing. Similarly,
in [59] Rinard et al. present another domain-specific dynamic adaptation technique for
automatic input rectification. This technique was realized as a prototype SOAP implementation that automatically rectifies potentially dangerous inputs by converting them
into typical inputs that the program is more likely to process correctly. Furthermore in
[60], Rinard et al. implement yet another dynamic adaptation technique called RCV that
enables reinforced systems to survive divide-by-zero and null dereference errors. This
technique employs a monitor which attaches itself to the target system whenever an error
occurs, mitigates its execution, and keeps on tracking the effects of the applied mitigation until they are flushed from the system’s state, in which case the monitor detaches
itself.

140

6. Related Work

These techniques can, to some extent, be considered analogous to our adaptation
scripts, in the sense that once an adaptation script is compiled, it generates a monitor
which dynamically analyses a system and intervenes whenever it detects the specified
erroneous behaviour. This is analogous to what the techniques presented by Rinard
et al. are doing in order to reinforce their respective systems. The major differences
however are that the adaptation properties presented in Rinard et al.’s work target a
different domain (i.e., not for actor systems), and they were implemented in a tailor-made
fashion, i.e., they were not specified in some high-level logic and then automatically
converted into the required adaptation monitor. By contrast, in our work we provide a
logic and a tool for specifying runtime adaptation properties for adapting actor-based
systems through actor-level manipulations.
Colombo et al. in [27, 22] integrate the notion of compensations within the Larva
runtime monitoring framework to allow for the undoing of undesired system actions.
Compensations combined with asynchronous monitoring allow for performance overheads to be minimised. This is done by providing the lagging asynchronous monitor
with the ability to rollback the system by reverting its state to a point prior to the occurrence of the detected anomaly, even in the case of late detection. This contrasts our
adaptations which rather then trying to rollback the system, they acknowledge that an
error has occurred and apply mitigation techniques to reduce and possibly eliminate the
repercussions of the detected misbehaviour.
Furthermore, compensations are generally user defined and related to a specific action; in fact it is generally assumed that a system action has a compensating action which
can be used during the rollback procedure once a property violation is detected. This opposes our work as we define and implement a number of adaptations (i.e., they are not
user defined) which are not related to any kind of system action. In fact our adaptations
do not assume any information about the code executed by the monitored system. Moreover, Colombo et al.’s work differs in the type of systems that they target. Whereas in
our work we target long-running (reactive) actor-based systems, in their work the authors
target long-lived transaction systems such as the EntroPay transaction system [27].

141

6. Related Work

6.3

Extending a Logic with a Notion of Adaptation

In [74] Zhang et al. developed A-LTL, an adaptation-based extension to Linear Temporal Logic (LTL) [20] for formally specifying adaptation requirements of a system in
temporal terms. Particularly, the extended logic allows a user to specify the way that an
adaptive system can transition from (i.e., adapt) one program (i.e., the source program)
into another program (i.e., the target program). This logic was also implemented as a
runtime verification tool called AMOebA-RT [42], which provides assurance that dynamically adaptive software satisfies its requirements. AMOebA-RT instruments adaptive programs to insert code which forwards runtime state information to a concurrent
monitoring process which verifies whether the adaptive system satisfies the adaptation
properties specified in A-LTL and LTL. AMOebA-RT therefore allows for continuous
monitoring and verification of post-release behavior of a software system, ensuring that
whenever an error occurs the adaptive software either flags an error report, or applies an
adaption to mitigate the error automatically.
This opposes our work as our adaptation scripts provide the synthesised monitors
with the ability of actually performing the required adaptations, thus converting normal
systems into adaptive systems where the monitor takes care of applying the respective
adaptations when required. This contrasts the monitors synthesised from A-LTL scripts
presented in Zhang et al.’s work, as these are used to verify that an adaptive system
applies the necessary adaptations as specified, and thus their monitors (unlike ours) are
incapable of performing the adaptations over the monitored system.

6.4

Static Analysis of Monitoring scripts

In [12] Bodden et al. present dependent advices − an AspectJ language extension containing dependency annotations for preserving crucial domain knowledge. This allows
dependent advices to execute only when their dependencies are satisfied, thus allowing
for optimizations that exploit this knowledge by removing advice-dispatch code from
program locations where these dependencies cannot be satisfied. This concept lead to
142

6. Related Work

the creation of Clara [13, 11] − a static-analysis framework that employs static typestate
analyses to automatically convert any AspectJ monitoring aspect into a residual runtime
monitor that only monitors events triggered by program locations that the analyses failed
to prove safe.
To a certain extent, AspectJ scripts augmented with dependent advices are similar to
our typed scripts, as they provide the static compiler with additional information about
the environment interacting with the system. In Bodden’s case dependencies dictate
whether an aspect will execute at runtime or not, thus allowing for static optimisations,
while in our case the types provide information which allows us to statically determine
whether a script is well formed or not i.e., whether it introduces synchronisation errors
or not. Therefore, our aims differ from those of Bodden et al.’s work, as in our work we
aimed to employ static analysis for our RA scripts so as to ensure a degree of correctness.
Conversely, in Bodden et al.’s work the main aim was to apply static analysis in order to
reduce the overheads imposed by the AspectJ scripts. Furthermore, our work also differs
in the way we evaluate our static analysis technique, since we provide type soundness
proof as opposed to Bodden et al. which provide empirical performance tests.
Castellani et al. [16] extend previous work by Dezani et al. [72], whereby they
present a model (calculus) of self-adaptive, multiparty communications, in which security violations occur when processes attempt to communicate messages using inappropriate security levels. The model consists in three components: global types, monitors,
and processes. Global types represent how processes interact and define reading permissions for each process. Monitors are then obtained by projecting a global type onto a
process thus obtaining a monitored process; hence global types are in some sense analogous to our typed scripts. Monitors are able to mitigate security violations using local
and global adaptations, for handling minor and serious violations, respectively.
Similar to our work, the authors also establish type soundness by which they ensure
that global protocols still execute correctly, even when the system adapts itself to preserve security. However, they assume that the processes requiring monitoring are also
well-typed, whereas we do not rely on such an assumption. Specifically we consider

143

6. Related Work

the system as a black-box and assume minimal information. This is reflected in the way
we model the system in our RA operational model, and in the type assumptions used
by our type system, whereby the only information that we assume about the monitored
system is whether its actors are active or suspended by our instrumentation. Hence our
adaptations can apply over any (instrumented) actor regardless of the code it executes.

144

7. Conclusion

In this thesis we sought to develop a Runtime Adaptation framework for actor systems
by extending an existing Runtime Verification tool, with adaptation functionality that
can be localised to individual concurrent system components, thereby allowing for specific actors to be mitigated while leaving the other actors unaffected. This demanded
addressing the following challenges:
(i) Effective runtime adaptation on actor systems requires introducing a degree of synchrony in our monitors. As synchrony is not natively supported by the actor model,
this requires studying ways for introducing synchronous monitoring in an efficient
manner.
(ii) An effective RA framework must also provide a number of adaptations that can be
used to mitigate erroneous system behaviour by modifying several architectural aspects of actor systems. We must therefore identify these adaptations and develop a
mechanism by which these can be made applicable to any actor system, i.e., regardless of the code that it executes. As RA properties are no longer passive, these may
introduce errors at runtime. Formalising the runtime behaviour of our RA scripts
thus relieves us from having to deal with the complexities of the implementation.
This therefore enables us to identify and better understand the subtle errors that our
RA monitors may introduce at runtime.
(iii) Formalisation also enables us to study ways how erroneous RA scripts can be de145

7. Conclusion

tected and rejected prior to being deployed, by employing static analysis techniques.
In this way we should therefore provide the specifier with a tool that serves as guidance for writing error-free runtime adaptation scripts. This tool must however provide an element of guarantee about its own correctness.
To address our first objective (i.e., (i)), in Chapter 3 we have therefore studied different techniques for introducing synchronous monitoring on top of an inherently asynchronous platform (i.e., the actor model). After integrating these techniques in detectEr,
we carried out a systematic assessment of the relative overheads incurred by these different monitoring techniques. This assessment allowed us to conclude that although an
incremental synchronisation approach still introduces a degree of synchronisation overheads, it however permits for these overheads to be minimised by employing synchronisation on a by-need basis.
In light of the obtained performance results, in Chapter 4 we addressed our second
objective, (i.e., (ii)). We thus identified a number of adaptation actions that allow for violations to be mitigated by modifying different aspects of actor systems, such as in the case
of process restarts and message interceptions. We then categorised these adaptations into
two classes, namely asynchronous and synchronous adaptations, where the latter require
synchronisation when applied over the respective adaptees. Synchronisation was therefore introduced by building upon the efficient incremental synchronisation methodology
identified in the previous chapter. Hence, by adding minimally intrusive annotations,
we augmented detectEr’s specification language with synchronisation mechanisms, for
blocking and releasing actors, along with implementations of the identified adaptation
actions. This allows the specifier to develop adaptation scripts that yield monitors which
are capable of mitigating erroneous behaviour by executing adaptations. A prototype
implementation of our Runtime Adaptation framework called adaptEr1 , was thus developed as a result of this extension to detectEr.
Furthermore, we also gave a formal specification of the augmented logic (with adap1

A prototype implementation of this tool
https://bitbucket.org/casian/adapter.

146

can

be

accessed

and

downloaded

from

7. Conclusion

tations) through a formal operational model. This allows us to predict how an adaptation
script operates at runtime without actually executing it, and thus enabled us to better
understand the subtle errors that our RA monitors may introduce. Hence this model permitted us to identify and formalise situations where our runtime adaptation framework
may introduce errors if synchronisation is not properly defined.
Based on this formal model, in Chapter 5 we addressed our third objective (i.e., (iii))
by developing a static type system for assisting the specifier into writing valid runtime
adaptation scripts. We identified a number of errors that erroneous RA scripts may
introduce at runtime in the monitored system. We then focused on showing how one
of these errors i.e., the synchronisation error formalised in Chapter 4, can be statically
detected. We finally evaluated our type system by proving type soundness wrt. typed
adaptation scripts, thereby guaranteeing that the monitors that are synthesised from a
valid adaptation script (i.e., a script which was accepted by our type system), can at no
point during execution introduce synchronisation errors.
Once we addressed these three objectives we established that there exists an important interplay between synchronisation and effective adaptation actions. Although this
introduces additional overheads, as well as, the possibility of introducing runtime errors,
we developed and evaluated concepts which enable adaptations to be performed efficiently, and which provide assistance for writing error-free scripts. Despite implementing these concepts wrt. actor systems developed in Erlang, we conjecture that similar
challenges would arise when developing a similar RA framework for other actor-based
technologies such as Scala [43] and Akka [1]. Furthermore, these concepts may also
be applicable to other inherently asynchronous architectures such as service-oriented
systems [48] and distributed systems [29]. Therefore despite requiring certain parts of
the implementation to be reformulated for the respective technologies, the core concepts
studied in this thesis (e.g., incremental synchronisation, the identified adaptations, etc.)
would still be relevant and applicable.

147

7. Conclusion

7.1

Future Work

In order to further improve our work, we propose the exploration of the following suggestions.

7.1.1

Implementing More Adaptations

In Chapter 4 we have identified a number of adaptation categories which modify different aspects of an actor system. We have however implemented only a representative
adaptation action for each category, as a proof-of-concept. We therefore propose that in
future work we implement other adaptation actions thus providing the user with more
adaptations that can be used for mitigating the detected violations.

7.1.2

An Algorithmic Type System

When the type system developed in Chapter 5, analyses two non-mutually exclusive
branches, it preserves linearity by using an environment splitting operation to assign
linear identifiers to only one concurrent branch, thereby eliminating cases which could
lead to race conditions. Although our type system is implementable, the inherent nondeterminism of environment splitting, does not provide a directly implementable algorithm for splitting the type environment. We conjecture that the development of an Algorithmic Type System [64] in which linear environment splitting can be performed
deterministically, should require minimal alterations to our existing type system. Furthermore, to prove that this algorithmic version is also sound, it suffices proving that it
corresponds to our existing type system, for which we have already proven type soundness.

7.1.3

Type inference

Although we provide a tool which assists the specifier into writing valid scripts, this requires the user to manually include additional annotations and to provide an environment

148

7. Conclusion

containing the initial type assumptions which are used by the type system. We conjecture that the user should ideally be relieved from this additional effort. We therefore
propose the exploration of type inference techniques that could be used to automatically
infer the required type assumptions and include the respective annotations. Hence a type
inference system could be used to analyse an untyped script in order to generate a type
environment and a typed script which typechecks wrt. our type system.

7.1.4

Static Detection of Other Errors

In this thesis we aimed to prove the concept that although erroneous RA scripts introduce
runtime errors, we can still provide mechanisms for detecting these errors prior to deployment. Although in Chapter 5, we identify a number of errors that our RA monitors
may introduce, in order to prove this concept we developed a type system which detects
one of these errors i.e., synchronisation errors which we formalised in Chapter 4. However we conjecture that in most cases, addressing the other errors would only require
minor alterations to our type analysis techniques.

149

A. Monitor Optimisations in detectEr

In this chapter we present additional information with regards the different monitor synthesis techniques that are present in the current implementation of the detectEr RV tool.
In Sec. A.1 we look into the original monitor synthesis algorithm, presented in [39] and
explain its major causes of inefficiencies. Following this in Sec. A.2 we give a brief
overview on how static and dynamic optimisations where proposed in [15] and augmented in detectEr as means for lowering these overheads to a more feasible standard.

A.1

Synthesising Unoptimised Monitors

The original synthesis algorithm [39] aims to be modular by generating independently
executing monitor combinators for each logical construct, interacting with one another
through message passing.1 For instance, the synthesis parallelises the analysis of the
subformulas ϕ1 and ϕ2 in a conjunction ϕ1 & ϕ2 by:
(i) synthesising concurrent monitor systems for ϕ1 and ϕ2 respectively; and
(ii) creating a conjunction monitor combinator that receives trace events and forks
(forwards) them to the independently-executing monitors of ϕ1 and ϕ2 .
Since the submonitor systems for ϕ1 and ϕ2 may be arbitrarily complex (needing to analyse a stream of events before reaching a verdict), the conjunction monitor is permanent
1

Every combinator is implemented as a (lightweight) Erlang process (actor) [17], uniquely identifiable
by its process ID. Messages sent to a process are received in its dedicated mailbox, and may be read
selectively using (standard) pattern-matching.

150

A. Monitor Optimisations in detectEr

in the monitor organisation generated, so as to fork and forward event streams of arbitrary length. It is also worth noting that the synthesis algorithm assumes formulas to be
guarded, where recursive variables appear under necessity formulas; this is required to
generate monitors that implement lazy unrolling of recursive formulas, thereby minimising overheads.

srv?{v, c}
−−−−−−−−−→

srv?{v0 , c0 }
−−−−−−−−−−−→
srv.c!(v−1)
−−−−−−−−−−−−−→

Figure A.1: Monitor Combinator Generation for Formula (A.1) and its execution wrt.
trace srv?{v, c}; srv.c!(v−1); srv?{v0 , c0 }

Example A.1.1. Consider a simple Erlang system implementing a predecessor server
receiving messages of the form (n, clientID) and returning n − 1 back to clientID whenever n > 0, but reporting the offending client to an error handler actor, err, whenever
n = 0. It may also announce termination of service by sending a message to end. A

151

A. Monitor Optimisations in detectEr

safety correctness property in our logic would be:


 ([srv.end! ]ff) & ([srv.err!z](if (x , 0 ∨ y , z) then ff else X)) 


max X. [srv?{x, y}] 


& ([srv.y!z](if z = (x − 1) then X else ff))
(A.1)

It is a recursive formula, max X. (. . . ), stating that, whenever the server implementation
receives a request (input action), [srv?{x, y}] . . ., with value x and return (client) address
y, then it should not:
1. terminate the service (before handing the client request), [srv.end! ]ff.
2. report an error, [srv . err!z] . . ., when x is not 0, or with a client other than the
offending one, y , z.
3. service the client request, [srv.y!z] . . ., with a value other than x − 1.
These conditions are invariant; maximal fixpoints capture this invariance for server implementations that may never terminate as they are considered correct as long as the
conditions above are not violated.
From formula (A.1), the monitor organisation m (depicted in Fig. A.1) is generated,
consisting of one process acting as the combinator for the necessity formula [srv?{x, y}]ϕ.
If an event of the form srv?{v, c} is received, the process pattern matches it with srv?{x, y}
(mapping variables x and y to the values v and c resp.) and spawns the (dashed) monitor
system shown underneath it in Fig. A.1, instantiating the variables x, y with v, c resp. The
subsystem consisting of three monitor subsystems, one for each subformula guarded by
[srv?{x, y}] in (A.1), connected by two conjunction forking monitors. When the next

trace event is received, e.g., srv.c!v−1 (a server reply to client c with value v−1), the
conjunction monitors replicate and forward this event to the three monitor subtrees. Two
of these subtrees do not pattern match this event and terminate, while the third subtree
(i.e., submonitor m2) pattern-matches it and instantiates z for (v−1), thereby also evaluating the conditional and unfolding the recursive variable X to monitor m. If another
server request event is received, srv?{v0 , c0 } (with potentially different client and value
arguments c0 and v0 ), the conjunction monitors forward it to m, pattern matching it and
generating a subsystem with two further conjunction combinators as before.
152



A. Monitor Optimisations in detectEr

Formula (A.1) is a pathological example, highlighting two inefficiencies of the unoptimised synthesised monitors. First, conjunction monitors mirror closely their syntactic
counterpart and can only handle forwarding to two sub-monitors. As a result, formulas
with nested conjunctions (as in formula (A.1)) translate into organisations of cascading
conjunction monitors that are inefficient at forwarding trace events.
Second, the current monitor implementation does not perform any monitor reorganisations at runtime. When a conjunction formula ϕ1 & ϕ2 is translated, the conjunction
combinator monitor organisation is kept permanent throughout its execution because it
is assumed that the respective sub-monitors for ϕ1 and ϕ2 are long-lived. This heuristic however does not apply in the case of formula (A.1), where two out of the three
sub-monitors terminate after receiving a single event. This feature, in conjunction with
recursive unfolding, creates chains of indirections through conjunction monitors with
only one child, as shown in Fig. A.1 (bottom row).

A.2

Monitor Optimisations

The first optimisation introduced in [15] is that of conjunction monitor combinators that
fork-out to an arbitrary number of monitor subsystems. For instance, the corresponding
monitor formula (A.1) would translate into a monitor organisation consisting of one conjunction combinator with three children (instead of two combinators with two children
each) as shown in Fig. A.2 (left). This is more efficient from from the point of view
of processes created, but also in terms of the number of replicated messages required
to perform the necessary event forwarding to monitor subsystems −e.g., the conjunction combinator of Fig. A.2 generates three message replications to forward an event to
the three sub-monitors, as opposed to the unoptimised approach which requires sending
four messages (see Fig. A.1). This static optimisation is conducted through syntactic
manipulation using a pre-processing phase.
The second optimisation [15] builds on the first one by allowing conjunction monitor
combinators to dynamically reorganise the monitor configuration so as to keep the event

153

A. Monitor Optimisations in detectEr

srv?{v, c}
−−−−−−−−−→

srv.c!(v−1)
−−−−−−−−−−−−−→

srv?{v0 , c0 }
−−−−−−−−−−−→

Figure A.2: Optimised Synthesis for Formula (A.1) and its execution wrt.
srv?{v, c}; srv.c!(v−1); srv?{v0 , c0 }

trace

flow structure as efficient as possible. In order to keep overheads low, this reconfiguration operation is kept local, where unaffected monitor subsystems should continue with
their runtime analysis while the restructuring is in process. Stated otherwise, the monitor reorganisation happens while trace events are still being received, and the operation
needs to guarantee that (i) no trace events are lost (ii) trace events are not reordered. Reorganisations are carried out by conjunction combinators, which are provided with the
capability to add and delete monitor subsystems from their internal list of children. For
instance, when an event causes a child sub-monitor to terminate, the parent (conjunction) monitor is sent a termination message which allows it to remove the terminated
sub-monitor from its child-list.
The dynamic restructuring protocol employed by this optimisation yields monitor
organisations with only one (eventual) conjunction node at the root, and a list of monitor
subsystems processing the forwarded events (a spider-like configuration). For instance,
for the event trace srv?{v, c}; srv . c!(v − 1); srv?{v0 , c0 }, the synthesised monitor for
formula (A.1) yields the evolution shown in Fig. A.2.

154

B. Additional Properties and Tabulated
Results

In this Chapter we present additional information related to the impact assessment experiments conducted in Chapter 3. In Sec. B.1 we provide an overview of two additional
properties written for the Yaws webserver, that we used along with properties (3.3) and
(3.4) for obtaining the performance results presented in the graphs in Figures 3.6, 3.7
and 3.8 given in Sec. 3.3.3. To provide an even clearer insight of the results presented in
these graphs, in Sec. B.2 we represent the same results as tables.

B.1

Additional Runtime Verification Properties

Here we present two other properties developed for the Yaws webserver and used in our
experiments while conducting our impact assessment analysis. Here we only present the
versions used for asynchronous and synchronous monitoring as the versions for Hybrid
monitoring can easily be inferred by converting all the falsities (ff) in the scripts to sff.

B.1.1

Five or more Headers Required Property

When sending a request to a webserver, HTTP clients also send a number of HTTP
headers containing information about themselves and about their request. Typically, an

155

B. Additional Properties and Tabulated Results

HTTP request should provide about 5 or 6 information headers (http header), so that
the webserver may correctly handle the request and send the appropriate response. A
property may be used to check and ensure that any request sent to the server contains at
least 5 headers. This behaviour may be verified using either property (B.1) or (B.2).
max X.
[AcceptorPid!{hPid, next, }]
[hPid . ret({yaws, do recv, 3, {ok, {http req, GET, , }}})]

(
[hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff
)&(
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff
)&(
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff
)&(
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff
)&(
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff
)&(
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]

 ([hPid . ret({yaws, do recv, 3, {ok, http eoh}})]X)
max Z. 
& ([hPid . ret({yaws, do recv, 3, {ok, {http header, }}})] Z)

(B.1)





)


Although these two properties may be considered equivalent, they do not translate
156

B. Additional Properties and Tabulated Results

into the same concurrent monitor. In fact the monitor generated from property (B.1)
is specified to immediately spawn 6 concurrent submonitors for each connected client,
upon receiving an HTTP request notification (http req) event. Five of these submonitors flag a violation when 4 or less headers are received, otherwise they terminate. The
sixth monitor reapplies the property for the connected client, when 5 or more headers
are received.
Conversely, in (B.2), submonitors are generated in a lazy manner, meaning that for
each event message, only two submonitors are spawned. One of these submonitors detects a violation if the(http eoh) message is received prematurely, otherwise it terminates. The other submonitor spawns two further submonitors if an HTTP information
header is received. The property is reapplied if the end-of-header message is received after 5 or more information headers. Although (B.1) is more readable then (B.2), the latter
is less resource intensive, as less submonitors are spawned for each connected client.
max X.
[AcceptorPid!{hPid, next, }]
[hPid . ret({yaws, do recv, 3, {ok, {http req, GET, , }}})]
([hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff) &

 [hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]

 ([hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff) &
 
  [hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
 
  ([hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff) &
  
   [hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
  
   ([hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff) &
   
    [hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
   
    ([hPid . ret({yaws, do recv, 3, {ok, http eoh}})]ff) &
    
     [hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]

    
 ([hPid . ret({yaws, do recv, 3, {ok, http eoh}})] X)
    
     max Z. 
& ([hPid . ret({yaws, do recv, 3, {ok, {http header, }}})] Z)
    

    









 
 
 
  
  
  

   
   
   
    
    
    
    
    
    



(B.2)

157

B. Additional Properties and Tabulated Results

B.1.2

Valid HTTP Request Format Property

In the HTTP protocol, every HTTP request must begin with a request notification header
(ie,http req). This request header must then be followed by a number of HTTP data
headers (ie, http header) which denote information about the requesting client and
other type of information. Finally each request must end with an end-of-header notification (ie, http eoh), denoting that the client has finished sending its request headers.
The following property was therefore developed to make sure that every HTTP request
received by the server follows this protocol.
max X .
[AcceptorPid!{hPid, next, }]
( [hPid . ret({yaws, do recv, 3, {ok, {http req, GET, , }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})] X
)&(
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})] X
)&(
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, http eoh}})]
[hPid . ret({yaws, do recv, 3, {ok, {http req, GET, , }}})] X
)&(
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})]
[hPid . ret({yaws, do recv, 3, {ok, {http req, GET, , }}})] ff
)&(
[hPid . ret({yaws, do recv, 3, {ok, http eoh}})] X
[hPid . ret({yaws, do recv, 3, {ok, {http header, }}})] ff)

(B.3)



This property checks that the HTTP headers in every client request follow the correct
ordering. The property recurs whenever the following non-violating behaviour is observed:
(i) A data header message (ie, http header) is received after a request header message (ie, http req).
(ii) Two data header messages are received subsequently.

158

B. Additional Properties and Tabulated Results

(iii) A data header message is followed by an end-of-header message (ie, http eoh),
which is then followed by another request header message.
The property fails when a data header message is received before a request header message, and also when a data header message is received after an end-of-header notification.

B.2

Detailed Result Tables

The following tables contain the same results plotted in the graphs given in Sec. 3.3.3
as Figures 3.6, 3.7 and 3.8. These results are structured in three subsections i.e., a
subsection for each monitor synthesis algorithm [39, 15] (i.e., unoptimised, statically
optimised and dynamically optimised) supported by detectEr (see Chapter A for details).
In each subsection we present: (i) the average CPU utilization (×106 CPU cycles) per
client request; (ii) the average memory consumption (MB) and (iii) the average response
time per client request (ms); obtained when monitoring the Yaws webserver for a number
properties using different monitoring approaches.

B.2.1

Results for Unoptimised Monitors
Average CPU utilization per Request (×106 CPU cycles)
Number of Requests

Monitoring modes

50

100

200

500

1000

2000

Baseline

14.556

14.408

14.127

14.171

14.365

14.269

Asynchronous

27.383

32.70

46.507

82.518

144.236

269.164

Hybrid

28.793

34.354

48.329

84.241

148.973

277.172

Synchronous

31.496

39.037

53.796

97.003

175.727

308.936

159

B. Additional Properties and Tabulated Results

Average Memory Consumption (MB)
Number of Requests
Monitoring modes

50

100

200

500

1000

2000

Baseline

25.62

25.68

25.63

25.68

26.17

26.25

Asynchronous

30.98

32.24

32.95

35.83

39.93

45.36

Hybrid

31.41

32.39

33.38

36.26

41.02

49.02

Synchronous

31.36

32.25

33.52

36.15

42.25

51.55

Average response time per Request (ms)
Number of Requests
Monitoring modes

50

100

200

500

1000

2000

Baseline

11.32

11.35

11.47

11.44

11.45

11.66

Asynchronous

14.54

15.82

17.75

24.74

40.45

66.02

Hybrid

17.3

17.331

19.75

27.3

44.82

75.62

Synchronous

17.63

19.08

23

36.27

60.82

112.75

B.2.2

Results for Statically Optimised Monitors
Average CPU utilization per Request (×106 CPU cycles)
Number of Requests

Monitoring modes

50

100

200

500

1000

2000

Baseline

14.556

14.408

14.127

14.171

14.365

14.269

Asynchronous

22.023

23.94

29.375

45.044

77.479

143.147

Hybrid

22.75

24.468

30.35

45.284

78.177

144.179

Synchronous

24.581

26.11

31.721

46.239

79.098

146.011

160

B. Additional Properties and Tabulated Results

Average Memory Consumption (MB)
Number of Requests
Monitoring modes

50

100

200

500

1000

2000

Baseline

25.62

25.68

25.63

25.68

26.17

26.25

Asynchronous

29.4

30.5

31.87

33.79

35.78

40.09

Hybrid

29.73

31.06

32.25

34.06

36.2

41.15

Synchronous

30.08

31.68

32.24

34.19

36.11

41.68

Average response times per Request (ms)
Number of Requests
Monitoring modes

50

100

200

500

1000

2000

Baseline

11.32

11.35

11.47

11.44

11.45

11.66

Asynchronous

13.66

14.91

16.31

20.06

27.29

48.84

Hybrid

15.66

15.94

16.85

21.41

29.95

53.08

Synchronous

18.07

18.14

19.61

25.74

39.11

67.91

B.2.3

Results for Dynamically Optimised Monitors
Average CPU utilization per Request (×106 CPU cycles)
Number of Requests

Monitoring modes

50

100

200

500

1000

2000

Baseline

14.556

14.408

14.127

14.171

14.365

14.269

Asynchronous

20.175

21.286

23.732

27.972

33.909

44.227

Hybrid

20.988

21.516

24.885

29.755

37.258

50.191

Synchronous

23.369

22.246

26.272

34.339

46.773

69.14

161

B. Additional Properties and Tabulated Results

Average Memory Consumption (MB)
Number of Requests
Monitoring modes

50

100

200

500

1000

2000

Baseline

25.62

25.68

25.63

25.68

26.17

26.25

Asynchronous

28.06

28.56

29.2

30.01

30.68

31.07

Hybrid

28.4

28.97

29.69

30.2

31.11

31.95

Synchronous

28.88

29.19

29.62

30.23

31.33

33.01

Average response times per Request (ms)
Number of Requests
Monitoring modes

50

100

200

500

1000

2000

Baseline

11.32

11.35

11.47

11.44

11.45

11.66

Asynchronous

12.23

12.74

13.1

14.15

16.18

21.8

Hybrid

13.11

13.02

13.31

15.53

18.39

23.26

Synchronous

15.05

15.014

15.14

16.74

20.91

29.68

162

C. Proofs for Auxiliary Lemmas

We divide this chapter into two main parts. In the first part (Sec. C.1) we present the
proof for Lemma C.1.1, an auxiliary lemma used when proving the Theorem of Semantic
Correspondence (i.e., Thm. 4.5.1 in Sec. 4.5.1).
In the second part (Sec. C.2 and C.3) we present the proofs for the auxiliary lemmas
required in proving the Theorem of Type Soundness (i.e., Thm. 5.2.1). We further divide
this part into two sections: in Sec. C.2 we give the proof details about the primary
auxiliary lemmas i.e., lemmas that are used directly by the main proofs presented in
Sec. 5.3; whereas in Sec. C.3 we prove the secondary auxiliary lemmas i.e., lemmas that
are used by the primary auxiliary lemmas. We now state the auxiliary lemmas that we
prove in both parts of this chapter:
The Correspondence Auxiliary Lemma:
γ

γ

Lemma C.1.1 ϕ −−→ ϕ0 and B, t v ϕ0 and A =⇒ B implies A, γt v ϕ
The Primary Soundness Auxiliary Lemmas:
Lemma C.2.1 (System Reduction)
γ

Γ0 = after(Γ, γ) and wf(Γ, s) and s →
− s0 implies wf(Γ0 , s0 )

Lemma C.2.2 (Monitor Reduction)
γ

Σ; Γ ` ϕ and hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i implies Σ; Γ0 ` ϕ0 and Γ0 = after(Γ, γ)

Lemma C.2.3 (Formula Substitution)
Σ; Γ0 ` max X. ϕ and Σ; Γ ` ϕ implies Σ; Γ ` ϕ[(clr(X )max X. ϕ)/X]

163

C. Proofs for Auxiliary Lemmas

Lemma C.2.4 (Weakening) (Σ,{X 7→ Γ0 }); Γ ` ϕ and X < fv(ϕ) implies Σ; Γ ` ϕ
Lemma C.2.5 (Term Substitution)
Σ; (Γ, bnd(e)) ` ϕ and mtch(e, α) = σ and dom(∆) ∩ dom(bnd(e)σ) = ∅
and ∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ implies Σ; (Γ, bnd(e)σ) ` ϕσ

The Secondary Soundness Auxiliary Lemmas:
Lemma C.3.1 Σ; Γ1 ` ϕ and Σ; Γ2 ` ψ and Γ = Γ1 + Γ2 implies Σ; Γ ` ϕ & ψ
γ

Lemma C.3.2 hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i and Σ; eff(Γ, r ) ` ϕ implies
γ

heff(Γ, r ), ∆, ϕi →
− heff(after(Γ, γ), r ), ∆0 , ϕ0 i and Γ0 = after(Γ, γ)
γ

Lemma C.3.3 Γ = Γ1 + Γ2 and hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i and Σ; Γ1 ` ϕ implies
γ

hΓ1 , ∆, ϕi →
− hΓ01 , ∆0 , ϕ0 i and Γ0 = Γ01 + Γ2
τ

Lemma C.3.4 excl(ϕ, ψ) = (rϕ , rψ ) and hΓ, ∆, ϕi →
− hΓ, ∆, ϕ0 i implies excl(ϕ0 , ψ) = (rϕ , rψ )
α

Lemma C.3.5 excl(ϕ, ψ) = (rϕ , rψ ) and hΓ, ∆, ϕi −
→ hΓ0 , ∆0 , ϕ0 i and
Σ; eff((Γ0 , Γ00 ), rψ ) ` ϕ0 and Σ; eff((Γ0 , Γ00 ), rϕ ) ` ψ0 implies Σ; (Γ0 , Γ00 ) ` ϕ0 & ψ0
µ

Lemma C.3.6 excl(ϕ, ψ) = (rϕ , rψ ) and hΓ, ∆, ϕi →
− hΓ0 , ∆, ϕ0 i and Σ; eff(Γ, rϕ ) ` ψ and
Σ; eff(after(Γ, µ), rψ ) ` ϕ0 implies Σ; after(Γ, µ) ` ϕ0 & ψ

Lemma C.3.7 excl(ϕ, ψ) = (rϕ , rψ ) and [(clr(X )max X. ϕ0 )/X] implies
excl(ϕ[(clr(X )max X. ϕ0 )/X], ψ[(clr(X )max X. ϕ0 )/X]) = (rϕ , rψ )

Lemma C.3.8 excl(ϕ, ψ) = (rϕ , rψ ) and [(clr(X )max X. ϕ0 )/X] implies
excl(ϕ[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

Note that when proving the Primary and Secondary auxiliary lemmas for Type Soundness, we omit from showing the proofs for cases involving structural equivalence (i.e.,
rStr), idempotency (rIdem1 and rIdem2) due to their triviality, as well as for cases that
are analogous to other cases, i.e., their proof requires minimal effort to be derived from
the proof of another case.
164

C. Proofs for Auxiliary Lemmas

C.1

Proving The Correspondence Auxiliary Lemma
γ

γ

To Prove Lemma C.1.1. ϕ −→ ϕ0 and B, t v ϕ0 and A =
⇒ B implies A, γt v ϕ
γ

Proof. By rule induction on ϕ −→ ϕ0 . The main cases are:
τ

Case rTru: From our rule premises we know that if b then ϕ else ψ −→ ϕ because
b ⇓ true

(C.1)

B, t v ϕ

(C.2)

and
τ

A=
⇒B

(C.3)

By (C.1), (C.2), (C.3) and defn of v we know A, τt v if b then ϕ else ψ as required.
Case rFls: The proof for this case is analogous to that of rTru.
α

Case rCn1: From our rule premises we know that ϕ & ψ −→ ϕ0 & ψ0 because
α

ϕ −→ ϕ0
α

ψ −→ ψ0

(C.4)
(C.5)

and
α

A =⇒ B
B, t v ϕ0 & ψ0

(C.6)
(C.7)

B, t v ϕ0
B, t v ψ0

(C.8)
(C.9)

By (C.7) and defn of v we know
or
By (C.4), (C.6), (C.8) and IH we know
A, αt v ϕ

(C.10)

A, αt v ψ

(C.11)

By (C.5), (C.6), (C.9) and IH we know

By (C.10), (C.11) and defn of v we know A, αt v ϕ & ψ as required.

165

C. Proofs for Auxiliary Lemmas
τ

Case rCn2: From our rule premises we know that ϕ & ψ −→ ϕ0 & ψ because
τ

ϕ −→ ϕ0

(C.12)

and
τ

A=
⇒B
B, t v ϕ0 & ψ

(C.13)
(C.14)

B, t v ϕ0
B, t v ψ

(C.15)
(C.16)

By (C.5) and defn of v we know
or

By (C.12), (C.13), (C.15) and IH we know
A, τt v ϕ

(C.17)

By (C.17) and the defn of v we know A, τt v ϕ & ψ as required.
Case rMax: From our rule premises we know
τ

max X. ϕ −→ ϕ[(max X. ϕ)/X]
τ

A=
⇒B
B, t v ϕ[(max X. ϕ)/X]

(C.18)
(C.19)
(C.20)

By (C.20) and defn of v we know
B, t v max X. ϕ

(C.21)

By (C.19) and (C.21) we know A, τt v max X. ϕ as required.
α

Case rNc1: From our rule premises we know [e]ϕ −→ ϕσ because
mtch(e, α) = σ

(C.22)

and
α

A =⇒ B
B, t v ϕσ

(C.23)
(C.24)

By (C.22), (C.23), (C.24) and defn of v we know A,ααt v [e]ϕ as required.
Case rNc2: From our rule premises we know [e]ϕ −→ tt because
mtch(e, α) = ⊥

166

(C.25)

C. Proofs for Auxiliary Lemmas

and
α

A =⇒ B
B, t v tt

(C.26)
(C.27)

Since by the defn of v we know that (C.27) can never be true, this means that we have


a contradiction.

C.2

Proving The Primary Soundness Auxiliary Lemmas

To Prove Lemma C.2.1. (System Reduction)
γ

Γ0 = after(Γ, γ) and wf(Γ, s) and s→
− s0 implies wf(Γ0 , s0 ).
γ

Proof. By Rule induction on s→
− s0 .
Case sNew: From our rule premises we know
τ

s→
− (s, i : ◦)
wf(Γ, s)
after(Γ, τ) = Γ0

(C.28)
(C.29)
(C.30)

By (C.29) and System Well-Formedness (Def. 5.3.1) we know
Γ( j) = lbid implies s( j) = •

(C.31)

By (C.30) and the defn of after (Def. 5.3.2) and since γ = τ we know that for any ϕ
Γ0 = Γ

(C.32)

Given that s ⊆ (s, i : ◦) and by (C.31) and (C.32) we know
Γ0 ( j) = lbid implies (s, i : ◦)( j) = •

(C.33)

By (C.33) and System Well-Formedness (Def. 5.3.1) we know
wf(Γ0 , (s, i : ◦))

(C.34)

∴ Case holds by (C.34).
Case sAct: From our rule premises we know
α

(s, i : ◦) →
− (s, i : ◦)

167

(C.35)

C. Proofs for Auxiliary Lemmas

because
subj(α) = i
ids(α) ⊆ dom (s, i : ◦)

(C.36)
(C.37)

wf(Γ, (s, i : ◦))
after(Γ, α) = Γ0

(C.38)
(C.39)

and

By (C.38) and System Well-Formedness (Def. 5.3.1) we know
Γ( j) = lbid implies (s, i : ◦)( j) = •

(C.40)

Since γ = α, by (C.39) and by defn of after (Def. 5.3.2) we know
Γ ⊆ Γ0

(C.41)

Recall that the type system is only able to change the type of a linear process id i : lid to
internal type lbid whenever a blk(i) command is type-checked, so as to signify that the
process reference should be blocked during runtime. This is because at runtime blk(i)
generates a b(i) event which causes process id i to block. This means that an α action
can neither directly introduce a process j : lbid in the new new type environment Γ0 , nor
block a process in state s. Hence, from (C.40) and (C.41) we can deduce
Γ0 ( j) = lbid implies (s, i : ◦)( j) = •

(C.42)

By (C.42) and System Well-Formedness (Def. 5.3.1) we know
wf(Γ0 , (s, i : ◦))

(C.43)

∴ Case holds by (C.43).
Case sBlk: From our rule premises we know
b(w )

(s, w : ◦) −−−→ (s, w : •)
wf(Γ, (s, w : ◦))
after(Γ, b(w )) = Γ0

(C.44)
(C.45)
(C.46)

By (C.46) the defn of after (Def. 5.3.2) and since γ = b(w ) we know
Γ = (Γ00 , w : lid)
Γ0 = (Γ00 , w : lbid)

(C.47)
(C.48)

By (C.45), (C.47) and System Well-Formedness (Def. 5.3.1) we know
(Γ00 , w : lid)( j) = lbid implies (s, w : ◦)( j) = •

168

(C.49)

C. Proofs for Auxiliary Lemmas

From (C.48), (C.49) and since by (C.44) we know that (s, w : ◦) becomes (s, w : •) we
can deduce
(Γ00 , w : lbid)( j) = lbid implies (s, w : •)( j) = •

(C.50)

By (C.48), (C.50) and System Well-Formedness (Def. 5.3.1) we know
wf(Γ0 , (s, w : •))

(C.51)

∴ Case holds by (C.51).
Case sRel: From our rule premises we know
r(w )

(s, w : •) −−→ (s, w : ◦)
wf(Γ, (s, w : •))
after(Γ, r(w )) = Γ0

(C.52)
(C.53)
(C.54)

By (C.54) the defn of after (Def. 5.3.2) and since γ = r(w ) we know
Γ = (Γ00 , w : lbid)
Γ0 = (Γ00 , w : lid)

(C.55)
(C.56)

By (C.53), (C.55) and System Well-Formedness (Def. 5.3.1) we know
(Γ00 , w : lbid)( j) = lbid implies (s, w : •)( j) = •

(C.57)

From (C.56), (C.57) and since by (C.52) we know that (s, w : •) becomes (s, w : ◦) we
can deduce
(Γ00 , w : lid)( j) = lbid implies (s, w : ◦)( j) = •

(C.58)

By (C.56), (C.58) and System Well-Formedness (Def. 5.3.1) we know
wf(Γ0 , (s, w : ◦))

(C.59)

∴ Case holds by (C.59).
Case sAdS: From our rule premises we know
s(w )

(s, w : •) −−−→ (s, w : •)
wf(Γ, (s, w : •))
after(Γ, s(w )) = Γ0

(C.60)
(C.61)
(C.62)

By (C.62), the defn of after (Def. 5.3.2) and since γ = s(w )
Γ0 = Γ

169

(C.63)

C. Proofs for Auxiliary Lemmas

By (C.61) and (C.63) we know
wf(Γ0 , (s, w : •))

(C.64)

∴ Case holds by (C.64).


Case sAdA: The proof for this case is analogous to that of case sAdS
To Prove Lemma C.2.2. (Monitor Reduction)
γ

Σ; Γ ` ϕ and hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i implies Σ; Γ0 ` ϕ0 and Γ0 = after(Γ, γ).
γ

Proof. By Rule induction on hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i.
Note: This is a continuation of the proof given in Sec. 5.3.5.
Case rTru: From our rule premises we know
τ

hΓ, ∆, if c then ϕ else ψi →
− hΓ, ∆, ϕi
(because c ⇓ true)
Σ; Γ ` if c then ϕ else ψ

(C.65)
(C.66)

By (C.66) and tIf we know
Σ; Γ ` ϕ
Σ; Γ ` ψ

(C.67)
(C.68)

By the defn of after (Def. 5.3.2) and since γ = τ we know
Γ = after(Γ, τ)

(C.69)

∴ Case holds by (C.67) and (C.69).
Note: The proof for cases rFls and rClr are analogous to the proof for case rTru.
Case rCn1: From our rule premises we know
α

hΓ, ∆, ϕ & ψi →
− h(Γ0 , Γ00 ), (∆0 ∪ ∆00 ), ϕ0 & ψ0 i

(C.70)

because
α

hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i
α

hΓ, ∆, ψi →
− hΓ00 , ∆00 , ψ0 i
dom(∆) = dom(∆0 ) ∩ dom(∆00 )

170

(C.71)
(C.72)
(C.73)

C. Proofs for Auxiliary Lemmas

and
Σ; Γ ` ϕ & ψ

(C.74)

As ϕ and ψ may either be exclusive or not, we must consider the following two subcases:
Subcase excl(ϕ, ψ) = (rϕ , rψ ): Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.74) and tCn2 we know
Σ; eff(Γ, rψ ) ` ϕ
Σ; eff(Γ, rϕ ) ` ψ

(C.75)
(C.76)

By (C.71), (C.75) and Lemma C.3.2 we know
α

heff(Γ, rψ ), ∆, ϕi →
− heff(after(Γ, α), rψ ), ∆0 , ϕ0 i
Γ0 = after(Γ, α)

(C.77)
(C.78)

By (C.72), (C.76) and Lemma C.3.2 we know
α

heff(Γ, rϕ ), ∆, ψi →
− heff(after(Γ, α), rϕ ), ∆00 , ψ0 i
Γ00 = after(Γ, α)

(C.79)
(C.80)

By (C.75), (C.77) and IH we know
Σ; eff(after(Γ, α), rψ ) ` ϕ0
after(eff(Γ, rψ ), α) = eff(after(Γ, α), rψ )

(C.81)
(C.82)

By (C.76), (C.79) and IH we know
Σ; eff(after(Γ, α), rϕ ) ` ψ0
after(eff(Γ, rϕ ), α) = eff(after(Γ, α), rϕ )

(C.83)
(C.84)

By (C.82), (C.84) and the defn of after (Def. 5.3.2) we know
eff(Γ, rψ ) ⊆ eff(after(Γ, α), rψ )
eff(Γ, rϕ ) ⊆ eff(after(Γ, α), rϕ )

(C.85)
(C.86)

Hence from (C.78), (C.80), (C.85) and (C.86) we can deduce
Γ ⊆ (Γ0 , Γ00 )

(C.87)

By (C.87) and the defn of after (Def. 5.3.2) we can deduce
after(Γ, α) = (Γ0 , Γ00 )

(C.88)

By (C.81), (C.83) and (C.88) we can deduce
Σ; eff((Γ0 , Γ00 ), rψ ) ` ϕ0
Σ; eff((Γ0 , Γ00 ), rϕ ) ` ψ0

171

(C.89)
(C.90)

C. Proofs for Auxiliary Lemmas

By (C.73) we know that we do not have any type incompatibilities, and since excl(ϕ, ψ) =
(rϕ , rψ ), by (C.71), (C.89), (C.90) and Lemma C.3.5 we know
Σ; (Γ0 , Γ00 ) ` ϕ0 & ψ0

(C.91)

∴ Subcase holds by (C.88) and (C.91).
Subcase excl(ϕ, ψ) = ⊥: Since excl(ϕ, ψ) = ⊥, by (C.74) and tCn1 we know
Γ = (Γ1 + Γ2 )
Σ; Γ1 ` ϕ
Σ; Γ2 ` ψ

(C.92)
(C.93)
(C.94)

By (C.71), (C.92), (C.93) and Lemma C.3.3 we know
α

hΓ1 , ∆, ϕi →
− hΓ01 , ∆0 , ϕ0 i
Γ0 = (Γ01 + Γ2 )

(C.95)
(C.96)

By (C.72), (C.92), (C.94) and Lemma C.3.3 we know
α

hΓ2 , ∆, ϕi →
− hΓ02 , ∆0 , ϕ0 i
Γ00 = (Γ1 + Γ02 )

(C.97)
(C.98)

By (C.93), (C.95) and IH we know
Σ; Γ01 ` ϕ0
Γ01 = after(Γ1 , α)

(C.99)
(C.100)

Σ; Γ02 ` ψ0
Γ02 = after(Γ2 , α)

(C.101)
(C.102)

By (C.94), (C.97) and IH we know

By (C.100), (C.102) and the defn of after (Def. 5.3.2) we know
Γ1 ⊆ Γ01
Γ2 ⊆ Γ02

(C.103)
(C.104)

Since by (C.73) we know that we do not have aliasing, then by (C.96), (C.98), (C.103)
and (C.104) we can deduce
(Γ0 , Γ00 ) = (Γ01 + Γ02 )

(C.105)

By (C.99), (C.101), (C.105) and Lemma C.3.1 we know
Σ; (Γ0 , Γ00 ) ` ϕ0 & ψ0

172

(C.106)

C. Proofs for Auxiliary Lemmas

By (C.92), (C.96), (C.98), (C.103) and (C.104) we can deduce
Γ ⊆ (Γ0 , Γ00 )

(C.107)

By (C.107) and the defn of after (Def. 5.3.2) we know
(Γ0 , Γ00 ) = after(Γ, α)

(C.108)

∴ Subcase holds by (C.106) and (C.108).
Case rCn2: From our rule premises we know
τ

hΓ, ∆, ϕ & ψi →
− hΓ, ∆0 , ϕ0 & ψi

(C.109)

because
τ

hΓ, ∆, ϕi →
− hΓ, ∆0 , ϕ0 i

(C.110)

Σ; Γ ` ϕ & ψ

(C.111)

and

As ϕ and ψ may either be exclusive or not, we must consider the following two subcases:
Subcase excl(ϕ, ψ) = (rϕ , rψ ): Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.111) and tCn2 we know
Σ; eff(Γ, rψ ) ` ϕ
Σ; eff(Γ, rϕ ) ` ψ

(C.112)
(C.113)

By (C.110), (C.112) and Lemma C.3.2 we know
τ

heff(Γ, rψ ), ∆, ϕi →
− heff(Γ, rψ ), ∆0 , ϕ0 i

(C.114)

By (C.112), (C.114) and IH we know
Σ; eff(Γ, rψ ) ` ϕ0

(C.115)

Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.114) and Lemma C.3.4 we can deduce
excl(ϕ0 , ψ) = (rϕ , rψ )

(C.116)

By (C.113), (C.115), (C.116) and tCn2 we know
Σ; Γ ` ϕ0 & ψ

(C.117)

By the defn of after (Def. 5.3.2) we know
Γ = after(Γ, τ)
∴ Subcase holds by (C.117) and (C.118).
173

(C.118)

C. Proofs for Auxiliary Lemmas

Subcase excl(ϕ, ψ) = ⊥: By (C.111) and tCn1 we know
Γ = (Γ1 + Γ2 )
Σ; Γ1 ` ϕ
Σ; Γ2 ` ψ

(C.119)
(C.120)
(C.121)

By (C.110), (C.119), (C.120) and Lemma C.3.3 we know
τ

hΓ1 , ∆, ϕi →
− hΓ1 , ∆0 , ϕ0 i

(C.122)

By (C.120), (C.122) and IH we know
Σ; Γ1 ` ϕ0
Γ1 = after(Γ1 , τ)

(C.123)
(C.124)

By (C.119), (C.121), (C.123) and Lemma C.3.1 we know
Σ; Γ ` ϕ0 & ψ

(C.125)

By the defn of after (Def. 5.3.2) we know
Γ = after(Γ, τ)

(C.126)

∴ Subcase holds by (C.125) and (C.126).
Case rCn3: From our rule premises we know
µ

hΓ, ∆, ϕ & ψi →
− hΓ0 , ∆, ϕ0 & ψi

(C.127)

because
µ

hΓ, ∆, ϕi →
− hΓ0 , ∆, ϕ0 i

(C.128)

Σ; Γ ` ϕ & ψ

(C.129)

and

As ϕ and ψ may either be exclusive or not, we must consider the following two subcases:
Subcase excl(ϕ, ψ) = (rϕ , rψ ): Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.129) and tCn2 we know
Σ; eff(Γ, rψ ) ` ϕ
Σ; eff(Γ, rϕ ) ` ψ

174

(C.130)
(C.131)

C. Proofs for Auxiliary Lemmas

By (C.128), (C.130) and Lemma C.3.2 we know
µ

heff(Γ, rψ ), ∆, ϕi →
− heff(after(Γ, µ), rψ ), ∆, ϕ0 i
Γ0 = after(Γ, µ)

(C.132)
(C.133)

By (C.130), (C.132) and IH we know
Σ; eff(after(Γ, µ), rψ ) ` ϕ0
after(eff(Γ, rψ ), µ) = eff(after(Γ, µ), rψ )

(C.134)
(C.135)

Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.128), (C.131), (C.134) and Lemma C.3.6 we know
Σ; after(Γ, µ) ` ϕ0 & ψ

(C.136)

∴ Subcase holds by (C.133) and (C.136).
Subcase excl(ϕ, ψ) = ⊥: By (C.129) and tCn1 we know
Γ = (Γ1 + Γ2 )
Σ; Γ1 ` ϕ
Σ; Γ2 ` ψ

(C.137)
(C.138)
(C.139)

By (C.128), (C.137), (C.138) and Lemma C.3.3 we know
µ

hΓ1 , ∆, ϕi →
− hΓ01 , ∆, ϕ0 i
Γ0 = (Γ01 + Γ2 )

(C.140)
(C.141)

By (C.138), (C.140) and IH we know
Σ; Γ01 ` ϕ0
Γ01 = after(Γ1 , µ)

(C.142)
(C.143)

By (C.139), (C.141), (C.142) and Lemma C.3.1 we know
Σ; Γ0 ` ϕ0 & ψ

(C.144)

By (C.137), (C.141) and (C.143) we deduce
(Γ01 + Γ2 = after(Γ1 , µ) + Γ2 ) ≡ (Γ0 = after(Γ, µ))

(C.145)

∴ Subcase holds by (C.144) and (C.145).
Case rRel: From our rule premises we know
r(w )

h(Γ, w : lbid), ∆, rel(w) ϕi −−→ h(Γ, w : lid), ∆, ϕi
Σ; (Γ, w : lbid) ` rel(w) ϕ
175

(C.146)
(C.147)

C. Proofs for Auxiliary Lemmas

By (C.147) and tRel we know
Σ; (Γ, w : lid) ` ϕ

(C.148)

By the defn of after (Def. 5.3.2) and since γ = r(w ) we know
after((Γ, w : lbid), r(w )) = (Γ, w : lid)

(C.149)

∴ Case holds by (C.148) and (C.149).
Case rBlk: From our rule premises we know
b(w )

h(Γ, w : lid), ∆, blk(w) ϕi −−−→ h(Γ, w : lbid), ∆, ϕi
Σ; (Γ, w : lid) ` blk(w) ϕ

(C.150)
(C.151)

By (C.151) and tBlk we know
Σ; (Γ, w : lbid) ` ϕ

(C.152)

By the defn of after (Def. 5.3.2) and since γ = b(w ) we know
after((Γ, w : lid), b(w )) = (Γ, w : lbid)

(C.153)

∴ Case holds by (C.152) and (C.153).
Case rAdA: From our rule premises we know
a(w )

hΓ, ∆, aA(w)r ϕi −−−→ hΓ, ∆, rel(r) ϕi
Σ; Γ ` aA(w)r ϕ

(C.154)
(C.155)

By (C.155) and tAdA we know
Γ = (Γ0 , w : lid)
Σ; Γ ` rel(r) ϕ

(C.156)
(C.157)

By the defn of after (Def. 5.3.2) and since γ = a(w ) we know
after(Γ, a(w )) = Γ

(C.158)

∴ Case holds by (C.157) and (C.158).
Case rAdS: The proof for this case is analogous to the proof of case rAdA.
Case rNc3: From our rule premises we know
α

hΓ, ∆, [eκ ] ρr ϕi →
− hΓ, ∆, rel(r) tti
176

(C.159)

C. Proofs for Auxiliary Lemmas

because
mtch(e, α) = ⊥

(C.160)

Σ; Γ ` [e] ρr ϕ

(C.161)

and

From (C.159) we know that [e] ρr ϕ reduces to
rel(r) tt

(C.162)

Since γ = α, by the defn of after (Def. 5.3.2) we know
Γ ⊆ after(Γ, α)

(C.163)

By (C.159) we know that Γ is not modified by the reduction, and hence from (C.163) we
can deduce
Γ = after(Γ, α)

(C.164)

As ρ ∈ {b, a} we must consider two subcases:
Subcase ρ = ‘b0 : By (C.161) and tNcB we know
Σ; Γ ` rel(r) tt

(C.165)

∴ Subcase holds by (C.164) and (C.165).
Subcase ρ = ‘a0 : The proof for this subcase is analogous to that of the previous subcase.

To Prove Lemma C.2.3. (Formula Substitution)
(Σ,{X 7→ Γ0 }); Γ ` ψ and Σ; Γ0 ` max X. ϕ implies (Σ,{X 7→ Γ0 }); Γ ` ψ[(clr(X )max X. ϕ)/X].
Proof. By rule induction on (Σ,{X 7→ Γ0 }); Γ ` ψ.
Case tFls: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` ff
Σ; Γ0 ` max X. ϕ

(C.166)
(C.167)

Since ff , X, by substitution we know
ff[(clr(X )max X. ϕ)/X] ≡ ff

(C.168)

By (C.166) and (C.168) we know
(Σ,{X 7→ Γ0 }); Γ ` ff[(clr(X )max X. ϕ)/X]
177

(C.169)

C. Proofs for Auxiliary Lemmas

∴ Case holds by (C.169).
Note: The proof for case tTru is analogous to the proof for case tFls.
Case tVar: We must consider two subcases, one where the formula variable Y is equal
to X (i.e.,, the substitution is applied) and another one where Y is not equal to X.
Subcase Y = X: From our premises we know
(Σ,{X 7→ Γ0 }); Γ ` X
Σ; Γ0 ` max X. ϕ

(C.170)
(C.171)

Since Y = X and by substitution we know
X[(clr(X )max X. ϕ)/X] ≡ (clr(X )max X. ϕ)

(C.172)

By (C.170) and tVar we know
Σ(X) ⊆ Γ ≡ Γ0 ⊆ Γ

(C.173)

By (C.171), (C.173) and transitivity we know
Σ; Γ ` max X. ϕ

(C.174)

Σ; Γ ` clr(X )max X. ϕ

(C.175)

By (C.174) and tClr we know

As we work up-to α-equivalence we know that Σ does not contain an entry for formula
variable X. In light of this, from (C.175) we can deduce
(Σ,{X 7→ Γ0 }); Γ ` clr(X )max X. ϕ

(C.176)

By (C.172) and (C.176) we know
(Σ,{X 7→ Γ0 }); Γ ` X[(clr(X )max X. ϕ)/X]

(C.177)

∴ Subcase holds by (C.177).
Subcase Y , X: From our premises we know
(Σ,{X 7→ Γ0 }); Γ ` Y
Σ; Γ0 ` max X. ϕ

(C.178)
(C.179)

Since Y , X, by substitution we know
Y[(clr(X )max X. ϕ)/X] ≡ Y

178

(C.180)

C. Proofs for Auxiliary Lemmas

By (C.178) and (C.180) we know
(Σ,{X 7→ Γ0 }); Γ ` Y[(clr(X )max X. ϕ)/X]

(C.181)

∴ Subcase holds by (C.181).
Case tMax: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` max Y. ψ

(C.182)

Σ,{X 7→ Γ0 , Y 7→ Γ}; Γ ` ψ

(C.183)

Σ; Γ0 ` max X. ϕ

(C.184)

because

and

By substitution we know
(max Y. ψ)[(clr(X )max X. ϕ)/X] ≡ max Y. (ψ[(clr(X )max X. ϕ)/X])

(C.185)

By (C.183), (C.184) and IH we know
Σ,{X 7→ Γ0 , Y 7→ Γ}; Γ ` ψ[(clr(X )max X. ϕ)/X]

(C.186)

By (C.186) and tMax we know
(Σ,{X 7→ Γ0 }); Γ ` max Y. (ψ[(clr(X )max X. ϕ)/X])

(C.187)

By (C.185) and (C.187) we know
(Σ,{X 7→ Γ0 }); Γ ` (max Y. ψ)[(clr(X )max X. ϕ)/X]

(C.188)

∴ Case holds by (C.188).
Case tClr: The proof for this case is analogous to that of case tMax
Case tAdS: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` sA(w)r ψ

(C.189)

Γ = Γ00 , w : lbid
(Σ,{X 7→ Γ0 }); Γ ` rel(r) ψ

(C.190)
(C.191)

because

179

C. Proofs for Auxiliary Lemmas

and
Σ; Γ0 ` max X. ϕ

(C.192)

By substitution we know
sA(w)r (ψ[(clr(X )max X. ϕ)/X]) ≡ (sA(w)r ψ)[(clr(X )max X. ϕ)/X]

(C.193)

By (C.191), (C.192) and IH we know
(Σ,{X 7→ Γ0 }); Γ ` rel(r) ψ[(clr(X )max X. ϕ)/X]

(C.194)

By (C.190), (C.194) and tAdS we know
(Σ,{X 7→ Γ0 }); Γ ` sA(w)r (ψ[(clr(X )max X. ϕ)/X])

(C.195)

By (C.193) and (C.195) we know
(Σ,{X 7→ Γ0 }); Γ ` (sA(w)r ψ)[(clr(X )max X. ϕ)/X]

(C.196)

∴ Case holds by (C.196).
Case tAdA: The proof for this case is analogous to the proof of cases tAdS.
Case tIf: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` if c then ψ1 else ψ2

(C.197)

(Σ,{X 7→ Γ0 }); Γ ` ψ1
(Σ,{X 7→ Γ0 }); Γ ` ψ2

(C.198)
(C.199)

Σ; Γ0 ` max X. ϕ

(C.200)

(if c then ψ1 else ψ2 )[(clr(X )max X. ϕ)/X]
if c then (ψ1 [(clr(X )max X. ϕ)/X]) else (ψ2 [(clr(X )max X. ϕ)/X])

(C.201)

because

and

By substitution we know
≡

By (C.198), (C.200) and IH we know
(Σ,{X 7→ Γ0 }); Γ ` ψ1 [(clr(X )max X. ϕ)/X]

(C.202)

By (C.199), (C.200) and IH we know
(Σ,{X 7→ Γ0 }); Γ ` ψ2 [(clr(X )max X. ϕ)/X]
180

(C.203)

C. Proofs for Auxiliary Lemmas

By (C.202), (C.203) and tIf we know
(Σ,{X 7→ Γ0 }); Γ ` if c then (ψ1 [(clr(X )max X. ϕ)/X]) else (ψ2 [(clr(X )max X. ϕ)/X])
(C.204)
By (C.201) and (C.204) we know
(Σ,{X 7→ Γ0 }); Γ ` (if c then ψ1 else ψ2 )[(clr(X )max X. ϕ)/X]

(C.205)

∴ Case holds by (C.205).
Case tCn1: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` ψ1 & ψ2

(C.206)

Γ = (Γ1 + Γ2 )
(Σ,{X 7→ Γ0 }); Γ1 ` ψ1
(Σ,{X 7→ Γ0 }); Γ2 ` ψ2
excl(ϕ, ψ) = ⊥

(C.207)
(C.208)
(C.209)
(C.210)

Σ; Γ0 ` max X. ϕ

(C.211)

because

and

By substitution we know
(ψ1 & ψ2 )[(clr(X )max X. ϕ)/X] ≡ (ψ1 [(clr(X )max X. ϕ)/X]) & (ψ2 [(clr(X )max X. ϕ)/X])
(C.212)
By (C.208), (C.211) and IH we know
(Σ,{X 7→ Γ0 }); Γ1 ` ψ1 [(clr(X )max X. ϕ)/X]

(C.213)

By (C.209), (C.211) and IH we know
(Σ,{X 7→ Γ0 }); Γ2 ` ψ2 [(clr(X )max X. ϕ)/X]

(C.214)

By (C.207), (C.213), (C.214) and Lemma C.3.1 we know
(Σ,{X 7→ Γ0 }); (Γ1 + Γ2 ) ` (ψ1 [(clr(X )max X. ϕ)/X]) & (ψ2 [(clr(X )max X. ϕ)/X]) (C.215)
By (C.207), (C.212) and (C.215) we know
(Σ,{X 7→ Γ0 }); Γ ` (ψ1 & ψ2 )[(clr(X )max X. ϕ)/X]
∴ Case holds by (C.216).

181

(C.216)

C. Proofs for Auxiliary Lemmas

Case tCn2: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` ψ1 & ψ2

(C.217)

excl(ψ1 , ψ2 ) = (r1 , r2 )

(Σ,{X 7→ Γ }); eff(Γ, r2 ) ` ψ1
(Σ,{X 7→ Γ0 }); eff(Γ, r1 ) ` ψ2

(C.218)
(C.219)
(C.220)

Σ; Γ0 ` max X. ϕ

(C.221)

because
0

and

By substitution we know
(ψ1 & ψ2 )[(clr(X )max X. ϕ)/X] ≡ (ψ1 [(clr(X )max X. ϕ)/X]) & (ψ2 [(clr(X )max X. ϕ)/X])
(C.222)
By (C.219), (C.221) and IH we know
(Σ,{X 7→ Γ0 }); eff(Γ, r2 ) ` ψ1 [(clr(X )max X. ϕ)/X]

(C.223)

By (C.220), (C.221) and IH we know
(Σ,{X 7→ Γ0 }); eff(Γ, r1 ) ` ψ2 [(clr(X )max X. ϕ)/X]

(C.224)

Since [(clr(X )max X. ϕ)/X], by (C.218) and Lemma C.3.7 we know
excl(ψ1 [(clr(X )max X. ϕ)/X], ψ2 [(clr(X )max X. ϕ)/X]) = (r1 , r2 )

(C.225)

By (C.223), (C.224), (C.225) and tCn2 we know
(Σ,{X 7→ Γ0 }); Γ ` (ψ1 [(clr(X )max X. ϕ)/X]) & (ψ2 [(clr(X )max X. ϕ)/X])

(C.226)

From (C.222) and (C.226) we can deduce
(Σ,{X 7→ Γ0 }); Γ ` (ψ1 & ψ2 )[(clr(X )max X. ϕ)/X]

(C.227)

∴ Case holds by (C.227).
Case tNcB: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` [e] br ψ

(C.228)

subj(e) = l

(C.229)
(C.230)
(C.231)

because
(Σ,{X 7→ Γ }); ((Γ, bnd(e))) ` blk(l) ψ
Σ; Γ ` rel(r) tt
0

182

C. Proofs for Auxiliary Lemmas

and
Σ; Γ0 ` max X. ϕ

(C.232)

By substitution we know
([e] br ψ)[(clr(X )max X. ϕ)/X] ≡ [e] br (ψ[(clr(X )max X. ϕ)/X])

(C.233)

By (C.230), (C.232) and IH we know
(Σ,{X 7→ Γ0 }); (Γ, bnd(e)) ` (blk(l) ψ)[(clr(X )max X. ϕ)/X]

(C.234)

By substitution we also know
(blk(w) ψ)[(clr(X )max X. ϕ)/X] ≡ blk(l) (ψ[(clr(X )max X. ϕ)/X])

(C.235)

By (C.234) and (C.235) we know
(Σ,{X 7→ Γ0 }); (Γ, bnd(e)) ` blk(l) (ψ[(clr(X )max X. ϕ)/X])

(C.236)

By (C.229), (C.231), (C.236) tNcB we know
(Σ,{X 7→ Γ0 }); (Γ, bnd(e)) ` [e] br (ψ[(clr(X )max X. ϕ)/X])

(C.237)

By (C.233) and (C.237) we know
(Σ,{X 7→ Γ0 }); (Γ, bnd(e)) ` ([e] br ψ)[(clr(X )max X. ϕ)/X]

(C.238)

∴ Case holds by (C.238).
Case tNcA: The proof for this case is analogous to that of case tNcB.
Case tRel: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` rel(w) ψ

(C.239)

Γ = (Γ00 , w : lbid)
(Σ,{X 7→ Γ0 }); Γ00 , w : lid ` ψ

(C.240)
(C.241)

Σ; Γ0 ` max X. ϕ

(C.242)

because

and

By substitution we know
rel(w) (ψ[(clr(X )max X. ϕ)/X]) ≡ (rel(w) ψ)[(clr(X )max X. ϕ)/X]

183

(C.243)

C. Proofs for Auxiliary Lemmas

By (C.241), (C.242) and IH we know
(Σ,{X 7→ Γ0 }); Γ00 , w : lid ` ψ[(clr(X )max X. ϕ)/X]

(C.244)

By (C.240), (C.244) and tRel we know
(Σ,{X 7→ Γ0 }); Γ ` rel(w) (ψ[(clr(X )max X. ϕ)/X])

(C.245)

By (C.243) and (C.245) we know
(Σ,{X 7→ Γ0 }); Γ ` (rel(w) ψ)[(clr(X )max X. ϕ)/X]

(C.246)

∴ Case holds by (C.246).
Case tBlk: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` blk(w) ψ

(C.247)

Γ = (Γ00 , w : lid)
(Σ,{X 7→ Γ0 }); Γ00 , w : lbid ` ψ

(C.248)
(C.249)

Σ; Γ0 ` max X. ϕ

(C.250)

because

and

By substitution we know
(blk(w) ψ)[(clr(X )max X. ϕ)/X] ≡ blk(w) (ψ[(clr(X )max X. ϕ)/X])

(C.251)

By (C.249), (C.250) and IH we know
(Σ,{X 7→ Γ0 }); Γ00 , w : lbid ` ψ[(clr(X )max X. ϕ)/X]

(C.252)

By (C.248), (C.252) and tBlk we know
(Σ,{X 7→ Γ0 }); Γ ` blk(w) (ψ[(clr(X )max X. ϕ)/X])

(C.253)

By (C.251) and (C.253) we know
(Σ,{X 7→ Γ0 }); Γ ` (blk(w) ψ)[(clr(X )max X. ϕ)/X]
∴ Case holds by (C.254).

(C.254)


To Prove Lemma C.2.4. (Weakening)
(Σ,{X 7→ Γ0 }); Γ ` ϕ and X < fv(ϕ) implies Σ; Γ ` ϕ.
Proof. By rule induction on (Σ,{X 7→ Γ0 }); Γ ` ϕ.
184

C. Proofs for Auxiliary Lemmas

Case tNcA: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` [e] ar ϕ

(C.255)

(Σ,{X 7→ Γ0 }); (Γ, bnd(e)) ` ϕ
(Σ,{X 7→ Γ0 }); Γ ` rel(r) tt

(C.256)
(C.257)

X < fv([e] ar ϕ)

(C.258)

X < fv(ϕ)

(C.259)

because

and

From (C.258) we can deduce

Since formula variable X does not feature in formula rel(r) tt we can therefore deduce
X < fv(rel(r) tt)

(C.260)

By (C.256), (C.259) and IH we know
Σ; (Γ, bnd(e)) ` ϕ

(C.261)

By (C.257), (C.260) and IH we know
Σ; Γ ` rel(r) tt

(C.262)

By (C.261), (C.262) and tNcA we know
Σ; Γ ` [e] ar ϕ

(C.263)

∴ Case holds by (C.263).
Case tNcB: The proof for this case is analogous to that of case tNcA.
Case tFls: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` ff
X < fv(ff)

(C.264)
(C.265)

Since (C.264) holds (typechecks) regardless of the contents of the formula environment
Σ we can conclude Σ; Γ ` ff as required.
Case tTru: The proof for this case is analogous to that of case tFls.
185

C. Proofs for Auxiliary Lemmas

Case tIf: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` if b then ϕ else ψ

(C.266)

(Σ,{X 7→ Γ0 }); Γ ` ϕ
(Σ,{X 7→ Γ0 }); Γ ` ψ

(C.267)
(C.268)

X < fv(if b then ϕ else ψ)

(C.269)

X < fv(ϕ)
X < fv(ψ)

(C.270)
(C.271)

Σ; Γ ` ϕ

(C.272)

Σ; Γ ` ψ

(C.273)

Σ; Γ ` if b then ϕ else ψ

(C.274)

because

and

From (C.269) we can deduce

By (C.267), (C.270) and IH we know

By (C.268), (C.271) and IH we know

By (C.272), (C.273) and tIf we know

∴ Case holds by (C.274).
Case tCn1 and tCn2: The proofs for these cases are analogous to that of case tIf.
Case tBlk: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` blk(w) ϕ

(C.275)

(Σ,{X 7→ Γ0 }); (Γ00 , w : lbid) ` ϕ
Γ = Γ00 , w : lid

(C.276)
(C.277)

X < fv(blk(w) ϕ)

(C.278)

because

and

186

C. Proofs for Auxiliary Lemmas

From (C.278) we can deduce
X < fv(ϕ)

(C.279)

Σ; (Γ00 , w : lbid) ` ϕ

(C.280)

By (C.276), (C.279) and IH we know

By (C.277), (C.280) and tBlk we know
Σ; Γ ` blk(w) ϕ

(C.281)

∴ Case holds by (C.281).
Case tRel, tAdA and tAdS: The proofs for these cases are analogous to that of case
tBlk.
Case tMax: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` max Y. ϕ

(C.282)

(Σ,{X 7→ Γ0 , Y 7→ Γ}); Γ0 ` ϕ

(C.283)

X < fv(max Y. ϕ)

(C.284)

X < fv(ϕ)

(C.285)

(Σ,{Y 7→ Γ}); Γ ` ϕ

(C.286)

Σ; Γ ` max Y. ϕ

(C.287)

because

and

From (C.284) we can deduce

By (C.283), (C.285) and IH we know

By (C.286) and tMax we know

∴ Case holds by (C.287).
Case tClr: The proofs for this case is analogous to that of case tMax.

187

C. Proofs for Auxiliary Lemmas

Case tVar: From our rule premises we know
(Σ,{X 7→ Γ0 }); Γ ` Y

(C.288)

(Σ,{X 7→ Γ0 })(Y) ⊆ Γ

(C.289)

X < fv(Y)

(C.290)

because

and

We now consider two subcases:
Subcase Y , X: Since Y , X we know that (C.289) still holds if we remove entry
X 7→ Γ0 from the formula environment, and hence we deduce
Σ(Y) ⊆ Γ

(C.291)

Hence by (C.291) and tVar we can conclude Σ; Γ ` Y as required.
Subcase Y = X: Since Y = X we know that (C.290) cannot ever be true. Hence this case


holds by contradiction.
To Prove Lemma C.2.5. (Term Substitution)
Σ; (Γ, bnd(e)) ` ϕ and mtch(e, α) = σ and dom(∆) ∩ dom(bnd(e)σ) = ∅
and ∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ implies Σ; (Γ, bnd(e)σ) ` ϕσ
Proof. By rule induction on Σ; (Γ, bnd(e)σ) ` ϕ.
Case tFls: From our rule premises we know
Σ; (Γ, bnd(e)) ` ff
mtch(e, α) = σ
dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ

(C.292)
(C.293)
(C.294)
(C.295)

Since ff has no term variables that can be substituted, we know
ffσ ≡ ff

(C.296)

By (C.292), (C.296) and given that ff typechecks with any type environment Γ we can
conclude
Σ; (Γ, bnd(e)σ) ` ffσ

188

(C.297)

C. Proofs for Auxiliary Lemmas

∴ Case holds by (C.297).
Note: The proof for cases tTru and tVar are analogous to the proof for case tFls.
Case tMax: From our rule premises we know
Σ; (Γ, bnd(e)) ` max X. ϕ

(C.298)

Σ; (Γ, bnd(e)) ` ϕ

(C.299)

mtch(e, α) = σ

(C.300)
(C.301)
(C.302)

because

and
dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ
By (C.299), (C.300), (C.301), (C.302) and IH we know
Σ; (Γ, bnd(e)σ) ` (ϕσ)

(C.303)

Σ; (Γ, bnd(e)σ) ` max X. (ϕσ)

(C.304)

By (C.303) and tMax we know

By (C.304) and data substitution we know
Σ; (Γ, bnd(e)σ) ` (max X. ϕ)σ

(C.305)

∴ Case holds by (C.305).
Case tClr: The proof for this case is analogous to that of case tMax
Case tAdS: From our rule premises we know
Σ; (Γ, bnd(e)) ` sA(w)r ϕ

(C.306)

Γ, bnd(e) = Γ0 , w : lbid
Σ; (Γ, bnd(e)) ` rel(r) ϕ

(C.307)
(C.308)

because

189

C. Proofs for Auxiliary Lemmas

and
mtch(e, α) = σ

dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ

(C.309)
(C.310)
(C.311)

By (C.308), (C.309), (C.310), (C.311) and IH we know
Σ; (Γ, bnd(e)σ) ` (rel(r) ϕ)σ

(C.312)

By (C.310) and (C.311) we know that we do not have any aliasing, hence we can safely
apply substitution σ on both sides of equation (C.307), such that we know
Γ, bnd(e)σ = (Γ0 , w : lbid)σ

(C.313)

By (C.312), (C.313) and tAdS we know
Σ; (Γ, bnd(e)σ) ` sA(w)r (ϕσ)

(C.314)

By (C.314) and data substitution we know
Σ; (Γ, bnd(e)σ) ` (sA(w)r ϕ)σ

(C.315)

∴ Case holds by (C.315).
Case tAdA: The proof for this case is very similar to the proof of cases tAdS.
Case tIf: From our rule premises we know
Σ; (Γ, bnd(e)) ` if c then ϕ1 else ϕ2

(C.316)

Σ; (Γ, bnd(e)) ` ϕ1
Σ; (Γ, bnd(e)) ` ϕ2

(C.317)
(C.318)

mtch(e, α) = σ

(C.319)
(C.320)
(C.321)

because

and
dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ
By (C.317), (C.319), (C.320), (C.321) and IH we know
Σ; (Γ, bnd(e)σ) ` ϕ1 σ

190

(C.322)

C. Proofs for Auxiliary Lemmas

By (C.318), (C.319), (C.320), (C.321) and IH we know
Σ; (Γ, bnd(e)σ) ` ϕ2 σ

(C.323)

By (C.322), (C.323) and tIf we know
Σ; (Γ, bnd(e)σ) ` if cσ then (ϕ1 σ) else (ϕ2 σ)

(C.324)

By (C.324) and data substitution we know
Σ; (Γ, bnd(e)σ) ` (if c then ϕ1 else ϕ2 )σ

(C.325)

∴ Case holds by (C.325).
Case tCn1: From our rule premises we know
Σ; (Γ, bnd(e)) ` ϕ1 & ϕ2

(C.326)

Γ, bnd(e) = (Γ1 + Γ2 )
Σ; Γ1 ` ϕ1
Σ; Γ2 ` ϕ2
excl(ϕ, ψ) = ⊥

(C.327)
(C.328)
(C.329)
(C.330)

mtch(e, α) = σ

(C.331)
(C.332)
(C.333)

because

and
dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ
By (C.328), (C.331), (C.332), (C.333) and IH we know
Σ; (Γ1 σ) ` ϕ1 σ

(C.334)

By (C.329), (C.331), (C.332), (C.333) and IH we know
Σ; (Γ2 σ) ` ϕ2 σ

(C.335)

By (C.332) and (C.333) we know that we do not have any aliasing, hence we can safely
apply substitution σ on both sides of equation (C.327), such that we know
Γ, bnd(e)σ = (Γ1 + Γ2 )σ

(C.336)

By (C.334), (C.335), (C.336) and Lemma C.3.1 we know
Σ; (Γ1 + Γ2 )σ ` (ϕ1 σ) & (ϕ2 σ)

191

(C.337)

C. Proofs for Auxiliary Lemmas

By (C.327), (C.337) and data substitution we know
Σ; (Γ, bnd(e))σ ` (ϕ1 & ϕ2 )σ

(C.338)

∴ Case holds by (C.338).
Case tCn2: From our rule premises we know
Σ; (Γ, bnd(e)) ` ϕ1 & ϕ2

(C.339)

excl(ψ1 , ψ2 ) = (r1 , r2 )

(C.340)
(C.341)
(C.342)

because
Σ; eff((Γ, bnd(e)), r2 ) ` ϕ1
Σ; eff((Γ, bnd(e)), r1 ) ` ϕ2
and
mtch(e, α) = σ

dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ

(C.343)
(C.344)
(C.345)

As by (C.340) we know that patterns ψ1 and ψ2 are exclusive, if we add more information
by applying substitution (C.343), we do not affect their mutual exclusion. Moreover, the
substitution can safely be applied as by (C.344) and (C.345) we know that we do not
have aliasing. Hence we can deduce
excl(ϕ1 σ, ϕ2 σ) = (r1 σ, r2 σ)

(C.346)

By (C.341), (C.343), (C.344), (C.345) and IH we know
Σ; eff(Γ, bnd(e), r2 )σ ` ϕ1

(C.347)

By (C.342), (C.343), (C.344), (C.345) and IH we know
Σ; eff(Γ, bnd(e), r1 )σ ` ϕ2

(C.348)

By (C.346), (C.347), (C.348) and tCn2 we know
Σ; (Γ, bnd(e)σ) ` (ϕ1 σ) & (ϕ2 σ)

(C.349)

By (C.349) and substitution we know
Σ; (Γ, bnd(e)σ) ` (ϕ1 & ϕ2 )σ
∴ Case holds by (C.350).

192

(C.350)

C. Proofs for Auxiliary Lemmas

Case tNcB: From our rule premises we know
Σ; (Γ, bnd(e)) ` [d] br ϕ

(C.351)

subj(d) = l

(C.352)
(C.353)
(C.354)

because
Σ; ((Γ, bnd(e)), bnd(d)) ` blk(l) ϕ
Σ; (Γ, bnd(e)) ` rel(r) tt
and
mtch(e, α) = σ

dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ

(C.355)
(C.356)
(C.357)

By (C.356) and (C.357) we know that we do not have any aliasing, hence we can safely
apply substitution σ on both sides of equation (C.352), such that we know
subj(dσ) = lσ

(C.358)

By (C.353), (C.355), (C.356), (C.357) and IH we know
≡

Σ; ((Γ, bnd(e)), bnd(d))σ ` (blk(l) ϕ)σ
Σ; ((Γ, bnd(e)σ), bnd(d)σ) ` blk(lσ) (ϕσ)

(C.359)

By (C.354), (C.355), (C.356), (C.357) and IH we know
Σ; (Γ, bnd(e)σ) ` rel(rσ) tt

(C.360)

By (C.358), (C.359), (C.360) and tNcB we know
Σ; (Γ, bnd(e)σ) ` [d] br σ (ϕσ)

(C.361)

By (C.361) and data substitution we know
Σ; (Γ, bnd(e)σ) ` ([d] br ϕ)σ

(C.362)

∴ Case holds by (C.362).
Case tNcA: The proof for this case is analogous to that of case tNcB.
Case tBlk: From our rule premises we know
Σ; (Γ, bnd(e)) ` blk(w) ϕ

193

(C.363)

C. Proofs for Auxiliary Lemmas

because
Γ, bnd(e) = Γ0 , w : lid
Σ; (Γ0 , w : lbid) ` ϕ

(C.364)
(C.365)

mtch(e, α) = σ

(C.366)
(C.367)
(C.368)

and
dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ
By (C.365), (C.366), (C.367), (C.368) and IH we know
Σ; (Γ0 , w : lbid)σ ` ϕσ

(C.369)

By (C.367) and (C.368) we know that we do not have any aliasing, hence we can safely
apply substitution σ on both sides of equation (C.364), such that we know
Γ, bnd(e)σ = (Γ0 , w : lid)σ

(C.370)

By (C.369), (C.370) and tBlk we know
Σ; (Γ, bnd(e)σ) ` blk(w) (ϕσ)

(C.371)

By (C.371) and data substitution we know
Σ; (Γ, bnd(e)σ) ` (blk(w) ϕ)σ

(C.372)

∴ Case holds by (C.372).
Case tRel: The proof for this case is analogous to that of case tBlk.

C.3



Proving The Secondary Soundness Auxiliary
Lemmas

To Prove Lemma C.3.1. Σ; Γ1 ` ϕ and Σ; Γ2 ` ψ and Γ = Γ1 + Γ2 implies Σ; Γ ` ϕ & ψ.
Proof. From our premises we know
Σ; Γ1 ` ϕ
Σ; Γ2 ` ψ
Γ = Γ1 + Γ2

194

(C.373)
(C.374)
(C.375)

C. Proofs for Auxiliary Lemmas

We consider two cases:
Case excl(ϕ, ψ) = ⊥: By (C.373), (C.374), (C.375) and tCn1 we know
Σ; Γ ` ϕ & ψ

(C.376)

∴ Case holds by (C.376).
Case excl(ϕ, ψ) = (rϕ , rψ ): By (C.373) and since excl(ϕ, ψ) = (rϕ , rψ ) we can deduce that
since rϕ represents a set of processes that may be released by ϕ in case this is trivially
satisfied, then we know that these processes are linear typed. Hence since by (C.375) we
know that Γ1 and Γ2 do not share linear typed references we can conclude
∀l ∈ rϕ · l : lin ∈ Γ1 ∧ l : lin < Γ2 (where lin = lid or lbid)

(C.377)

Same argument applies for (C.374), such that we know
∀l ∈ rψ · l : lin ∈ Γ2 ∧ l : lin < Γ1

(C.378)

Hence by (C.373), (C.375), (C.378) and defn of eff (Def. 5.1.1) we know that the sideeffects of ψ (i.e., when the processes in rψ are released) do not interfere with ϕ, as these
branches do not share linear variables, such that we can deduce
Σ; eff((Γ1 + Γ2 ), rψ ) ` ϕ ≡ Σ; eff(Γ, rψ ) ` ϕ

(C.379)

Similarly by (C.374), (C.375), (C.377) and defn of eff (Def. 5.1.1) we can deduce
Σ; eff((Γ1 + Γ2 ), rϕ ) ` ψ ≡ Σ; eff(Γ, rϕ ) ` ψ

(C.380)

Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.379), (C.380) and tCn2 we know
Σ; Γ ` ϕ & ψ

(C.381)

∴ Case holds by (C.381).


γ

To Prove Lemma C.3.2. hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i and Σ; eff(Γ, r ) ` ϕ implies
γ

heff(Γ, r ), ∆, ϕi →
− heff(after(Γ, γ), r ), ∆0 , ϕ0 i and Γ0 = after(Γ, γ)
γ

Proof. By rule induction on hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i.
Case rTru: From our rule premises we know
τ

hΓ, ∆, if b then ϕ else ψi →
− hΓ, ∆, ϕi
Σ; eff(Γ, r ) ` if b then ϕ else ψ

(C.382)
(C.383)

Since γ = τ and by defn of after (Def. 5.3.2) we know
Γ0 = after(Γ, τ)
195

(C.384)

C. Proofs for Auxiliary Lemmas

By (C.383) and tIf we know
Σ; eff(Γ, r ) ` ϕ

(C.385)

Since reduction (C.382) can be performed regardless of the contents of Γ and by (C.383),
(C.384) and (C.385) we can deduce
τ

heff(Γ, r ), ∆, if b then ϕ else ψi →
− heff(after(Γ, τ), r ), ∆, ϕi

(C.386)

∴ Case holds by (C.384) and (C.386).
Note: The proofs for cases rFls, rClr and rMax are analogous to the proof of case
rTru.
Case rRel: From our rule premises we know
r(w )

h(Γ, w : lbid), ∆, rel(w) ϕi −−→ h(Γ, w : lid), ∆, ϕi
Σ; eff((Γ, w : lbid), r ) ` rel(w) ϕ

(C.387)
(C.388)

Since γ = r(w ) and by defn of after (Def. 5.3.2) we know
(Γ, w : lid) = after((Γ, w : lbid), r(w ))

(C.389)

By (C.388) and tRel we know
Σ; eff((Γ, w : lid), r ) ` ϕ

(C.390)

By (C.388) we know that even if we apply the eff function on environment (Γ, w : lbid),
reduction (C.387) can still be performed. Hence by (C.390) and the defn of after
(Def. 5.3.2) we can deduce
r(w )

heff((Γ, w : lbid), r ), ∆, rel(w) ϕi −−→ heff((after(Γ, w : lbid), r(w )), r ), ∆, ϕi

(C.391)

∴ Case holds by (C.389) and (C.391).
Note: The proofs for cases rBlk, rAdA and rAdS are analogous to the proof of case
rRel.
Case rNc1: From our rule premises we know
α

hΓ, ∆, [eκ ] bw ϕi →
− h(Γ, Γ0 ), ∆0 , blk(i) ϕσi

196

(C.392)

C. Proofs for Auxiliary Lemmas

because
mtch(e, α) = σ

Γ0 = bnd(e)σ
dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ

(C.393)
(C.394)
(C.395)
(C.396)
(C.397)

Σ; eff(Γ, r ) ` [e] bw ϕ

(C.398)

subj(α) = i

and

Since γ = α, Γ ⊆ (Γ, Γ0 ) and by defn of after (Def. 5.3.2) we know
(Γ, Γ0 ) = after(Γ, α)

(C.399)

subj(e) = l

(C.400)
(C.401)

By (C.398) and tNcB we know
Σ; (eff(Γ, r ), bnd(e)) ` blk(l) ϕ

Since newly bound variables cannot be of the type lbid, then by the defn of eff (Def. 5.1.1)
we know
eff(bnd(e), r ) ≡ bnd(e)

(C.402)

Hence by (C.401) and (C.402) we know
Σ; (eff((Γ, bnd(e)), r )) ` blk(l) ϕ

(C.403)

By (C.394) and (C.400) we know that lσ = i, hence by (C.393), (C.396), (C.397),
(C.403) and Lemma C.2.5 (Term Substitution) we know
Σ; (eff((Γ, bnd(e)σ), r )) ` blk(i) ϕσ

(C.404)

By (C.398) and (C.403) we know that even if we apply the eff function on environment
Γ, reduction (C.392) can still be performed, as by (C.404) we know that it reduces into a
form which also typechecks. Hence by (C.404) and the defn of after (Def. 5.3.2) we can
deduce
α

heff(Γ, r ), ∆, [eκ ] bw ϕi →
− heff(after(Γ, α), r ), ∆, blk(i) ϕσi
∴ Case holds by (C.399) and (C.405).
Case rNc2: The proof for this case is analogous to the proof for case rNc1.

197

(C.405)

C. Proofs for Auxiliary Lemmas

Case rNc3: From our rule premises we know
α

hΓ, ∆, [eκ ] ρw ϕi →
− hΓ, ∆, rel(w) tti

(C.406)

mtch(e, α) = ⊥

(C.407)

Σ; eff(Γ, r ) ` [e] ρw ϕ

(C.408)

because

and

Since γ = α, Γ ⊆ Γ and by defn of after (Def. 5.3.2) we know
Γ = after(Γ, α)

(C.409)

Since ρ ∈ {b, a} we must consider two subcases:
Subcase ρ = b: By (C.408) and tNcB we know
Σ; eff(Γ, r ) ` rel(w) tt

(C.410)

By (C.408) and (C.410) we know that reduction (C.406) can still be performed regardless
of the contents of Γ, hence by (C.409) we can deduce
α

heff(Γ, r ), ∆, [eκ ] ρw ϕi →
− heff(Γ, r ), ∆, rel(w) tti

(C.411)

∴ Subcase holds by (C.409) and (C.411).
Subcase ρ = a: The proof for this subcase is analogous to that of the previous subcase
(i.e., ρ = b).
Case rCn1: From our rule premises we know
α

hΓ, ∆, ϕ & ψi →
− h(Γ0 , Γ00 ), ∆0 ∪ ∆00 , ϕ0 & ψ0 i

(C.412)

because
α

hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i
α

(C.413)

hΓ, ∆, ψi →
− hΓ00 , ∆00 , ψ0 i
dom(∆) = dom(∆0 ) ∩ dom(∆00 )

(C.414)
(C.415)

Σ; eff(Γ, r ) ` ϕ & ψ

(C.416)

and

We must consider two subcases:

198

C. Proofs for Auxiliary Lemmas

Subcase excl(ϕ, ψ) = (rϕ , rψ ): Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.416) and tCn2 we know
Σ; eff(Γ, rrψ ) ` ϕ
Σ; eff(Γ, rrϕ ) ` ψ

(C.417)
(C.418)

By (C.413), (C.417) and IH we know
α

heff(Γ, rrψ ), ∆, ϕi →
− heff(after(Γ, α), rrψ ), ∆0 , ϕ0 i
Γ0 = after(Γ, α)

(C.419)
(C.420)

By (C.414), (C.418) and IH we know
α

heff(Γ, rrϕ ), ∆, ψi →
− heff(after(Γ, α), rrϕ ), ∆00 , ψ0 i
Γ00 = after(Γ, α)

(C.421)
(C.422)

By (C.413) and (C.419) we know that formula ϕ can reduce into ϕ0 regardless of whether
we apply the side-effects or not. Hence we can deduce
α

heff(Γ, r ), ∆, ϕi →
− heff(after(Γ, α), r ), ∆0 , ϕ0 i

(C.423)

The same argument applies by (C.414) and (C.421), such that we know we know
α

heff(Γ, r ), ∆, ψi →
− heff(after(Γ, α), r ), ∆00 , ψ0 i

(C.424)

By (C.420), (C.422) and the defn of after (Def. 5.3.2) we know
Γ ⊆ Γ0
Γ ⊆ Γ00

(C.425)
(C.426)

Γ ⊆ (Γ0 , Γ00 )

(C.427)

By (C.425) and (C.426) we know

By (C.427) and and the defn of after (Def. 5.3.2) we know
(Γ0 , Γ00 ) = after(Γ, α)

(C.428)

By (C.415), (C.423), (C.424), (C.428) and rCn1 we know
α

heff(Γ, r ), ∆, ϕ & ψi →
− heff(after(Γ, α), r ), ∆0 ∪ ∆00 , ϕ0 & ψ0 i

(C.429)

∴ Subcase holds by (C.428) and (C.429).
Subcase excl(ϕ, ψ) = ⊥: Since excl(ϕ, ψ) = ⊥, by (C.416) and tCn1 we know
Σ; eff(Γ1 , r ) ` ϕ
Σ; eff(Γ2 , r ) ` ψ
eff(Γ, r ) = eff(Γ1 , r ) + eff(Γ2 , r )
199

(C.430)
(C.431)
(C.432)

C. Proofs for Auxiliary Lemmas

Since by (C.432) we know eff(Γ1 , r ) ⊆ eff(Γ, r ) and eff(Γ2 , r ) ⊆ eff(Γ, r ), then by (C.430)
and (C.431) we can deduce
Σ; eff(Γ, r ) ` ϕ
Σ; eff(Γ, r ) ` ψ

(C.433)
(C.434)

By (C.413), (C.433) and IH we know
α

heff(Γ, r ), ∆, ϕi →
− heff(after(Γ, α), r ), ∆0 , ϕ0 i
Γ0 = after(Γ, α)

(C.435)
(C.436)

By (C.414), (C.434) and IH we know
α

heff(Γ, r ), ∆, ϕi →
− heff(after(Γ, α), r ), ∆00 , ψ0 i
Γ00 = after(Γ, α)

(C.437)
(C.438)

By (C.436), (C.438) and the defn of after (Def. 5.3.2) we know
(Γ0 , Γ00 ) = after(Γ, α)

(C.439)

By (C.415), (C.435), (C.437), (C.439) and rCn1 we know
α

heff(Γ, r ), ∆, ϕ & ψi →
− heff(after(Γ, α), r ), ∆0 ∪ ∆00 , ϕ0 & ψ0 i

(C.440)

∴ Subcase holds by (C.439) and (C.440).
Case rCn2: From our rule premises we know
τ

hΓ, ∆, ϕ & ψi →
− hΓ, ∆0 , ϕ0 & ψi

(C.441)

because
τ

hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i

(C.442)

Σ; eff(Γ, r ) ` ϕ & ψ

(C.443)

and

We must consider two subcases:
Subcase excl(ϕ, ψ) = (rϕ , rψ ): Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.443) and tCn2 we know
Σ; eff(Γ, rrψ ) ` ϕ
Σ; eff(Γ, rrϕ ) ` ψ

200

(C.444)
(C.445)

C. Proofs for Auxiliary Lemmas

By (C.442), (C.444) and IH we know
τ

heff(Γ, rrψ ), ∆, ϕi →
− heff(after(Γ, τ), rrψ ), ∆0 , ϕ0 i
Γ = after(Γ, τ)

(C.446)
(C.447)

By (C.442) and (C.446) we know that formula ϕ can reduce into ϕ0 regardless of whether
we apply the side-effects or not. Hence we can deduce
τ

heff(Γ, r ), ∆, ϕi →
− heff(after(Γ, τ), r ), ∆0 , ϕ0 i

(C.448)

By (C.448) and rCn2 we know
τ

heff(Γ, r ), ∆, ϕ & ψi →
− heff(after(Γ, τ), r ), ∆0 , ϕ0 & ψi

(C.449)

∴ Subcase holds by (C.447) and (C.449).
Subcase excl(ϕ, ψ) = ⊥: Since excl(ϕ, ψ) = ⊥, by (C.443) and tCn1 we know
Σ; eff(Γ1 , r ) ` ϕ
Σ; eff(Γ2 , r ) ` ψ
eff(Γ, r ) = eff(Γ1 , r ) + eff(Γ2 , r )

(C.450)
(C.451)
(C.452)

Since by (C.452) we know eff(Γ1 , r ) ⊆ eff(Γ, r ) and eff(Γ2 , r ) ⊆ eff(Γ, r ), then by (C.450)
we can deduce
Σ; eff(Γ, r ) ` ϕ

(C.453)

By (C.442), (C.453) and IH we know
τ

heff(Γ, r ), ∆, ϕi →
− heff(after(Γ, τ), r ), ∆0 , ϕ0 i
Γ = after(Γ, τ)

(C.454)
(C.455)

By (C.454) and rCn2 we know
τ

heff(Γ, r ), ∆, ϕ & ψi →
− heff(after(Γ, τ), r ), ∆0 , ϕ0 & ψi

(C.456)

∴ Subcase holds by (C.455) and (C.456).
Case rCn3: The proof for this case is analogous to the proof of case rCn2.
γ

To Prove Lemma C.3.3. Γ = Γ1 + Γ2 and hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i and Σ; Γ1 ` ϕ
γ

implies hΓ1 , ∆, ϕi →
− hΓ01 , ∆0 , ϕ0 i and Γ0 = Γ01 + Γ2
γ

Proof. By rule induction on hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i.

201



C. Proofs for Auxiliary Lemmas

Case rTru: From our rule premises we know
τ

hΓ, ∆, if b then ϕ else ψi →
− hΓ, ∆, ϕi
Σ; Γ1 ` if b then ϕ else ψ
Γ = Γ1 + Γ2

(C.457)
(C.458)
(C.459)

By (C.458) and tIf we know
Σ; Γ1 ` ϕ

(C.460)

Since reduction (C.457) can be performed regardless of the contents of Γ and by (C.458)
and (C.460) we can deduce
τ

hΓ1 , ∆, if b then ϕ else ψi →
− hΓ1 , ∆, ϕi

(C.461)

∴ Case holds by (C.459) and (C.461).
Note: The proofs for cases rFls, rClr and rMax are analogous to the proof of case
rTru.
Case rRel: From our rule premises we know
r(w )

h(Γ, w : lbid), ∆, rel(w) ϕi −−→ h(Γ, w : lid), ∆, ϕi
Σ; Γ1 ` rel(w) ϕ
(Γ, w : lid) = Γ1 + Γ2

(C.462)
(C.463)
(C.464)

By (C.463) and tRel we know
Γ1 = (Γ001 , w : lbid)
Γ01 = (Γ001 , w : lid)
Σ; Γ01 ` ϕ

(C.465)
(C.466)
(C.467)

By (C.465) and (C.466) we know that reduction (C.462) can still be performed using
subset Γ1 instead of Γ. Hence we can deduce
r(w )

hΓ1 , ∆, rel(w) ϕi −−→ hΓ01 , ∆, ϕi

(C.468)

By (C.464) and (C.466) we can conclude
(Γ, w : lid) = Γ01 + Γ2
∴ Case holds by (C.468) and (C.469).

202

(C.469)

C. Proofs for Auxiliary Lemmas

Note: The proofs for cases rBlk, rAdA and rAdS are analogous to the proof of case
rRel.
Case rNc1: From our rule premises we know
α

hΓ, ∆, [eκ ] br ϕi →
− h(Γ, Γ0 ), ∆0 , blk(i) ϕσi

(C.470)

mtch(e, α) = σ

Γ0 = bnd(e)σ
dom(∆) ∩ dom(bnd(e)σ) = ∅
∀(x, y) : lid ∈ bnd(e) · x , y =⇒ xσ , yσ

(C.471)
(C.472)
(C.473)
(C.474)
(C.475)

Σ; Γ1 ` [e] br ϕ
Γ = Γ1 + Γ2

(C.476)
(C.477)

Σ; (Γ1 , bnd(e)) ` blk(l) ϕ

(C.478)

because
subj(α) = i

and

By (C.476) and tNcB we know

By (C.471), (C.474), (C.475), (C.478) and Lemma C.2.5 (Term Substitution) we know
Σ; (Γ1 , bnd(e)σ) ` blk(i) ϕσ

(C.479)

Since by (C.473) and (C.479) we know that blk(i) ϕσ typechecks with (Γ1 , Γ0 ), this means
that reduction (C.470) can still be performed using subset Γ1 instead of Γ, such that we
know
α

hΓ1 , ∆, [eκ ] br ϕi →
− h(Γ1 , Γ0 ), ∆0 , blk(i) ϕσi

(C.480)

By (C.477) and (C.473) we can conclude
(Γ, Γ0 ) = (Γ1 , Γ0 ) + Γ2

(C.481)

∴ Case holds by (C.480) and (C.481).
Case rNc2: The proof for this case is analogous to the proof for case rNc1.
Case rNc3: From our rule premises we know
α

hΓ, ∆, [eκ ] ρr ϕi →
− hΓ, ∆, rel(r) tti

203

(C.482)

C. Proofs for Auxiliary Lemmas

because
mtch(e, α) = ⊥

(C.483)

Σ; Γ1 ` [e] ρr ϕ
Γ = Γ1 + Γ2

(C.484)
(C.485)

and

Since ρ ∈ {b, a} we must consider two subcases:
Subcase ρ = b: By (C.484) and tNcB we know
Σ; Γ1 ` rel(r) tt

(C.486)

By (C.484) and (C.486) we know that reduction (C.482) can still be performed regardless
of the contents of Γ, hence by (C.485) we can deduce
α

hΓ1 , ∆, [eκ ] ρr ϕi →
− hΓ1 , ∆, rel(r) tti

(C.487)

∴ Subcase holds by (C.485) and (C.487).
Subcase ρ = a: The proof for this subcase is analogous to that of the previous subcase
(i.e., ρ = b).
Case rCn1: From our rule premises we know
α

hΓ, ∆, ϕ & ψi →
− h(Γ0 , Γ00 ), ∆0 ∪ ∆00 , ϕ0 & ψ0 i

(C.488)

because
α

hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i
α

(C.489)

hΓ, ∆, ψi →
− hΓ00 , ∆00 , ψ0 i
dom(∆) = dom(∆0 ) ∩ dom(∆00 )

(C.490)
(C.491)

Σ; Γ1 ` ϕ & ψ
Γ = Γ1 + Γ2

(C.492)
(C.493)

and

We must consider two subcases:
Subcase excl(ϕ, ψ) = (rϕ , rψ ): Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.492) and tCn2 we know
Σ; eff(Γ1 , rψ ) ` ϕ
Σ; eff(Γ1 , rϕ ) ` ψ

204

(C.494)
(C.495)

C. Proofs for Auxiliary Lemmas

Since by (C.493) we know that Γ1 ⊆ Γ, then from (C.494) and (C.495) we can deduce
Σ; eff(Γ, rψ ) ` ϕ
Σ; eff(Γ, rϕ ) ` ψ

(C.496)
(C.497)

By (C.489), (C.496) and Lemma C.3.2 we know
α

heff(Γ, rψ ), ∆, ϕi →
− heff(Γ0 , rψ ), ∆0 , ϕ0 i

(C.498)

By (C.490), (C.497) and Lemma C.3.2 we know
α

heff(Γ, rϕ ), ∆, ψi →
− heff(Γ00 , rϕ ), ∆00 , ψ0 i

(C.499)

By (C.493), (C.494), (C.495) and defn of eff (Def. 5.1.1) we know
eff(Γ, rψ ) = eff(Γ1 , rψ ) + Γ2
eff(Γ, rϕ ) = eff(Γ1 , rϕ ) + Γ2

(C.500)
(C.501)

By (C.494), (C.498), (C.500) and IH we know
α

heff(Γ1 , rψ ), ∆, ϕi →
− heff(Γ01 , rψ ), ∆0 , ϕ0 i
eff(Γ0 , rψ ) = eff(Γ01 , rψ ) + Γ2 =⇒ Γ0 = Γ01 + Γ2

(C.502)
(C.503)

By (C.495), (C.499), (C.501) and IH we know
α

heff(Γ1 , rϕ ), ∆, ψi →
− heff(Γ001 , rϕ ), ∆00 , ψ0 i
eff(Γ00 , rϕ ) = eff(Γ001 , rϕ ) + Γ2 =⇒ Γ00 = Γ001 + Γ2

(C.504)
(C.505)

From (C.489), (C.502) and (C.503) we know that formula ϕ can reduce into ϕ0 regardless
of whether we apply the side-effects or not. Hence we can deduce
α

hΓ1 , ∆, ϕi →
− hΓ01 , ∆0 , ϕ0 i

(C.506)

The same argument applies by (C.490), (C.504) and (C.505), such that we know we
know
α

hΓ1 , ∆, ψi →
− hΓ001 , ∆00 , ψ0 i

(C.507)

By (C.491), (C.506), (C.507) and rCn1 we know
α

hΓ1 , ∆, ϕ & ψi →
− h(Γ01 , Γ001 ), ∆0 ∪ ∆00 , ϕ0 & ψ0 i

(C.508)

From (C.503) and (C.505) we can conclude
(Γ0 , Γ00 ) = (Γ01 , Γ001 ) + Γ02
∴ Subcase holds by (C.508) and (C.509).

205

(C.509)

C. Proofs for Auxiliary Lemmas

Subcase excl(ϕ, ψ) = ⊥: Since excl(ϕ, ψ) = ⊥, by (C.492) and tCn1 we know
Σ; Γ3 ` ϕ
Σ; Γ4 ` ψ
Γ1 = Γ3 + Γ4

(C.510)
(C.511)
(C.512)

Since by (C.512) we know Γ3 ⊆ Γ1 and Γ4 ⊆ Γ1 , then by (C.510) and (C.511) we can
deduce
Σ; Γ1 ` ϕ
Σ; Γ1 ` ψ

(C.513)
(C.514)

By (C.489), (C.493), (C.513) and IH we know
α

hΓ1 , ∆, ϕi →
− hΓ01 , ∆0 , ϕ0 i
Γ0 = Γ01 + Γ2

(C.515)
(C.516)

By (C.490), (C.493), (C.514) and IH we know
α

hΓ1 , ∆, ϕi →
− hΓ001 , ∆00 , ψ0 i
Γ00 = Γ001 + Γ2

(C.517)
(C.518)

By (C.491), (C.515), (C.517) and rCn1 we know
α

hΓ1 , ∆, ϕ & ψi →
− h(Γ01 , Γ001 ), ∆0 ∪ ∆00 , ϕ0 & ψ0 i

(C.519)

From (C.516) and (C.518) we can deduce
(Γ0 , Γ00 ) = (Γ01 , Γ001 ) + Γ2

(C.520)

∴ Subcase holds by (C.519) and (C.520).
Case rCn2: From our rule premises we know
τ

hΓ, ∆, ϕ & ψi →
− hΓ, ∆0 , ϕ0 & ψi

(C.521)

because
τ

hΓ, ∆, ϕi →
− hΓ, ∆0 , ϕ0 i

(C.522)

Σ; Γ1 ` ϕ & ψ
Γ = Γ1 + Γ2

(C.523)
(C.524)

and

We must consider two subcases:

206

C. Proofs for Auxiliary Lemmas

Subcase excl(ϕ, ψ) = (rϕ , rψ ): Since excl(ϕ, ψ) = (rϕ , rψ ), by (C.523) and tCn2 we know
Σ; eff(Γ1 , rψ ) ` ϕ
Σ; eff(Γ1 , rϕ ) ` ψ

(C.525)
(C.526)

Since by (C.524) we know that Γ1 ⊆ Γ, then from (C.525) we can deduce
Σ; eff(Γ, rψ ) ` ϕ

(C.527)

By (C.522), (C.527) and Lemma C.3.2 we know
τ

heff(Γ, rψ ), ∆, ϕi →
− heff(Γ, rψ ), ∆0 , ϕ0 i

(C.528)

By (C.524), (C.525) and the defn of eff (Def. 5.1.1) we know
eff(Γ, rψ ) = eff(Γ1 , rψ ) + Γ2

(C.529)

By (C.525), (C.528), (C.529) and IH we know
τ

heff(Γ1 , rψ ), ∆, ϕi →
− heff(Γ1 , rψ ), ∆0 , ϕ0 i
eff(Γ, rψ ) = eff(Γ1 , rψ ) + Γ2 =⇒ Γ = Γ1 + Γ2

(C.530)
(C.531)

From (C.522), (C.530) and (C.531) we know that formula ϕ can reduce into ϕ0 using
subset Γ1 , as the reduction can still be applied regardless of whether we apply the sideeffects or not. Hence we can deduce
τ

hΓ1 , ∆, ϕi →
− hΓ1 , ∆0 , ϕ0 i

(C.532)

By (C.532) and rCn2 we know
τ

hΓ1 , ∆, ϕ & ψi →
− hΓ1 , ∆0 , ϕ0 & ψi

(C.533)

∴ Subcase holds by (C.531) and (C.533).
Subcase excl(ϕ, ψ) = ⊥: Since excl(ϕ, ψ) = ⊥, by (C.523) and tCn1 we know
Σ; Γ3 ` ϕ
Σ; Γ4 ` ψ
Γ1 = Γ3 + Γ4

(C.534)
(C.535)
(C.536)

Since by (C.536) we know Γ3 ⊆ Γ1 , then from (C.534) we can deduce
Σ; Γ1 ` ϕ

(C.537)

By (C.522), (C.524), (C.537) and IH we know
τ

hΓ1 , ∆, ϕi →
− hΓ1 , ∆0 , ϕ0 i
Γ = Γ1 + Γ2
207

(C.538)
(C.539)

C. Proofs for Auxiliary Lemmas

By (C.538) and rCn2 we know
τ

hΓ1 , ∆, ϕ & ψi →
− hΓ1 , ∆0 , ϕ0 & ψi

(C.540)

∴ Subcase holds by (C.539) and (C.540).
Case rCn3: The proof for this case is analogous to the proof of case rCn2.



To Prove Lemma C.3.4.
τ

excl(ϕ, ψ) = (rϕ , rψ ) and hΓ, ∆, ϕi →
− hΓ, ∆0 , ϕ0 i implies excl(ϕ0 , ψ) = (rϕ , rψ ).
τ

Proof. By rule induction on hΓ, ∆, ϕi →
− hΓ, ∆0 , ϕ0 i.
Case rTru: From our rule premises we know
τ

hΓ, ∆, if c then ϕ1 else ϕ2 i →
− hΓ, ∆, ϕ1 i
excl(if c then ϕ1 else ϕ2 , ψ) = (rϕ , rψ )

(C.541)
(C.542)

By (C.542) and defn of excl (Def. 5.1.2) we know
excl(ϕ1 , ψ) = excl(ϕ2 , ψ) = (rϕ , rψ )

(C.543)

∴ Case holds by (C.543).
Case rFls: The proof for this case is analogous to the proof for rTru.
Case rMax: From our rule premises we know
τ

hΓ, ∆, max X. ϕi →
− hΓ, ∆, ϕ[(clr(X )max X. ϕ)/X]i
excl(max X. ϕ, ψ) = (rϕ , rψ )

(C.544)
(C.545)

By (C.545) and defn of excl (Def. 5.1.2) we know
excl(ϕ, ψ) = (rϕ , rψ )

(C.546)

By [(clr(X )max X. ϕ)/X], (C.546) and Lemma C.3.8 we know
excl(ϕ[(clr(X )max X. ϕ)/X], ψ) = (rϕ , rψ )

∴ Case holds by (C.547).
Case rClr: The proof for this case is analogous to the proof for rMax.
208

(C.547)

C. Proofs for Auxiliary Lemmas

Case rCn2: From our rule premises we know
τ

hΓ, ∆, ϕ1 & ϕ2 i →
− hΓ, ∆0 , ϕ01 & ϕ2 i

(C.548)

because
τ

hΓ, ∆, ϕ1 i →
− hΓ, ∆0 , ϕ01 i

(C.549)

excl((ϕ1 & ϕ2 ), ψ) = (r(ϕ1 & ϕ2 ) , rψ )

(C.550)

and

By (C.550) and defn of excl (Def. 5.1.2) we know
excl(ϕ1 , ψ) = (rϕ1 , rψ )

(C.551)
(C.552)
(C.553)

excl(ϕ2 , ψ) = (rϕ2 , rψ )

r(ϕ1 & ϕ2 ) = rϕ1 ∪ rϕ2
By (C.549), (C.551) and IH we know
excl(ϕ01 , ψ) = (rϕ1 , rψ )

(C.554)

By (C.552), (C.553), (C.554) and defn of excl (Def. 5.1.2) we know
excl((ϕ01 & ϕ2 ), ψ) = (r(ϕ1 & ϕ2 ) , rψ )

(C.555)

∴ Case holds by (C.555).
Note: The remaining cases do not apply as we only consider τ actions.



α

To Prove Lemma C.3.5. excl(ϕ, ψ) = (rϕ , rψ ) and hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i and
Σ; eff((Γ0 , Γ00 ), rψ ) ` ϕ0 and Σ; eff((Γ0 , Γ00 ), rϕ ) ` ψ0 implies Σ; (Γ0 , Γ00 ) ` ϕ0 & ψ0 .
α

Proof. By rule induction on hΓ, ∆, ϕi →
− hΓ0 , ∆0 , ϕ0 i.
Case rNc1: From our rule premises we know
α

hΓ, ∆, [eκ ] br ϕi →
− hΓ0 , ∆0 , blk(i) ϕσi

(C.556)

mtch(e, α) = σ

(C.557)

because
and others . . .

209

C. Proofs for Auxiliary Lemmas

and
excl([e] br ϕ, ψ) = (r, rψ )

Σ; eff((Γ , Γ ), rψ ) ` blk(i) ϕσ
Σ; eff((Γ0 , Γ00 ), r ) ` ψ0
0

00

(C.558)
(C.559)
(C.560)

By (C.558) and defn of excl (Def. 5.1.2) we know
∀e1 ∈ fps(ψ) · mtch(e, e1 ) = ⊥

(C.561)

By (C.557) we know that system event α matches pattern e, but by (C.561) we also know
that α does not match any of the concurrent necessities defined in ψ. This means that all
the other necessities defined in ψ are trivially satisfied. Hence we can deduce
k

ψ0 = &n=1 (rel(r)tt)n

(C.562)

Since [e] br ϕ reduces into blk(i) ϕσ, by (C.562) and defn of excl (Def. 5.1.2) we know
excl(blk(i) ϕσ, ψ0 ) = (ε, rψ )

(C.563)

Since ε ⊆ r, and by defn of eff (Def. 5.1.1), we know that (C.560) still typechecks using
ε instead of r given that we know (C.562). (Remember that the eff function “releases”
a number of blocked ids from a type environment Γ, so if we don’t release them ψ0 =
&kn=1 (rel(r)tt)n still typechecks). Hence we can deduce
Σ; eff((Γ0 , Γ00 ), ε) ` ψ0

(C.564)

By (C.559), (C.563), (C.564) and tCn2 we know
Σ; (Γ0 , Γ00 ) ` blk(i) ϕσ & ψ0

(C.565)

∴ Case holds by (C.565).
Case rNc2: The proof for this case is analogous to that of case rNc1.
Case rNc3: From our rule premises we know
α

hΓ, ∆, [eκ ] ρr ϕi →
− hΓ, ∆, rel(r) tti

(C.566)

mtch(e, α) = ⊥

(C.567)

because

210

C. Proofs for Auxiliary Lemmas

and
excl([e] ρr ϕ, ψ) = (r, rψ )

(C.568)
(C.569)
(C.570)

Σ; eff((Γ , Γ ), rψ ) ` rel(r) tt
Σ; eff((Γ0 , Γ00 ), r ) ` ψ0
0

00

By (C.568) and defn of excl (Def. 5.1.2) we know
∀e1 ∈ fps(ψ) · mtch(e, e1 ) = ⊥

(C.571)

By (C.567) we know that system event α does not match pattern e, and by (C.571) we
know that the opposing branch ψ may have lived on. We must therefore consider these
two cases:
k
k
Subcase ψ also died − ψ0 = &n=1 (rel(r)tt)n : Since [eκ ] ρr ϕ reduces to rel(r) tt, ψ = &n=1 (rel(r)tt)n ,
and by defn of excl (Def. 5.1.2) we know
excl(rel(r) tt, ψ0 ) = (r, rψ )

(C.572)

By (C.569), (C.570), (C.572) and tCn2 we know
Σ; (Γ0 , Γ00 ) ` rel(r) tt & ψ0

(C.573)

∴ Subcase holds by (C.573).
Subcase ψ lived on − ψ , &n=1 (rel(r)tt)n : Since [eκ ] ρr ϕ reduces to rel(r) tt ψ , &n=1 (rel(r)tt)n ,
and by defn of excl (Def. 5.1.2) we know
k

k

excl(rel(r) tt, ψ0 ) = (r, ε)

(C.574)

Since ε ⊆ rψ , and by defn of eff (Def. 5.1.1), we know that (C.569) still typechecks using
ε instead of rψ given that we know (C.574). Hence we can deduce
Σ; eff((Γ0 , Γ00 ), ε) ` rel(r) tt

(C.575)

By (C.570), (C.574), (C.575) and tCn2 we know
Σ; (Γ0 , Γ00 ) ` rel(r) tt & ψ0

(C.576)

∴ Subcase holds by (C.576).
Case rCn1: From our rule premises we know
α

hΓ, ∆, ϕ1 & ϕ2 i →
− hΓ0 , ∆0 ∪ ∆00 , ϕ01 & ϕ02 i
excl(ϕ1 & ϕ2 , ψ) = (rϕ , rψ )
Σ; eff((Γ0 , Γ00 ), rψ ) ` ϕ01 & ϕ02
Σ; eff((Γ0 , Γ00 ), rϕ ) ` ψ0

211

(C.577)
(C.578)
(C.579)
(C.580)

C. Proofs for Auxiliary Lemmas

As by (C.578) we know that branch ϕ1 & ϕ2 is mutually exclusive to ψ, then we have to
consider the following three subcases:
k
k
Subcase ϕ01 & ϕ02 = &n=1 (rel(r)tt)n and ψ0 , &n=1 (rel(r)tt)n : As we assume that ϕ1 & ϕ2 rek

duces into &n=1 (rel(r)tt)n , by (C.578) and defn of excl (Def. 5.1.2) we know
k

excl(&n=1 (rel(r)tt)n , ψ0 ) = (rϕ , ε)

(C.581)

k

Since ϕ01 & ϕ02 = &n=1 (rel(r)tt)n , and defn of eff (Def. 5.1.1) we know that (C.579) still holds
if rϕ = ε. (Remember that the eff function “releases” a number of blocked ids from a type
k
environment Γ, so if we don’t release them ψ0 = &n=1 (rel(r)tt)n still typechecks). Hence
we can deduce
Σ; eff((Γ0 , Γ00 ), ε) ` ϕ01 & ϕ02

(C.582)

By (C.580), (C.581), (C.582) and tCn2 we know
Σ; (Γ0 , Γ00 ) ` (ϕ01 & ϕ02 ) & ψ0

(C.583)

∴ Subcase holds by (C.583).
k

k

Subcase ψ0 = &n=1 (rel(r)tt)n and ϕ01 & ϕ02 , &n=1 (rel(r)tt)n : The proof for this subcase is analogous to that of the previous subcase.
k

k

Subcase ϕ01 & ϕ02 = &n=1 (rel(r)tt)n and ψ0 = &n=1 (rel(r)tt)n : As we assume that both branches
k

ϕ1 & ϕ2 and ψ are trivially satisfied, i.e., reduce into the form &n=1 (rel(r)tt)n , by (C.578) and
defn of excl (Def. 5.1.2) we know
excl(ϕ01 & ϕ02 , ψ0 ) = (rϕ , rψ )

(C.584)

By (C.579), (C.580), (C.584) and tCn2 we know
Σ; (Γ0 , Γ00 ) ` (ϕ01 & ϕ02 ) & ψ0

(C.585)

∴ Subcase holds by (C.585).
Note: The remaining cases do not apply as we only consider α actions.
µ

To Prove Lemma C.3.6. excl(ϕ, ψ) = (rϕ , rψ ) and hΓ, ∆, ϕi →
− hΓ0 , ∆, ϕ0 i and
Σ; eff(Γ, rϕ ) ` ψ and Σ; eff(after(Γ, µ), rψ ) ` ϕ0 implies Σ; after(Γ, µ) ` ϕ0 & ψ
µ

Proof. By rule induction on hΓ, ∆, ϕi →
− hΓ0 , ∆, ϕ0 i.

212



C. Proofs for Auxiliary Lemmas

Case rBlk: From our rule premises we know
b(w )

h(Γ, w : lid), ∆, blk(w) ϕi −−−→ h(Γ, w : lbid), ∆, ϕi
excl(blk(w) ϕ, ψ) = (ε, rψ )
Σ; eff(after((Γ, w : lid), b(w )), rψ ) ` ϕ
Σ; eff((Γ, w : lid), ε) ` ψ

(C.586)
(C.587)
(C.588)
(C.589)

By (C.588) and the defn of after (Def. 5.3.2) we know
Σ; eff((Γ, w : lbid), rψ ) ` ϕ

(C.590)

We know that by the defn of excl (Def. 5.1.2), (C.587) can only hold if ψ represents a
collection of trivially satisfied branches, hence we can deduce
excl(ϕ, ψ) = (ε, rψ )
k
n=1 (rel(r)tt)n

ψ =&

(C.591)
(C.592)

By (C.592) we know that for (C.589) to hold, then every identifier in the release set (rψ )
of ψ must be of the type lbid. Since we know that w : lid, then we know for sure that
release-set rψ does not intersect with blocking set w (otherwise (C.589) would not hold).
Hence we know that ψ can typecheck regardless of the type of w (i.e., it typechecks both
if we have w : lid or w : lbid), such that we can conclude
Σ; eff((Γ, w : lbid), ε) ` ψ

(C.593)

By (C.590), (C.591), (C.593), tCn2 and the defn of after (Def. 5.3.2) we know
Σ; after((Γ, w : lid), b(w )) ` ϕ & ψ

(C.594)

∴ Case holds by (C.594).
Case rRel: From our rule premises we know
r(r )

h(Γ, r : lbid), ∆, rel(r) ϕi −−→ h(Γ, r : lid), ∆, ϕi
excl(rel(r) ϕ, ψ) = (rϕ , rψ )
Σ; eff(after((Γ, r : lbid), r(r )), rψ ) ` ϕ
Σ; eff((Γ, r : lbid), r ) ` ψ

(C.595)
(C.596)
(C.597)
(C.598)

By (C.596) and the defn of excl (Def. 5.1.2) we must consider 2 subcases:
k

Subcase ϕ , tt and ψ = &n=1 (rel(r)tt)n : The proof for this subcase is analogous to the
proof of case rBlk.

213

C. Proofs for Auxiliary Lemmas

Subcase ϕ = tt: Since ϕ = tt, by (C.596), (C.597) and (C.598) we know
excl(rel(r) tt, ψ) = (r, rψ )

Σ; eff(after((Γ, r : lbid), r(r )), rψ ) ` tt
Σ; eff((Γ, r : lbid), r ) ` ψ

(C.599)
(C.600)
(C.601)

By (C.599) and the defn of excl (Def. 5.1.2) we know
excl(tt, ψ) = (ε, rψ )

(C.602)

By (C.600) and the defn of after (Def. 5.3.2) we know
Σ; eff((Γ, r : lid), rψ ) ` tt

(C.603)

By (C.601) and the defn of eff (Def. 5.1.1) we know
Σ; eff((Γ, r : lid), ε) ` ψ

(C.604)

By (C.602), (C.603), (C.604), tCn2 and the defn of after (Def. 5.3.2) we know
Σ; after((Γ, r : lbid), r(r )) ` tt & ψ

(C.605)

∴ Case holds by (C.605).
Case rAdS: From our rule premises we know
s(w )

h(Γ, w : lbid), ∆, sA(w)r ϕi −−−→ h(Γ, w : lid), ∆, rel(r) ϕi
excl(sA(w)r ϕ, ψ) = (ε, rψ )
Σ; eff(after((Γ, w : lbid), s(w )), rψ ) ` rel(r) ϕ
Σ; eff((Γ, w : lbid), ε) ` ψ

(C.606)
(C.607)
(C.608)
(C.609)

By (C.607) and the defn of excl (Def. 5.1.2) we know
excl(rel(r) ϕ, ψ) = (ε, rψ )
k
n=1 (rel(r)tt)n

ψ =&

(C.610)
(C.611)

By (C.608) and the defn of after (Def. 5.3.2) we know
Σ; eff((Γ, w : lbid), rψ ) ` rel(r) ϕ

(C.612)

By (C.610), (C.609), (C.612), tCn2 and the defn of after (Def. 5.3.2) we know
Σ; after((Γ, w : lbid), s(w )) ` rel(r) ϕ & ψ
∴ Case holds by (C.613).

214

(C.613)

C. Proofs for Auxiliary Lemmas

Case rAdA: The proof for this case is analogous to the proof for case rAdS.
Case rCn3: From our rule premises we know
µ

hΓ, ∆, ϕ1 & ϕ2 i →
− hΓ0 , ∆, ϕ01 & ϕ2 i
excl(ϕ1 & ϕ2 , ψ) = (rϕ , rψ )
Σ; eff(after(Γ, µ), rψ ) ` ϕ01 & ϕ2
Σ; eff(Γ, rϕ ) ` ψ

(C.614)
(C.615)
(C.616)
(C.617)

As by (C.615) we know that branch ϕ1 & ϕ2 is mutually exclusive to ψ, based on the defn
of excl (Def. 5.1.2) we have to consider the following three subcases:
k

k

Subcase ϕ1 & ϕ2 = &n=1 (rel(r)tt)n and ψ , &n=1 (rel(r)tt)n : By our subcase assumptions and
(C.614) we can deduce that the reduction can only be made to issue a release of one of
the releases sets declared in the collection of trivially satisfied branches ϕ1 & ϕ2 . Hence
we can conclude
Γ = Γ00 , rϕ : lbid
µ = r ϕ1
rϕ = rϕ1 , rϕn−1
ϕ1 & ϕ2 = rel(rϕ1 ) &

&kn=1 (rel(r)tt)n−1

(C.618)
(C.619)
(C.620)


(C.621)

k
n=1 (rel(r)tt)n−1

(C.622)
ϕ01 & ϕ2 = &
where rϕ1 is the release set of an arbitrary trivially satisfied branch rel(rϕ1 )
By (C.615), (C.620), (C.621), (C.622) and the defn of excl (Def. 5.1.2) we know
k

excl(ϕ01 & ϕ2 , ψ) ≡ excl( &n=1 (rel(r)tt)n−1 , ψ) = (rϕn−1 , ε)

(C.623)

k

Since ψ , &n=1 (rel(r)tt)n , by (C.616), (C.617), (C.618) and (C.619) we can deduce
Σ; eff(after((Γ00 , rϕ : lbid), r(ϕ1 )), ε) ` ϕ01 & ϕ2
Σ; eff((Γ00 , rϕ : lbid), rϕ ) ` ψ

(C.624)
(C.625)

By (C.624), (C.620) and the defn of after (Def. 5.3.2) we know
Σ; eff((Γ00 , rϕn−1 : lbid, rϕ1 : lid), ε) ` ϕ01 & ϕ2

(C.626)

By (C.625), (C.620) and the defn of eff (Def. 5.1.1) we know
Σ; eff((Γ00 , rϕn−1 : lbid, rϕ1 : lid), rϕn−1 ) ` ψ

(C.627)

By (C.623), (C.626), (C.627), tCn2 and the defn of after (Def. 5.3.2) we know
Σ; (Γ00 , rϕn−1 : lbid, rϕ1 : lid) ` (ϕ01 & ϕ2 ) & ψ
≡ Σ; (after((Γ00 , rϕn−1 : lbid), r(rϕ1 ))) ` (ϕ01 & ϕ2 ) & ψ
215

(C.628)

C. Proofs for Auxiliary Lemmas

∴ Subcase holds by (C.629).
k

k

k

k

Subcase ϕ1 & ϕ2 = &n=1 (rel(r)tt)n and ψ = &n=1 (rel(r)tt)n : The proof for this subcase follows a similar argument as per the previous subcase.
k

Subcase ϕ1 & ϕ2 , &n=1 (rel(r)tt)n and ψ = &n=1 (rel(r)tt)n : As we assume that ψ = &n=1 (rel(r)tt)n ,
n
o
if ϕ1 commits an action µ ∈ b(rϕ ), r(rϕ ), . . . and becomes ϕ01 , then we are certain that
r does not conflict with any release set in pertaining to ψ, as otherwise (C.617) would
k
not hold. Therefore we can deduce that after committing an action µ, ψ = &n=1 (rel(r)tt)n
would not be effected, such that we can conclude
Σ; eff(after(Γ, µ), ε) ` ψ
excl(ϕ01 & ϕ2 , ψ) = (ε, rψ )

(C.629)
(C.630)

By (C.616), (C.629), (C.630) and tCn2 we conclude
Σ; (after(Γ, µ)) ` (ϕ01 & ϕ2 ) & ψ

(C.631)

∴ Subcase holds by (C.631).
Note: The remaining cases do not apply as we only consider µ actions.



To Prove Lemma C.3.7. excl(ϕ, ψ) = (rϕ , rψ ) and [(clr(X )max X. ϕ0 )/X] implies
excl(ϕ[(clr(X )max X. ϕ0 )/X], ψ[(clr(X )max X. ϕ0 )/X]) = (rϕ , rψ ).

Proof. From our premises we know
excl(ϕ, ψ) = (rϕ , rψ )
[(clr(X )max X. ϕ0 )/X]

(C.632)
(C.633)

By (C.632), (C.633) and Lemma C.3.8 we know
excl(ϕ[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

(C.634)

By (C.634) and symmetry we know
excl(ψ, ϕ[(clr(X )max X. ϕ0 )/X]) = (rψ , rϕ )

(C.635)

By (C.633), (C.635) and Lemma C.3.8 we know
excl(ψ[(clr(X )max X. ϕ0 )/X], ϕ[(clr(X )max X. ϕ0 )/X]) = (rψ , rϕ )

(C.636)

By (C.636) and symmetry we know
excl(ϕ[(clr(X )max X. ϕ0 )/X], ψ[(clr(X )max X. ϕ0 )/X]) = (rϕ , rψ )

(C.637)

∴ Lemma holds by (C.637).



To Prove Lemma C.3.8. excl(ϕ, ψ) = (rϕ , rψ ) and [(clr(X )max X. ϕ0 )/X] implies
excl(ϕ[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ ).

216

C. Proofs for Auxiliary Lemmas

Proof. By structural induction on ϕ.
Case ϕ = max Y. ϕ: From our rule premises we know
excl(max Y. ϕ, ψ) = (rϕ , rψ )

(C.638)
(C.639)

[(clr(X )max X. ϕ )/X]
0

We must consider two subcases.
k

Subcase ψ , &n=1 (rel(r)tt)n : By (C.638) and defn of excl (Def. 5.1.2) we know
excl(ϕ, ψ) = (rϕ , rψ )

(C.640)

By (C.639), (C.640) and IH we know
excl(ϕ[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

(C.641)

By (C.641) and defn of excl (Def. 5.1.2) we can deduce
excl((max Y. ϕ[(clr(X )max X. ϕ0 )/X]), ψ) = (rϕ , rψ )

(C.642)

By (C.642) and substitution we know
excl((max Y. ϕ)[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

(C.643)

∴ Subcase holds by (C.643).
k

k

Subcase ψ = &n=1 (rel(r)tt)n : By defn of excl (Def. 5.1.2) and since ψ = &n=1 (rel(r)tt)n we
know that no matter what ϕ is, the excl function always returns ε as the release set of ϕ.
Hence by (C.638) and (C.639) we know
k

excl((max Y. ϕ), &n=1 (rel(r)tt)n ) =
k

excl((max Y. ϕ)[(clr(X )max X. ϕ0 )/X], &n=1 (rel(r)tt)n ) = (ε, rψ )

(C.644)

∴ Subcase holds by (C.644).
Case ϕ = clr(Y )ϕ: The proof for this case is analogous to that of case ϕ = max Y. ϕ
Case ϕ = ϕ1 & ϕ2 : From our rule premises we know
excl((ϕ1 & ϕ2 ), ψ) = (rϕ , rψ )

[(clr(X )max X. ϕ )/X]
0

217

(C.645)
(C.646)

C. Proofs for Auxiliary Lemmas

By (C.645) and defn of excl (Def. 5.1.2) we know
excl(ϕ1 , ψ) = (rϕ1 , rψ )

(C.647)
(C.648)
(C.649)

excl(ϕ2 , ψ) = (rϕ2 , rψ )

rϕ = (rϕ1 ∪ rϕ2 )
By (C.646), (C.647) and IH we know
excl(ϕ1 [(clr(X )max X. ϕ0 )/X], ψ) = (rϕ1 , rψ )

(C.650)

By (C.646), (C.648) and IH we know
excl(ϕ2 [(clr(X )max X. ϕ0 )/X], ψ) = (rϕ2 , rψ )

(C.651)

By (C.649), (C.650), (C.651) and defn of excl (Def. 5.1.2) we know
excl((ϕ1 [(clr(X )max X. ϕ0 )/)] & (ϕ2 [(clr(X )max X. ϕ0 )/X]), ψ) = (rϕ , rψ )

(C.652)

By (C.652) and substitution we know
excl((ϕ1 & ϕ2 )[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

(C.653)

∴ Case holds by (C.653).
Case ϕ = [e] ρr ϕ: From our rule premises we know
excl([e] ρr ϕ, ψ) = (rϕ , rψ )

(C.654)
(C.655)

[(clr(X )max X. ϕ )/X]
0

We must consider two subcases.
k

Subcase ψ , &n=1 (rel(r)tt)n : By (C.654) and defn of excl (Def. 5.1.2) we know
excl(rel(r) tt, ψ) = (r, rψ )

(C.656)
(C.657)

∀e1 ∈ fps(ψ) · mtch(e, e1 ) = ⊥
Since rel(r) tt ≡ (rel(r) tt)[(clr(X )max X. ϕ0 )/X], by (C.656) we know
excl((rel(r) tt)[(clr(X )max X. ϕ0 )/X], ψ) = (r, rψ )

(C.658)

By (C.657), (C.658) and defn of excl (Def. 5.1.2) we can deduce
excl(([e] ρr ϕ)[(clr(X )max X. ϕ0 )/X], ψ) = (r, rψ )

(C.659)

∴ Subcase holds by (C.659).
k

k

Subcase ψ = &n=1 (rel(r)tt)n : By defn of excl (Def. 5.1.2) and since ψ = &n=1 (rel(r)tt)n we
know that no matter what ϕ is, the excl function always returns ε as the release set of ϕ.
218

C. Proofs for Auxiliary Lemmas

Hence by (C.654) and (C.655) we know
excl([e] ρr ϕ, &n=1 (rel(r)tt)n ) =
k

excl(([e] ρr ϕ)[(clr(X )max X. ϕ0 )/X], &n=1 (rel(r)tt)n ) = (ε, rψ )
k

(C.660)

∴ Subcase holds by (C.660).
Case ϕ = sA(r)w ϕ: From our rule premises we know
excl(sA(r)w ϕ, ψ) = (rϕ , rψ )

(C.661)
(C.662)

[(clr(X )max X. ϕ )/X]
0

k

By defn of excl (Def. 5.1.2) we know that (C.654) can only be true if ψ = &n=1 (rel(r)tt)n
(because otherwise, excl would have returned ⊥). Hence we know
k

ψ =&n=1 (rel(r)tt)n

(C.663)

Since we know (C.663), then we also know that no matter what ϕ is, the excl function
always returns ε as the release set of ϕ. Hence by (C.661), (C.662) and defn of excl
(Def. 5.1.2) we can deduce
k

excl(sA(r)w ϕ, &n=1 (rel(r)tt)n ) =
k

excl((sA(r)w ϕ)[(clr(X )max X. ϕ0 )/X],&n=1 (rel(r)tt)n ) = (ε, rψ )

(C.664)

∴ Case holds by (C.664).
Note: The proofs for cases ϕ = aA(r)w ϕ, ϕ = blk(i) ϕ and ϕ = ff are analogous to the proof
for case ϕ = sA(r)w ϕ.
Case ϕ = rel(r) ϕ: From our rule premises we know
excl(rel(r) ϕ, ψ) = (rϕ , rψ )

[(clr(X )max X. ϕ )/X]
0

(C.665)
(C.666)

By defn of excl (Def. 5.1.2) we know that (C.665) can only be true in the following two
subcases (because otherwise, excl would have returned ⊥).
Subcase ϕ = tt: Since ϕ = tt, by (C.665) and defn of excl (Def. 5.1.2) we know
excl(rel(r) tt, ψ) = (r, rψ )

(C.667)

By defn of substitution we know
(rel(r) tt)[(clr(X )max X. ϕ0 )/X], ψ ≡ rel(r) tt
219

(C.668)

C. Proofs for Auxiliary Lemmas

By (C.667) and (C.668) we deduce
excl((rel(r) tt)[(clr(X )max X. ϕ0 )/X], ψ) = (r, rψ )

(C.669)

∴ Subcase holds by (C.669).
k

k

Subcase ψ = &n=1 (rel(r)tt)n : By defn of excl (Def. 5.1.2) and since ψ = &n=1 (rel(r)tt)n we
know that no matter what ϕ is, the excl function always returns ε as the release set of ϕ.
Hence by (C.665) and (C.666) we know
k

excl(rel(r) ϕ, &n=1 (rel(r)tt)n ) =
k

excl((rel(r) ϕ)[(clr(X )max X. ϕ0 )/X], &n=1 (rel(r)tt)n ) = (ε, rψ )

(C.670)

∴ Subcase holds by (C.670).
Case ϕ = if c then ϕ else ψ: From our rule premises we know
excl(if c then ϕ else ψ, ψ) = (rϕ , rψ )

(C.671)
(C.672)

[(clr(X )max X. ϕ )/X]
0

We must consider the following two subcases:
k

Subcase ψ , &n=1 (rel(r)tt)n : By (C.671) and defn of excl (Def. 5.1.2) we know
excl(ϕ1 , ψ) = (rϕ , rψ )

(C.673)
(C.674)

excl(ϕ2 , ψ) = (rϕ , rψ )

By (C.672), (C.673) and IH we know
excl(ϕ1 [(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

(C.675)

By (C.672), (C.674) and IH we know
excl(ϕ2 [(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

(C.676)

By (C.672), (C.674) and defn of excl (Def. 5.1.2) we can conclude
excl(if c then (ϕ1 [(clr(X )max X. ϕ0 )/X]) else (ϕ2 [(clr(X )max X. ϕ0 )/X]), ψ) = (rϕ , rψ )

(C.677)
By (C.677) and substitution we know
excl((if c then ϕ1 else ϕ2 )[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

(C.678)

∴ Subcase holds by (C.678).
k

k

Subcase ψ = &n=1 (rel(r)tt)n : By defn of excl (Def. 5.1.2) and since ψ = &n=1 (rel(r)tt)n we
know that no matter what ϕ is, the excl function always returns ε as the release set of ϕ.
220

C. Proofs for Auxiliary Lemmas

Hence by (C.671) and (C.672) we know
k

excl(if c then ϕ1 else ϕ2 , &n=1 (rel(r)tt)n ) =
k

excl((if c then ϕ1 else ϕ2 )[(clr(X )max X. ϕ0 )/X], &n=1 (rel(r)tt)n ) = (ε, rψ )

(C.679)

∴ Subcase holds by (C.679).
Case ϕ = tt: From our rule premises we know
excl(tt, ψ) = (rϕ , rψ )

(C.680)
(C.681)

[(clr(X )max X. ϕ )/X]
0

From (C.680) and since tt ≡ rel(ε) tt we can deduce
excl(rel(ε) tt, ψ) = (rϕ , rψ )

(C.682)

By (C.681), (C.682) and IH we know
excl((rel(ε) tt)[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

≡

excl(tt[(clr(X )max X. ϕ0 )/X], ψ) = (rϕ , rψ )

(C.683)

∴ Case holds by (C.683).
Case ϕ = Y: From our rule premises we know
excl(Y, ψ) = (rϕ , rψ )

(C.684)
(C.685)

[(clr(X )max X. ϕ )/X]
0

k

By defn of excl (Def. 5.1.2) we know that (C.684) can only be true if ψ = &n=1 (rel(r)tt)n
(because otherwise, excl would have returned ⊥). Hence we know
k

ψ =&n=1 (rel(r)tt)n

(C.686)

Since we know (C.686), then we also know that no matter what ϕ is (i.e., irrespective of
whether Y = X or not), the excl function always returns ε as the release set of ϕ. Hence
by (C.684), (C.685) and defn of excl (Def. 5.1.2) we can deduce
k

excl(Y, &n=1 (rel(r)tt)n ) =
k

excl(Y[(clr(X )max X. ϕ0 )/X],&n=1 (rel(r)tt)n ) = (ε, rψ )

∴ Case holds by (C.687).

(C.687)


221

References
[1] Akka website. http://www.akka.io.
[2] An architectural blueprint for autonomic computing. Technical report, IBM, 2005.
[3] L. Aceto and A. Ingólfsdttir. Testing hennessy-milner logic with recursion. In
FoSSaCS, volume 1578 of LNCS, pages 41–55. Springer, 1999.
[4] G. Agha. Actors: A Model of Concurrent Computation in Distributed Systems.
MIT Press, Cambridge, MA, USA, 1986.
[5] G. A. Agha, P. Thati, and R. Ziaei. Formal methods for distributed processing.
chapter Actors: A Model for Reasoning About Open Distributed Systems, pages
155–176. Cambridge University Press, New York, NY, USA, 2001.
[6] J. H. Andrews and Y. Zhang. General test result checking with log file analysis.
IEEE Trans. Software Eng., 29:634–648, 2003.
[7] J. Armstrong. Programming Erlang. The Pragmatic Bookshelf, 2007.
[8] H. Barringer, Y. Falcone, K. Havelund, G. Reger, and D. E. Rydeheard. Quantified
event automata: Towards expressive and efficient runtime monitors. In FM, volume
7436 of LNCS, pages 68–84. Springer, 2012.
[9] A. Bauer and Y. Falcone. Decentralised ltl monitoring. In Formal Methods (FM),
volume 7436 of LNCS, pages 85–100. Springer, 2012.
[10] A. Bauer, M. Leucker, and C. Schallhart. Runtime verification for ltl and tltl. ACM
Trans. Softw. Eng. Methodol., 20:14:1–14:64, 2011.
[11] E. Bodden. Efficient hybrid typestate analysis by determining continuationequivalent states. ICSE ’10, pages 5–14, New York. ACM.
[12] E. Bodden, F. Chen, and G. Rosu. Dependent advice: A general approach to optimizing history-based aspects. In Proceedings of the 8th ACM International Conference on Aspect-oriented Software Development, AOSD ’09, pages 3–14, New
York, NY, USA, 2009. ACM.

222

References

[13] E. Bodden and P. Lam. Clara: Partially evaluating runtime monitors at compile
time. In Runtime Verification, volume 6418 of LNCS, pages 74–88. Springer Berlin
Heidelberg, 2010.
[14] R. Carlsson. An introduction to core erlang. In PLI01 (Erlang Workshop), 2001.
[15] I. Cassar, A. Francalanza, and S. Said. Improving runtime overheads for detecter.
In FESCA, London, UK, 2015, volume 178 of EPTCS, pages 1–8, 2015.
[16] I. Castellani, M. Dezani-Ciancaglini, and J. A. Pérez. Self-adaptation and secure
information flow in multiparty structured communications: A unified perspective.
In BEAT, pages 9–18, 2014.
[17] F. Cesarini and S. Thompson. ERLANG Programming. O’Reilly, 1st edition, 2009.
[18] F. Chen and G. Roşu. Java-MOP: A monitoring oriented programming environment
for Java. In TACAS’05, volume 3440 of LNCS, pages 546–550. Springer, 2005.
[19] F. Chen and G. Roşu. MOP: An Efficient and Generic Runtime Verification Framework. In OOPSLA, pages 569–588. ACM press, 2007.
[20] E. M. Clarke, Jr., O. Grumberg, and D. A. Peled. Model Checking. MIT Press,
Cambridge, MA, USA, 1999.
[21] C. Colombo. Practical runtime monitoring with impact guarantees of java programs
with real-time constraints. Master’s thesis, University of Malta, 2008. MSc(ICT)
Thesis.
[22] C. Colombo. Runtime Verification and Compensations. PhD thesis, University Of
Malta, 2013.
[23] C. Colombo, A. Francalanza, and R. Gatt. Elarva: A monitoring tool for erlang. In
RV, volume 7186 of LNCS, pages 370–374. Springer, 2011.
[24] C. Colombo, A. Francalanza, R. Mizzi, and G. J. Pace. polylarva: Runtime verification with configurable resource-aware monitoring boundaries. In SEFM, pages
218–232, 2012.
[25] C. Colombo, G. Pace, and G. Schneider. Larva — Safer monitoring of real-time
java programs (Tool paper). In SEFM, pages 33–37, 2009.
[26] C. Colombo and G. J. Pace. Fast-forward runtime monitoring - an industrial case
study. In RV, volume 7687 of LNCS, pages 214–228. Springer, 2012.
[27] C. Colombo, G. J. Pace, and P. Abela. Safer asynchronous runtime monitoring
using compensations. Form. Methods Syst. Des., 41(3):269–294, 2012.
[28] X. Corporation and P. A. R. Center. The aspectj programming guide, 2012.
http://eclipse.org/aspectj/doc/next/progguide/index.html.
223

References

[29] G. Coulouris, J. Dollimore, T. Kindberg, and G. Blair. Distributed Systems: Concepts and Design. Addison-Wesley Publishing Company, USA, 5th edition, 2011.
[30] M. d’Amorim and K. Havelund. Event-based runtime verification of java programs.
SIGSOFT Softw. Eng. Notes, 30(4):1–7, 2005.
[31] B. D’Angelo, S. Sankaranarayanan, C. Sánchez, W. Robinson, B. Finkbeiner, H. B.
Sipma, S. Mehrotra, and M. Z. LOLA: Runtime Monitoring of Synchronous Systems. In TIME, pages 166–174. IEEE, 2005.
[32] N. Decker, M. Leucker, and D. Thoma. jUnitRV - adding runtime verification to
jUnit. In NASA Formal Methods, volume 7871 of LNCS, pages 459–464. SpringerVerlag Berlin Heidelberg, Springer, 2013.
[33] N. Delgado, A. Q. Gates, and S. Roach. A taxonomy and catalog of runtime
software-fault monitoring tools. IEEE Trans. Softw. Eng., 30(12):859–872, 2004.
[34] D. Drusinsky. Monitoring temporal rules combined with time series. In Computer
Aided Verification, volume 2725 of LNCS, pages 114–117. Springer, 2003.
[35] A. Erdödi. Exago: Property monitoring via log file analysis. (Presented at the
Erlang User Group Meeting, London, 2010).
[36] Y. Falcone, J.-C. Fernandez, and L. Mounier. What can you verify and enforce at
runtime? STTT, 14(3):349–382, 2012.
[37] Y. Falcone, M. Jaber, T.-H. Nguyen, M. Bozga, and S. Bensalem. Runtime verification of component-based systems. In SEFM, volume 7041 of LNCS, pages
204–220. Springer, 2011.
[38] A. Francalanza, A. Gauci, and G. J. Pace. Distributed System Contract Monitoring.
JLAP, 82(5-7):186–215, 2013.
[39] A. Francalanza and A. Seychell. Synthesising Correct concurrent Runtime Monitors. Formal Methods in System Design (FMSD), pages 1–36, 2014.
[40] L.-Å. Fredlund. A Framework for Reasoning about Erlang Code. PhD thesis,
Royal Institute of Technology, Stockholm, Sweden, 2001.
[41] C. Galea Valletta. Augmenting synchronous and asynchronous monitors in elarva.
Final Year Project, 2013.
[42] H. Goldsby, B. Cheng, and J. Zhang. Amoeba-rt: Run-time verification of adaptive
software. In Models in Software Engineering, volume 5002 of LNCS, pages 212–
224. Springer Berlin Heidelberg, 2008.
[43] P. Haller and F. Sommers. Actors in Scala. Artima Inc., USA, 2012.

224

References

[44] K. Havelund. A scala dsl for rete-based runtime verification. In Runtime Verification, volume 8174 of LNCS, pages 322–327. Springer Berlin Heidelberg, 2013.
[45] K. Havelund and G. Roşu. Monitoring java programs with java pathexplorer. In
RV (in connection with CAV), volume 55:2 of ENTCS, pages 200–217, 2001.
[46] A. Hernandez.
Yaws 1.89: Directory traversal vulnerability.
Online
at http://www.exploit-db.com/exploits/15371/, 2010.
Accessed on
1/12/2014.
[47] A. Holovaty and J. Kaplan-Moss. The Definitive Guide to Django: Web Development Done Right, Second Edition. Apress, Berkely, CA, USA, 2nd edition, 2009.
[48] F. Irmert, T. Fischer, and K. Meyer-Wegener. Runtime adaptation in a serviceoriented component model. SEAMS ’08, pages 97–104. ACM, 2008.
[49] G. Jacques-Silva, B. Gedik, R. Wagle, K.-L. Wu, and V. Kumar. Building userdefined runtime adaptation routines for stream processing applications. Proc.
VLDB Endow., 5(12):1826–1837, 2012.
[50] M. A. Kalareh. Evolving Software Systems for Self-Adaptation. PhD thesis, University of Waterloo, Ontario, Canada, 2012.
[51] S. Kell. A survey of practical software adaptation techniques. J. UCS, 14(13):2110–
2157, 2008.
[52] Z. Kessin. Building Web Applications with Erlang: Working with REST and Web
Sockets on Yaws. O’Reilly Media, 2012.
[53] M. Kim, M. Viswanathan, S. Kannan, I. Lee, and O. Sokolsky. Java-MaC: A runtime assurance approach for Java programs. Formal Methods in System Design,
24(2):129–155, 2004.
[54] A. Krasnopolski. AOP for Erlang. http://erlaop.sourceforge.net/.
[55] M. Leucker and C. Schallhart. A brief account of Runtime Verification. JLAP,
78(5):293 – 303, 2009.
[56] J. Ligatti, L. Bauer, and D. Walker. Edit automata: enforcement mechanisms for
run-time security policies. IJIS, 4(1-2):2–16, 2005.
[57] J. Ligatti and S. Reddy. A theory of runtime enforcement, with results. In Computer
Security ESORICS 2010, volume 6345 of LNCS, pages 87–100. Springer Berlin
Heidelberg, 2010.
[58] M. Logan, E. Merritt, and R. Carlsson. Erlang and OTP in Action. Manning, 2011.
[59] F. Long, V. Ganesh, M. Carbin, S. Sidiroglou, and M. Rinard. Automatic input
rectification. ICSE, pages 80–90. IEEE Press, 2012.
225

References

[60] F. Long, S. Sidiroglou-Douskos, and M. Rinard. Automatic runtime error repair
and containment via recovery shepherding. SIGPLAN Not., 49:227–238.
[61] P. O. Meredith and G. Rosu. Runtime verification with the rv system. In RV,
volume 6418 of LNCS, pages 136–152. Springer, 2010.
[62] R. Milner. Communication and concurrency. Prentice-Hall, Inc., Upper Saddle
River, NJ, USA, 1989.
[63] P. Oreizy, N. Medvidovic, and R. N. Taylor. Runtime software adaptation: Framework, approaches, and styles. ICSE Companion ’08, pages 899–910, New York,
NY, USA, 2008. ACM.
[64] B. C. Pierce, editor. Advanced Topics in Types and Prog. Languages. MIT Press,
2005.
[65] M. Rinard, C. Cadar, D. Dumitran, D. Roy, T. Leu, and W. Beebee. Enhancing
Availability & Security through Failure-oblivious Computing. OSDI, pages 303–
316. USENIX, 2004.
[66] G. Roşu and K. Havelund. Rewriting-based techniques for runtime verification.
Automated Software Engg., 12(2):151–197, 2005.
[67] S. Saudrais, A. Staikopoulos, and S. Clarke. Using specification models for runtime
adaptations.
[68] K. Sen, A. Vardhan, G. Agha, and G. Roşu. Efficient decentralized monitoring of
safety in distributed systems. ICSE, pages 418–427, 2004.
[69] W. R. Stevens and S. A. Rago. Advanced Programming in the UNIX Environment,
Second Edition. Addison-Wesley Professional, 2nd edition, 2008.
[70] H. Svensson, L.-Å. Fredlund, and C. Benac Earle. A unified semantics for future
erlang. In Erlang Workshop, pages 23–32. ACM, 2010.
[71] C. Talcott, M. Sirjani, and S. Ren. Comparing three coordination models: Reo, arc,
and rrd. ENTCS, 194(4):39–55, 2008.
[72] B. Venneri, M. Coppo, and M. Dezani-Ciancaglini. Self-adaptive monitors for
multiparty sessions. Euromicro, pages 688–696, 2014.
[73] S. Vinoski. Yaws: Yet another web server. IEEE Internet Computing, 15(4):90–94,
2011.
[74] J. Zhang and B. H. Cheng. Using temporal logic to specify adaptive program
semantics. Journal of Systems and Software, 79(10):1361 – 1369, 2006.

226

