[concurrency-interest] Future Get/Done Race Condition

David Holmes dcholmes at optusnet.com.au
Tue Jan 30 18:24:42 EST 2007


Sam,

done() is called after the internal synchronization used to block get() is
released, so yes the thread that was doing get() can proceed to execute code
for a long time before done() actually gets to be executed.

The timing of these hook methods is always somewhat subjective. In some ways
it might have been better to invoke done() prior to the actual release - in
the same way that a barrier action for a CyclicBarrier is executed prior to
the release of the barrier.

The only way I can think to achieve what you want is to override all the get
methods to add a "waitForDone" call that blocks until your done() method
unblocks it. This is a bit crude but can be easily done with a
CountDownLatch:

    class MyFutureTask extends FutureTask {

       CountDownLatch done = new CountDownLatch(1);
       public V get() throws ... {
          try { return super.get(); }
          finally {
             done.await();
          }
        }

       // timed get() is a bit trickier :)

       protected void done() {
          try {
             // real stuff
          }
          finally {
             done.countDown();
          }
      }
  }

Cheers,
David Holmes

> -----Original Message-----
> From: concurrency-interest-bounces at cs.oswego.edu
> [mailto:concurrency-interest-bounces at cs.oswego.edu]On Behalf Of Sam
> Berlin
> Sent: Wednesday, 31 January 2007 8:34 AM
> To: Concurrency-interest at cs.oswego.edu
> Subject: [concurrency-interest] Future Get/Done Race Condition
>
>
> Hi Folks,
>
> We recently started running our tests on a faster multi-processor
> machine and ran into a few race conditions, one of which we're unsure
> of how to fix.  What we're experiencing is that a Thread that has
> created a future and calls 'get' on it can retrieve the result before
> the future has 'done' called on itself.  This leads to more code being
> able to act before the finished-state of the future has cleaned itself
> up.
>
> The reason this is a problem is that we want to limit the number of
> outgoing pings sent to a host.  When Manager.ping(Host) is called, it
> synchronizes on a map and checks to see if there's an outstanding
> future for that host, and if so, returns it.  Otherwise (if there's no
> outstanding future), it creates one, adds it to the map, submits it to
> an executor service, and returns it.  When the future is finished,
> it's overridden done method synchronizes on the map and removes
> itself.
>
> The end result is the following code sometimes works & sometimes doesn't:
>
> ---
>   // Setup response scenario, send ping & assert response
>   Future<PingResult> future = pingManager.ping(host);
>   PingResult result = future.get();
>   // check response assertions
>   // Change scenario so no response is sent..
>   try {
>       pingManager.ping(host).get();
>       fail("shouldn't have gotten a response!");
>       // Note: this fails occasionally because the above
>       // future's done method isn't called yet (which removes
>       // the outstanding future), so the pingManager
>       // is returning the same Future as above.
>   } catch(ExcecutionException expected) {}
> ---
>
> We can easily add a small sleep, or a Thread.yield() to make sure
> other threads process (and the done() is called), but I'm wondering if
> there's any better way.
>
> Thanks very much for any ideas.
>
> Sam
> _______________________________________________
> Concurrency-interest mailing list
> Concurrency-interest at altair.cs.oswego.edu
> http://altair.cs.oswego.edu/mailman/listinfo/concurrency-interest



More information about the Concurrency-interest mailing list