Chris@0: moduleHandler = $module_handler; Chris@0: $this->lock = $lock; Chris@0: $this->queueFactory = $queue_factory; Chris@0: $this->state = $state; Chris@0: $this->accountSwitcher = $account_switcher; Chris@0: $this->logger = $logger; Chris@0: $this->queueManager = $queue_manager; Chris@14: $this->time = $time ?: \Drupal::service('datetime.time'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * {@inheritdoc} Chris@0: */ Chris@0: public function run() { Chris@0: // Allow execution to continue even if the request gets cancelled. Chris@0: @ignore_user_abort(TRUE); Chris@0: Chris@0: // Force the current user to anonymous to ensure consistent permissions on Chris@0: // cron runs. Chris@0: $this->accountSwitcher->switchTo(new AnonymousUserSession()); Chris@0: Chris@0: // Try to allocate enough time to run all the hook_cron implementations. Chris@18: Environment::setTimeLimit(240); Chris@0: Chris@0: $return = FALSE; Chris@0: Chris@0: // Try to acquire cron lock. Chris@0: if (!$this->lock->acquire('cron', 900.0)) { Chris@0: // Cron is still running normally. Chris@0: $this->logger->warning('Attempting to re-run cron while it is already running.'); Chris@0: } Chris@0: else { Chris@0: $this->invokeCronHandlers(); Chris@0: $this->setCronLastTime(); Chris@0: Chris@0: // Release cron lock. Chris@0: $this->lock->release('cron'); Chris@0: Chris@0: // Return TRUE so other functions can check if it did run successfully Chris@0: $return = TRUE; Chris@0: } Chris@0: Chris@0: // Process cron queues. Chris@0: $this->processQueues(); Chris@0: Chris@0: // Restore the user. Chris@0: $this->accountSwitcher->switchBack(); Chris@0: Chris@0: return $return; Chris@0: } Chris@0: Chris@0: /** Chris@0: * Records and logs the request time for this cron invocation. Chris@0: */ Chris@0: protected function setCronLastTime() { Chris@0: // Record cron time. Chris@14: $request_time = $this->time->getRequestTime(); Chris@14: $this->state->set('system.cron_last', $request_time); Chris@0: $this->logger->notice('Cron run completed.'); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Processes cron queues. Chris@0: */ Chris@0: protected function processQueues() { Chris@0: // Grab the defined cron queues. Chris@0: foreach ($this->queueManager->getDefinitions() as $queue_name => $info) { Chris@0: if (isset($info['cron'])) { Chris@0: // Make sure every queue exists. There is no harm in trying to recreate Chris@0: // an existing queue. Chris@0: $this->queueFactory->get($queue_name)->createQueue(); Chris@0: Chris@0: $queue_worker = $this->queueManager->createInstance($queue_name); Chris@0: $end = time() + (isset($info['cron']['time']) ? $info['cron']['time'] : 15); Chris@0: $queue = $this->queueFactory->get($queue_name); Chris@0: $lease_time = isset($info['cron']['time']) ?: NULL; Chris@0: while (time() < $end && ($item = $queue->claimItem($lease_time))) { Chris@0: try { Chris@0: $queue_worker->processItem($item->data); Chris@0: $queue->deleteItem($item); Chris@0: } Chris@0: catch (RequeueException $e) { Chris@0: // The worker requested the task be immediately requeued. Chris@0: $queue->releaseItem($item); Chris@0: } Chris@0: catch (SuspendQueueException $e) { Chris@0: // If the worker indicates there is a problem with the whole queue, Chris@0: // release the item and skip to the next queue. Chris@0: $queue->releaseItem($item); Chris@0: Chris@0: watchdog_exception('cron', $e); Chris@0: Chris@0: // Skip to the next queue. Chris@0: continue 2; Chris@0: } Chris@0: catch (\Exception $e) { Chris@0: // In case of any other kind of exception, log it and leave the item Chris@0: // in the queue to be processed again later. Chris@0: watchdog_exception('cron', $e); Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Invokes any cron handlers implementing hook_cron. Chris@0: */ Chris@0: protected function invokeCronHandlers() { Chris@0: $module_previous = ''; Chris@0: Chris@0: // If detailed logging isn't enabled, don't log individual execution times. Chris@0: $time_logging_enabled = \Drupal::config('system.cron')->get('logging'); Chris@0: $logger = $time_logging_enabled ? $this->logger : new NullLogger(); Chris@0: Chris@0: // Iterate through the modules calling their cron handlers (if any): Chris@0: foreach ($this->moduleHandler->getImplementations('cron') as $module) { Chris@0: Chris@0: if (!$module_previous) { Chris@0: $logger->notice('Starting execution of @module_cron().', [ Chris@0: '@module' => $module, Chris@0: ]); Chris@0: } Chris@0: else { Chris@0: $logger->notice('Starting execution of @module_cron(), execution of @module_previous_cron() took @time.', [ Chris@0: '@module' => $module, Chris@0: '@module_previous' => $module_previous, Chris@0: '@time' => Timer::read('cron_' . $module_previous) . 'ms', Chris@0: ]); Chris@0: } Chris@0: Timer::start('cron_' . $module); Chris@0: Chris@0: // Do not let an exception thrown by one module disturb another. Chris@0: try { Chris@0: $this->moduleHandler->invoke($module, 'cron'); Chris@0: } Chris@0: catch (\Exception $e) { Chris@0: watchdog_exception('cron', $e); Chris@0: } Chris@0: Chris@0: Timer::stop('cron_' . $module); Chris@0: $module_previous = $module; Chris@0: } Chris@0: if ($module_previous) { Chris@0: $logger->notice('Execution of @module_previous_cron() took @time.', [ Chris@0: '@module_previous' => $module_previous, Chris@0: '@time' => Timer::read('cron_' . $module_previous) . 'ms', Chris@0: ]); Chris@0: } Chris@0: } Chris@0: Chris@0: }