@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Update some Phabricator behaviors for changes to Futures

Summary:
Depends on D21053. Ref T11968. Three things have changed:

- Overseers can no longer use FutureIterator to continue execution of an arbitrary list of futures from any state. Use FuturePool instead.
- Same with repository daemons.
- Probably (?) fix an API change in the Harbormaster exec future.

Test Plan:
- Ran "bin/phd debug task" and "bin/phd debug pull", no longer saw Future-management related errors.
- The Harbormaster future is easiest to test by just seeing if production works once this change is deployed there.

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T11968

Differential Revision: https://secure.phabricator.com/D21054

+60 -32
+12 -5
src/applications/harbormaster/future/HarbormasterExecFuture.php
··· 25 25 } 26 26 27 27 public function isReady() { 28 + if ($this->hasResult()) { 29 + return true; 30 + } 31 + 28 32 $future = $this->getFuture(); 29 33 30 - $result = $future->isReady(); 34 + $is_ready = $future->isReady(); 31 35 32 36 list($stdout, $stderr) = $future->read(); 33 37 $future->discardBuffers(); ··· 40 44 $this->stderr->append($stderr); 41 45 } 42 46 43 - return $result; 44 - } 47 + if ($future->hasResult()) { 48 + $this->setResult($future->getResult()); 49 + } 45 50 46 - protected function getResult() { 47 - return $this->getFuture()->getResult(); 51 + // TODO: This should probably be implemented as a FutureProxy; it will 52 + // not currently propagate exceptions or sockets properly. 53 + 54 + return $is_ready; 48 55 } 49 56 50 57 }
+25 -9
src/applications/repository/daemon/PhabricatorRepositoryPullLocalDaemon.php
··· 73 73 $futures = array(); 74 74 $queue = array(); 75 75 76 + $future_pool = new FuturePool(); 77 + 78 + $future_pool->getIteratorTemplate() 79 + ->setUpdateInterval($min_sleep); 80 + 76 81 $sync_wait = phutil_units('2 minutes in seconds'); 77 82 $last_sync = array(); 78 83 ··· 214 219 $display_name)); 215 220 216 221 unset($queue[$id]); 217 - $futures[$id] = $this->buildUpdateFuture( 222 + 223 + $future = $this->buildUpdateFuture( 218 224 $repository, 219 225 $no_discovery); 220 226 227 + $futures[$id] = $future->getFutureKey(); 228 + 229 + $future_pool->addFuture($future); 221 230 break; 222 231 } 223 232 } ··· 230 239 phutil_count($queue))); 231 240 } 232 241 233 - if ($futures) { 234 - $iterator = id(new FutureIterator($futures)) 235 - ->setUpdateInterval($min_sleep); 242 + if ($future_pool->hasFutures()) { 243 + while ($future_pool->hasFutures()) { 244 + $future = $future_pool->resolve(); 236 245 237 - foreach ($iterator as $id => $future) { 238 246 $this->stillWorking(); 239 247 240 248 if ($future === null) { 241 249 $this->log(pht('Waiting for updates to complete...')); 242 - $this->stillWorking(); 243 250 244 251 if ($this->loadRepositoryUpdateMessages()) { 245 252 $this->log(pht('Interrupted by pending updates!')); ··· 249 256 continue; 250 257 } 251 258 252 - unset($futures[$id]); 253 - $retry_after[$id] = $this->resolveUpdateFuture( 254 - $pullable[$id], 259 + $future_key = $future->getFutureKey(); 260 + $repository_id = null; 261 + foreach ($futures as $id => $key) { 262 + if ($key === $future_key) { 263 + $repository_id = $id; 264 + unset($futures[$id]); 265 + break; 266 + } 267 + } 268 + 269 + $retry_after[$repository_id] = $this->resolveUpdateFuture( 270 + $pullable[$repository_id], 255 271 $future, 256 272 $min_sleep); 257 273
+23 -18
src/infrastructure/daemon/PhutilDaemonOverseer.php
··· 32 32 private $inAbruptShutdown; 33 33 private $inGracefulShutdown; 34 34 35 + private $futurePool; 36 + 35 37 public function __construct(array $argv) { 36 38 PhutilServiceProfiler::getInstance()->enableDiscardMode(); 37 39 ··· 160 162 public function run() { 161 163 $this->createDaemonPools(); 162 164 165 + $future_pool = $this->getFuturePool(); 166 + 163 167 while (true) { 164 168 if ($this->shouldReloadDaemons()) { 165 169 $this->didReceiveSignal(SIGHUP); 166 170 } 167 - 168 - $futures = array(); 169 171 170 172 $running_pools = false; 171 173 foreach ($this->getDaemonPools() as $pool) { ··· 180 182 } 181 183 182 184 foreach ($pool->getFutures() as $future) { 183 - $futures[] = $future; 185 + $future_pool->addFuture($future); 184 186 } 185 187 186 188 if ($pool->getDaemons()) { ··· 190 192 191 193 $this->updateMemory(); 192 194 193 - $this->waitForDaemonFutures($futures); 195 + if ($future_pool->hasFutures()) { 196 + $future_pool->resolve(); 197 + } else { 198 + if (!$this->shouldShutdown()) { 199 + sleep(1); 200 + } 201 + } 194 202 195 - if (!$futures && !$running_pools) { 203 + if (!$future_pool->hasFutures() && !$running_pools) { 196 204 if ($this->shouldShutdown()) { 197 205 break; 198 206 } ··· 202 210 exit($this->err); 203 211 } 204 212 213 + private function getFuturePool() { 214 + if (!$this->futurePool) { 215 + $pool = new FuturePool(); 205 216 206 - private function waitForDaemonFutures(array $futures) { 207 - assert_instances_of($futures, 'ExecFuture'); 217 + // TODO: This only wakes if any daemons actually exit, or 1 second 218 + // passes. It would be a bit cleaner to wait on any I/O, but Futures 219 + // currently can't do that. 208 220 209 - if ($futures) { 210 - // TODO: This only wakes if any daemons actually exit. It would be a bit 211 - // cleaner to wait on any I/O with Channels. 212 - $iter = id(new FutureIterator($futures)) 221 + $pool->getIteratorTemplate() 213 222 ->setUpdateInterval(1); 214 - foreach ($iter as $future) { 215 - break; 216 - } 217 - } else { 218 - if (!$this->shouldShutdown()) { 219 - sleep(1); 220 - } 223 + 224 + $this->futurePool = $pool; 221 225 } 226 + return $this->futurePool; 222 227 } 223 228 224 229 private function createDaemonPools() {