Reputation: 9893
I'm trying to write an integration test that causes an InterruptedException
to be raised from the production code:
@Test
public void test() {
productionObject = new ProductionObject(
com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor());
Thread.currentThread().interrupt();
assertThat(productionObject.execute(), equalTo(defaultResponse));
}
Inside productionObject
's implementation:
try {
for (Future<T> future : executorService.invokeAll(tasks))) {
results.add(future.get());
}
return results;
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // preserve interrupt flag
return defaultResponse;
}
Inside AbstractQueuedSynchronizer.acquireSharedInterruptibly()
I see:
if (Thread.interrupted())
throw new InterruptedException();
So I would expect this test to pass consistently.
I've seen this fail in our build server (results
are returned rather than defaultResponse
). I've been unable to reproduce the failure locally, running the test in a while (true) loop, and simulating higher load by running glxgears with software rendering ;-) Can anyone spot my mistake, give me some suggestions on where to look, or suggest tools that could help me?
Upvotes: 2
Views: 631
Reputation: 9893
I've "fixed" this by interrupting the thread from within the Callable
rather than from the test method itself. This makes the interruption occur closer to call to acquireSharedInterruptibly()
.
I can only imagine that somewhere on the code path the interrupt flag is sometimes being cleared (perhaps by JUnit or Maven surefire, which are executing test methods in parallel). I've probably only reduced the likelihood of the race condition, rather than fixing it :-/
Upvotes: 0
Reputation: 116878
Strange. I read the code the same way you do. I see:
FutureTask.get()
calls Sync.get()
. I assume we are dealing with FutureTask
here.Sync.get()
calls Sync.innerGet()
Sync.innerGet()
calls acquireSharedInterruptibly(0)
;Which has the code right off:
if (Thread.interrupted())
throw new InterruptedException();
I would think that this would always throw. Maybe there is some sort of race condition so the thread does not yet know that it has been interrupted? Have you tried to sleep for 100ms after you interrupt the thread?
I just ran the following test on my multi-cpu Mac and it never fails so it does not look like a race condition -- at least with my architecture and JRE version 1.6.0_41.
for (long i = 0; i < 10000000; i++) {
Thread.currentThread().interrupt();
assertTrue(Thread.interrupted());
}
Upvotes: 1
Reputation: 7388
Using sameThreadExecutor()
in this context might actually be contra-productive since the interrupt might than occur in one of the tasks. Otherwise the code looks fine. Try using kicking off actual other threads and let one of the tasks wait long enough for your interrupt.
Upvotes: 0