Review request for 8022208: Intermittent test failures in java/lang/Thread/ThreadStateTest.java

David Holmes david.holmes at oracle.com
Mon Nov 4 20:42:30 PST 2013


Hi Mandy,

On 5/11/2013 1:20 PM, Mandy Chung wrote:
> David,
>
> Rereading your comment and I think you misread the switch statement (see

Yes I did - many times - sorry.

> my comments below).  In any case, I revisited ThreadStateController.java
> and looked int the potential races and have done further cleanup.  Here
> is the updated webrev.
> http://cr.openjdk.java.net/~mchung/jdk8/webrevs/8022208/webrev.02/
>
> I'll let them run longer to see if it can uncover any race.  How does
> this version look?

This looks good to me. One nit that caused me some confusion - it took 
me a while to realize that in transitionTo eg:

  221     public void transitionTo(Thread.State tstate) throws 
InterruptedException {
  222         switch (tstate) {
  223             case RUNNABLE:
  224                 nextState(RUNNABLE);
  225                 break;

The case value, eg RUNNABLE, and the arg to nextState, eg RUNNABLE, are 
two completely different values! Can I suggest using S_xxx for the int 
states (why not an enum?).

Typo: awaitArrive should be awaitAdvance

Thanks,
David

> Mandy
>
> On 10/31/2013 5:38 PM, David Holmes wrote:
>> Hi Mandy,
>>
>> On 1/11/2013 5:11 AM, Mandy Chung wrote:
>>> Updated webrev that has a new
>>> test/lib/testlibrary/ThreadStateController.java and also change to use
>>> AtomicInteger:
>>> http://cr.openjdk.java.net/~mchung/jdk8/webrevs/8022208/webrev.01/
>>
>> Sorry but I don't see how this works - and that applies to the old
>> version too! (Which I know I've looked at before so this is somewhat
>> unsettling for me :( ).
>>
>> First I found it confusing to track which thread was which in the
>> refactoring (which in itself is a good idea). Inside
>> ThreadStateController "myThread" doesn't mean anything - that comes
>> from the calling context - I suggest changing to refer to "this
>> thread" or "the current thread" as appropriate. eg "Have the current
>> thread wait until this thread is about to block" or whatever is needed.
>>
>> Looking at an actual test we have eg:
>>
>>    myThread.goWaiting();
>>   ThreadStateController.checkThreadState(myThread, Thread.State.WAITING);
>>
>> First: why is checkThreadState static rather than just an instance
>> method?
>>
>
> I'm happy with the new version of ThreadStateController.java with the
> above cleanup.
>
>> So goWaiting is supposed to tell myThread to go to a "waiting" state
>> so that the main thread can then verify that. Lets assume for
>> arguments sake that the thread is currently RUNNABLE so it is
>> currently looping around in run() doing the little math exercise.
>> goWaiting does:
>>
>>   public void goWaiting() {
>>     System.out.println("Waiting myThread to go waiting.");
>>     setState(WAITING);
>>     // wait for MyThread to get to just before wait on object.
>>     phaser.arriveAndAwaitAdvance();
>>  }
>>
>> and setState does:
>>
>>    case WAITING:
>>    case TIMED_WAITING:
>>        state = newState;
>>        synchronized (lock) {
>>           lock.notify();
>>        }
>>        break;
>
> The switch statement is looking at the current state (i.e. RUNNABLE).
>
> The above WAITING and TIMED_WAITING cases indicate that this thread is
> in waiting and thus the request to transition to another state has first
> to notify this thread so that it can proceed with the state transition.
>
>
>>
>>
>> so as soon as we update "state" myThread is capable of changing what
>> it is doing in run() to:
>>
>>    case WAITING: {
>>       synchronized (lock) {
>>          // signal main thread.
>>          phaser.arrive();
>>          System.out.println("  myThread is going to wait.");
>>          try {
>>              lock.wait();
>>           } catch (InterruptedException e) {
>>              // ignore
>>              interrupted.incrementAndGet();
>>           }
>>       }
>>       break;
>>
>> so now we have a race between the two threads to see who can grab the
>> lock first. If it is the main thread then we issue a notify when
>> nothing is waiting and so the subsequent wait() by myThread will
>> potentially wait forever. At least in that case the main thread will
>> see that it is waiting!
>>
>
> If this thread is currently in WAITING state, the main thread calls
> goTimedWaiting, before it notifies this thread, this thread gets waken
> up due to spurious wakeup or interrupt, then the race depending on who
> grabs the lock first as you described above will happen.
>
>> If "myThread" wins the race it will wait after signalling the phaser
>> and the main thread will then issue the notify allowing myThread to
>> proceed (and do what? process the WAITING case again??). So there is
>> no guarantee that myThread will be waiting when the main thread checks
>> the state!
>>
>> Similarly for issuing the unpark in the parking cases.
>>
>> AFAICS the basic approach here should be:
>> - tell myThread to go to state X
>> - wait until myThread should be in state X
>> - verify myThread is in state X
>
> That's what the test does.
>
>> - allow myThread to escape from state X
>
> Is this really needed?  This would require the main thread to notify
> myThread the verification is done.  The test currently goes to the new
> state for validation and it seems to me that this isn't needed.
>
> Mandy


More information about the serviceability-dev mailing list