/Workshop3/OpenIdWithTorrents/lib/PHPTracker/Threading/Forker.php

https://github.com/oskarp/4ME102 · PHP · 201 lines · 84 code · 27 blank · 90 comment · 12 complexity · ea4c6dc148770c0a82ec81bb312a23a1 MD5 · raw file

  1. <?php
  2. /**
  3. * Declaring ticks to pass between callbacks.
  4. *
  5. * Setting this higher than 1 to always listen to process exists even if they
  6. * happen quickly after each other, ie. always having pcntl_wait being executed
  7. * when a child process exists.
  8. * Therefore this is more or less the ticks elapsed between pcntl_fork and pcntl_wait.
  9. */
  10. declare( ticks = 10 );
  11. /**
  12. * Class to fork its process to N childprocesses executing the same code.
  13. *
  14. * Ideal for maintaining one listening socket and accept connections in multiple
  15. * processes.
  16. *
  17. * @package PHPTracker
  18. * @subpackage Threading
  19. */
  20. abstract class PHPTracker_Threading_Forker
  21. {
  22. /**
  23. * Number of child processes wanted.
  24. *
  25. * @var integer
  26. */
  27. protected $wanted_children;
  28. /**
  29. * Array of active child processes' PIDs. Keys represent "slot" indexes.
  30. *
  31. * @var array
  32. */
  33. protected $children = array();
  34. /**
  35. * Executing setup method of the inheriting class, then fork child processes.
  36. *
  37. * The number of children forked is a number returned by the constructorParentProcess
  38. * method of the inheriting class. If it's negative, processea re automatically recreated.
  39. * The method passes all its parameters to the setup method of the inheriting
  40. * class.
  41. */
  42. final public function start()
  43. {
  44. $arguments = func_get_args();
  45. // Calling parent set-up method with the same parameters as this constructor.
  46. $this->wanted_children = call_user_func_array( array( $this, 'startParentProcess' ), $arguments );
  47. // If children are negative, they are automatically recreated when terminate.
  48. $permanent = $this->wanted_children < 0;
  49. $this->wanted_children = abs( $this->wanted_children );
  50. $this->forkChildren( $this->wanted_children, $permanent );
  51. }
  52. /**
  53. * Detaches process from console and starts forking.
  54. *
  55. * Requires php-posix!
  56. *
  57. * @see self::start()
  58. */
  59. final public function startDetached()
  60. {
  61. // Forking one child process and closing the parent,
  62. // because if the parent is already a session leader, it cannot leave it.
  63. // It is because session group an dprocess group has the same ID as their
  64. // leader process. Now if you assign the leader process to another
  65. // session/process group, the IDs will collide.
  66. $pid = $this->fork();
  67. if ( $pid > 0 )
  68. {
  69. // We are in the parent, so we terminate.
  70. exit( 0 );
  71. }
  72. // Becoming leader of a new session/process group - detaching from shell.
  73. $sid = posix_setsid();
  74. if ( false === $sid )
  75. {
  76. throw new PHPTracker_Threading_Error( 'Unable to become session leader (detaching).' );
  77. }
  78. // We have to handle hangup signals (send when session leader terminates), otherwise it makes child process to stop.
  79. pcntl_signal( SIGHUP, SIG_IGN );
  80. // Forking again for not being session/process group leaders will disallow the process
  81. // to "accidentally" open a controlling terminal for itself (System V OSs).
  82. $pid = $this->fork();
  83. if ( $pid > 0 )
  84. {
  85. // We are in the parent, so we terminate.
  86. exit( 0 );
  87. }
  88. // Releasing current directory and closing open file descriptors (standard IO).
  89. chdir( '/' );
  90. fclose( STDIN );
  91. fclose( STDOUT );
  92. // PHP still thinks we are a webpage, and since we closed standard output,
  93. // whenever we echo, it will assume that the client abandoned the connection,
  94. // so it silently stops running.
  95. // We can tell it here not to do it.
  96. ignore_user_abort( true );
  97. // Let the world know about our process ID in a standard way.
  98. file_put_contents( '/var/run/phptracker', getmypid() );
  99. // Finally we start the procedure as we would without detaching.
  100. $arguments = func_get_args();
  101. return call_user_func_array( array( $this, 'start' ), $arguments );
  102. }
  103. /**
  104. * Initializing method to call before forking. Gets params from constructor.
  105. *
  106. * @return Number of forks to create. If negative, forks are recreated when exiting and absolute values is used.
  107. */
  108. abstract public function startParentProcess();
  109. /**
  110. * Initializing method to call after forking. Called on each children.
  111. *
  112. * @param integer $slot The slot (numbered index) of the fork. Reused when recreating process.
  113. */
  114. abstract public function startChildProcess( $slot );
  115. /**
  116. * Forking N childprocesses, initializing them and maintaining their number.
  117. *
  118. * This method constantly monitors exiting child processes and recreates them.
  119. *
  120. * @throws PHPTracker_Threading_Error When forking is unsuccessful.
  121. * @param boolean $permanent If true, exiting prpocesses will be recreated.
  122. * @param integer $n_children Number of children to fork first.
  123. */
  124. public function forkChildren( $n_children, $permanent )
  125. {
  126. if ( 0 >= $n_children ) return;
  127. do
  128. {
  129. for ( $slot = 0; $slot < $n_children; ++$slot )
  130. {
  131. if ( isset( $this->children[$slot] ) )
  132. {
  133. // Process already running in this slot.
  134. continue;
  135. }
  136. $pid = $this->fork();
  137. if ( $pid )
  138. {
  139. $this->children[$slot] = $pid;
  140. }
  141. else
  142. {
  143. return $this->startChildProcess( $slot );
  144. }
  145. }
  146. while( !$permanent && pcntl_wait( $status ) )
  147. {
  148. // If we don't need to recreate child processes on exit
  149. // we just wait for them to die to avoid zombies.
  150. }
  151. $pid_exit = pcntl_wait( $status ); // Check the status?
  152. if ( false !== ( $slot = array_search( $pid_exit, $this->children ) ) )
  153. {
  154. unset( $this->children[$slot] );
  155. }
  156. } while ( true );
  157. }
  158. /**
  159. * Forks the currently running process.
  160. *
  161. * @throws PHPTracker_Threading_Error if the forking is unsuccessful.
  162. * @return integer Forked process ID or 0 if you are in the child already.
  163. */
  164. protected function fork()
  165. {
  166. $pid = pcntl_fork();
  167. if( -1 == $pid )
  168. {
  169. throw new PHPTracker_Threading_Error( 'Unable to fork.' );
  170. }
  171. return $pid;
  172. }
  173. }
  174. ?>