Let's see how firstCompletedOf is implemented:
def firstCompletedOf[T](futures: TraversableOnce[Future[T]])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() val completeFirst: Try[T] => Unit = p tryComplete _ futures foreach { _ onComplete completeFirst } p.future }
When executing { futures foreach { _ onComplete completeFirst } function { _ onComplete completeFirst } saved somewhere through ExecutionContext.execute . Where exactly this function is stored does not matter, we just know that it needs to be saved somewhere so that it can be selected later and executed in the thread pool when the thread becomes available.
This function closes above completeFirst , which closes above p . Thus, while there is another future (from futures ) that needs to be completed, there is a link to p that prevents it from garbage collection (although in this case the chances are that firstCompletedOf already returned by removing p from the stack).
When the first future ends, it stores the result in a promise (by invoking p.tryComplete ). Since the promise p contains the result, the result is achieved at least until p reaches the level, and, as we have seen, p be achieved if at least one of the futures not been completed, for this reason the result cannot be collected before all futures are completed.
UPDATE : Now the question is: can this be fixed? I think it's possible. All we need to do is ensure that the first future completes the βcrossing out" of the p reference in a thread-safe way, which can be done using an example using AtomicReference. Something like that:
def firstCompletedOf[T](futures: TraversableOnce[Future[T]])(implicit executor: ExecutionContext): Future[T] = { val p = Promise[T]() val pref = new java.util.concurrent.atomic.AtomicReference(p) val completeFirst: Try[T] => Unit = { result: Try[T] => val promise = pref.getAndSet(null) if (promise != null) { promise.tryComplete(result) } } futures foreach { _ onComplete completeFirst } p.future }
I tested it, and, as expected, it allows you to get a result that collects garbage as soon as the first future is completed. He must behave the same in all other respects.
Regis jean-gilles
source share