Jump to content

signal handling in an object in a forked process


shapeshifter

Recommended Posts

I'm currently rewriting a reporting engine in PHP 5 that was originally done in Perl (ugh!)

 

The original engine simply polled a database table for new jobs and forked a process for every one it found to process and output as required. I don't want to change that too radically, but I do want to extend this so I can pause or terminate a spawned process from within the main engine.

 

My code is all OO. When the code forks, the child creates an object of type 'Report'. Class report has it's own signal handler method and I register the signals I want to handle in the constructor using this syntax:

 

pcntl_signal(SIGUSR1, array(&$this, "sig_handler"));

 

As the Report class is just a skeleton right now, for the purposes of experimentation I have created a method that simply loops round printing off some abitrary line every second.

 

In the main engine from where the process was forked, I wait a few secs then send:

posix_kill($pid, SIGUSR1);

 

This actually works just fine as far as I can see. It inturrupts the print loop in the forked process and executes the code for the right signal. The only code I have there right now is a debug statement, and there lies my problem....

 

I have a static class 'Debug' which is basically a wrapper around 'print'. I call this in the Report constructor and the method that does the looping and it works just fine, but when I call Debug::Output in the signal handler, I get a "call to undefined method Debug::output()" fatal error (note lower case in method name).

 

I also have signal handling in my main file, and this uses the static class just fine (although that's not OO (yet)), so what is going on?

 

Once this is sorted, my other problem is that I want to be able to get info back from the spawned process without it exiting. Can this be done without resorting to all that piping stuff?

 

I should point out that my PHP knowledge is pretty limited and I'm working in FreeBSD 6.2. And this is a purely server-side program (would use C++ if I were allowed to, but PHP is cool).

Link to comment
Share on other sites

Well here's a cut down version of the Report object:

 

abstract class Report {

private $pInput;

public function __construct($_input) {

$pInput = $_input;

pcntl_signal(SIGTERM, array(&$this, "SigHandler"));

pcntl_signal(SIGUSR1, array(&$this, "SigHandler"));

Debug::Ouput("Report object constructed");

}

 

private function SigHandler($signo) {

Debug::Ouput("I'm the report classes sig handler!");

switch ($signo) {

case SIGTERM:

Debug::Ouput("handling a sigterm in report object!");

exit;

break;

case SIGUSR1:

Debug::Ouput("handling a SIGUSR1 in report object!");

exit;

break;

}

}

 

public function MainLoop() { // just to show object is doing stuff

while(1) {

sleep(1);

Debug::Output("I'm a silly child and will keep printing til I get a SIGnal");

}

}

}

 

class ReportManagement extends Report {

// to be done.

}

 

 

In the parent, the relevent code looks like this:

 

// pulled a new job requirement blah blah, so fork a new process

$pid = pcntl_fork(); Debug::Output("pid returned = $pid\n");

 

if ($pid == -1) {

Debug::Output("fork me! Didn't work. do stuff etc");

ExitProg();

}

else if (!$pid) {

Debug::Output("In child no.$i\n");

$report = new ReportManagement(new Input("<xml>test</xml>"));

$report->MainLoop();

}

else {

Debug::Output("I'm the daddy!");

sleep(5);

posix_kill($pid, SIGUSR1);

}

 

This calls the child's sig handler successfully, but gives a fatal error on the Debug::Output even though that works just fine in the constructor and MainLoop methods.

Link to comment
Share on other sites

It could also be you just pasted your code outside of the proper tags and failed to read the posting rules.

 

Me, if it does not have the code tags around I rarely help cause it makes it harder to know the code you are looking at is good code and not screwed up when posted.

Link to comment
Share on other sites

Sorry, I'm new to the forum and have not yet familiarised myelf with such rules.

 

It would have been impossible to post all of the original code verbatim (my project is spread over 11 files), but the majority of the code I posted (the 'report' class stuff) is self-contained in its own file, and nothing was omitted from that apart from the opening and closing php tags.

 

The last bit is abridged from a main.php file.

Link to comment
Share on other sites

Yes, but I can't confirm it for another 9 hours or so. I know for a fact that the right handler is being called and the right switch made. It's just using the static class from within the signal handler that won't work.

 

Call me Captain Stupid, but I can't work out how to edit my post with the code. Assumed there would be an icon or something by my posts? The help page mentions a 'modified' button I can't see for the life of me. If that sounds odd it's cos I rarely if ever use forums and work out my problems for myself. But this has me flummoxed!

 

edit: just noticed one on this last post. can I only edit the last post, or is it cos I posted the original block of code from a different IP address?

Link to comment
Share on other sites

I assume you're getting the Debug::Ouput("I'm the report classes sig handler!"); but not the Debug::Ouput("handling a SIGUSR1 in report object!"); or is it failing on both?

 

What exactly is the fatal error you're getting? Is it possible that the parent process is terminating immediately after issuing the posix_kill() and taking the child process down with it?

 

And

$pInput = $_input;

should probably be

$this->pInput = $_input;

though that has nothing to do with your problem, and I'm assuming it's just a typo in copying the skeleton rather than the real code.

 

 

Link to comment
Share on other sites

The fatal error is:

  Fatal error: Call to undefined method Debug::ouput()

 

And I can confirm that the signal handler fails on any use of the Debug static class (even though I can use Debug in the other methods just fine). Otherwise it works just how it should.

 

I should have said that I have a while(1) loop at the bottom of my main file to prevent the parent exiting, so that's not the problem.

 

I use 'declare(ticks=1)' right at the top of my main file. Tried adding it at the top of the report class too, but has no effect either way.

 

And you were very diplomatic, Mark, but that is a bug. Hadn't noticed it cos it's still just a skeleton. I come from a C++ background so not used to HAVING to use 'this'. Bit of a pain, but PHP is still a million times better than perl [cough! spit!]

Link to comment
Share on other sites

Debug::Ouput("I'm the report classes sig handler!");
Debug::Ouput("handling a sigterm in report object!");
Debug::Ouput("handling a SIGUSR1 in report object!");
Debug::Output("I'm a silly child and will keep printing til I get a SIGnal");

And there I think we've got it... and a silly typo (ever the diplomat) when it comes down to it.

 

Take a very careful look at the above, particularly the spelling of Output

 

Link to comment
Share on other sites

Well how dumb am I? I did all the hard work with processes and signal handling then do sommat stupid. Very typical of me.  Thanks Mark.

 

As a lame excuse, I was distracted by the lower case 'o' when I always give function/method names upper case starting letters.

 

Now my problem is passing information between processes other than pre-defined signals (like how long a job took to run etc). Will I have to resort to pipes?

Link to comment
Share on other sites

Well how dumb am I? I did all the hard work with processes and signal handling then do sommat stupid. Very typical of me.As a lame excuse, I was distracted by the lower case 'o' when I always give function/method names upper case starting letters.

Don't get me started :-)

I spent nearly an hour today tring to figure out why a function I was profiling for performance wasn't working... finally realised that I was defining my $testdata = array(...) and calling the function using $result = factors($testData);

 

Now my problem is passing information between processes other than pre-defined signals (like how long a job took to run etc). Will I have to resort to pipes?
I was intrigued by your problem because I wrote a similar threaded scheduler script, with each child process spawning off grandchildren as well; and I chose to use socket pairs to communicate between the parent and child... and maintained a listener on the top-level parent so that I could telnet to it and issue commands directly that would then be filtered down to the appropriate level for requesting status data, issuing shutdown commands, etc.
Link to comment
Share on other sites

I'm just reading up on socket pairs now, although I could get around the issue by giving extra functionality to the child processes (like limited reading and writing to the report-queue in the database).

 

I don't need the telnet side of things as I plan to put an interactive web-front-end over the report queue which is constantly monitored by the parent.

 

If I get stuck I'll be back! Thanks again for finding my typo, Mrak (sic)

Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.