test first java concurrency for the classroom l.
Download
Skip this Video
Loading SlideShow in 5 Seconds..
Test-First Java Concurrency for the Classroom PowerPoint Presentation
Download Presentation
Test-First Java Concurrency for the Classroom

Loading in 2 Seconds...

play fullscreen
1 / 53

Test-First Java Concurrency for the Classroom - PowerPoint PPT Presentation


  • 95 Views
  • Uploaded on

Test-First Java Concurrency for the Classroom. SIGCSE 2010 Mathias Ricken and Robert Cartwright Rice University March 12, 2009. Test-driven development. Concurrent programming. Two Trends. Brian Goetz, Java Concurrency in Practice , Addison-Wesley, 2006. Unit Testing Benefits.

loader
I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
capcha
Download Presentation

PowerPoint Slideshow about 'Test-First Java Concurrency for the Classroom' - misae


An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.


- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
test first java concurrency for the classroom

Test-First Java Concurrency for the Classroom

SIGCSE 2010

Mathias Ricken and Robert Cartwright

Rice University

March 12, 2009

two trends
Test-driven development

Concurrent programming

Two Trends

Brian Goetz, Java Concurrency in Practice, Addison-Wesley, 2006

unit testing benefits
Unit Testing Benefits
  • Occurs early
  • Automates testing
  • Keeps the shared repository clean
  • Prevents bugs from reoccurring
  • Allows safe refactoring
  • Serves as documentation
unit testing in assignments
Unit Testing in Assignments
  • Hand out test cases to students
    • Improves confidence and understanding
  • Instill good practices
    • Require students to extend test suites
  • Automated grading
    • Part graded automatically, part by hand
moore s law requires concurrency
Moore’s Law Requires Concurrency

Adopted fromSutter 2009

concurrency is difficult
Concurrency Is Difficult

Unit testing not effective in multi-threaded programs

existing unit testing frameworks
Existing Unit Testing Frameworks
  • JUnit, TestNG
  • Don’t detect test failures in threads other than main thread
    • Failures in event thread not detected either
  • Don’t ensure that other threads terminate
  • Tests that should fail may succeed
sample junit tests
Sample JUnit Tests

publicclass SimpleTest extends TestCase {

public void testException() {

thrownew RuntimeException("booh!");

}

public void testAssertion() {

assertEquals(0, 1);

}

}

Both tests fail.

Both tests fail.

}

if (0!=1)

throw new AssertionFailedError();

junit test with child thread
JUnit Test with Child Thread

publicclass SimpleTest extends TestCase {

public void testException() {

new Thread() {

public void run() {

thrownew RuntimeException("booh!");

}

}.start();

}

}

Main thread

new Thread() {

public void run() {

thrownew RuntimeException("booh!");

}

}.start();

thrownew RuntimeException("booh!");

Uncaught exception, test should fail but does not!

Child thread

end of test

spawns

Main thread

success!

uncaught!

Child thread

concjunit
ConcJUnit
  • Backward compatible replacement for JUnit
  • Detects exceptions in all threads
    • Exception handler for all child threads and the event thread
  • Ensures that child threads have terminated and event thread is done
    • Enumerate live threads after test
    • Inspect event queue
  • Requires all child threads to be joined
    • Analyze join graph
thread creation coordinates
Thread Creation Coordinates
  • In Thread.start() record stack trace of Thread.currentThread()
    • Easy to find where a thread that caused a failure was started
    • Also shows where threads that outlived the test were started
creation coordinates example
Creation Coordinates Example

class Main {

void foo() {

// which one?

new Helper(true).start();

new Helper(false).start();

// ...

}

}

class Helper extends Thread {

void m() { if (b) Assert.fail(); }

public void run() { m(); }

private boolean b;

// …

}

AssertionError:

at Helper.m(Helper.java:2)

at Helper.run(Helper.java:3)

Started at:

at Main.foo(Main.java:4)

at Main.bar(Main.java:15)

