• Main Page
  • Related Pages
  • Namespaces
  • Data Structures
  • Files
  • Examples
  • File List

E:/E/GEAMP/www/openbiz/openbiz/others/ZendX/Console/Process/Unix.php

00001 <?php
00031 abstract class ZendX_Console_Process_Unix
00032 {
00036     const VOID_METHOD = 'void_method';
00037 
00041     const RETURN_METHOD = 'void_method';
00042     
00048     private $_name;
00049 
00055     private $_pid = null;
00056 
00062     private $_puid = null;
00063 
00069     private $_guid = null;
00070 
00076     private $_isRunning = false;
00077 
00083     private $_isChild = false;
00084 
00090     private $_internalIpcData = array();
00091 
00097     private $_internalIpcKey;
00098 
00104     private $_internalSemKey;
00105 
00112     private $_ipcIsOkay;
00113 
00119     private $_ipcSegFile;
00120 
00126     private $_ipcSemFile;
00127 
00144     public function __construct($puid = null, $guid = null, $umask = null)
00145     {
00146         if (substr(PHP_OS, 0, 3) === 'WIN') {
00147             require_once 'ZendX/Console/Process/Exception.php';
00148             throw new ZendX_Console_Process_Exception('Cannot run on windows');
00149         } else if (!in_array(substr(PHP_SAPI, 0, 3), array('cli', 'cgi'))) {
00150             require_once 'ZendX/Console/Process/Exception.php';
00151             throw new ZendX_Console_Process_Exception('Can only run on CLI or CGI enviroment');
00152         } else if (!function_exists('shmop_open')) {
00153             require_once 'ZendX/Console/Process/Exception.php';
00154             throw new ZendX_Console_Process_Exception('shmop_* functions are required');
00155         } else if (!function_exists('pcntl_fork')) {
00156             require_once 'ZendX/Console/Process/Exception.php';
00157             throw new ZendX_Console_Process_Exception('pcntl_* functions are required');
00158         } else if (!function_exists('posix_kill')) {
00159             require_once 'ZendX/Console/Process/Exception.php';
00160             throw new ZendX_Console_Process_Exception('posix_* functions are required');
00161         }
00162     
00163         $this->_isRunning = false;
00164 
00165         $this->_name = md5(uniqid(rand()));
00166         $this->_guid = $guid;
00167         $this->_puid = $puid;
00168 
00169         if ($umask !== null) {
00170             umask($umask);
00171         }
00172 
00173         // Try to create the shared memory segment. The variable
00174         // $this->_ipcIsOkay contains the return code of this operation and must
00175         // be checked before forking
00176         if ($this->_createIpcSegment() && $this->_createIpcSemaphore()) {
00177             $this->_ipcIsOkay = true;
00178         } else {
00179             $this->_ipcIsOkay = false;
00180         }
00181     }
00182     
00186     public function __destruct()
00187     {
00188         if ($this->isRunning()) {
00189             $this->stop();
00190         }
00191     }
00192     
00207     public function start()
00208     {
00209         if (!$this->_ipcIsOkay) {
00210             require_once 'ZendX/Console/Process/Exception.php';
00211             throw new ZendX_Console_Process_Exception('Unable to create SHM segments for process communications');
00212         }
00213 
00214         // @see http://www.php.net/manual/en/function.pcntl-fork.php#41150
00215         @ob_end_flush();
00216         
00217         pcntl_signal(SIGCHLD, SIG_IGN);
00218 
00219         $pid = @pcntl_fork();
00220         if ($pid === -1) {
00221             require_once 'ZendX/Console/Process/Exception.php';
00222             throw new ZendX_Console_Process_Exception('Forking process failed');
00223         } else if ($pid === 0) {
00224             // This is the child
00225             $this->_isChild = true;
00226            
00227             // Sleep a second to avoid problems
00228             sleep(1);
00229             
00230             // Install the signal handler
00231             pcntl_signal(SIGUSR1, array($this, '_sigHandler'));
00232 
00233             // If requested, change process identity
00234             if ($this->_guid !== null) {
00235                 posix_setgid($this->_guid);
00236             }
00237 
00238             if ($this->_puid !== null) {
00239                 posix_setuid($this->_puid);
00240             }
00241 
00242             // Run the child
00243             try {
00244                 $this->_run();
00245             } catch (Exception $e) {
00246                 // We have to catch any exceptions and clean up the process,
00247                 // else we will have a memory leak.
00248             }
00249 
00250             // Destroy the child after _run() execution. Required to avoid
00251             // unuseful child processes after execution
00252             exit(0);
00253         } else {
00254             // Else this is the parent
00255             $this->_isChild   = false;
00256             $this->_isRunning = true;
00257             $this->_pid       = $pid;
00258         }
00259     }
00260     
00269     public function stop()
00270     {
00271         $success = false;
00272 
00273         if ($this->_pid > 0) {
00274             $status = 0;
00275             
00276             posix_kill($this->_pid, 9);
00277             pcntl_waitpid($this->_pid, $status, WNOHANG);
00278             $success = pcntl_wifexited($status);
00279             $this->_cleanProcessContext();
00280         }
00281 
00282         return $success;
00283     }
00284 
00290     public function isRunning()
00291     {       
00292         return $this->_isRunning;
00293     }
00294 
00305     public function setVariable($name, $value)
00306     {
00307         if ($name[0] === '_') {
00308             require_once 'ZendX/Console/Process/Exception.php';
00309             throw new ZendX_Console_Process_Exception('Only internal functions may use underline (_) as variable prefix');
00310         }
00311 
00312         $this->_writeVariable($name, $value);
00313     }
00314 
00322     public function getVariable($name)
00323     {
00324         $this->_readFromIpcSegment();
00325 
00326         if (isset($this->_internalIpcData[$name])) {
00327             return $this->_internalIpcData[$name];
00328         } else {
00329             return null;
00330         }
00331     }
00332 
00343     public function getLastAlive()
00344     {
00345         $pingTime = $this->getVariable('_pingTime');
00346 
00347         return ($pingTime === null ? 0 : (time() - $pingTime));
00348     }
00349 
00355     public function getPid()
00356     {
00357         return $this->_pid;
00358     }
00359     
00370     protected function _setAlive()
00371     {
00372         $this->_writeVariable('_pingTime', time());
00373     }
00374     
00375 
00385     protected function _callCallbackMethod($methodName, array $argList = array(), $type = self::VOID_METHOD)
00386     {
00387         // This is the parent, so we really cannot execute the method. Check
00388         // arguments passed to the method.
00389         if ($type === self::RETURN_METHOD) {
00390             $this->_internalIpcData['_callType'] = self::RETURN_METHOD;
00391         } else {
00392             $this->_internalIpcData['_callType'] = self::VOID_METHOD;
00393         }
00394 
00395         // These setting are common to both the calling types
00396         $this->_internalIpcData['_callMethod'] = $methodName;
00397         $this->_internalIpcData['_callInput']  = $argList;
00398 
00399         // Write the IPC data to the shared segment
00400         $this->_writeToIpcSegment();
00401 
00402         // Now we need to differentiate a bit.
00403         switch ($this->_internalIpcData['_callType']) {
00404             case VOID_METHOD:
00405                 // Notify the child so it can process the request
00406                 $this->_sendSigUsr1();
00407                 break;
00408 
00409             case RETURN_METHOD:
00410                 // Set the semaphorew
00411                 shmop_write($this->_internalSemKey, 1, 0);
00412 
00413                 // Notify the child so it can process the request
00414                 $this->_sendSigUsr1();
00415 
00416                 // Block until the child process return
00417                 $this->_waitForIpcSemaphore();
00418 
00419                 // Read from the SHM segment. The result is stored into
00420                 // $this->_internalIpcData['_callOutput']
00421                 $this->_readFromIpcSegment();
00422 
00423                 // Data are returned. Now we can reset the semaphore
00424                 shmop_write($this->_internalSemKey, 0, 1);
00425 
00426                 // Return the result. Hence no break required here
00427                 return $this->_internalIpcData['_callOutput'];
00428         }
00429     }
00430     
00436     abstract protected function _run();
00437     
00443     private function _sendSigUsr1()
00444     {
00445         if ($this->_pid > 0) {
00446             posix_kill($this->_pid, SIGUSR1);
00447         }
00448     }
00449     
00457     private function _writeVariable($name, $value)
00458     {
00459         $this->_internalIpcData[$name] = $value;
00460         $this->_writeToIpcSegment();
00461     }
00462 
00468     private function _cleanProcessContext()
00469     {
00470         shmop_delete($this->_internalIpcKey);
00471         shmop_delete($this->_internalSemKey);
00472 
00473         shmop_close($this->_internalIpcKey);
00474         shmop_close($this->_internalSemKey);
00475 
00476         @unlink($this->_ipcSegFile);
00477         @unlink($this->_ipcSemFile);
00478 
00479         $this->_isRunning = false;
00480         $this->_pid       = null;
00481     }
00482 
00490     private function _sigHandler($signo)
00491     {
00492         switch ($signo) {
00493             case SIGTERM:
00494                 // Handle shutdown tasks. Hence no break is require
00495                 exit;
00496 
00497             case SIGUSR1:
00498                 // This is the User-defined signal we'll use. Read the SHM segment
00499                 $this->_readFromIpcSegment();
00500 
00501                 if (isset($this->_internalIpcData['_callType'])) {
00502                     $method = $this->_internalIpcData['_callMethod'];
00503                     $params = $this->_internalIpcData['_callInput'];
00504 
00505                     switch ($this->_internalIpcData['_callType']) {
00506                         case self::VOID_METHOD:
00507                             // Simple call the (void) method and return immediatly
00508                             // no semaphore is placed into parent, so the processing
00509                             // is async
00510                             call_user_func(array($this, $method), $params);
00511                             break;
00512 
00513                         case self::RETURN_METHOD:
00514                             // Process the request
00515                             $this->_internalIpcData['_callOutput'] = call_user_func(array($this, $method), $params);
00516 
00517                             // Write the result into IPC segment
00518                             $this->_writeToIPCsegment();
00519 
00520                             // Unlock the semaphore but block _writeToIpcSegment()
00521                             shmop_write($this->_internalSemKey, 0, 0);
00522                             shmop_write($this->_internalSemKey, 1, 1);
00523                             break;
00524                     }
00525                 }
00526                 break;
00527                 
00528             default:
00529                 // Ignore all other singals
00530                 break;
00531         }
00532     }
00533 
00539     private function _waitForIpcSemaphore()
00540     {
00541         while (true) {
00542             $okay = shmop_read($this->_internalSemKey, 0, 1);
00543 
00544             if ($okay === 0) {
00545                 break;
00546             }
00547 
00548             usleep(10);
00549         }
00550     }
00551 
00558     private function _readFromIpcSegment()
00559     {
00560         $serializedIpcData = shmop_read($this->_internalIpcKey,
00561                                         0,
00562                                         shmop_size($this->_internalIpcKey));
00563 
00564         if ($serializedIpcData === false) {
00565             require_once 'ZendX/Console/Process/Exception.php';
00566             throw new ZendX_Console_Process_Exception('Fatal error while reading SHM segment');
00567         }
00568 
00569         $data = @unserialize($serializedIpcData);
00570         
00571         if ($data !== false) {
00572             $this->_internalIpcData = $data;
00573         }
00574     }
00575 
00582     private function _writeToIpcSegment()
00583     {
00584         // Read the transaction bit (2 bit of _internalSemKey segment). If it's
00585         // value is 1, we're into the execution of a PHP_FORK_RETURN_METHOD, so
00586         // we must not write to segment (data corruption)
00587         if (shmop_read($this->_internalSemKey, 1, 1) === 1) {
00588             return;
00589         }
00590 
00591         $serializedIpcData = serialize($this->_internalIpcData);
00592 
00593         // Set the exchange array (IPC) into the shared segment
00594         $shmBytesWritten = shmop_write($this->_internalIpcKey,
00595                                        $serializedIpcData,
00596                                        0);
00597 
00598         // Check if lenght of SHM segment is enougth to contain data
00599         if ($shmBytesWritten !== strlen($serializedIpcData)) {
00600             require_once 'ZendX/Console/Process/Exception.php';
00601             throw new ZendX_Console_Process_Exception('Fatal error while writing to SHM segment');
00602         }
00603     }
00604 
00611     private function _createIpcSegment()
00612     {
00613         $this->_ipcSegFile = realpath(sys_get_temp_dir()) . '/' . rand() . $this->_name . '.shm';
00614         touch($this->_ipcSegFile);
00615 
00616         $shmKey = ftok($this->_ipcSegFile, 't');
00617         if ($shmKey === -1) {
00618             require_once 'ZendX/Console/Process/Exception.php';
00619             throw new ZendX_Console_Process_Exception('Could not create SHM segment');
00620         }
00621 
00622         $this->_internalIpcKey = @shmop_open($shmKey, 'c', 0644, 10240);
00623 
00624         if (!$this->_internalIpcKey) {
00625             @unlink($this->_ipcSegFile);
00626             return false;
00627         }
00628 
00629         return true;
00630     }
00631 
00638     private function _createIpcSemaphore()
00639     {
00640         $this->_ipcSemFile = realpath(sys_get_temp_dir()) . '/' . rand() . $this->_name . '.sem';
00641         touch($this->_ipcSemFile);
00642 
00643         $semKey = ftok($this->_ipcSemFile, 't');
00644         if ($semKey === -1) {
00645             require_once 'ZendX/Console/Process/Exception.php';
00646             throw new ZendX_Console_Process_Exception('Could not create semaphore');
00647         }
00648 
00649         $this->_internalSemKey = @shmop_open($semKey, 'c', 0644, 10);
00650 
00651         if (!$this->_internalSemKey) {
00652             @unlink($this->_ipcSemFile);
00653             return false;
00654         }
00655 
00656         return true;
00657     }
00658 }

Generated on Thu Apr 19 2012 17:01:18 for openbiz by  doxygen 1.7.2