Title: Testing Concurrent Java Programs using Randomized Scheduling
1Testing Concurrent Java Programs usingRandomized
Scheduling
- Scott D. Stoller
- Presented by Guy David
2The Problems Root
- Concurrency introduces non-determinism
- Multiple executions of the same test may have
different interleaving (and different results!). - The Java Language Specification provides
essentially no guarantees about the thread
scheduler.
interleaving
An interleaving is a sequence of concurrent
events that occurred in a specific execution of P
3Example
static final int NUM 4 static int Global
0 public static void main(String argv)
Global 0 threads0 new Adder(1)
threads1 new Adder(10) threads2 new
Adder(100) threads3 new Adder(1000)
// Start Threads for(int
i0iltNUMi) threadsi.start()
try Thread.sleep(1000) catch(Exception
exc) // Print Results
System.out.println(Global)
4class Adder extends Thread int Local
Adder(int i) Local i public
void add() example.Global Local
public void run() add()
5Different approach Model Checking
- Model checkers are powerful tools that can help
pinpoint errors in concurrent programs. - Verify correctness.
- Aim to control all non-determinism and
exhaustively explore the systems possible
behaviors. - The serious drawback limited scalability
6Our Approach
- Insert calls to a scheduling function at
selected points in the program under test. - The scheduling function either does nothing or
causes a context switch. - Less systematic but more scalable.
- Implemented in a tool called RStest -
Random Scheduling test.
7The Scheduling Function
- Simple - makes the choice blindly using a
pseudo-random number generator. - More sophisticated - combine randomness with
heuristics that weight the choices. - The scheduling function may cause a context
switch by invoking Thread.yield() or
Thread.sleep().
8The Test Stages
- Transform the given program by inserting the
scheduling functions. - Execute the transformed program repeatedly to
test it. - If a bug was found - the program is re-executed
with the same seed to help reproduce the error. - (reproducibility is likely but not guaranteed.)
9Similar tool - ConTest
- Was developed by a team at the IBM Haifa Research
Laboratory during the past 3 to 4 years. - ConTest is a much more mature and comprehensive
system. - Contains a capture-and-replay mechanism.
- Contains a deadlock detection component.
10RStest Distinction
- Compared to ConTest, RStest inserts fewer calls
to the scheduling function, without reducing the
probability of finding a wide range of errors,
and without compromising probabilistic
completeness. - probabilistic completeness
- every error reachable according to the Java
Language Spec. is reachable in the transformed
program, independent of the underlying thread
scheduler.
11Why the effort to reduce the number of calls to
the sched-fn?
- Reduces the slowdown caused by calls to the
scheduling function. - Reduces the average number of context switches in
counterexamples produced by the tool ? easier to
understand.
12Where to Insert Calls to Sched Fn?
- Easy Answer at all concurrent events.
- Specifically
- access to instance field of object (including
arrays) - access to static field
- synchronization operation
- class initialization (implicit shared state and
sync.)
concurrent events
by definition, the events whose order determines
the result of the program.
13Reducing the Calls to Sched Fn
- By classification of storage locations and
operation, we can reduce the number of calls to
the scheduling function. - How is it done?
14Synchronization in Java Bytecode Overview (1)
- To synchronize threads, the Java programming
language uses monitors, which are a high-level
mechanism for allowing only one thread at a time
to execute a region of code protected by the
monitor. - There is a lock associated with every object.
- A lock is free iff each acquire of it has been
matched by a release.
15Synchronization in Java Bytecode Overview (2)
- monitorenter and monitorexit are instructions
that implement the lock and unlock actions. - A method declared as synchronized, implicitly
acquires the lock associated with the target
object (the this argument). When exiting, it
releases the lock.
16Synchronization in Java Bytecode Overview (3)
- wait, notify, and notifyAll are final native
methods of Object. - o.wait() - adds the calling thread t to os wait
set (the set of threads waiting on o), releases
os lock, and suspends t. - o.notify() - non-deterministically selects a
thread t in os wait set, removes t from the set,
and notifies t - o.notifyAll() - removes all threads from os
wait set and notifies each of them. - t.interrupt() - interrupt thread t.
17Classification of Storage Locations
Initialization of a location x must end before
concurrent access to x by multiple threads is
possible. Therefor, the JVM automatically
provides synchronization for class initialization
to ensure that other threads cannot access x
until the class initializer for C terminates.
- Unshared - A storage location is unshared if it
accessed by at most one thread. - Local variables and parameters are unshared.
- Heap locations and static locations may be shared
or unshared. - Protected - A location x is protected , if,
- after initialization of x
- all accesses to x are read (x is read-only)
- some lock l is hold at every access to x (l
protects x) - Unprotected - Storage locations that are shared
and not protected.
18- Any location can safely be classified as
unprotected. - To minimize the number of calls to the scheduling
function, locations should be classified as
unshared or protected whenever possible.
19Classification of Operations
- Visible - An operation is visible if it
- accesses an unprotected location.
- is a potentially blocking synchronization
operation or a call to Thread.interrupt . - is non-deterministic.
-
20The Program Transformation
- Obtain a list of unshared objects.
- Obtain a list of protected objects.
- Other objects are treated as unprotected by
default. - For most bytecode instructions, it is easy to
determine statically whether the instruction
performs a visible operation. - Calls to the scheduling function are inserted
immediately before visible operations.
21Obtaining the Classification of Objects (1)
- Statically
- Escape analysis
- escape analysis is used to determine whether
an object - may escape the method (i.e., is not local to the
method) that created the object. - may escape the thread that created the object
(i.e., other threads may access the object).
22Obtaining the Classification of Objects (2)
- Dynamically Run-Time Monitoring
- initial classification - a simple choice is to
initially classify all classes as unshared. - If a violation of the classification occurs, the
classification is automatically modified - unshared class C
protected - protected class C
unprotected
Reclassify
Reclassify
23Unshared Violation check Algorithm
Reached an object access instruction that
accesses an object o of class C
call to a static method checkUnshared(o), which
maintains a hash map firstAccessed that maps
each object reference o to the first thread that
accessed o.
check whether Thread.currentThread()
firstAccessed.get(o)
o is first accessed?
An entry that maps o to Thread.currentThread()
is created
yes
no
no
Classification violation
yes
Continue running
No violation
Reclassify to protected
Continue running
24Protected Violation Check Algorithm
The Lockset Algorithm
o.lockSet set of locks that protected o so far
after init. o.readOnly whether all accesses to o
after init were reads.
At end of initialization of o
o.readOnly true o.lockSet all locks
At each subsequent access to o
o.readOnly o.readOnly (current access is a
read) o.lockSet o.lockSet
heldLocks(currentThread)
o is protected iff o.readOnly o.lockSet !
25Design of the Scheduling Function
- The semantics of Java does not constrain which
thread runs next after a context switch. - Probabilistic completeness of the approach
requires that a call to the scheduling function
has a non-zero probability of transferring
control to each runable thread. - Depending on the JVM used during testing, a
single call to yield might not achieve this.
26Example
- Thread t1 Thread t2 Thread t3
- t2.start a2 a3
- a1
- t3.start
- b1
27Try to create the access order
a1, a2, a3, b1
- Thread t1 Thread t2 Thread t3
- t2.start
- a2
a3 - a1
- t3.start
- sched1b
- b1
sched2
sched3
sched1a
Ready List t3 t2
t1
t3
28Solution
- static java.util.Random prng ...
- static float contextSwitchProb ...
- public static void schedFn()
- while (prng.nextFloat() lt contextSwitchProb)
Thread.yield() -
29Try to create the access order
a1, a2, a3, b1
- Thread t1 Thread t2 Thread t3
- t2.start
- a2
a3 - a1
- t3.start
- sched1b
- b1
sched2
sched3
sched1a
Ready List t3 t2
t1
t3
30Experimental Results
31Summary
- Testing concurrent java programs using randomized
scheduling. - The approach -Insert calls to a scheduling
function at selected points in the program under
test (scalable). - RStest inserts fewer calls to the scheduling
function, without reducing the probability of
finding a wide range of errors - By classification of storage locations and
operation, we can reduce the number of calls to
the scheduling function. - The classification is obtained by static or
dynamic analysis. - The scheduling function has a non-zero
probability of transferring control to each
runable thread.
32Reference
- Testing Concurrent Java Programs using Randomized
Scheduling, Scott D. Stoller. - James Gosling, Bill Joy, Guy Steele, and Gilad
Bracha. The Java Language Specification. Addison
Wesley, 2nd edition, 2000. - Escape Analysis for Java, Jong-Deok Choi Manish
Gupta Mauricio Serrano Vugranam C. Sreedhar Sam
Midkiff. - Chapter 20 of Inside the Java Virtual Machine,
Thread Synchronization by Bill Venners.
33Any Questions?