TimeoutExecutor.php 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. <?php
  2. namespace React\Dns\Query;
  3. use React\EventLoop\Loop;
  4. use React\EventLoop\LoopInterface;
  5. use React\Promise\Promise;
  6. final class TimeoutExecutor implements ExecutorInterface
  7. {
  8. private $executor;
  9. private $loop;
  10. private $timeout;
  11. public function __construct(ExecutorInterface $executor, $timeout, LoopInterface $loop = null)
  12. {
  13. $this->executor = $executor;
  14. $this->loop = $loop ?: Loop::get();
  15. $this->timeout = $timeout;
  16. }
  17. public function query(Query $query)
  18. {
  19. $promise = $this->executor->query($query);
  20. $loop = $this->loop;
  21. $time = $this->timeout;
  22. return new Promise(function ($resolve, $reject) use ($loop, $time, $promise, $query) {
  23. $timer = null;
  24. $promise = $promise->then(function ($v) use (&$timer, $loop, $resolve) {
  25. if ($timer) {
  26. $loop->cancelTimer($timer);
  27. }
  28. $timer = false;
  29. $resolve($v);
  30. }, function ($v) use (&$timer, $loop, $reject) {
  31. if ($timer) {
  32. $loop->cancelTimer($timer);
  33. }
  34. $timer = false;
  35. $reject($v);
  36. });
  37. // promise already resolved => no need to start timer
  38. if ($timer === false) {
  39. return;
  40. }
  41. // start timeout timer which will cancel the pending promise
  42. $timer = $loop->addTimer($time, function () use ($time, &$promise, $reject, $query) {
  43. $reject(new TimeoutException(
  44. 'DNS query for ' . $query->describe() . ' timed out'
  45. ));
  46. // Cancel pending query to clean up any underlying resources and references.
  47. // Avoid garbage references in call stack by passing pending promise by reference.
  48. assert(\method_exists($promise, 'cancel'));
  49. $promise->cancel();
  50. $promise = null;
  51. });
  52. }, function () use (&$promise) {
  53. // Cancelling this promise will cancel the pending query, thus triggering the rejection logic above.
  54. // Avoid garbage references in call stack by passing pending promise by reference.
  55. assert(\method_exists($promise, 'cancel'));
  56. $promise->cancel();
  57. $promise = null;
  58. });
  59. }
  60. }