Spot the mistake.

Abigail on 2006-10-06T11:00:21

I wrote a program that was monitoring three handles (pipes in this case). I want to do something when there's something to read on any of the pipes, and also when there's an error. Hence:

my $p1 = open (...) or die;
my $p2 = open (...) or die;
my $p3 = open (...) or die;
my $BITS = "";
vec ($BITS, fileno ($_), 1) = 1 for $p1, $p2, $p3;
while (1) {
    my $rbits = $BITS;
    my $ebits = $BITS;
    select ($rbits, undef, $ebits, TIMEOUT);
    if ($ebits) {raise_hell ()}
    if (vec ($rbits, fileno ($p1), 1) == 1) {something ()}
    if (vec ($rbits, fileno ($p2), 1) == 1) {something_else ()}
    if (vec ($rbits, fileno ($p3), 1) == 1) {another_thing ()}
}
But this didn't behave I wanted it to. Can you spot the mistake?


select modifies its arguments

nicholas on 2006-10-06T11:45:48

select ($rbits, undef, $ebits, TIMEOUT);

The first 3 arguments to select are value/return - on input you give a bitmask of the file descriptors you are interested in, on output only the bits corresponding "interesting" file descriptors are set. So it's likely that after the first call to select, all the bits were zeroed, and so for subsequent calls the arguments you passed in say that you're not interested in any file descriptors, so you never call any of your action functions. The solution is something like

select ($rout = $rbits, undef, $eout = $ebits, TIMEOUT);
so that $rbits and $ebits are never modified, with the action tests on $rout and $eout

Re:select modifies its arguments

Abigail on 2006-10-06T12:12:56

I know, and if you look at the code, it's setting $ebits and $rbits in each iteration. $BITS is set once, and it's not modified. Just before the select loop, $ebits and $rbits are initialized with $BITS.

Re:select modifies its arguments

nicholas on 2006-10-06T12:23:17

Ah OK. So I was right when I had this hunch on hitting "Submit" that I'd missed something. :-)

Reinventing the wheel

jdavidb on 2006-10-06T13:10:13

Isn't there a module for this? Your mistake is in writing your own buggy code instead of using somebody's better tested module. :P

Re:Reinventing the wheel

Abigail on 2006-10-06T13:35:37

I've no idea whether there's a module for this. But I'm pretty sure locating the module, reading the documentation, and convincing myself the module does what I want, and is bugfree would take longer than writing and debugging a few lines myself.

Re:Reinventing the wheel

BinGOs on 2006-10-06T14:11:34

Speaking of wheels, this seems an ideal candidate for a POE session and POE::Wheel::FollowTail.

Re:Reinventing the wheel

Abigail on 2006-10-06T14:46:31

Somehow it feels wrong to install a rather large selection of modules only to shave off a few lines of a less than 100 line stand alone script.

shave off lines (Reinventing the wheel)

shmem on 2006-12-23T12:20:38

... specially if shaving off a few lines means writing others (probably as much as you've saved), from which at first glance you absolutely won't get what the heck they are about, and what's going on behind the scenes: that's the wrong way of abstraction.

Re:Reinventing the wheel

kane on 2006-10-07T09:49:35

Yes, it is called IO::Select, released with perl 5.3.7.

Wrong order

VSarkiss on 2006-10-06T13:43:45

Obviously, you should be doing another_thing() in response to $p2 coming ready. I wish you wouldn't post such trivial problems.

:-)

fileno argument?

runrig on 2006-10-06T16:29:40

Opening a pipe returns the pid. Don't you want to pass the filehandle, not the pid, to fileno()?

Can it really be that simple?

Aristotle on 2006-10-06T16:46:08

open does not return the filehandle, so it would appear that $p1 and friends are not what you seem to expect based on the rest of the code.

Re:Can it really be that simple?

Abigail on 2006-10-06T20:21:46

While that's a mistake in the fragment I gave, I didn't make that mistake in the program.

$ebits string is always true

uri on 2006-10-06T21:00:03

well, you never say what you expect it to do nor what it seems to do. this is a classic case of saying it doesn't work without saying anything else. you should know better than that! :)

one issue i see is that you don't handle when a socket/pipe closes. without removing a closed handle from that data it looks like it may do an infinite loop of reading zero bytes.

but the bug i see is the if( $ebits ) line. since $ebits is a char string and not likely be '0' or '', it will be true all the time. you need to either check those bits like with $rbits or do some check for any bit set (there is a ffs bit scan function somewhere out there).

and you don't need the == 1 when you check $rbits as it will do the right boolean test for you.

as for event loop modules, event.pm is very stable, trivial to learn and i think it would be even less code (and much clearer IMO) than you have now. pretty much 3 calls (looped if you'd like) to make read-events. and it will not have the 'bug' you have since it has been very clean for years.

uri