at Main.main(Main.java:25)

concurrency examples
Concurrency Examples
  • In-class discussion
    • Multi-threaded counter: data races
    • Multi-threaded bank: deadlock
  • Homework
    • Bounded buffer
    • Readers-writer lock
    • Test suite handed out to help students
  • Multi-threaded Breakout
example counter
Example: Counter
  • Class that can increment an integer variable N times
  • Write test first

public class CounterTest extends TestCase {

final long PER_THREAD = 1000000;

public void testSingle() {

Counter c = new Counter();

c.incrementNTimes(PER_THREAD);

assertEquals(PER_THREAD, c.getCount());

}

}

counter implementation
Counter: Implementation
  • Write implementation

public class Counter {

private long count = 0;

public long getCount() { return count; }

public void incrementNTimes(long n) {

for(long i=0; i<n; ++i) { ++count; }

}

}

Test passes!

counter multi threaded test
Counter: Multi-threaded Test
  • Write multi-threaded test

public void testMulti() {

final Counter c = new Counter();

for(int i=0; i<NUM_THREADS; ++i) {

new Thread() {

public void run() {

c.incrementNTimes(PER_THREAD);

}

}.start();

}

TestUtils.waitForOtherThreads();

assertEquals(NUM_THREADS*PER_THREAD,c.getCount());

}

Test fails (most likely)!

shared data
Shared Data
  • Why does the multi-threaded counter test fail?
    • Thecount field is shared among threads
    • The ++count operation is not atomic
    • Thread may be interrupted after reading count, but before writing back to count

count=0 regA=? regB=?

A1 regA = count; 0 0 ?

B1 regB = count; 0 0 0

A2 regA = regA + 1; 0 1 0

A3 count = regA; 1 1 0

B2 regB = regB + 1; 1 1 1

B3 count = regB; 1 1 1

data races
Data Races
  • Definition
    • Two threads access the same data
    • At least one access is a write
    • Nothing prevents the order from changing
  • Would like code to execute atomically (without interruption)
    • Java does not support atomicity(for general code)
java locks synchronized
Java Locks & Synchronized
  • Java provides “lock objects” and synchronized blockssynchronized(lock) { ++count; }
    • Thread must compete for ownership of lock object before entering synchronized block
    • Synchronized block is not atomic
    • But once a thread has a lock object, no other thread can execute code protected by the same lock object
counter re write
Counter: Re-Write
  • Rewrite implementation

// ...

private Object lock = new Object();

public void incrementNTimes(long n) {

for(long i=0; i<n; ++i) {

synchronized(lock) { ++count; }

}

}

Test passes!

concurrency still difficult
Concurrency Still Difficult
  • Even race-free, deadlock-free programs are not deterministic
    • Thread scheduling is essentially non-deterministic
  • Different schedules may compute different results
    • May or may not be acceptable, depending on the task
multi threaded breakout
Multi-threaded Breakout
  • Uses ACM Java Task Force material
    • Based on “Breakout - Nifty Assignment” by Eric Roberts, SIGCSE 2006
  • Multiple balls, each in its own thread
    • Atomicity assumption when removing bricks
    • Ends game before all bricks are removed
  • Other problems
    • X,Y coordinate changes not atomic
    • X,Y coordinates not volatile or synchronized, event thread may never see the updates
  • Correctly synchronized version still not deterministic
future work
Future Work
  • Testing all schedules is intractable
  • Insert random delays/yields before synchronization operations
    • Must consider volatile variable accesses to comply with Java Memory Model
    • Re-run program several times
    • Can detect a number of sample problems
  • Record schedule, replay if test fails
    • Makes failures reproducible if found

*3

conclusion
Conclusion
  • Unit testing has important benefits in industry and in the classroom
  • Concurrent programming is becoming more important, and it’s difficult
  • ConcJUnit helps…

www.concutest.orgwww.drjava.org

