Recent advances in the formal verification of message-passing programs are based on proving that programs correctly implement a given protocol. Many existing verification techniques for message-passing programs assume that at most one thread may attempt to send or receive on a channel endpoint at any given point in time, and expressly forbid endpoint sharing. Approaches that do allow such sharing often do not prove that channels obey their protocols. In this paper, we identify two principles that can guarantee obedience to a communication protocol even in the presence of endpoint sharing. Firstly, threads may concurrently use an endpoint in any way that does not advance the state of the protocol. Secondly, threads may compete for receiving on an endpoint provided that the successful reception of the message grants them ownership of that endpoint retrospectively. We develop a program logic based on separation logic that unifies these principles and allows fine-grained reasoning about endpoint-sharing programs. We demonstrate its applicability on a number of examples. The program logic is shown sound against an operational semantics of programs, and proved programs are guaranteed to follow the given protocols and to be free of data races, memory leaks, and communication errors. © 2014 Published by Elsevier B.V.
IntroductionMessage-passing idioms appear everywhere in today's software: from the Message Passing Interface (MPI) used in highperformance computing, to the inter-process communication layer in Android apps, and to Web services. As for other forms of concurrency, naively checking the correctness of a message-passing system is severely impaired by the combinatorial explosion of the number of possible interactions between the components of the system. One way to tackle this issue is to develop formal verification techniques for message-passing programs, such that reasoning about a system is tantamount to reasoning about each component in isolation. A promising avenue in this respect is to separate the study of programs from the study of the protocols they are meant to implement, i.e. prove that a program correctly implements a protocol on the one hand, and reason about that protocol independently of its implementation on the other hand. In this context, protocols act both as specifications of what a program is allowed to do and as descriptions of the actions that programs must expect from the environment. Two main approaches coexist for describing such protocols: session types on the one hand [30], used to police interactions in programs expressed either in the π -calculus [20] or in a message-passing variant of Java [21], and channel contracts on the other hand [7], used for instance to describe the protocols in the Sing programming language [13] developed for the Singularity operating system [22]. High-level protocol descriptions such as these allow the program verification effort to be split between checking properties at the level of the protocol itself on the one hand, and checking obedience of each threa...