Changeset View
Changeset View
Standalone View
Standalone View
src/hardpoint/ArcanistHardpointTask.php
- This file was added.
| <?php | |||||
| final class ArcanistHardpointTask | |||||
| extends Phobject { | |||||
| private $request; | |||||
| private $query; | |||||
| private $objects; | |||||
| private $isComplete; | |||||
| private $generator; | |||||
| private $hasRewound; | |||||
| private $sendFuture; | |||||
| private $blockingRequests = array(); | |||||
| private $blockingFutures = array(); | |||||
| public function setRequest(ArcanistHardpointRequest $request) { | |||||
| $this->request = $request; | |||||
| return $this; | |||||
| } | |||||
| public function getRequest() { | |||||
| return $this->request; | |||||
| } | |||||
| public function setQuery(ArcanistHardpointQuery $query) { | |||||
| $this->query = $query; | |||||
| return $this; | |||||
| } | |||||
| public function getQuery() { | |||||
| return $this->query; | |||||
| } | |||||
| public function setObjects(array $objects) { | |||||
| $this->objects = $objects; | |||||
| return $this; | |||||
| } | |||||
| public function getObjects() { | |||||
| return $this->objects; | |||||
| } | |||||
| public function isComplete() { | |||||
| return $this->isComplete; | |||||
| } | |||||
| public function updateTask() { | |||||
| if ($this->isComplete()) { | |||||
| return false; | |||||
| } | |||||
| // If we're blocked by other requests, we have to wait for them to | |||||
| // resolve. | |||||
| if ($this->getBlockingRequests()) { | |||||
| return false; | |||||
| } | |||||
| // If we're blocked by futures, we have to wait for them to resolve. | |||||
| if ($this->getBlockingFutures()) { | |||||
| return false; | |||||
| } | |||||
| $query = $this->getQuery(); | |||||
| // If we've previously produced a generator, iterate it. | |||||
| if ($this->generator) { | |||||
| $generator = $this->generator; | |||||
| $has_send = false; | |||||
| $send_value = null; | |||||
| // If our last iteration generated a single future and it was marked to | |||||
| // be sent back to the generator, resolve the future (it should already | |||||
| // be ready to resolve) and send the result. | |||||
| if ($this->sendFuture) { | |||||
| $has_send = true; | |||||
| $future = $this->sendFuture; | |||||
| $this->sendFuture = null; | |||||
| $send_value = $future->resolve(); | |||||
| } | |||||
| if ($has_send && !$this->hasRewound) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Generator has never rewound, but has a value to send. This '. | |||||
| 'is invalid.')); | |||||
| } | |||||
| if (!$this->hasRewound) { | |||||
| $this->hasRewound = true; | |||||
| $generator->rewind(); | |||||
| } else if ($has_send) { | |||||
| $generator->send($send_value); | |||||
| } else { | |||||
| $generator->next(); | |||||
| } | |||||
| if ($generator->valid()) { | |||||
| $result = $generator->current(); | |||||
| if ($result instanceof Future) { | |||||
| $result = new ArcanistHardpointFutureList($result); | |||||
| } | |||||
| if ($result instanceof ArcanistHardpointFutureList) { | |||||
| $futures = $result->getFutures(); | |||||
| $is_send = $result->getSendResult(); | |||||
| $this->getRequest()->getEngine()->addFutures($futures); | |||||
| foreach ($futures as $future) { | |||||
| $this->blockingFutures[] = $future; | |||||
| } | |||||
| if ($is_send) { | |||||
| if (count($futures) === 1) { | |||||
| $this->sendFuture = head($futures); | |||||
| } else { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Hardpoint future list is marked to send results to the '. | |||||
| 'generator, but the list does not have exactly one future '. | |||||
| '(it has %s).', | |||||
| phutil_count($futures))); | |||||
| } | |||||
| } | |||||
| return true; | |||||
| } | |||||
| $is_request = ($result instanceof ArcanistHardpointRequest); | |||||
| $is_request_list = ($result instanceof ArcanistHardpointRequestList); | |||||
| if ($is_request || $is_request_list) { | |||||
| if ($is_request) { | |||||
| $request_list = array($result); | |||||
| } else { | |||||
| $request_list = $result->getRequests(); | |||||
| } | |||||
| // TODO: Make sure these requests have already been added to the | |||||
| // engine. | |||||
| foreach ($request_list as $blocking_request) { | |||||
| $this->blockingRequests[] = $blocking_request; | |||||
| } | |||||
| return true; | |||||
| } | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Hardpoint generator (for query "%s") yielded an unexpected '. | |||||
| 'value. Generators may only yield "Future" or '. | |||||
| '"ArcanistHardpointRequest" objects, got "%s".', | |||||
| get_class($query), | |||||
| phutil_describe_type($result))); | |||||
| } | |||||
| $this->generator = null; | |||||
| $result = $generator->getReturn(); | |||||
| $this->attachResult($result); | |||||
| return true; | |||||
| } | |||||
| $objects = $this->getObjects(); | |||||
| $hardpoint = $this->getRequest()->getHardpoint(); | |||||
| $result = $query->loadHardpoint($objects, $hardpoint); | |||||
| if ($result instanceof Generator) { | |||||
| $this->generator = $result; | |||||
| $this->hasRewound = false; | |||||
| // If we produced a generator, we can attempt to iterate it immediately. | |||||
| return $this->updateTask(); | |||||
| } | |||||
| $this->attachResult($result); | |||||
| return true; | |||||
| } | |||||
| public function getBlockingRequests() { | |||||
| $blocking = array(); | |||||
| foreach ($this->blockingRequests as $key => $request) { | |||||
| if (!$request->isComplete()) { | |||||
| $blocking[$key] = $request; | |||||
| } | |||||
| } | |||||
| $this->blockingRequests = $blocking; | |||||
| return $blocking; | |||||
| } | |||||
| public function getBlockingFutures() { | |||||
| $blocking = array(); | |||||
| foreach ($this->blockingFutures as $key => $future) { | |||||
| if (!$future->hasResult() && !$future->hasException()) { | |||||
| $blocking[$key] = $future; | |||||
| } | |||||
| } | |||||
| $this->blockingFutures = $blocking; | |||||
| return $blocking; | |||||
| } | |||||
| private function attachResult($result) { | |||||
| $objects = $this->getObjects(); | |||||
| $hardpoint = $this->getRequest()->getHardpoint(); | |||||
| $definition = $this->getRequest()->getHardpointDefinition(); | |||||
| $is_vector = $definition->isVectorHardpoint(); | |||||
| foreach ($result as $object_key => $value) { | |||||
| if (!isset($objects[$object_key])) { | |||||
| throw new Exception( | |||||
| pht( | |||||
| 'Bad object key ("%s").', | |||||
| $object_key)); | |||||
| } | |||||
| $object = $objects[$object_key]; | |||||
| if ($is_vector) { | |||||
| $object->mergeHardpoint($hardpoint, $value); | |||||
| } else { | |||||
| if (!$object->hasAttachedHardpoint($hardpoint)) { | |||||
| $object->attachHardpoint($hardpoint, $value); | |||||
| } | |||||
| } | |||||
| } | |||||
| $this->isComplete = true; | |||||
| } | |||||
| } | |||||