REACHABILITY UNDER CONTEXTUAL LOCKING

. The pairwise reachability problem for a multi-threaded program asks, given control locations in two threads, whether they can be simultaneously reached in an execution of the program. The problem is important for static analysis and is used to detect statements that are concurrently enabled. This problem is in general undecidable even when data is abstracted and when the threads (with recursion) synchronize only using a ﬁnite set of locks. Popular programming paradigms that limit the lock usage patterns have been identiﬁed under which the pairwise reachability problem becomes decidable. In this paper, we consider a new natural programming paradigm, called contextual locking, which ties the lock usage to calling patterns in each thread: we assume that locks are released in the same context that they were acquired and that every lock acquired by a thread in a procedure call is released before the procedure returns. Our main result is that the pairwise reachability problem is polynomial-time decidable for this new programming paradigm as well. The problem becomes undecidable if the locks are reentrant; reentrant locking is a recursive locking mechanism which allows a thread in a multi-threaded program to acquire the reentrant lock multiple times.


Introduction
In static analysis of sequential programs [8], such as control-flow analysis, data-flow analysis, points-to analysis, etc., the semantics of the program and the data that it manipulates is abstracted, and the analysis concentrates on computing fixed-points over a lattice using the control-flow in the program.For instance, in flow-sensitive context-sensitive pointsto analysis, a finite partition of the heap locations is identified, and the analysis keeps track of the set of possibilities of which variables point may point to each heap-location partition, propagating this information using the control-flow graph of the program.In fact, several static analysis questions can be formulated as reachability in a pushdown system that captures the control-flow of the program (where the stack is required to model recursion) [11].
In concurrent programs, abstracting control-flow is less obvious, due to the various synchronization mechanisms used by threads to communicate and orchestrate their computations.One of the most basic questions is pairwise reachability: given two control locations pc 1 and pc 2 in two threads of a concurrent program, are these two locations simultaneously reachable?This problem is very basic to static analysis, as many analysis techniques would, when processing pc 1 , take into account the interference of concurrent statements, and hence would like to know if a location like pc 2 is concurrently reachable.Data-races can also be formulated using pairwise reachability, as it amounts to asking whether a read/write to a location (or an abstract heap region) is concurrently reachable with a write to the same location (or region).More sophisticated verification techniques like deductive verification can also utilize such an analysis.For instance, in an Owicki-Gries style proof [9] of a concurrent program, the invariant at pc 1 must be proved to be stable with respect to concurrent moves by the environment, and hence knowing whether pc 2 is concurrently reachable will help determine whether the statement at pc 2 need be considered for stability.
Pairwise reachability of control locations is hence an important problem.Given that individual threads may employ recursion, this problem can be modeled as reachability of multiple pushdown systems that synchronize using the synchronization constructs in the concurrent program, such as locks, barriers, etc.However, it turns out that even when synchronization is limited to using just locks, pairwise reachability is undecidable [10].Consequently, recently, many natural restrictions have been identified under which pairwise reachability is decidable.
One restriction that yields a decidable pairwise reachability problem is nested locking [6,5]: if each thread performs only nested locking (i.e.locks are released strictly in the reverse order in which they are acquired), then pairwise reachability is known to be decidable [6].The motivation for nested locking is that many high-level locking constructs in programming languages naturally impose nested locking.For instance the synchronize(o) {...} statement in Java acquires the lock associated with o, executes the body, and releases the lock, and hence nested synchronized blocks naturally model nested locking behaviors.The usefulness of the pairwise reachability problem was demonstrated in [6] where the above decision procedure for nested locking was used to find bugs in the Daisy file system.Nested locking has been generalized to the paradigm of bounded lock chaining for which pairwise reachability has also been proved to be decidable [3,4].
In this paper, we study a different restriction on locking, called contextual locking.A program satisfies contextual locking if each thread, in every context, acquires new locks and releases all these locks before returning from the context.Within the context, there is no requirement of how locks are acquired and released; in particular, the program can acquire and release locks in a non-nested fashion or have unbounded lock chains.
The motivation for contextual locking comes from the fact that this is a very natural restriction.First, note that it's very natural for programmers to release locks in the same context they were acquired; this makes the acquire and release occur in the same syntactic code block, which is a very simple way of managing lock acquisitions.
Secondly, contextual locking is very much encouraged by higher-level locking constructs in programming languages.For example, consider the code fragment of a method, in Java [7] shown in Figure 1.The above code takes the lock associated with done followed later by a lock associated with object r.In order to proceed, it wants done to be equal to 1 (a signal public void m() { synchronized(done) { ... synchronized(r) { ... while (done=0) try { done.wait();} ... } Figure 1: Synchronized blocks in Java from a concurrent thread, say, that it has finished some activity), and hence the thread waits on done, which releases the lock for done, allowing other threads to proceed.When some other thread issues a notify, this thread wakes up, reacquires the lock for done, and proceeds.
Notice that despite having synchronized blocks, the wait() statement causes releases of locks in a non-nested fashion (as it exhibits the sequence acq lock done; acq lock r; rel lock done; acq lock done; rel lock r; rel lock done).However, note that the code above does satisfy contextual locking; the lock m acquires are all released before the exit, because of the synchronized-statements.Thus, we believe that contextual locking is a natural restriction that is adhered to in many programs.
The first result of this paper is that pairwise reachability is decidable under the restriction of contextual locking.It is worth pointing out that this result does not follow from the decidability results for nested locking or bounded lock chains [6,3].Unlike nested locking and bounded lock chains, contextual locking imposes no restrictions on the locking patterns in the absence of recursive function calls; thus, programs with contextual locking may not adhere to the nested locking or bounded lock chains restrictions.Second, the decidability of nested locking and bounded lock chains relies on a non-trivial observation that the number of context switches needed to reach a pair of states is bounded by a value that is independent of the size of the programs. 1However, such a result of a bounded number of context switches does not seem to hold for programs with contextual locking.Thus, the proof techniques used to establish decidability are different as well.
We give a brief outline of the proof ideas behind our decidability result.We observe that if a pair of states is simultaneously reachable by some execution, then they are also simultaneously reachable by what we call a well-bracketed computation.A concurrent computation of two threads is not well-bracketed, if in the computation one process, say P 0 , makes a call which is followed by the other process (P 1 ) making a call, but then P 0 returns from its call before P 1 does (but after P 1 makes the call).We then observe that every well-bracketed computation of a pair of recursive programs can simulated by a single recursive program.Thus, decidability in polynomial time follows from observations about reachability in pushdown systems [1].
The second result of the paper concerns reentrant locks.The standard mutex locks are blocking, i.e., if a lock is held by some thread, then any attempt to acquire it by any thread (including the owning thread) fails and the requesting thread blocks.Some programming languages such as Java support (non-blocking) reentrant locks.Reentrant locks are recursive locks; if a thread attempts to acquire a reentrant lock it already holds then the thread succeeds.The lock becomes free only when the owning thread releases the lock as many times as it acquires the lock.
We consider the case of multi-threaded programs synchronizing through reentrant locks and show that the pairwise reachability problem is undecidable for contextual reentrant locking (even for non-recursive programs).The undecidability result is obtained by a reduction from the emptiness problem of a 2-counter machine.As the locking mechanism is reentrant, a counter can be simulated by a lock.Counter increment and counter decrement can be simulated by lock acquisition and lock release respectively.The "zero-test" in the reduction is simulated by synchronization between the threads.
The rest of the paper is organized as follows.Section 2 introduces the model of concurrent pushdown systems communicating via locks and presents its semantics.Our main decidability result is presented in Section 3. The undecidability result for contextual reentrant locking is presented in Section 4. Conclusions are presented in Section 5.
Note: An extended abstract of the paper co-authored by Rohit Chadha, P. Madhusudan and Mahesh Viswanathan appeared in [2] and contains the decidability result for pairwise reachability under contextual (non-reentrant) locking.The undecidability result for contextual reentrant locking was obtained subsequently by Rémi Bonnet and Rohit Chadha, and has not been presented elsewhere.

Model
The set of natural numbers shall be denoted by N. The set of functions from a set A to B shall be denoted by B A .Given a function f ∈ B A , the function f | a →b shall be the unique function g defined as follows: g(a) = b and for all a ′ = a, g(a 2.1.Pushdown Systems.For static analysis, recursive programs are usually modeled as pushdown systems.Since we are interested in modeling threads in concurrent programs we will also need to model locks for communication between threads.Formally, Definition: Given a finite set Lcks, a pushdown system (PDS) P using Lcks is a tuple (Q, Γ, qs, δ) where For a PDS P, the semantics is defined as a transition system.The configuration of a PDS P is the product of the set of control states Q and the stack which is modeled as a word over the stack alphabet Γ.For a thread P using Lcks, we have to keep track of the locks being held by P. Thus the set of configurations of P using Lcks is where 2 Lcks is the powerset of Lcks.Furthermore, the transition relation is no longer just a relation between configurations but a binary relation on 2 Lcks × Conf P since the thread now executes in an environment, namely, the free locks (i.e., locks not being held by any other thread).Formally, Definition: A PDS P = (Q, Γ, qs, δ) using Lcks gives a labeled transition relation where Labels = {int, cll, rtn} ∪ {acq(l), rel(l) | l ∈ Lcks} and −→ P is defined as follows.

2.2.
Multi-pushdown systems.Concurrent programs are modeled as multi-pushdown systems.For our paper, we assume that threads in a concurrent program communicate only through locks which leads us to the following definition.Definition: Given a finite set Lcks, a n-pushdown system (n-PDS) CP communicating via Lcks is a tuple (P 1 , . . ., P n ) where each P i is a PDS using Lcks.
Given a n-PDS CP, we will assume that the set of control states and the stack symbols of the threads are mutually disjoint.Definition: The semantics of a n-PDS CP = (P 1 , . . ., P n ) communicating via Lcks is given as a labeled transition system T = (S, s 0 , −→) where • S is said to be the set of configurations of CP and is the set , where Q i is the set of states of P i and Γ i is the stack alphabet of P i .• s 0 is the initial configuration and is ((qs 1 , ǫ, ∅), • • • , (qs m , ǫ, ∅)) where qs i is the initial state of P i .• The set of labels on the transitions is Labels × {1, . . ., n} where Labels ) and for all j = i, q j = q ′ j , w j = w ′ j and hld j = hld ′ j .Notation: −→ s m such that s 0 is the initial configuration of CP.The label of the computation Comp, denoted Label(Comp), is said to be the word (λ −→ s j+1 is said to be a procedure call by thread i.Similarly, we can define procedure return, internal action, acquisition of lock l and release of lock l by thread i.A procedure return Example 2.1.Consider the two-threaded program showed in Figure 2.For sake of convenience, we only show the relevant actions of the programs.Figure 3 shows computations whose labels are as follows: Label(Comp1) = (cll, 0)(acq(l1), 0)(cll, 1)(acq(l2), 0)(rel(l1), 0)(acq(l1), 1) (rel(l2), 0)(rtn, 0)(rel(l1), 1)(rtn, 1) and  2.4.Contextual locking.In this paper, we are considering the pairwise reachability problem when the threads follow contextual locking.Informally, this means that-• every lock acquired by a thread in a procedure call must be released before the corresponding return is executed, and • the locks held by a thread just before a procedure call is executed are not released during the execution of the procedure.Formally, Definition: A thread i in a n-PDS CP = (P 1 , . . ., P n ) is said to follow contextual locking if whenever s ℓ (cll,i) −→ s ℓ+1 and s j (rtn,i) −→ s j+1 are matching procedure call and return along a Example 2.2.Consider the 2-threaded program shown in Figure 2.Both the threads P0 and P1 follow contextual locking.The program P2 in Figure 4 does not follow contextual locking.
Example 2.3.Consider the 2-threaded program in Figure 5.The two threads P3 and P4 follow contextual locking as there is no recursion!However, the two threads do not follow either the discipline of nested locking [6] or of bounded lock chaining [3].Hence, algorithms of [6,3] cannot be used to decide the pairwise reachability question for this program.Notice that the computations of this pair of threads require an unbounded number of context switches as the two threads proceed in lock-step fashion.The locking pattern exhibited by these threads can present in any program with contextual locking as long as this pattern is within a single calling context (and not across calling contexts).Such locking patterns when used in a non-contextual fashion form the crux of undecidability proofs for multi-threaded programs synchronizing via locks [6].The pairwise reachability problem for a multi-threaded program asks whether two given states in two threads can be simultaneously reached in an execution of the program.Formally, Definition: Given a n-PDS CP = (P 1 , . . ., P n ) communicating via Lcks, indices 1 ≤ i, j ≤ n with i = j, and control states q i and q j of threads P i and P j respectively, Reach(CP, q i , q j ) denote the predicate that there is a computation s 0 −→ • • • −→ s m of CP such that CntrlSt i (s m ) = q i and CntrlSt j (s m ) = q j .The pairwise control state reachability problem asks if Reach(CP, q i , q j ) is true.
The pairwise reachability problem for multi-threaded programs communicating via locks was first studied in [10], where it was shown to be undecidable.Later, Kahlon et.al. [6] showed that when the locking pattern is restricted the pairwise reachability problem is decidable.In this paper, we will show that the problem is decidable for multi-threaded programs in which each thread follows contextual locking.Before we show this result, note that it suffices to consider programs with only two threads [6].Proposition 3.1.Given a n-PDS CP = (P 1 , . . ., P n ) communicating via Lcks, indices 1 ≤ i, j ≤ n with i = j and control states q i and q j of P i and P j respectively, let CP i,j be the 2-PDS (P i , P j ) communicating via Lcks.Then Reach(CP, q i , q j ) iff Reach(CP i,j , q i , q j ).
Thus, for the rest of the section, we will only consider 2-PDS.

3.1.
Well-bracketed computations.The key concept in the proof of decidability is the concept of well-bracketed computations, defined below.
Definition: Let CP = (P 0 , P 1 ) be a 2-PDS via Lcks and let Comp = s 0 (λ 1 1 ) −→ s m be a computation of CP.Comp is said to be non-well-bracketed if there exist 0 ≤ ℓ 1 < ℓ 2 < ℓ 3 < m and i ∈ {0, 1} such that −→ s ℓ 3 +1 are matching call and returns of P i , and is a procedure call of thread P 1−i whose matching return either occurs after ℓ 3 + 1 or does not occur at all.Furthermore, the triple (ℓ 1 , ℓ 2 , ℓ 3 ) is said to be a witness of non-well-bracketing of Comp.
Comp is said to be well-bracketed if it is not non-well-bracketed.
Example 3.2.Recall the 2-threaded program from Example 2.1 shown in Figure 2. The computation Comp1 (see Figure 3) is non-well-bracketed, while the computation Comp2 (see Figure 3) is well-bracketed.On the other hand, all the computations of the 2-threaded program in Example 2.3 (see Figure 5) are well-bracketed as the two threads are nonrecursive.
If there is a computation that simultaneously reaches control states p ∈ P 0 and q ∈ P 1 then there is a well-bracketed computation that simultaneously reaches p and q: Lemma 3.3.Let CP = (P 0 , P 1 ) be a 2-PDS communicating via Lcks such that each thread follows contextual locking.Given control states p ∈ P 0 and q ∈ P 1 , we have that Reach(CP, p, q) iff there is a well-bracketed computation s wb 0 −→ • • • −→ s wb r of CP such that CntrlSt 0 (s wb r ) = p and CntrlSt 1 (s wb r ) = q. Proof.
−→ s m be a non-well-bracketed computation that simultaneously reaches p and q.Let ℓ mn be the smallest ℓ 1 such that there is a witness (ℓ 1 , ℓ 2 , ℓ 3 ) of non-well-bracketing of Comp nwb .Observe now that it suffices to show that there is another computation Comp mod of the same length as Comp nwb that simultaneously reaches p and q and • either Comp mod is well-bracketed, • or if Comp mod is non-well-bracketed then for each witness (ℓ ′ 1 , ℓ ′ 2 , ℓ ′ 3 ) of non-well-bracketing of Comp mod , it must be the case ℓ ′ 1 > ℓ mn .We show how to construct Comp mod .Observe first that any witness (ℓ mn , ℓ 2 , ℓ 3 ) of non-wellbracketing of Comp nwb must necessarily agree in the third component ℓ 3 .Let ℓ rt denote this component.Let ℓ sm be the smallest ℓ 2 such that (ℓ mn , ℓ 2 , ℓ rt ) is a witness of nonwell-bracketing of Comp mod .Thus, the transition s ℓmn −→ s ℓmn+1 and s ℓrt −→ s ℓrt+1 are matching procedure call and return of some thread P r while the transition s ℓsm −→ s ℓsm+1 is a procedure call of c ′ by thread P 1−r whose return happens only after ℓ rt .Without loss of generality, we can assume that r = 0.
The fact that if Comp mod is non-well-bracketed then there is no witness (ℓ ′ 1 , ℓ ′ 2 , ℓ ′ 3 ) of nonwell-bracketing with ℓ ′ 1 ≤ ℓ mn will follow from the following observations on Label(Comp nwb ).† v 1 cannot contain any returns of P 1 which have a matching call that occurs in u (by construction of ℓ mn ).† † All calls of P 1 in v 1 must return either in v 1 or after c ′ is returned.But the latter is not possible (by construction of ℓ sm ).Thus, all calls of P 1 in v 1 must return in v 1 .
Formally, Comp mod is constructed as follows.We fix some notation.For each 0 ≤ j ≤ m, let Conf j 0 = Conf 0 (s j ) and Conf j 1 = Conf 1 (s j ).Thus s j = (Conf j 0 , Conf j 1 ).( 1) The first ℓ sm transitions of Comp mod are the same as Comp nwb , i.e., initially (2) Consider the sequence of transitions s ℓsm Let k be the number of transitions of P 0 in this sequence and let be the indices such that s jn (λ jn+1 ,0) −→ s jn+1 .Note that it must be the case that for each 1 ≤ n < k, . Observe now that, thanks to contextual locking, the set of locks held by P 1 in Conf ℓsm 1 is a subset of the locks held by P 1 in Conf for each 1 ≤ n ≤ k.Thus we can extend Comp mod by applying the k transitions of P 0 used to obtain s jn −→ s jn+1 in Comp nwb .In other words, Comp mod is now . Thus the set of locks held by P 0 in s ′ ℓsm+k is exactly the set of locks held by P 0 at Conf ℓmn 0 .
(3) Consider the sequence of transitions s ℓsm −→ s ℓ rt+1 in Comp.Let t be the number of transitions of P 1 in this sequence and let −→ s jn+1 .Note that it must the case that for each 1 ≤ n < t, ).
Observe now that, thanks to contextual locking, the set of locks held by P 0 in Conf ℓrt+1 0 is exactly the set of locks held by P 0 at Conf ℓmn 0 and the latter is a subset of the locks held by P 0 in Conf ℓ jn 0 for each 1 ≤ n ≤ t.Thus we can extend Comp mod by applying the t transitions of P 1 used to obtain s jn −→ s jn+1 in Comp nwb .In other words, Comp mod is now −→ s ′ ℓsm+k+t .Observe now that the extended Comp mod is a sequence of rt + 1 transitions and that the final configuration of Comp mod , s ′ ℓsm+k+t = (Conf rt+1 0 , Conf rt+1

1
) is exactly the configuration s rt+1 .(4) Thus, now we can extend Comp mod as Clearly, Comp mod has the same length as Comp nwb and simultaneously reaches p and q.
3.2.Deciding pairwise reachability.We are ready to show that the problem of checking pairwise reachability is decidable.

Theorem 3.4.
There is an algorithm that given a 2-threaded program CP = (P 0 , P 1 ) communicating via Lcks such that P 0 and P 1 follow contextual locking, and control states p and q of P 0 and P 1 respectively decides if Reach(P, p, q) is true or not.Furthermore, if m and n are the sizes of the programs P 0 and P 1 and ℓ the number of elements of Lcks, then this algorithm has a running time of 2 O(ℓ) O((mn) 3 ).
Proof.The main idea behind the algorithm is to construct a single PDS P comb = (Q, Γ, qs, δ) which simulates all the well-bracketed computations of CP.P comb simulates a well-bracketed computation as follows.The set of control states of P comb is the product of control states of P 0 and P 1 .The single stack of P comb keeps track of the stacks of P 0 and P 1 : it is the sequence of those calls of the well-bracketed computation which have not been returned.Furthermore, if the stack of P comb is w then the stack of P 0 is the projection of w onto the stack symbols of P 0 and the stack of P 1 is the projection of w onto the stack symbols of P 1 .Thus, the top of the stack is the most recent unreturned call and if it belongs to P i , well-bracketing ensures that no previous unreturned call is returned without returning this call.

Reentrant locking
We now turn our attention to reentrant locking.Recall that a reentrant lock is a recursive lock which allows the thread owning a lock to acquire the lock multiple times; the owning thread must release the lock an equal number of times before another thread can acquire the lock.Thus, the set of configurations of a thread P using reentrant locks is the set Q × Γ * × N Lcks (and not the set Q × Γ * × 2 Lcks as in non-reentrant locks).Intuitively, the elements of a configuration (q, w, hld) of P now have the following meaning: q is the control state of P, w the contents of the stack and hld : Lcks → N is a function that tells the number of times each lock has been acquired by P. The semantics of a PDS P using reentrant Lcks is formally defined as: 2 Definition: A PDS P = (Q, Γ, qs, δ) using reentrant Lcks gives a labeled transition relation where Labels = {int, cll, rtn} ∪ {acq(l), rel(l) | l ∈ Lcks} and −→ P is defined as follows.
• fr : (q, w, hld) rel(l) −→ P fr ∪ {l} : (q ′ , w, hld| l →0 ) if ((q, l), q ′ ) ∈ δ rel and hld(l) = 1.The semantics of n-PDS CP = (P 1 , . . ., P n ) communicating via reentrant Lcks is given as a transition system on (Q ) where Q i and Γ i are the set of states and the stack alphabet of process P i respectively.Formally, Definition: The semantics of a n-PDS CP = (P 1 , . . ., P n ) communicating via reentrant Lcks is given as a labeled transition system T = (S, s 0 , −→) where • S is said to be the set of configurations of CP and is the set , where Q i is the set of states of P i and Γ i is the stack alphabet of P i .• The initial configuration s 0 is (qs 1 , ǫ, 0), • • • , (qs m , ǫ, 0) where qs i is the initial local state of P i and 0 ∈ N Lcks is the function which takes the value 0 for each l ∈ Lcks.• The set of labels on the transitions is Labels × {1, . . ., n} where Labels = {int, cll, rtn} ∪ {acq(l), rel(l) | l ∈ Lcks}.The labeled transition relation (λ,i) −→ is defined as follows.
4.1.Contextual locking.We now adapt contextual locking to reentrant locks.Informally, contextual reentrant locking means that-• each instance of a lock acquired by a thread in a procedure call must be released before the corresponding return is executed, and • the instances of locks held by a thread just before a procedure call is executed are not released during the execution of the procedure.Formally, 2 The definition of PDS P using reentrant locks P is exactly the definition of PDS P using locks P (see Section 2).out, thread P 0 1 holds one instance of lock r i and thread P 0 2 holds one instance of lock t i .To carry out the zero-test, P 0 1 carries out the following sequence of lock acquisitions and releases: acq(t i )rel(r i )acq(l i )rel(t i )acq(r i )rel(l i ) and P 0 2 carries out the following sequence: acq(l i )rel(t i )acq(r i )rel(l i )acq(t i )rel(r i ).
Intuitively, the zero-test "commences" by P 0 2 trying to acquire lock l i .If the lock acquisition succeeds then P 0 2 "learns" that counter i is indeed zero.P 0 2 then releases the lock t i in order to "inform" P 0 1 that the counter i is 0 and "waits" for thread P 0 1 to update its state.To test that counter i is indeed 0, thread P 0 1 tries to acquire lock t i .If it succeeds in acquiring the lock then thread P 0 1 learns that counter i is indeed 0. It updates its state, releases lock r i to "inform" thread P 0 2 that it has updated the state.Thread P 0 2 learns that P 0 1 has updated its state by acquiring lock r i .Now, P 0 2 releases lock l i so that it can "tell" P 0 1 to release the lock t i .Thread P 0 1 acquires lock l i ; releases lock t i and waits for lock r i to be released.P 0 2 can now acquire the lock t i .After acquiring t i , P 0 2 releases lock r i and transitions back to the state q * .Thread P 0 1 acquires lock r i and releases lock l i .Now, we have that P 0 1 holds lock r i and P 0 2 holds lock t i as before and nobody holds l i .Now P 0 1 can continue simulating M.
Let hld ǫ, hld 1 ), (q * , ǫ, hld 2 )).The construction of P 0 ensures that there is a computation of CP 0 starting from init and ending in a state ((q f , ǫ, hld ′ 1 ), (q * , ǫ, hld ′ 2 )) for some hld ′ 1 , hld ′ 2 iff M halts.Now, CP = (P 1 , P 2 ) is constructed from CP 0 as follows.The thread P 1 initially performs the following sequence of lock acquisitions and releases: acq(h ′ )acq(r 1 )acq(r 2 )acq(h)rel(h ′ ); makes a transition to q s and starts behaving like thread P 0 1 .The thread P 2 initially performs the following sequence of lock acquisitions and releases: acq(h)acq(t 1 )acq(t 2 )rel(h)acq(h ′ ); makes a transition to q * and starts behaving like thread P 0 2 .The construction ensures that Reach(CP, q f , q * ) is true iff M halts.The formal construction of CP has been carried out in the Appendix.

Conclusions
The paper investigates the problem of pairwise reachability of multi-threaded programs communicating using only locks.We identified a new restriction on locking patterns, called contextual locking, which requires threads to release locks in the same calling context in which they were acquired.Contextual locking appears to be a natural restriction adhered to by many programs in practice.The main result of the paper is that the problem of pairwise reachability is decidable in polynomial time for programs in which the locking scheme is contextual.Therefore, in addition to being a natural restriction to follow, contextual locking may also be more amenable to practical analysis.We observe that these results do not follow from results in [6,5,3,4] as there are programs with contextual locking that do not adhere to the nested locking principle or the bounded lock chaining principle.The proof principles underlying the decidability results are also different.Our results can also be mildly extended to handling programs that release locks a bounded stack-depth away from when they were acquired (for example, to handle procedures that call a function that acquires a lock, and calls another to release it before it returns).
There are a few open problems immediately motivated by the results on contextual locking in this paper.First, decidability of model checking with respect to fragments of LTL under the contextual locking restriction remains open.Next, while our paper establishes the decidability of pairwise reachability, it is open if the problem of checking if 3 (or more) threads simultaneously reach given local states is decidable for programs with contextual locking.Finally, from a practical standpoint, one would like to develop analysis algorithms that avoid to construct the cross-product of the two programs to check pairwise reachability.
We also considered the case of reentrant locking mechanism and established that the pairwise reachability under contextual reentrant locking is undecidable.The status of the pairwise reachability problem for the case when the locks are nested (and not necessarily contextual) is open.This appears to be a very difficult problem.Our reasons for believing this is that the problem of checking control state reachability in a PDS with one counter and no zero tests can be reduced to the problem of checking pairwise reachability problem in a 2-threaded program communicating via a single (and hence nested) reentrant lock.The latter is a long standing open problem.
For a more complete account for multi-threaded programs, other synchronization primitives such as thread creation and barriers should be taken into account.Combining lockbased approaches such as ours with techniques for other primitives is left to future investigation.

Figure 3 :
Figure 3: Computations Comp1 and Comp2.Transitions of P 0 are shown as solid edges while transition of P 1 are shown as dashed edges; hence the process ids are dropped from the label of transitions.Matching calls and returns are shown with dotted edges.

Figure 5 :
Figure 5: A 2-threaded program with unbounded lock chains

Figure 6 :
Figure 6: Computations Comp nwb and Comp mod .Transitions of P 0 are shown as solid edges and transitions of P 1 are shown as dashed edges; hence process ids are dropped from the label of transitions.Matching calls and returns are shown with dotted edges.Observe that all calls of P 1 in v 1 have matching returns within v 1 .

5. 1 .
Acknowledgements.P. Madhusudan was supported in part by NSF Career Award 0747041.Mahesh Viswanathan was supported in part by NSF CNS 1016791 and NSF CCF 1016989.Rohit Chadha was at LSV, ENS Cachan & INRIA during the time research was carried out.