protected $pollTimeouts = array();
protected $lastPoll = array();
+ public $shutdown = false; // Did we do a graceful shutdown?
+ public $respawn = true; // Should we respawn after shutdown?
+
/**
* @param string $id process ID to use in logging/monitoring
*/
if ($multiSite !== null) {
$this->multiSite = $multiSite;
}
- if ($this->multiSite) {
- $this->sites = $this->findAllSites();
- } else {
- $this->sites = array(common_config('site', 'server'));
- }
- if (empty($this->sites)) {
- throw new Exception("Empty status_network table, cannot init");
- }
-
- foreach ($this->sites as $site) {
- if ($site != common_config('site', 'server')) {
- StatusNet::init($site);
- }
- $this->initManagers();
- }
+ $this->initManagers();
}
/**
- * Initialize IoManagers for the currently configured site
- * which are appropriate to this instance.
+ * Initialize IoManagers which are appropriate to this instance;
+ * pass class names or instances into $this->instantiate().
+ *
+ * If setup and configuration may vary between sites in multi-site
+ * mode, it's the subclass's responsibility to set them up here.
*
- * Pass class names into $this->instantiate()
+ * Switching site configurations is an acceptable side effect.
*/
abstract function initManagers();
- /**
- * Pull all local sites from status_network table.
- * @return array of hostnames
- */
- protected function findAllSites()
- {
- $hosts = array();
- $sn = new Status_network();
- $sn->find();
- while ($sn->fetch()) {
- $hosts[] = $sn->hostname;
- }
- return $hosts;
- }
-
/**
* Instantiate an i/o manager class for the current site.
* If a multi-site capable handler is already present,
* we don't need to build a new one.
*
- * @param string $class
+ * @param mixed $manager class name (to run $class::get()) or object
*/
- protected function instantiate($class)
+ protected function instantiate($manager)
{
- if (is_string($class) && isset($this->singletons[$class])) {
- // Already instantiated a multi-site-capable handler.
- // Just let it know it should listen to this site too!
- $this->singletons[$class]->addSite(common_config('site', 'server'));
- return;
+ if (is_string($manager)) {
+ $manager = call_user_func(array($class, 'get'));
}
- $manager = $this->getManager($class);
-
- if ($this->multiSite) {
- $caps = $manager->multiSite();
- if ($caps == IoManager::SINGLE_ONLY) {
+ $caps = $manager->multiSite();
+ if ($caps == IoManager::SINGLE_ONLY) {
+ if ($this->multiSite) {
throw new Exception("$class can't run with --all; aborting.");
}
- if ($caps == IoManager::INSTANCE_PER_PROCESS) {
- // Save this guy for later!
- // We'll only need the one to cover multiple sites.
- $this->singletons[$class] = $manager;
- $manager->addSite(common_config('site', 'server'));
- }
+ } else if ($caps == IoManager::INSTANCE_PER_PROCESS) {
+ $manager->addSite();
}
- $this->managers[] = $manager;
- }
-
- protected function getManager($class)
- {
- if(is_object($class)){
- return $class;
- } else {
- return call_user_func(array($class, 'get'));
+ if (!in_array($manager, $this->managers, true)) {
+ // Only need to save singletons once
+ $this->managers[] = $manager;
}
}
{
$this->logState('init');
$this->start();
+ $this->checkMemory(false);
- while (true) {
+ while (!$this->shutdown) {
$timeouts = array_values($this->pollTimeouts);
$timeouts[] = 60; // default max timeout
$write = array();
$except = array();
$this->logState('listening');
- common_log(LOG_DEBUG, "Waiting up to $timeout seconds for socket data...");
+ //common_debug("Waiting up to $timeout seconds for socket data...");
$ready = stream_select($read, $write, $except, $timeout, 0);
if ($ready === false) {
$this->logState('idle');
$this->idle();
- $memoryLimit = $this->softMemoryLimit();
- if ($memoryLimit > 0) {
- $usage = memory_get_usage();
- if ($usage > $memoryLimit) {
- common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
- break;
- } else if (common_config('queue', 'debug_memory')) {
- common_log(LOG_DEBUG, "Memory usage $usage");
- }
- }
+ $this->checkMemory();
}
$this->logState('shutdown');
$this->finish();
}
+ /**
+ * Check runtime memory usage, possibly triggering a graceful shutdown
+ * and thread respawn if we've crossed the soft limit.
+ *
+ * @param boolean $respawn if false we'll shut down instead of respawning
+ */
+ protected function checkMemory($respawn=true)
+ {
+ $memoryLimit = $this->softMemoryLimit();
+ if ($memoryLimit > 0) {
+ $usage = memory_get_usage();
+ if ($usage > $memoryLimit) {
+ common_log(LOG_INFO, "Queue thread hit soft memory limit ($usage > $memoryLimit); gracefully restarting.");
+ if ($respawn) {
+ $this->requestRestart();
+ } else {
+ $this->requestShutdown();
+ }
+ } else if (common_config('queue', 'debug_memory')) {
+ $fmt = number_format($usage);
+ common_log(LOG_DEBUG, "Memory usage $fmt");
+ }
+ }
+ }
+
/**
* Return fully-parsed soft memory limit in bytes.
* @return intval 0 or -1 if not set
$owners[] = "thread:" . $this->id;
$this->monitor->stats($key, $owners);
}
+
+ /**
+ * For IoManagers to request a graceful shutdown at end of event loop.
+ */
+ public function requestShutdown()
+ {
+ $this->shutdown = true;
+ $this->respawn = false;
+ }
+
+ /**
+ * For IoManagers to request a graceful restart at end of event loop.
+ */
+ public function requestRestart()
+ {
+ $this->shutdown = true;
+ $this->respawn = true;
+ }
+
}