http://www.perlmonks.org?node_id=67846

leons has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks

I hope someone can give me some advice on the following. Right now
I'm writing a program which will be used for load-test purposes on
one of our mailservers here.

The program can be configured to send a certain amount of messages
over a configurable number of sessions to another system. (No, this
is not for spam-purposes ..... just to make sure ;-)

At first I wrote the program in a kind of way that it would open the
preferred number of sessions and then it would send the messages over
these sessions. This would happen then, more or less, sequentially

I didn't take me long to figure out that it would definitely be a
lot quicker if I were to fork the same number of processes as there
needed to be sessions. Which would then, each start it's own session
to the mailserver

After that came the problem. The program had to have a progress-counter
which would state the number of messages that had been sent. The first
thing I tried was having all the child processes write a '.' to a text-
file, which would then be read by the parent (every 5 seconds or so) for
as long as the number of dots was less than the number of messages that
had need to be sent.

This worked perfectly although it wasn't a very elegant way of doing this,
I think. Then someone proposed to use signals ... which is what I did.
Every child would send a SIGUSR2 to the parent each time a message was sent.
But strange enough the total of signals sent to the parent never came close
to the actual number of messages sent. Not knowing how this signalling stuff
actually worked, someone told me a signal was nothing more than some flag in a
register. Which perfectly explained the fact that there were 'messages missing'
... this was ofcourse because of the fact that some signals were send simulta-
neously.

So I hope someone can give me some advice on this timing/signalling problem ...
or should I just stick with the Dot-solution ? Which, I must confess,
doesn't have my preference

Any advice is more than welcome !

Thanks, Leon

Replies are listed 'Best First'.
Re: Using signals in Perl
by kal (Hermit) on Mar 28, 2001 at 20:17 UTC

    One method is to keep pipes open to the children - if you have a pipe which the parent reads and the child writes, you can send your dots (valid progess indicators :) down the pipe. You then remove file locking problems (which you have with writing dots to a file). You end up with a parent with a whole bunch of pipes - just do a select() on these, and you'll get woken up whenever there's anything to read from them.

    Pipes were pretty much invented for this - they survive fork (i.e., both child and parent can still see it!). Pipes rock :) Programming Perl has some other good examples of IPC - shared memory, sockets, etc., but all that's a bit overboard for what you want. The usual pipe way is:

    pipe(READHANDLE, WRITEHANDLE); my $result = fork(); if ($result>0) { # child process, we write to the pipe } else { # parent process, or error - might want to check before # we try to read from pipe :)) }

    If you want more than one child, just keep the handles in an array so you don't lose them, and do a select() in the parent when all the children are active.

    Oh, and beware of buffering - probably best to turn that off when debugging :)

Re: Using signals in Perl
by merlyn (Sage) on Mar 28, 2001 at 20:48 UTC
    You might never see as many signals as you have events, because signals are a "1-bit" message. If a second signal arrives before the first one has started to be handled, the kernel just goes "yeah yeah, I'm already gonna send him one of those!" and discards it.

    By the way, this is the complaint I have with that bad child-reaper meme that uses a wait inside a SIGCHLD handler. Multiple SIGCHLDs will be stacked as one, and therefore some kids will be missed and zombified at random.

    -- Randal L. Schwartz, Perl hacker

Re: Using signals in Perl
by mikfire (Deacon) on Mar 28, 2001 at 20:15 UTC
    Ouch. Those signals are gonna hurt. You have wandered hard into a great example of why you should avoid signals in perl if possible. Basically, you were handling a signal when another signal arrived and went of to handle the second signal. Signals are not re-entrant, which means the first signal was dropped on the floor and escaped into the ether.

    How about using the open call which forks a child and attaches the child's STDOUT to a file handle? From there, it is a matter of having the children printing to STDOUT and the parent using select() to check for waiting input.

    See perldoc perlipc under the "Safe Pipe Opens" section for the syntax a a discussion of how to use the open() call effectively.

    mikfire

Re: Using signals in Perl
by physi (Friar) on Mar 28, 2001 at 20:21 UTC
    Hi,
    I've got a quick look in the Cookbook.
    There is a Module IPC::Shareable, which allows you
    to share a variable between many processes. Maybe this help's.
    Or another idea is that your childprocesses do not send a SIGNAL after
    every mail send, but after 10,20,30. So your Parent process got not that much Signals. But normaly I would say,
    that the SIGUSR2 - Methode should work.

    A third way might be to open your childs with pipe.
    like:
    use IO::Handle; if ($pid = open(CHILD1, "-|")) { here you can read fom STDIN that what your child will tell you, but I don't know if this methode works with multiple childs. }
    -----------------------------------
    --the good, the bad and the physi--
    -----------------------------------
Re (tilly) 1: Using signals in Perl
by tilly (Archbishop) on Mar 28, 2001 at 21:56 UTC
    I wouldn't use signals with Perl. Tell whoever gave you that advice that you have it from a reliable source - ie some stranger on a website - that signals in Perl are not reliable.

    But if you only need to know when the kids died, just pollwaitpid periodically to find out if any more kids exited.

Re: Using signals in Perl
by Albannach (Monsignor) on Mar 28, 2001 at 20:15 UTC
    Just a couple of ideas off the top of my head as I've not done this type of thing:

    - instead of a signal for every message, send one for every 10th or some pre-set number. Better would be to get each child to signal on every 10% complete of whatever their task was. This would still cause problems if their tasks were very small though, and it certainly wouldn't ensure that no signals were lost.

    - Try a whole different approach using IPC::Shareable, and have a look at this thread which may help.

    --
    I'd like to be able to assign to an luser

Re: Using signals in Perl
by suaveant (Parson) on Mar 28, 2001 at 20:09 UTC
    A suggestion for your old way, which isn't terrible, instead of having the parent read the file, just use -s to read the size of the file. Each . is one byte, should be quicker than opening and reading the file. - Ant
Re: Using signals in Perl
by ChOas (Curate) on Mar 29, 2001 at 13:50 UTC
    Hey!

    Just a quick one: I think you could do this easily, using Semaphores,
    I mean.. I think they would be great at this job...

    Check out IPC::Semaphore

    GreetZ!,
      ChOas

    print "profeth still\n" if /bird|devil/;