Beefy Boxes and Bandwidth Generously Provided by pair Networks
laziness, impatience, and hubris
 
PerlMonks  

Errno being set to strange values when using IO::Poll

by eest (Initiate)
on Sep 25, 2009 at 18:42 UTC ( [id://797581]=perlquestion: print w/replies, xml ) Need Help??

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

Dear Perl Monks,

I have been playing around with IO::Poll in a learning-project of mine. However, when trying to implement a handler for the SIGHUP signal, it seems the errno being returned from poll() is not set to what I expect.

I have written the following code in an attempt to show the exact issue I am facing:

#!/usr/bin/perl use strict; use warnings; use IO::Poll qw(POLLIN POLLHUP POLLERR); use IO::Socket; ### Behavior of SIGHUP handler. # # 0 = print string and nothing else. Errno = "Interrupted system call" +. Expected behaviour. # 1 = same as 0 plus open/close of filehandle. Errno = "Inappropriate +ioctl for device" regardless of contents in file. # 2 = same as 1 plus reading a line from file into scalar before closi +ng. If file is empty, then errno is empty. Otherwise, Errno = "Bad fi +le descriptor" # 3 = same as 2 but setting $/ to undef, reading until end of file int +o scalar. Errno is emtpy regardless of contents in file. # 4 = Similar to 2/3 but reading file into an array. Errno is empty re +gardless of contents in file. # my $behaviour = 0; ### Variables you might want to change. my $file = "file.txt"; my $poll_timeout = 5; my $handle; my $pret; my $readbuffer; my $rret; my $string; my @ready; my @strings; unless (-r $file) { print "File \"$file\" is not readable...\n"; exit 1; } sub SIGHUP_handler { print "Got SIGHUP...\n"; if ($behaviour == 1) { open(FILE, $file); close FILE; } elsif ($behaviour == 2) { open(FILE, $file); $string = <FILE>; close FILE; } elsif ($behaviour == 3) { open(FILE, $file); undef $/; $string = <FILE>; close FILE; } elsif ($behaviour == 4) { open(FILE, $file); @strings = <FILE>; close FILE; } } $SIG{HUP} = 'SIGHUP_handler'; my $poll = IO::Poll->new; my $sock = new IO::Socket::INET( PeerAddr => "127.0.0.1", PeerPort => "12000", Proto => 'tcp') or die "Error creating socket: $!"; $poll->mask($sock, POLLIN); while(1) { $pret = $poll->poll($poll_timeout); if ($pret == 0) { syswrite(STDOUT, "Timeout occured...\n"); } elsif ($pret == -1) { syswrite(STDOUT, "Error occured: $!\n"); } else { @ready = $poll->handles(); foreach $handle (@ready) { $rret = sysread($handle, $readbuffer, 1024); if ($rret == 0) { syswrite(STDOUT, "No data to read on socket, exiting...\n" +); exit 1; } syswrite(STDOUT, "msg: $readbuffer"); } } }

The problem is this: When the process recieves a signal during a blocking system call, poll(), I am expecting it to return EINTR, or "Interrupted system call". This is what happens when my SIGHUP handler does nothing else but outputting some text.

However, when i start to fiddle around with file handles inside my signal handler, errno is set to something else completely.

What you need to reproduce this:
  • A file. The code looks for "file.txt" by default. As is described in the comments in the code, having and not having content inside the file is part of the fun.
  • A TCP socket on localhost. I used nc -l -p 12000 to create this on my linux box. The code tries to connect to to port 12000 by default.
  • A way to send SIGHUP to the perl process, like kill -HUP <PID> or something.

If you managed to get this far, then please go ahead and start mixing empty/non-empty file with different $behaviour modes.

Could anyone please explain to me why the contents of $! is changing in this way? Is this the expected behaviour? Am I doing something terribly wrong?

I also noticed something (presumably) strange when looking at this with strace. When running the code with $behaviour set to "2" while having some content in the file being opened, I failed to see any sign of a system call returning EBADF, even though $! seems to be set to "Bad file descriptor".

Any feedback is greatly appreciated :).

Replies are listed 'Best First'.
Re: Errno being set to strange values when using IO::Poll
by Illuminatus (Curate) on Sep 25, 2009 at 20:05 UTC
    Perhaps you should read the description of $! in perlvar if you do not understand.

    You are making system calls in your signal handler. By convention, errno gets set by the kernel at the point the system call 'fails', ie when it is interrupted by the signal. Any system calls made inside the signal handler have the potential for altering the value of errno. system calls may change errno, whether successful or not, but the value of errno is only meaningful if the call returned a failure value. The 'easy' answer is to have your handler set $! to EINTR (or text equivalent) before ending.

    While on the soapbox, let me remind you that making system calls and altering global variables from within a signal handler (except protected variables) is generally a bad idea (unless the handler is simply a cleanup before exiting). Many things that are 'thread-safe' are not 'signal-handler-safe'. It is usually better to design your code so that all the handler does is set a mutex or protected variable that is checked or waited on in the main portion of the code, or a separate thread.

    fnord

      (except protected variables)

      What are protected variables in Perl?

        Sorry, I was speaking metaphorically. In general, I mean a variable that you must go through a mutex/semaphore to get to...
Re: Errno being set to strange values when using IO::Poll
by ikegami (Patriarch) on Sep 25, 2009 at 18:48 UTC
    What if you localize $! in your handler?
Re: Errno being set to strange values when using IO::Poll
by afoken (Chancellor) on Sep 26, 2009 at 10:24 UTC

    I see several problems:

    • You place a lot of code into your signal handler. Older perls have real problems with that. Newer perls try to cope with the underlying problem by default, but can still show the old behaviour. See "Signals" and "Deferred Signals (Safe Signals)" in perlipc. The safe way of handling signals is just to set a flag and handle the signal in the main loop of the program.
    • Your signal handler modifies errno a.k.a. $!.
    • The code both inside and outside of your signal handler lacks lots of error checks, and you are not using autodie.

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
Re: Errno being set to strange values when using IO::Poll
by eest (Initiate) on Oct 03, 2009 at 19:58 UTC
    Thanks a lot for your pointers and valuable information! :).

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://797581]
Approved by ikegami
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others cooling their heels in the Monastery: (3)
As of 2024-04-26 00:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found