notes27
Notes
  • Also cannot detect uncaught exceptions in a program’s uncaught exception handler (JLS limitation) ←
  • Only add edge if joined thread is really dead; do not add if join ended spuriously. ←
  • Have not studied probabilities or durations for sleeps/yields:One inserted delay may negatively impact a second inserted delayExample: If both notify() and wait() are delayed. ←
spurious wakeup
Spurious Wakeup

publicclass Test extends TestCase {

public void testException() {

Thread t = new Thread(new Runnable() {

public void run() {

thrownew RuntimeException("booh!");

}

});

t.start();

while(t.isAlive()) {

try { t.join(); }

catch(InterruptedException ie) { }

}

}

}

Thread t = new Thread(new Runnable() {

public void run() {

thrownew RuntimeException("booh!");

}

});

t.start();

while(t.isAlive()) {

try { t.join(); }

catch(InterruptedException ie) { }

}

thrownew RuntimeException("booh!");

Loop since join() may end spuriously

image attribution30
Image Attribution
  • Left image on Two Trends: Test Driven Development, Damian Cugley.
  • Right image on Two Trends: adapted from Brian Goetz et al. 2006, Addison Wesley.
  • Graph on Moore’s Law:Adapted from Herb Sutter 2009
  • Image on Concurrency Is Difficult:Caption Fridays
changes to junit 1 of 3
Changes to JUnit (1 of 3)
  • Thread group with exception handler
    • JUnit test runs in a separate thread, not main thread
    • Child threads are created in same thread group
    • When test ends, check if handler was invoked

Reasoning:

  • Uncaught exceptions in all threads must cause failure
junit test with child thread33
JUnit Test with Child Thread

publicclass Test extends TestCase {

public void testException() {

new Thread(new Runnable() {

public void run() {

thrownew RuntimeException("booh!");

}

}).start();

}

}

new Thread(new Runnable() {

public void run() {

thrownew RuntimeException("booh!");

}

}).start();

thrownew RuntimeException("booh!");

invokes

checks

TestGroup’s Uncaught Exception Handler

junit test with child thread34
JUnit Test with Child Thread

publicclass Test extends TestCase {

public void testException() {

new Thread() {

public void run() {

thrownew RuntimeException("booh!");

}

}.start();

}

}

new Thread() {

public void run() {

thrownew RuntimeException("booh!");

}

}.start();

thrownew RuntimeException("booh!");

spawns and joins

resumes

Main thread

failure!

check exception handler

end of test

Test thread

uncaught!

invokes exception handler

Child thread

child thread outlives parent
Child Thread Outlives Parent

publicclass Test extends TestCase {

public void testException() {

new Thread() {

public void run() {

thrownew RuntimeException("booh!");

}

}.start();

}

}

new Thread() {

public void run() {

thrownew RuntimeException("booh!");

}

}.start();

thrownew RuntimeException("booh!");

check exception handler

Main thread

success!

Test thread

Too late!

end of test

uncaught!

invokes exception handler

Child thread

changes to junit 2 of 3
Changes to JUnit (2 of 3)
  • Check for living child threads after test ends

Reasoning:

  • Uncaught exceptions in all threads must cause failure
  • If the test is declared a success before all child threads have ended, failures may go unnoticed
  • Therefore, all child threads must terminate before test ends
check for living child threads
Check for Living Child Threads

publicclass Test extends TestCase {

public void testException() {

new Thread() {

public void run() {

thrownew RuntimeException("booh!");

}

}.start();

}

}

new Thread() {

public void run() {

thrownew RuntimeException("booh!");

}

}.start();

thrownew RuntimeException("booh!");

check for living

child threads

check group’s handler

Main thread

failure!

Test thread

end of test

uncaught!

invokes group’s handler

Child thread

correctly written test
Correctly Written Test

publicclass Test extends TestCase {

public void testException() {

Thread t = new Thread() {

public void run() { /* child thread */ }

};

t.start();

t.join();

}

}

Thread t = new Thread() {

public void run() { /* child thread */ }

};

