Skip to content

Instantly share code, notes, and snippets.

@spajak
Created June 11, 2019 16:35
Show Gist options
  • Save spajak/eb30af9488bb89ab0ebdfa3f5f496174 to your computer and use it in GitHub Desktop.
Save spajak/eb30af9488bb89ab0ebdfa3f5f496174 to your computer and use it in GitHub Desktop.
Forking in PHP 7 done right. No zombies!!
<?php
/**
* Forking in PHP 7 done right. Requires PHP >= 7.1 and Process Control extension (pcntl)
*
* @author Sebastian Pająk
* Good article in the subject that inspired me: https://ruslanspivak.com/lsbaws-part3
*/
// Enable asynchronous signal handling (as of PHP 7.1)
pcntl_async_signals(true);
// How many children to fork
define('MAX_CHILDREN', 10);
function console($message) {
file_put_contents('php://stdout', $message."\n");
}
// Function that gets called in a forked process
function childProcess() {
$pid = getmypid();
console(" We are now in a child process. PID: $pid");
sleep(5);
console(" Finishing child process. PID: $pid");
}
// Register handler for signal that is sent to a parent process whenever
// one of its child processes terminates or stops.
$result = pcntl_signal(SIGCHLD, function($signo, $siginfo) {
// Killing zombies :)
console("Got SIGCHLD signal");
while (0 != $pid = pcntl_wait($status, WNOHANG)) {
if ($pid == -1) return;
}
});
if (false === $result) {
throw new RuntimeException('Could not register SIGCHLD signal!');
}
// Register handler for signal that is sent when the user types the INTR character
// (normally Ctrl+c).
$result = pcntl_signal(SIGINT, function($signo, $siginfo) {
console("Got SIGINT signal");
console("Waiting for children to finish...");
while (0 < pcntl_wait($status, WNOHANG)) {
usleep(20000);
}
console("Stopping master process");
// Restore default handlers. Probably not needed here as we are dying anyway.
// But it may be used in other situations.
pcntl_signal(SIGCHLD, SIG_DFL);
pcntl_signal(SIGINT, SIG_DFL);
exit(0);
});
if (false === $result) {
throw new RuntimeException('Could not register SIGINT signal!');
}
console("Starting master process");
$count = 0;
// Master loop. Creates a new child every 1 sec
while ($count < MAX_CHILDREN) {
if (-1 === $pid = pcntl_fork()) {
throw new RuntimeException('Could not fork!');
}
if (0 === $pid) { // <=== If true then we are inside a forked process
childProcess();
exit(0);
}
$count += 1;
sleep(1);
}
console("Master process has done his job. Sleeping...");
while (true) {
sleep(1);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment