Modular Verification of Synchronization with Reentrant Locks Aysu Betin Can

advertisement
Modular Verification of Synchronization
with Reentrant Locks
Tevfik Bultan
Fang Yu
Department of Computer Science
University of California
Santa Barbara, CA, USA
Aysu Betin Can
Informatics Institute
Middle East Technical University
Ankara, Turkey
Concurrent programming with Locks
• A common synchronization approach in concurrent
programming is to use locks
– A thread that wants to access a shared resource first
acquires a lock
– After the thread is done with accessing the shared
resource it releases the lock
• Some locks are reentrant
– A reentrant lock can be acquired multiple times by the
same thread without releasing it
– A reentrant lock is released by a thread when the
number of acquire and release operations executed by
that thread become equal
Concurrent Programming in Java
• Java programs use lock based synchronization
• The default synchronization mechanism in Java uses the
synchronized keyword and is based on reentrant locks
• The java.util.concurrent package provides
specialized locking mechanism such as:
ReentrantLock, ReentrantReadWriteLock
• Java programmers can write their own synchronization
policies using Java synchronization operations:
synchronized, wait, notify, notifyAll
Correctness
• How can we check that a concurrent program that uses
locks does not have synchronization errors?
Correctness
• How can we check that a concurrent program that uses
locks does not have synchronization errors?
1. We have to check that the lock (i.e., the
synchronization policy) is implemented correctly
• We call this Lock Behavior Verification
Correctness
• How can we check that a concurrent program that uses
locks does not have synchronization errors?
1. We have to check that the lock (i.e., the
synchronization policy) is implemented correctly
• We call this Lock Behavior Verification
2. We need to make sure that each concurrent thread in
the program uses the locks correctly
• We call this Lock Interface Verification
Concurrent Program Model
• We assume that a concurrent program consists
– A set of threads
– Shared variables
• Modifiable only via shared operations
– Lock variables
• Modifiable only via lock operations
– Local variables for each thread
• Modifiable only by one thread via local operations
An Example
• Consider a concurrent program that uses a read-write lock
to protect access to a shared variable
• Shared operations:
– read, write
• Lock operations:
– read_enter, read_exit, write_enter,
write_exit
Lock Operations
• We partition the lock operations to
– Acquire (A1, A2, …, An) and Release (R1, R2, … , Rn)
operations where the Al and Rl correspond to acquire
and release operations for lock l
• For the Read-Write lock
– A1 = {read_enter} and R1 = {read_exit}
– A2 = {write_enter} and R2 = {write_exit}
• We can think of the Read-Write lock as consisting of
– one read lock (lock with index 1 above) and
– one write lock (lock with index 2 above)
where both of these locks are implemented using the
same lock variables
Reentrant Locks
• We model reentrant locks as follows:
– An acquire operation in Al executed by thread t should
not block if
• the number of operations from Al that have been
executed by thread t is greater than
• the number of operations from Rl that have been
executed by thread t
• We model this behavior by keeping a reentry count for
each lock-thread pair
– Reentry count for thread t and lock l is incremented by
one when thread t executes an operation in Al
– Reentry count for thread t and lock l is decremented by
one when thread t executes an operation in Rl
Lock Interfaces
• A lock interface defines the acceptable call sequences for
each thread that uses the lock
– In other words: A lock interface specifies the correct
execution ordering for lock and shared operations for a
single thread
• For example, before a thread calls the read operation, it
must have called the read_enter operation one more
time than the read_exit operation
– Note that this type of constraints cannot be specified
using finite state machines
Lock interfaces
• We specify lock interfaces as extended finite state
machines (EFSMs)
• In the lock interface EFSM there is a reentry count for each
lock keeping track of the difference between number of
acquire and release calls for that lock
• Each transition in the EFSM is labeled by a lock operation
or a shared operation
– and can also have a guarded command updating the
reentry counts
Example Lock Interface
read_enter
cr:= cr+1
read
read_enter
cr:= cr+1
read_exit
[cr=1] cr:= cr-1
write_exit
[cw=1] cw:= cw-1
write_enter
cw:= cw+1
cr and cw are
reentry counts for
read and write
locks, respectively
read_exit
[cr>1] cr:= cr-1
write_enter
cw:= cw+1
read
write
write_exit
[cw>1] cw:= cw+1
Lock Behavior
• We assume that locks are implemented using guarded
commands
• An example Read-Write lock implementation
integer nr;
boolean busy;
Lock variables
initial: !busy and nr=0;
Lock
operations
read_enter: [!busy] nr := nr+1;
read_exit: nr := nr-1;
write_enter: [!busy && nr=0] busy := true;
write_exit: busy := false;
Lock Behavior Machine
• Lock behavior machine consists of
– The lock behavior specification, and
– One finite state machine for each thread
• This finite state machine keeps track of the set of
locks held by that thread
• Unlike the lock interface machine, these are finite
state machines without the reentry counts
read_enter
read_exit
write_exit
write_enter
Lock Behavior Machine
• While checking the lock specification we do not need to
keep track of the reentry counts
– The values of lock variables change only in transitions
that change a reentry count from 0 to 1 or from 1 to 0
– The acquire and release calls that do change a re-entry
count from 0 to 1 or from 1 to 0 can be abstracted away
as far as lock behavior is concerned
read_enter
cr=0
cw=0
cr≥1
cw=0
read_exit
write_exit
write_enter
cr=0
cw≥1
Verification Framework
• Interface Verification:
– Check that each thread in the concurrent program calls
the lock operations and shared operations according to
the lock interface machine
• Behavior Verification:
– Check that lock specification is correct
• Specify ACTL properties about the lock behavior and
check them on the lock behavior machine
Verification Framework
• We developed several helper classes in Java which allow
developers to
– Write lock behavior specifications using guarded
commands
– Write lock interface specifications as finite state
machines
• The helper classes we provide implement the guarded
commands specified by the user using Java
synchronization primitives
• We also wrote tools that
– Extract the lock behavior machine for behavior
verification
– Isolate each thread based on the lock interface
specifications for interface verification
Verification Framework
Lock
Classes
Concurrent
Program
Thread
Thread
Thread
Classes
Verification Framework
Lock
Classes
Concurrent
Program
Thread
Thread
Thread
Classes
Lock
Behavior
Machine
Verification Framework
Lock
Classes
Concurrent
Program
Thread
Thread
Thread
Classes
Lock
Behavior
Machine
Behavior
Verification
Counting
Abstraction
Action
Language
Verifier
Verification Framework
Lock
Behavior
Machine
Lock
Classes
Counting
Abstraction
Concurrent
Program
Lock
Interface
Machine
Thread
Thread
Thread
Classes
Behavior
Verification
Thread
Isolation
Thread
Class
Action
Language
Verifier
Verification Framework
Lock
Behavior
Machine
Lock
Classes
Behavior
Verification
Action
Language
Verifier
Counting
Abstraction
Concurrent
Program
Lock
Interface
Machine
Thread
Thread
Thread
Classes
Interface
Verification
Java
Path Finder
Thread
Isolation
Thread
Class
Modular Verification
• Modularity 1
– Interface and behavior verification are done separately
• Assume guarantee reasoning: We verify the lock
behavior assuming that the threads obey the lock
interfaces and then we check interface conformance
• Modularity 2
– Interface verification is done one thread at a time
• Thread-modular interface verification
Modular Design / Modular Verification
Locks
Shared
Data
Thread n
Thread 2
Thread 1
Concurrent Program
Modular Design / Modular Verification
Thread n
Thread 2
Thread 1
Concurrent Program
Locks
Shared
Data
Lock Behavior
Modular
Behavior
Verification
Modular Design / Modular Verification
Thread Modular Interface Verification
Thread 1
Thread 2
Thread n
Thread n
Thread 2
Thread 1
Concurrent Program
Interface
Machine
Interface
Machine
Interface
Machine
Locks
Shared
Data
Lock Behavior
Modular
Behavior
Verification
Behavior Verification
• Analyzing lock behavior by checking ACTL properties
about the lock behavior
– Verify the lock properties assuming that all the threads
adhere to the lock interface
• Behavior verification with Action Language Verifier
– An infinite state symbolic CTL model checker (uses
conservative approximations, widening)
– We wrote a translator which translates lock classes
written using guarded commands to Action Language
– Using counting abstraction we can check the lock
behavior with respect to arbitrary number of threads
Arbitrary Number of Threads?
• What if we wish to check the read-write lock with respect to
arbitrary number of threads?
• Counting abstraction
– Create an integer variable for each thread state
– Each variable counts the number of threads in a
particular state
– Generate updates and guards for these variables based
on the specification
• Counting abstraction is automated
Verification of Read-Write Lock with ALV
Integers
Booleans
Time
(seconds)
Memory
(Mbytes)
RW-4
1
5
0.05
6.6
RW-8
1
9
0.09
7
RW-16
1
17
0.21
8
RW-32
1
33
0.56
10.8
RW-64
1
65
1.77
20.6
RW-P
7
1
0.06
9.1
Interface Verification
• A thread is correct with respect to a lock interface if all the
call sequences generated by the thread can also be
generated by the lock interface machine
– Checks if all the threads invoke lock methods in the
order specified in the interfaces
– Checks if the threads access shared data only at the
correct interface states
• Interface verification with Java PathFinder
– Verify Java implementations of threads
– Correctness criteria are specified as assertions (if a
thread calls a lock operation or shared operation that it
should not call, an assertion violation occurs)
• Look for assertion violations
– Thread-modular verification with thread Isolation
Thread Isolation: Part 1
• Interaction among threads
• Threads can interact with each other in only two ways:
– Executing lock operations
– Executing shared data operations
• To isolate the threads
– Replace locks with lock interface state machines
– Replace shared data with shared stubs
Thread Isolation: Part 2
• Interaction among a thread and its environment
• Modeling thread’s call to its environment with stubs
– File I/O, updating GUI components, socket operations,
RMI call to another program
• Replace with pre-written or generated stubs
• Modeling the environment’s influence on threads with
drivers
– Thread initialization, RMI events, GUI events
• Enclose with drivers that generate all possible events
that influence controller access
• These steps typically lead to unsound, bounded verification
A Case Study
• Automated Airspace Concept by NASA researchers
automates the decision making in air traffic control
• The most important challenge is achieving high
dependability
• Automated Airspace Concept includes a failsafe short term
conflict detection component called Tactical Separation
Assisted Flight Environment (TSAFE)
– It is responsible for detecting conflicts in flight plans of
the aircraft within 1 minute from the current time
– Dependability of this component is even more important
than the dependability of the rest of the system
– It should be a smaller, isolated component compared to
the rest of the system so that it can be verified
TSAFE
TSAFE functionality:
1. Display aircraft position
2. Display aircraft planned route
3. Display aircraft future projected route trajectory
4. Show conformance problems
TSAFE Architecture
User
Radar feed
<<TCP/IP>>
Feed Parser
Server
Client
Flight
Database
EventThread
<<RMI>>
Graphical
Client
Computation
21,057 lines of code with 87 classes
Timer
Reengineering TSAFE
• Found all the synchronization statements in the code
(synchronize, wait, notify, notifyAll)
• Identified 6 shared objects protected by these
synchronization statements
• Re-implemented the synchronization using our own lock
implementations
– Used 2 instances of a reader-writer lock and 3 instances
of a mutex lock for synchronization
Interface Verification Performance
Thread
Time (seconds)
Memory (MBytes)
Server-RMI
177.85
63.15
Server-Event
11.62
22.14
Server-Feed
194.78
52.44
Client-RMI
11.82
26.54
Client-Event
451.54
82.55
Related Work
• This work is based on our earlier work [Betin Can and
Bultan ASE 2004, ASE 2005]
– Earlier work did not consider re-entrant locks
• There has been earlier work on analyzing Java’s reentrant
locking mechanisms [Abraham-Mumm et al. FOSSACS
2002, Haack et al. APLAS 2008]
– We focus on custom lock implementations and their
usage
• Thread modular verification [Flanagan and Qadeer SPIN
2003] has been proposed before
• There has been many work on modular reasoning based
on interfaces [e.g. Chakrabarti et al. CAV 2002]
• We apply these general concepts to checking reentrant
locks
Conclusions
• Verification of Re-entrant locks can be done modularly
– Separate interface verification from behavior verification
using interface machines
• Interface verification
– Makes sure that each thread calls the re-entrant locks
according to the lock interface specification
– Interface verification can be done thread-modularly
• Behavior verification
– Makes sure that the lock behavior is correct with respect
to the given CTL properties
– During behavior verification re-entry counts can be
abstracted away
– Using counting abstraction lock behavior can be
checked with respect to arbitrary number of threads
Conclusions
• Behavior verification with ALV
– Verification of basic lock specifications such as readwrite locks using the infinite state model checking
techniques is feasible
• Interface verification with JPF
– Thread isolation and environment generation are
challenging problems that are hard to do fully
automatically
• Require writing stubs and drivers
THE END
• Questions?
– Please send your questions to: bultan@cs.ucsb.edu
Download