Top Banner

of 53

Test-First Java Concurrency for the Classroom

Apr 04, 2018

Download

Documents

Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
  • 7/31/2019 Test-First Java Concurrency for the Classroom

    1/53

    Test-First Java Concurrency

    for the Classroom

    SIGCSE 2010

    Mathias Ricken and Robert Cartwright

    Rice University

    March 12, 2009

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    2/53

    2

    Two Trends

    Test-driven development Concurrent programming

    Brian Goetz, Java Concurrency inPractice, Addison-Wesley, 2006

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    3/53

    3

    Unit Testing Benefits

    Occurs early Automates testing

    Keeps the shared repository clean

    Prevents bugs from reoccurring

    Allows safe refactoring

    Serves as documentation

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    4/53

    4

    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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    5/53

    5

    Moores Law Requires Concurrency

    Adopted from

    Sutter 2009

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    6/53

    6

    Concurrency Is Difficult

    Unit testing not effective in

    multi-threaded programs

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    7/53

    7

    Existing Unit Testing Frameworks

    JUnit, TestNG

    Dont detect test failures in threads other

    than main thread Failures in event thread not detected either

    Dont ensure that other threads terminate

    Tests that should fail may succeed

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    8/53

    8

    Sample JUnit Tests

    publicclass SimpleTest extends TestCase {

    public void testException() {

    thrownew RuntimeException("booh!");

    }

    public void testAssertion() {assertEquals(0, 1);

    }

    }if (0!=1)

    throw newAssertionFailedError();

    } Both testsfail.

    Both testsfail.

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    9/53

    9

    JUnit Test with Child Thread

    publicclass SimpleTest 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!");

    Mainthread

    Child

    thread

    Main

    thread

    Child

    thread

    spawns

    uncaught!

    end of

    testsuccess!

    Uncaught exception,

    test should fail but

    does not!

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    10/53

    10

    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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    11/53

    11

    Thread Creation Coordinates

    In Thread.start() record stack trace ofThread.currentThread()

    Easy to find where a thread that caused afailure was started

    Also shows where threads that outlived thetest were started

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    12/53

    12

    Creation Coordinates Example

    class Main {

    void foo() {

    // which one?

    new Helper(true).start();new Helper(false).start();

    // ...

    }

    }

    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)

    class Helper extends Thread {

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

    public void run() { m(); }

    private boolean b;

    // }

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    13/53

    13

    ConcJUnit Demo

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    14/53

    14

    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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    15/53

    15

    Example: Counter

    Class that can increment an integer variableN 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());

    }

    }

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    16/53

    16

    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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    17/53

    17

    Counter: Multi-threaded Test

    Write multi-threaded testpublic void testMulti() {

    final Counter c = new Counter();

    for(int i=0; i

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    18/53

    18

    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, butbefore writing back to count

    count=0 regA=? regB=?

    A1 regA = count; 0 0 ?

    B1 regB = count; 0 0 0A2 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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    19/53

    19

    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)

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    20/53

    20

    Java Locks & Synchronized

    Java provides lock objects andsynchronized blocks

    synchronized(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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    21/53

    21

    Counter: Re-Write

    Rewrite implementation

    // ...

    private Object lock = new Object();

    public void incrementNTimes(long n) {

    for(long i=0; i

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    22/53

    22

    Concurrency Still Difficult

    Even race-free, deadlock-free programsare not deterministic

    Thread scheduling is essentially non-

    deterministic

    Different schedules may compute

    different results

    May or may not be acceptable, depending

    on the task

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    23/53

    23

    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 notdeterministic

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    24/53

    24

    Future Work

    Testing all schedules is intractable

    Insert random delays/yields beforesynchronization operations

    Must considervolatile variable accesses tocomply 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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    25/53

    25

    Conclusion

    Unit testing has important benefits inindustry and in the classroom

    Concurrent programming is becoming more

    important, and its difficult

    ConcJUnit helps

    www.concutest.org

    www.drjava.org

    http://www.concutest.org/http://www.drjava.org/http://www.drjava.org/http://www.concutest.org/
  • 7/31/2019 Test-First Java Concurrency for the Classroom

    26/53

    Notes

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    27/53

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    28/53

    28

    publicclass Test extends TestCase {

    public voidtestException() {

    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 voidrun() {

    thrownew RuntimeException("booh!");

    }

    });

    t.start();

    while(t.isAlive()) {

    try { t.join(); }

    catch(InterruptedException ie) { }

    }

    thrownew RuntimeException("booh!");

    Loop sincejoin() may end

    spuriously

    4.

    Spurious Wakeup

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    29/53

    Image Attribution

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    30/53

    30

    Image Attribution

    1. Left image on Two Trends: Test Driven

    Development, Damian Cugley.

    2. Right image on Two Trends: adapted from Brian

    Goetz et al. 2006, Addison Wesley.

    3. Graph on Moores Law:

    Adapted from Herb Sutter 2009

    4. Image on Concurrency Is Difficult:

    Caption Fridays

    http://www.oxfordcc.co.uk/newsletters/003/softwaredevelopmentpractices.htmlhttp://www.oxfordcc.co.uk/newsletters/003/softwaredevelopmentpractices.htmlhttp://www.javaconcurrencyinpractice.com/http://www.javaconcurrencyinpractice.com/http://www.gotw.ca/publications/concurrency-ddj.htmhttp://www.captionfridays.com/2008/40-i-want-to-go-first/http://www.captionfridays.com/2008/40-i-want-to-go-first/http://www.gotw.ca/publications/concurrency-ddj.htmhttp://www.javaconcurrencyinpractice.com/http://www.javaconcurrencyinpractice.com/http://www.oxfordcc.co.uk/newsletters/003/softwaredevelopmentpractices.htmlhttp://www.oxfordcc.co.uk/newsletters/003/softwaredevelopmentpractices.html
  • 7/31/2019 Test-First Java Concurrency for the Classroom

    31/53

    Extra Slides

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    32/53

    32

    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 threadsmust causefailure

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    33/53

    33

    JUnit Test with Child Thread

    publicclass Test extends TestCase {

    public voidtestException() {

    new Thread(new Runnable() {

    public void run() {

    thrownew RuntimeException("booh!");

    }

    }).start();

    }

    }

    new Thread(new Runnable() {

    public voidrun() {

    thrownew RuntimeException("booh!");

    }

    }).start();

    thrownew RuntimeException("booh!");

    invokes

    TestGroups Uncaught

    Exception Handler

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    34/53

    34

    JUnit Test with Child Thread

    publicclass Test extends TestCase {

    public voidtestException() {

    new Thread() {

    public void run() {

    thrownew RuntimeException("booh!");

    }

    }.start();

    }

    }

    new Thread() {

    public voidrun() {

    thrownew RuntimeException("booh!");

    }

    }.start();

    thrownew RuntimeException("booh!");

    Test

    thread

    Child

    thread

    uncaught!

    end of test

    Mainthread

    spawns and joins resumescheck

    exception

    handlerinvokes

    exception

    handler

    failure!

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    35/53

    35

    Child Thread Outlives Parent

    publicclass Test extends TestCase {

    public voidtestException() {

    new Thread() {

    public void run() {

    thrownew RuntimeException("booh!");

    }

    }.start();

    }

    }

    new Thread() {

    public voidrun() {

    thrownew RuntimeException("booh!");

    }

    }.start();

    thrownew RuntimeException("booh!");

    Test

    thread

    Child

    thread

    uncaught!end of test

    success!

    invokes

    exceptionhandler

    Mainthread

    check exception

    handler

    Too late!

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    36/53

    36

    Changes to JUnit (2 of 3)

    Check for living child threads after test ends

    Reasoning: Uncaught exceptions in all threadsmust cause

    failure

    If the test is declared a success before all childthreads have ended, failures may go unnoticed

    Therefore, all child threads must terminate

    before test ends

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    37/53

    37

    Check for Living Child Threads

    publicclass Test extends TestCase {

    public voidtestException() {

    new Thread() {

    public void run() {

    thrownew RuntimeException("booh!");

    }

    }.start();

    }

    }

    new Thread() {

    public voidrun() {

    thrownew RuntimeException("booh!");

    }

    }.start();

    thrownew RuntimeException("booh!");

    Test

    thread

    Child

    thread

    uncaught!end of test

    failure!

    invokes

    groupshandler

    Mainthread

    check for living

    child threads

    check groups

    handler

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    38/53

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    39/53

    39

    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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    40/53

    40

    Fork/Join Model

    Parent thread joins with each of its childthreads

    May be too limited for a general-purposeprogramming language

    Child

    thread 1

    Child

    thread 2

    Mainthread

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    41/53

    41

    Example of Other Join Models

    Chain of child threads guaranteed tooutlive parent

    Main thread joins with last thread of

    chain

    Child

    thread 1

    Child

    thread 2

    Main

    thread

    Child

    thread 3

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    42/53

    42

    Modifying the Java Runtime

    Changing Thread.start()and join()

    Need to modify Java Runtime Library

    Utility to process users 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

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    43/53

    43

    Join with All Offspring Threads

    Main thread joins with all offspringthreads, regardless of what thread

    spawned them

    Child

    thread 1

    Child

    thread 2

    Main

    thread

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    44/53

    44

    Generalize to Join Graph

    Threads as nodes; edges to joinedthread

    Test is well-formed as long as all

    threads are reachable from main thread

    Child

    thread 1

    Child

    thread 2

    Main

    thread

    Child

    thread 3

    MT

    CT1

    CT2

    CT3

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    45/53

    45

    Child

    thread 1

    Childthread 2

    Main

    threadMT

    CT1

    CT2

    Childthread 1

    Child

    thread 2

    Main

    threadMT

    CT1

    CT2

    Join Graph Examples

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    46/53

    46

    Childthread 1

    Child

    thread 2

    Main

    threadMT

    CT1

    CT2

    Unreachable Nodes

    An unreachable node has not beenjoined

    Child thread may outlive the test

    C i h G h

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    47/53

    47

    child

    Thread

    main

    ThreadMT

    CT

    Constructing the Graph

    // in mainThread

    childThread.start();

    Add node forchildThread

    C t ti th G h

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    48/53

    48

    // in mainThreadchildThread.join();

    When leaving join(), add edge frommainThread to childThread

    child

    Thread

    main

    ThreadMT

    CT

    Constructing the Graph

    *2

    E l M lti th d d B k

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    49/53

    49

    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!

    D dl k

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    50/53

    50

    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])

    // cant continue, locks[0]

    // is owned by thread A */synchronized(locks[1])

    // cant continue, locks[1]

    // is owned by thread B */

    L k A i iti O d

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    51/53

    51

    Lock Acquisition Order

    No deadlock if both threads had attempted toacquire lock 0 first

    When acquiring more than one lock object,always acquire them in the same order e.g. acquire lower accounts lock object first

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

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

    accounts[from] -= amount;accounts[to] += amount;

    }

    }

    H k A i t

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    52/53

    52

    Homework Assignment

    Common structures students will seetime 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

    M Th k T

  • 7/31/2019 Test-First Java Concurrency for the Classroom

    53/53

    Many Thanks To

    My advisor Corky Cartwright

    My committee members

    Walid Taha David Scott

    Bill Scherer

    NSF and Texas ATP For providing partial funding