Пример #1
0
 /**
  * Enter main loop
  *
  * The <var>$time</var> parameter can have different meanings:
  * <ul>
  * <li>int or float > 0 : the main loop will run once and will wait for activity for a maximum of <var>$time</var> seconds</li>
  * <li>0 : the main loop will run once and will not wait for activity when polling, only handling waiting packets and timers</li>
  * <li>int or float < 0 : the main loop will run for -<var>$time</var> seconds exactly, whatever may happen</li>
  * <li>NULL : the main loop will run forever</li>
  * </ul>
  *
  * @param float $time how much time should we run, if omited nanoserv will enter an endless loop
  * @param array $user_streams if specified, user streams will be polled along with internal streams
  * @return array the user streams with pending data
  * @since 0.9
  */
 public static function Run($time = NULL, array $user_streams = NULL)
 {
     $tmp = 0;
     $ret = array();
     if (isset($time)) {
         if ($time < 0) {
             $poll_max_wait = -$time;
             $exit_mt = microtime(true) - $time;
         } else {
             $poll_max_wait = $time;
             $exit = true;
         }
     } else {
         $poll_max_wait = 60;
         $exit = false;
     }
     do {
         $t = microtime(true);
         // Timers
         if (self::$timers_updated) {
             usort(self::$timers, function (Timer $a, Timer $b) {
                 return $a->microtime > $b->microtime;
             });
             self::$timers_updated = false;
         }
         unset($next_timer_md);
         foreach (self::$timers as $k => $tmr) {
             if ($tmr->microtime > $t) {
                 $next_timer_md = $tmr->microtime - $t;
                 break;
             } else {
                 if ($tmr->active) {
                     $tmr->Deactivate();
                     call_user_func($tmr->callback);
                 }
             }
             unset(self::$timers[$k]);
         }
         if (self::$timers_updated) {
             $t = microtime(true);
             usort(self::$timers, function (Timer $a, Timer $b) {
                 return $a->microtime > $b->microtime;
             });
             foreach (self::$timers as $k => $tmr) {
                 if ($tmr->microtime > $t) {
                     $next_timer_md = $tmr->microtime - $t;
                     break;
                 }
             }
             self::$timers_updated = false;
         }
         // Write buffers to non blocked sockets
         foreach (self::$write_buffers as $write_buffers) {
             if (!$write_buffers || $write_buffers[0]->socket->blocked || !$write_buffers[0]->socket->connected) {
                 continue;
             }
             foreach ($write_buffers as $wb) {
                 while ($wb->Waiting_Data() && !$wb->socket->blocked) {
                     $wb->Write();
                     if (!$wb->Waiting_Data()) {
                         array_shift(self::$write_buffers[$wb->socket->id]);
                         if (!self::$write_buffers[$wb->socket->id]) {
                             self::Free_Write_Buffers($wb->socket->id);
                         }
                         break;
                     }
                 }
             }
         }
         unset($handler, $write_buffers, $l, $c, $wbs, $wb, $data);
         // Prepare socket arrays
         $fd_lookup_r = $fd_lookup_w = $rfd = $wfd = $efd = array();
         foreach (self::$listeners as $l) {
             if ($l->active && (!$l->forking || self::$nb_forked_processes <= self::$max_forked_processes)) {
                 $rfd[] = $l->socket->fd;
                 $fd_lookup_r[(int) $l->socket->fd] = $l;
             }
         }
         foreach (self::$connections as $c) {
             if ($c->socket->pending_crypto) {
                 $cr = $c->socket->Enable_Crypto();
                 if ($cr === true) {
                     $c->on_Accept();
                 } else {
                     if ($cr === false) {
                         $c->on_Connect_Fail(Connection_Handler::FAIL_CRYPTO);
                         self::Free_Connection($c);
                     } else {
                         $rfd[] = $c->socket->fd;
                         $fd_lookup_r[(int) $c->socket->fd] = $c;
                     }
                 }
             } else {
                 if ($c->socket->connected) {
                     if (!$c->socket->block_reads) {
                         $rfd[] = $c->socket->fd;
                         $fd_lookup_r[(int) $c->socket->fd] = $c;
                     }
                 } else {
                     if ($c->socket->connect_timeout < $t) {
                         $c->on_Connect_Fail(Connection_Handler::FAIL_TIMEOUT);
                         self::Free_Connection($c);
                     } else {
                         if ($c->socket->pending_connect) {
                             $wfd[] = $c->socket->fd;
                             $fd_lookup_w[(int) $c->socket->fd] = $c;
                         }
                     }
                 }
             }
         }
         foreach (self::$dgram_handlers as $l) {
             if ($l->active) {
                 $rfd[] = $l->socket->fd;
                 $fd_lookup_r[(int) $l->socket->fd] = $l;
             }
         }
         foreach (self::$write_buffers as $wbs) {
             if ($wbs[0]->socket->blocked) {
                 $wfd[] = $wbs[0]->socket->fd;
                 $fd_lookup_w[(int) $wbs[0]->socket->fd] = self::$connections[$wbs[0]->socket->id];
             }
         }
         foreach (self::$forked_pipes as $fp) {
             $rfd[] = $fp->fd;
             $fd_lookup_r[(int) $fp->fd] = $fp;
         }
         if (isset($user_streams)) {
             foreach ((array) $user_streams[0] as $tmp_r) {
                 $rfd[] = $tmp_r;
             }
             foreach ((array) $user_streams[1] as $tmp_w) {
                 $wfd[] = $tmp_w;
             }
         }
         // Main select
         $wait_mds = array($poll_max_wait);
         if (isset($next_timer_md)) {
             $wait_mds[] = $next_timer_md;
         }
         if (isset($exit_mt)) {
             $wait_mds[] = $exit_mt - $t;
         }
         $wait_md = min($wait_mds);
         $tv_sec = (int) $wait_md;
         $tv_usec = ($wait_md - $tv_sec) * 1000000;
         if (($rfd || $wfd) && @stream_select($rfd, $wfd, $efd, $tv_sec, $tv_usec)) {
             foreach ($rfd as $act_rfd) {
                 $handler = $fd_lookup_r[(int) $act_rfd];
                 if (!isset($handler)) {
                     // User stream
                     $ret[0][] = $act_rfd;
                 } else {
                     if ($handler instanceof Connection_Handler) {
                         if ($handler->socket->pending_crypto) {
                             $cr = $handler->socket->Enable_Crypto();
                             if ($cr === true) {
                                 $handler->on_Accept();
                             } else {
                                 if ($cr === false) {
                                     $handler->on_Connect_Fail(Connection_Handler::FAIL_CRYPTO);
                                     self::Free_Connection($handler);
                                 }
                             }
                         } else {
                             if (!$handler->socket->connected) {
                                 continue;
                             }
                         }
                         $data = $handler->socket->Read();
                         if ($data === "" || $data === false) {
                             if ($handler->socket->Eof()) {
                                 // Disconnected socket
                                 $handler->socket->connected = false;
                                 $handler->on_Disconnect();
                                 self::Free_Connection($handler);
                             }
                         } else {
                             // Data available
                             $handler->on_Read($data);
                         }
                     } else {
                         if ($handler instanceof Datagram_Handler) {
                             $from = "";
                             $data = $handler->socket->Read_From($from);
                             $handler->on_Read($from, $data);
                         } else {
                             if ($handler instanceof Listener) {
                                 while ($fd = $handler->socket->Accept()) {
                                     // New connection accepted
                                     $sck = new Socket($fd, $handler->socket->crypto_type);
                                     $hnd = new $handler->handler_classname($handler->handler_options);
                                     $hnd->socket = $sck;
                                     if ($handler->forking) {
                                         $hnd->on_Fork_Prepare();
                                         if (self::Fork() === 0) {
                                             $hnd->on_Fork_Done();
                                             self::$write_buffers = self::$listeners = array();
                                             self::$connections = array($sck->id => $hnd);
                                             self::$forked_connection = $hnd;
                                             self::Clear_Timers();
                                             if ($sck->Setup()) {
                                                 $hnd->on_Accept();
                                             }
                                             $handler = $hnd = $sck = $l = $c = $wbs = $wb = $fd_lookup_r = $fd_lookup_w = $loops = false;
                                             break;
                                         }
                                         $hnd->on_Fork_Done();
                                         if (self::$nb_forked_processes >= self::$max_forked_processes) {
                                             break;
                                         }
                                     } else {
                                         self::$connections[$sck->id] = $hnd;
                                         if ($sck->Setup()) {
                                             $hnd->on_Accept();
                                         }
                                     }
                                     unset($sck, $hnd);
                                 }
                             } else {
                                 if ($handler instanceof IPC_Socket) {
                                     while ($ipcm = $handler->Read()) {
                                         if (!($ipcq = unserialize($ipcm)) || !is_object($o = self::$shared_objects[$ipcq["oid"]])) {
                                             continue;
                                         }
                                         switch ($ipcq["action"]) {
                                             case "G":
                                                 $handler->Write(serialize($o->{$ipcq}["var"]));
                                                 break;
                                             case "S":
                                                 $o->{$ipcq}["var"] = $ipcq["val"];
                                                 break;
                                             case "C":
                                                 Shared_Object::$caller_pid = $handler->pid;
                                                 $handler->Write(serialize(call_user_func_array(array($o, $ipcq["func"]), $ipcq["args"])));
                                                 break;
                                         }
                                     }
                                     unset($o, $ipcq, $ipcm);
                                 }
                             }
                         }
                     }
                 }
             }
             foreach ($wfd as $act_wfd) {
                 $handler = $fd_lookup_w[$act_wfd];
                 if (!isset($handler)) {
                     // User stream
                     $ret[1][] = $act_wfd;
                 } else {
                     if ($handler->socket->connected) {
                         // Unblock buffered write
                         if ($handler->socket->Eof()) {
                             $handler->on_Disconnect();
                             self::Free_Connection($handler);
                         } else {
                             $handler->socket->blocked = false;
                         }
                     } else {
                         if ($handler->socket->pending_connect) {
                             // Pending connect
                             if ($handler->socket->Eof()) {
                                 $handler->on_Connect_Fail(Connection_Handler::FAIL_CONNREFUSED);
                                 self::Free_Connection($handler);
                             } else {
                                 $handler->socket->Setup();
                                 $handler->socket->connected = true;
                                 $handler->socket->pending_connect = false;
                                 $handler->on_Connect();
                             }
                         }
                     }
                 }
             }
         }
         if (self::$nb_forked_processes && !self::$child_process) {
             while (($pid = pcntl_wait($tmp, WNOHANG)) > 0 && self::$nb_forked_processes--) {
                 unset(self::$forked_pipes[$pid]);
             }
         }
         if ($ret) {
             return $ret;
         } else {
             if (isset($exit_mt)) {
                 $exit = $exit_mt <= $t;
             }
         }
     } while (!$exit);
 }