t.start();

t.join(); // wait until child thread has ended

/* child thread */

check for living

child threads

check group’s handler

Main thread

success!

Test thread

end of test

Child thread

*4

changes to junit 3 of 3
Changes to JUnit (3 of 3)
  • Check if any child threads were not joined

Reasoning:

  • All child threads must terminate before test ends
  • Without join() operation, a test may get “lucky”
  • Require all child threads to be joined
fork join model
Fork/Join Model
  • Parent thread joins with each of its child threads
  • May be too limited for a general-purpose programming language

Main thread

Child thread 1

Child thread 2

example of other join models
Example of Other Join Models
  • Chain of child threads guaranteed to outlive parent
  • Main thread joins with last thread of chain

Main thread

Child thread 1

Child thread 2

Child thread 3

modifying the java runtime
Modifying the Java Runtime
  • Changing Thread.start()and join()
    • Need to modify Java Runtime Library
    • Utility to process user’s rt.jar file
    • Put new jar file on boot classpath:-Xbootclasspath/p:newrt.jar
  • Still works without modified Thread class
    • Just does not emit “lucky” warnings
join with all offspring threads
Join with All Offspring Threads
  • Main thread joins with all offspring threads, regardless of what thread spawned them

Main thread

Child thread 1

Child thread 2

generalize to join graph
Generalize to Join Graph
  • Threads as nodes; edges to joined thread
  • Test is well-formed as long as all threads are reachable from main thread

Main thread

MT

Child thread 1

CT1

Child thread 2

CT2

Child thread 3

CT3

join graph examples
Join Graph Examples

Main thread

MT

Child thread 1

CT1

Child thread 2

CT2

Main thread

MT

Child thread 1

CT1

Child thread 2

CT2

unreachable nodes
Unreachable Nodes
  • An unreachable node has not been joined
    • Child thread may outlive the test

Main thread

MT

Child thread 1

CT1

Child thread 2

CT2

constructing the graph
Constructing the Graph

// in mainThreadchildThread.start();

  • Add node for childThread

main Thread

MT

childThread

CT

constructing the graph48
Constructing the Graph

// in mainThreadchildThread.join();

  • When leaving join(), add edge from mainThread to childThread

main Thread

MT

child

Thread

CT

*2

example multi threaded bank
Example: Multi-threaded Bank
  • Program simulating checking accounts
  • Account balances are shared data
    • To avoid data races, use synchronized
    • Need access to two accounts for transfers

synchronized(locks[from]) {

synchronized(locks[to]) {

accounts[from] -= amount;

accounts[to] += amount;

}

}

Test hangs!

deadlock
Deadlock
  • Thread A transfers from account 0 to 1
  • Thread B transfers from account 1 to 0
  • Thread A gets interrupted after acquiring locks[0]

// thread A // thread B

synchronized(locks[0]) {

synchronized(locks[1]) {

synchronized(locks[0])

// can’t continue, locks[0]

// is owned by thread A */

synchronized(locks[1])

// can’t continue, locks[1]

// is owned by thread B */

lock acquisition order
Lock Acquisition Order
  • No deadlock if both threads had attempted to acquire lock 0 first
  • When acquiring more than one lock object, always acquire them in the same order
    • e.g. acquire lower account’s lock object first

synchronized(locks[Math.min(from,to)]) {

synchronized(locks[Math.max(from,to)]) {

accounts[from] -= amount;

accounts[to] += amount;

}

}

homework assignment
Homework Assignment
  • Common structures students will see time and again
    • Bounded buffer
    • Readers-writer lock
  • Grade correctness and efficiency, e.g.
    • Maximize concurrency
    • Only wake up as few threads as possible
  • Provide students with test suites
many thanks to
Many Thanks To…
  • My advisor
    • Corky Cartwright
  • My committee members
    • Walid Taha
    • David Scott
    • Bill Scherer
  • NSF and Texas ATP
    • For providing partial funding