From 2daf9b16aeb1abbceefc889ede9f1304fdfecd90 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 1 Jul 2020 05:06:19 -0700 Subject: [PATCH] Improve resolution behaviors of FutureProxy Summary: See PHI1764. See PHI1802. Address two resolution behaviors for FutureProxy: - FutureProxy may throw an exception directly from iteration via "FutureIterator" (see PHI1764). This is wrong: futures should throw only when resolved. - FutureProxy can not change an exception into a result, or a result into an exception, or an exception into a different exception. Being able to proxy the full range of result and exception behavior is useful, particularly for Conduit (see PHI1802). Make "FutureProxy" more robust in how it handles exceptions from proxied futures. Test Plan: Used this script to raise an exception during result processing: ``` resolve(); } catch (Exception $ex) { echo "Caught exception properly on resolution.\n"; } } ``` Before this change, the exception is raised in the `foreach()` loop. After this change, the exception is raised at resolution time. Differential Revision: https://secure.phabricator.com/D21383 --- src/future/Future.php | 2 +- src/future/FutureProxy.php | 30 ++++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/future/Future.php b/src/future/Future.php index 3c371bc4..1e52c48a 100644 --- a/src/future/Future.php +++ b/src/future/Future.php @@ -63,7 +63,7 @@ abstract class Future extends Phobject { $this->hasStarted = true; $this->startServiceProfiler(); - $this->isReady(); + $this->updateFuture(); } final public function updateFuture() { diff --git a/src/future/FutureProxy.php b/src/future/FutureProxy.php index 77c8c5bb..d7e00cd6 100644 --- a/src/future/FutureProxy.php +++ b/src/future/FutureProxy.php @@ -27,27 +27,29 @@ abstract class FutureProxy extends Future { } public function isReady() { - if ($this->hasResult()) { + if ($this->hasResult() || $this->hasException()) { return true; } $proxied = $this->getProxiedFuture(); + $proxied->updateFuture(); - $is_ready = $proxied->isReady(); + if ($proxied->hasResult() || $proxied->hasException()) { + try { + $result = $proxied->resolve(); + $result = $this->didReceiveResult($result); + } catch (Exception $ex) { + $result = $this->didReceiveException($ex); + } catch (Throwable $ex) { + $result = $this->didReceiveException($ex); + } - if ($proxied->hasResult()) { - $result = $proxied->getResult(); - $result = $this->didReceiveResult($result); $this->setResult($result); + + return true; } - return $is_ready; - } - - public function resolve() { - $this->getProxiedFuture()->resolve(); - $this->isReady(); - return $this->getResult(); + return false; } public function getReadSockets() { @@ -73,4 +75,8 @@ abstract class FutureProxy extends Future { abstract protected function didReceiveResult($result); + protected function didReceiveException($exception) { + throw $exception; + } + }