public function renderModuleStatus(AphrontRequest $request)
 {
     $viewer = $request->getViewer();
     $collectors = PhabricatorGarbageCollector::getAllCollectors();
     $collectors = msort($collectors, 'getCollectorConstant');
     $rows = array();
     $rowc = array();
     foreach ($collectors as $key => $collector) {
         $class = null;
         if ($collector->hasAutomaticPolicy()) {
             $policy_view = phutil_tag('em', array(), pht('Automatic'));
         } else {
             $policy = $collector->getRetentionPolicy();
             if ($policy === null) {
                 $policy_view = pht('Indefinite');
             } else {
                 $days = ceil($policy / phutil_units('1 day in seconds'));
                 $policy_view = pht('%s Day(s)', new PhutilNumber($days));
             }
             $default = $collector->getDefaultRetentionPolicy();
             if ($policy !== $default) {
                 $class = 'highlighted';
                 $policy_view = phutil_tag('strong', array(), $policy_view);
             }
         }
         $rowc[] = $class;
         $rows[] = array($collector->getCollectorConstant(), $collector->getCollectorName(), $policy_view);
     }
     $table = id(new AphrontTableView($rows))->setRowClasses($rowc)->setHeaders(array(pht('Constant'), pht('Name'), pht('Retention Policy')))->setColumnClasses(array(null, 'pri wide', null));
     $header = id(new PHUIHeaderView())->setHeader(pht('Garbage Collectors'))->setSubheader(pht('Collectors with custom policies are highlighted. Use ' . '%s to change retention policies.', phutil_tag('tt', array(), 'bin/garbage set-policy')));
     return id(new PHUIObjectBoxView())->setHeader($header)->setTable($table);
 }
 protected function getCollector($const)
 {
     $collectors = PhabricatorGarbageCollector::getAllCollectors();
     $collector_list = array_keys($collectors);
     sort($collector_list);
     $collector_list = implode(', ', $collector_list);
     if (!$const) {
         throw new PhutilArgumentUsageException(pht('Specify a collector with "%s". Valid collectors are: %s.', '--collector', $collector_list));
     }
     if (empty($collectors[$const])) {
         throw new PhutilArgumentUsageException(pht('No such collector "%s". Choose a valid collector: %s.', $const, $collector_list));
     }
     return $collectors[$const];
 }
 /**
  * Update garbage collection, possibly collecting a small amount of garbage.
  *
  * @return bool True if there is more garbage to collect.
  * @task garbage
  */
 private function updateGarbageCollection()
 {
     // If we're ready to start the next collection cycle, load all the
     // collectors.
     $next = $this->nextCollection;
     if ($next && PhabricatorTime::getNow() >= $next) {
         $this->nextCollection = null;
         $all_collectors = PhabricatorGarbageCollector::getAllCollectors();
         $this->garbageCollectors = $all_collectors;
     }
     // If we're in a collection cycle, continue collection.
     if ($this->garbageCollectors) {
         foreach ($this->garbageCollectors as $key => $collector) {
             $more_garbage = $collector->runCollector();
             if (!$more_garbage) {
                 unset($this->garbageCollectors[$key]);
             }
             // We only run one collection per call, to prevent triggers from being
             // thrown too far off schedule if there's a lot of garbage to collect.
             break;
         }
         if ($this->garbageCollectors) {
             // If we have more work to do, return true.
             return true;
         }
         // Otherwise, reschedule another cycle in 4 hours.
         $now = PhabricatorTime::getNow();
         $wait = phutil_units('4 hours in seconds');
         $this->nextCollection = $now + $wait;
     }
     return false;
 }