Code Review 7021010: java/lang/Thread/ThreadStateTest.java fails intermittently

David Holmes David.Holmes at oracle.com
Thu Jun 23 10:34:13 UTC 2011


Chris Hegarty said the following on 06/23/11 20:22:
> 
> On 06/23/11 06:33 AM, David Holmes wrote:
>  > Sorry for the delay on this ...
>  >
>  > I concur with Mandy that using arrive() the thread must always be
>  > RUNNABLE or the expected next state. Hence the new check is ok.
> 
> Thanks David,
> 
>  > With the new synchronization (and perhaps even the old) it seems to me
>  > that here:
>  >
>  > private void setState(int newState) {
>  > switch (state) {
>  > case BLOCKED:
>  > while (state == BLOCKED) {
>  > goSleep(20);
>  > }
>  > state = newState;
>  > break;
>  >
>  > the sleep loop is unnecessary as after setting the new state the main
>  > thread will call arriveAndAwaitAdvance.
> 
> In its current form we can't remove this ( I only found out after 
> removing it and testing ). The reason is that after the main thread 
> releases globalLock, MyThread sets the state to RUNNABLE (to keep it 
> busy before the next state is set). But goWaiting may be called on the 
> main thread before MyThread sets the state to be RUNNABLE. So the 
> WAITING state could be lost and MyThread go into an infinite loop.

Ouch! That's a nasty race. :(

David
-----

> I think it may be best to just reduce the sleep from 20ms to 10ms.
> 
>  > Also here:
>  >
>  > try {
>  > Thread.sleep(1000000);
>  > } catch (InterruptedException e) {
>  > // finish sleeping
>  > interrupted();
>  > }
>  >
>  > the interrupted() call is redundant as the interrupt state is already
>  > cleared when the IE is thrown.
> 
> Thanks, I'll remove this redundant call.
> 
> Updated Webrev:
>   http://cr.openjdk.java.net/~chegar/7021010/jdk8.webrev.02/webrev/
> 
> -Chris
> 
>  >
>  > Cheers,
>  > David
>  >
>  >
>  > David Holmes said the following on 06/22/11 20:05:
>  >> Mandy,
>  >>
>  >> I need to study the test in more detail to see exactly how the thread
>  >> is supposed to change state and in what order. I'm not yet convinced
>  >> that arrive() in place of arriveAndAwaitAdvance() doesn't introduce
>  >> races.
>  >>
>  >> David
>  >>
>  >> Mandy Chung said the following on 06/22/11 13:18:
>  >>> On 6/21/11 7:12 PM, Chris Hegarty wrote:
>  >>>> [...]
>  >>>>
>  >>>> > I'm not sure the extra check in checkThreadState that the thread 
> must
>  >>>> > be RUNNABLE is valid. What if you are transitioning the thread 
> from a
>  >>>> > blocked to non-blocked state, you may still see it blocked on the
>  >>>> > first call to getState.
>  >>>>
>  >>>> > L130-116: I agree with David that this test is not needed.
>  >>>> > Like the RuntimeException listed in ProblemList.txt shows
>  >>>> > that the target thread is in WAITING state but expected to
>  >>>> > be RUNNABLE.
>  >>>>
>  >>>> The main thread executes goBlocked, goWaiting, goTimedWaiting, etc.
>  >>>> These methods set the state and then wait for the phaser to advance.
>  >>>> The phaser will not advance until MyThread triggers it, at which
>  >>>> point the thread should either be RUNNABLE or the expected state,
>  >>>> right? Or have I missed something?
>  >>>>
>  >>> With the change from calling arriveAndAwaitAdvance to calling arrive,
>  >>> I think you're probably right that the thread should either be
>  >>> RUNNABLE or the expected state. If calling arriveAndAwaitAdvance, the
>  >>> thread delays and waits for the main thread to arrive. The main
>  >>> thread may see that the target thread is in a state waiting for
>  >>> phaser to advance (depending on the implementation).
>  >>>
>  >>>> I added this extra check since we are now relaxing the check for the
>  >>>> expected state. I just thought it would be more correct than
>  >>>> allowing any state, but if you feel it too strict ( or still
>  >>>> incorrect ) I can remove it.
>  >>>>
>  >>>
>  >>> With the change to call arrive, it seems fine to keep this check. I'd
>  >>> like to get David's opinion on this.
>  >>>
>  >>>> > I also don't understand why you moved the terminated check to after
>  >>>> > the join() - if the thread is failing to terminate then the join(),
>  >>>> > which is untimed, will simply not return and the test will hang
>  >>>> > until timed-out by the harness.
>  >>>>
>  >>>> > L98: Are you seeing some timing issue if you keep this
>  >>>> > checkThreadState
>  >>>> > before the join and thus you moved it after the join?
>  >>>>
>  >>>> No timing issue. I did this for simplicity, given that the state
>  >>>> should be TERMINATED when join returns. Either way MyThread.run must
>  >>>> complete before the threads state will be set to TERMINATED.
>  >>>> Invoking checkThreadState before that point just seems more likely
>  >>>> encounter retries. I'm ok with either, just let me know if you want
>  >>>> it reverted back to the original.
>  >>>>
>  >>> The thread could move to TERMINATED state once it completes execution
>  >>> and while the main thread is not yet notified. Since there is no
>  >>> timing issue and MyThread is calling arrive rather than
>  >>> arriveAndAwaitsAdvance, I would say keep the checkThreadState call
>  >>> before the join.
>  >>>
>  >>>> > I also don't think the use of the Phaser is appropriate here as you
>  >>>> > are actually delaying the thread from making the state change. 
> In the
>  >>>> > original code the target thread signals the main thread that it is
>  >>>> > about to go to state X and continues to advance to state X (modulo
>  >>>> > preemption etc). But with the Phaser the target thread indicates it
>  >>>> > is about to go to state X and then waits for the main thread -
>  >>>> > consequently it is more likely that when the main thread calls
>  >>>> > checkThreadState that the target has not yet reached the desired
>  >>>> > state and so the main thread will have
>  >>>> > to loop. This isn't incorrect it just seems to me that in the 
> "wrong"
>  >>>> > configuration the test may not take a lot longer in relative terms.
>  >>>> > Maybe the additional clarity is worth it though ...
>  >>>>
>  >>>> When MyThread invokes arriveAndAwaitAdvance, then it should be the
>  >>>> final party to arrive and therefore "probably" shouldn't wait. But
>  >>>> you raise a good point, myThread should really just invoke
>  >>>> phaser.arrive() rather that arriveAndAwaitAdvance.
>  >>>>
>  >>>> Updated Webrev:
>  >>>> http://cr.openjdk.java.net/~chegar/7021010/jdk8.webrev.01/webrev/
>  >>> Looks good. Thanks for fixing this timing issue.
>  >>> Mandy



More information about the core-libs-dev mailing list