public function startClock($interval, SignalHandler $sh = null, InputHandler $ih = null) { L::level(L::DEBUG) && L::log(L::DEBUG, __CLASS__, "Clock Started, interval %s", array($interval)); $elapsed = 0.0; $start_time = microtime(true); $continue = true; while ($continue) { L::level(L::DEBUG) && L::log(L::DEBUG, __CLASS__, "Tick %s seconds", array($elapsed)); $this->notifyTickObservers($elapsed); $processing_time = microtime(true) - $start_time; if ($processing_time > $interval) { L::level(L::WARNING) && L::log(L::WARNING, __CLASS__, "Notification took %s, which is longer than interval %s", array($processing_time, $interval)); } else { $this->sleep($interval - $processing_time); } $end_time = microtime(true); $elapsed = $end_time - $start_time; $start_time = $end_time; // test for exit condition via CTRL-C / SIGTERM if ($sh) { $sh->test(); $continue = $continue && !$sh->shouldExit(); } // test for exit condition via User Input if ($ih) { $ih->process(); $continue = $continue && !$ih->shouldExit(); } $continue = $continue && !$this->exit_notified; } }
/** * Add a callback that listens to a specific signal. * @param string $signal_type * @param function $callback * @param int $priority */ public function Listen($signal_type, $callback, $priority = 10) { if (!isset($this->listeners[$signal_type])) { $this->listeners[$signal_type] = array(); } $this->listeners[$signal_type][] = array('callback' => $callback, 'priority' => $priority); $this->listeners[$signal_type] = SignalHandler::Sort($this->listeners[$signal_type], 'priority'); }
public static function start() { if (!extension_loaded('pcntl')) { return; } foreach (self::$handledSignals as $signal) { pcntl_signal($signal, function ($signal) { SignalHandler::$shouldExitForSignal = true; }); } }
/** * Calls signal handlers for pending signals. * * @return $this */ public function dispatch() { $this->signalHandler->dispatch(); return $this; }
/** * Sets up and couples the event-driven history monitoring components, * and then starts the clock. * * A signal handler is installed to catch Ctrl-C, although it's still * safer to shutdown ScratchLive! first if you want everything scrobbled * correctly. * * --post-process can be used to replay a file. * --manual to ticks on user input (for debugging. Overrides --post-process for ticks). * --csv can be used to replay from a CSV fake-file. * These options can be combined. * */ protected function monitor($filename) { // Use the caching version via Dependency Injection. This means that all // new SSLTracks created using a SSLTrackFactory will get a RuntimeCachingSSLTrack // that knows how to ask the cache about expensive lookups (such as getID3 stuff). Inject::map('SSLTrackFactory', new SSLTrackCache()); if ($this->manual_tick) { // tick when the user presses enter $pseudo_ts = $real_ts = new CrankHandle(); } elseif ($this->post_process) { $pseudo_ts = $real_ts = new InstantTickSource(); } else { // tick based on the clock $pseudo_ts = $real_ts = new TickSource(); } if ($this->post_process) { // $mon is TickObservable // $hfm is DiffObservable if ($this->csv) { $mon = $hfm = new SSLHistoryFileCSVInjector($filename); } else { $mon = $hfm = new SSLHistoryFileReplayer($filename); } $pseudo_ts = $mon; $hfm->addExitObserver($real_ts); } else { $mon = new TailMonitor(); $mon->setFilenameSource($this); $hfm = new SSLHistoryFileMonitor($filename, $mon); } $sh = new SignalHandler(); //$ih = new InputHandler(); $rtm = new SSLRealtimeModel(); $rtm_printer = new SSLRealtimeModelPrinter($rtm); $npm = new NowPlayingModel(); $sm = new ScrobbleModel(); // the ordering here is important. See the README.txt for a collaboration diagram. $pseudo_ts->addTickObserver($this->plugin_manager); $real_ts->addTickObserver($mon); $pseudo_ts->addTickObserver($npm); $hfm->addDiffObserver($rtm); $rtm->addTrackChangeObserver($rtm_printer); $rtm->addTrackChangeObserver($npm); $rtm->addTrackChangeObserver($sm); // get the PluginWrapper that wraps all other plugins. $pw = $this->plugin_manager->getObservers(); // add all of the PluginWrappers to the various places. $pseudo_ts->addTickObserver($pw[0]); $hfm->addDiffObserver($pw[0]); $rtm->addTrackChangeObserver($pw[0]); $npm->addNowPlayingObserver($pw[0]); $sm->addScrobbleObserver($pw[0]); $sh->install(); //$ih->install(); $this->plugin_manager->onStart(); // Tick tick tick. This only returns if a signal is caught $real_ts->startClock($this->sleep, $sh); $rtm->shutdown(); $this->plugin_manager->onStop(); }
/** * Start Forking * * @param object $ProcessObject * @final */ public final function Run(&$ProcessObject) { // Check for ProcessObject existence if (!is_object($ProcessObject) || !$ProcessObject instanceof IProcess) { self::RaiseError("Invalid Proccess object", E_ERROR); } // Set class property $this->ProcessObject = $ProcessObject; $pid = posix_getpid(); if ($this->PIDDir) { $this->Logger->debug("Touch process PID file {$pid}"); @touch("{$this->PIDDir}/{$pid}"); } $this->Logger->debug("Executing 'OnStartForking' routine"); // Run routines before threading $this->ProcessObject->OnStartForking(); $this->Logger->debug("'OnStartForking' successfully executed."); if (count($this->ProcessObject->ThreadArgs) != 0) { // Add handlers to signals $this->SignalHandler->SetSignalHandlers(); $this->Logger->debug("Executing ProcessObject::ForkThreads()"); // Start Threading $this->ForkThreads(); // Wait while threads working $iteration = 1; while (true) { if (count($this->PIDs) == 0) { break; } if ($this->ChildProcessExecTimeLimit != 0) { foreach ($this->PIDs as $ipid => $ipid_info) { if ($ipid_info['start_time'] + $this->ChildProcessExecTimeLimit < time()) { $this->Logger->error(sprintf(_("Maximum execution time of %s seconds exceeded in %s. Killing process..."), $this->ChildProcessExecTimeLimit, get_class($this->ProcessObject) . "(Child PID: {$ipid_info['pid']})")); posix_kill($ipid, SIGKILL); } } } sleep(2); if ($iteration++ == 10) { $this->Logger->debug("Goin to MPWL. PIDs(" . implode(", ", array_keys($this->PIDs)) . ")"); // // Zomby not needed. // $pid = pcntl_wait($status, WNOHANG | WUNTRACED); if ($pid > 0) { $this->Logger->debug("MPWL: pcntl_wait() from child with PID# {$pid} (Exit code: {$status})"); foreach ((array) $this->PIDs as $ipid => $ipid_info) { if ($ipid == $pid) { if ($this->PIDDir) { $this->Logger->debug("Delete thread PID file {$pid}"); @unlink($this->PIDDir . "/" . $pid); } unset($this->PIDs[$ipid]); } } $this->ForkThreads(); } foreach ($this->PIDs as $ipid => $ipid_info) { $res = posix_kill($ipid, 0); $this->Logger->debug("MPWL: Sending 0 signal to {$ipid} = " . intval($res)); if ($res === FALSE) { $this->Logger->debug("MPWL: Deleting '{$ipid}' from PIDs queue"); if ($this->PIDDir) { $this->Logger->debug("Delete thread PID file {$ipid}"); @unlink($this->PIDDir . "/" . $ipid); } unset($this->PIDs[$ipid]); } } $iteration = 1; } } } else { $this->Logger->debug("ProcessObject::ThreadArgs is empty. Nothing to do."); } $pid = posix_getpid(); if ($this->PIDDir) { $this->Logger->debug("Delete Process PID file {$pid}"); @unlink("{$this->PIDDir}/{$pid}"); } $this->Logger->debug("All childs exited. Executing OnEndForking routine"); // Run routines after forking $this->ProcessObject->OnEndForking(); $this->Logger->debug("Main process complete. Exiting..."); exit; }