hertzsprung
hertzsprung

Reputation: 9893

Interrupting a thread prior to calling Future.get()

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

Answers (3)

hertzsprung
hertzsprung

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

Gray
Gray

Reputation: 116878

Strange. I read the code the same way you do. I see:

  1. FutureTask.get() calls Sync.get(). I assume we are dealing with FutureTask here.
  2. Sync.get() calls Sync.innerGet()
  3. Sync.innerGet() calls acquireSharedInterruptibly(0);
  4. 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

Stephan
Stephan

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

Related Questions