scheduler = $scheduler; $this->syncUtils = $options['syncUtils']; $this->logger = $options['logger']; $this->fallback = new ABJ_404_Solution_NGramCronFallback( $optionStore, $scheduler, $clock, array( 'logger' => $this->logger, 'runBatch' => \Closure::fromCallable($options['runBatch']), 'runAfterResponseCallback' => function (): void { $this->runLocked(); }, ) ); } /** Schedule the chain while keeping fallback admission outside its lock. */ public function scheduleRebuild(): bool { $lockKey = 'ngram_schedule'; $uniqueID = $this->syncUtils->synchronizerAcquireLockTry($lockKey); if (empty($uniqueID)) { $this->logger->debugMessage("N-gram rebuild scheduling: Another process holds the lock. Skipping."); return true; } try { $scheduled = $this->scheduler->scheduleRebuild(); } finally { $this->syncUtils->synchronizerReleaseLock($uniqueID, $lockKey); } $this->arm(); return $scheduled; } /** @return bool */ public function arm(): bool { if (!ABJ_404_Solution_RequestKind::isAdminPageRender() || !ABJ_404_Solution_PluginAdminAuthorization::currentUserCanAccessPluginAdmin()) { return false; } return $this->fallback->arm(); } /** Run one admitted slice under the existing shared rebuild lock. */ public function runLocked(): void { $lockKey = 'ngram_rebuild'; $uniqueID = $this->syncUtils->synchronizerAcquireLockTry($lockKey); if (empty($uniqueID)) { $this->logger->debugMessage('N-gram fallback skipped because another rebuild holds the lock.'); return; } try { $this->fallback->runLocked(); } finally { $this->syncUtils->synchronizerReleaseLock($uniqueID, $lockKey); } } }