]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - lib/iomaster.php
Removed plugin Google-Analytics as this is free/libre and decentralized
[quix0rs-gnu-social.git] / lib / iomaster.php
index ce77b53b2e6c3f35059bf62ead9ebb007581d099..7072761f2d2a03be6f34e5a66aae6eb445826044 100644 (file)
@@ -27,7 +27,7 @@
  * @link      http://status.net/
  */
 
-class IoMaster
+abstract class IoMaster
 {
     public $id;
 
@@ -38,6 +38,9 @@ class IoMaster
     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
      */
@@ -52,89 +55,47 @@ class IoMaster
         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);
-            }
-
-            $classes = array();
-            if (Event::handle('StartIoManagerClasses', array(&$classes))) {
-                $classes[] = 'QueueManager';
-                if (common_config('xmpp', 'enabled') && !defined('XMPP_EMERGENCY_FLAG')) {
-                    $classes[] = 'XmppManager'; // handles pings/reconnects
-                    $classes[] = 'XmppConfirmManager'; // polls for outgoing confirmations
-                }
-            }
-            Event::handle('EndIoManagerClasses', array(&$classes));
 
-            foreach ($classes as $class) {
-                $this->instantiate($class);
-            }
-        }
+        $this->initManagers();
     }
 
     /**
-     * Pull all local sites from status_network table.
-     * @return array of hostnames
+     * 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.
+     *
+     * Switching site configurations is an acceptable side effect.
      */
-    protected function findAllSites()
-    {
-        $hosts = array();
-        $sn = new Status_network();
-        $sn->find();
-        while ($sn->fetch()) {
-            $hosts[] = $sn->hostname;
-        }
-        return $hosts;
-    }
+    abstract function initManagers();
 
     /**
      * 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 (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)
-    {
-        return call_user_func(array($class, 'get'));
+        if (!in_array($manager, $this->managers, true)) {
+            // Only need to save singletons once
+            $this->managers[] = $manager;
+        }
     }
 
     /**
@@ -148,8 +109,9 @@ class IoMaster
     {
         $this->logState('init');
         $this->start();
+        $this->checkMemory(false);
 
-        while (true) {
+        while (!$this->shutdown) {
             $timeouts = array_values($this->pollTimeouts);
             $timeouts[] = 60; // default max timeout
 
@@ -170,7 +132,7 @@ class IoMaster
                 $write = array();
                 $except = array();
                 $this->logState('listening');
-                common_log(LOG_INFO, "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) {
@@ -190,7 +152,7 @@ class IoMaster
 
             if ($timeout > 0 && empty($sockets)) {
                 // If we had no listeners, sleep until the pollers' next requested wakeup.
-                common_log(LOG_INFO, "Sleeping $timeout seconds until next poll cycle...");
+                common_log(LOG_DEBUG, "Sleeping $timeout seconds until next poll cycle...");
                 $this->logState('sleep');
                 sleep($timeout);
             }
@@ -201,20 +163,38 @@ class IoMaster
             $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;
-                }
-            }
+            $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
@@ -223,8 +203,7 @@ class IoMaster
     {
         $softLimit = trim(common_config('queue', 'softlimit'));
         if (substr($softLimit, -1) == '%') {
-            $limit = trim(ini_get('memory_limit'));
-            $limit = $this->parseMemoryLimit($limit);
+            $limit = $this->parseMemoryLimit(ini_get('memory_limit'));
             if ($limit > 0) {
                 return intval(substr($softLimit, 0, -1) * $limit / 100);
             } else {
@@ -242,9 +221,10 @@ class IoMaster
      * @param string $mem
      * @return int
      */
-    protected function parseMemoryLimit($mem)
+    public function parseMemoryLimit($mem)
     {
         // http://www.php.net/manual/en/faq.using.php#faq.using.shorthandbytes
+        $mem = strtolower(trim($mem));
         $size = array('k' => 1024,
                       'm' => 1024*1024,
                       'g' => 1024*1024*1024);
@@ -253,7 +233,7 @@ class IoMaster
         } else if (is_numeric($mem)) {
             return intval($mem);
         } else {
-            $mult = strtolower(substr($mem, -1));
+            $mult = substr($mem, -1);
             if (isset($size[$mult])) {
                 return substr($mem, 0, -1) * $size[$mult];
             } else {
@@ -350,12 +330,31 @@ class IoMaster
      * for per-queue and per-site records.
      *
      * @param string $key counter name
-     * @param array $owners list of owner keys like 'queue:jabber' or 'site:stat01'
+     * @param array $owners list of owner keys like 'queue:xmpp' or 'site:stat01'
      */
     public function stats($key, $owners=array())
     {
         $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;
+    }
+
 }