Beefy Boxes and Bandwidth Generously Provided by pair Networks
Welcome to the Monastery
 
PerlMonks  

sysread blocking ??

by azadian (Sexton)
on Nov 05, 2020 at 20:03 UTC ( #11123435=perlquestion: print w/replies, xml ) Need Help??

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

Sysread() returns 0 at EOF, which I understand to mean that there is no data waiting to be read. My program seems to indicate that sysread() isn't returning until it has data to read.
#!/usr/bin/perl # teof2 20201104 NHA # Test eof() for IPC over socketpair filehandle. use diagnostics; use strict 'subs'; use strict 'refs'; use Socket; use IO::Handle; my $child; #filehandle to child process my $parent; #filehandle to parent process my $pid; #Process ID of child process # r e a d L i n e # This expects to read one line (\n-terminated, but the newline is cho +mped). # Returns the line, which will be empty if no characters are waiting t +o be read. sub readLine { my ($fh) = @_; my $buf = ''; my $offset = 0; my $cnt = 0; my $stat; do { $stat = sysread $fh, $buf, 4096-$offset, $offset; die "sysread: $!\n" unless defined($stat); $offset += $stat; } while ($stat and "\n" ne substr($buf, -1)); chomp $buf; #print STDERR time%100, " #readLine returning <$buf>\n"; return $buf; } #readLine() # w r i t e L i n e # Writes a buffer to the filehandle. sub writeLine { my ($fh, $buf) = @_; my $offset = 0; my $remaining = length($buf); do { my $stat = syswrite($fh, $buf, $remaining, $offset); die "syswrite: $!\n" unless defined($stat); $remaining -= $stat; $offset += $stat; } while (0 < $remaining); } #writeLine() socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; $child->autoflush(1); $parent->autoflush(1); #$child->blocking(0) or die "blocking: $!\n"; #$parent->blocking(0) or die "blocking: $!\n"; if ($pid = fork()) { #parent close $parent or die "close: $!\n"; while (1) { sleep 1; print STDOUT time%100, ": polling child\n"; if (my $line = readLine($child)) { print STDOUT time%100, ": received <$line>\n"; if ('E_O_F' eq $line) { my $stat = wait; die "wait returned $stat\n" unless $stat == $pid; print STDOUT time%100, ": child reaped, parent exiting +\n"; exit 0; } } else { print STDOUT time%100, ": no input from child yet\n"; } } } else { #child die "cannot fork: $!" unless defined $pid; close $child or die "close: $!\n"; writeLine($parent, time%100 . ": child started\n"); sleep 6; writeLine($parent, time%100 . ": child wrote again\n"); sleep 2; writeLine($parent, "E_O_F\n"); #causes termination close $parent or die "close: $!\n"; print STDOUT time%100, ": child exiting\n"; exit; }
When I run this I get:
= ./teof2 89: polling child 89: received <88: child started> 90: polling child 94: received <94: child wrote again> 95: polling child 96: received <E_O_F> 96: child exiting 96: child reaped, parent exiting

Note that I call sysread() at second 90, and it doesn't return until 94, when the child finally decides to write again.

If I uncomment-out the blocking(0) lines in the middle of the program, I get the following:
= ./teof2 16: polling child 16: received <15: child started> 17: polling child Uncaught exception from user code: sysread: Resource temporarily unavailable main::readLine(GLOB(0x55a6c475a4d8)) called at ./teof2 line 63

As near as I can tell, setting nonblocking shouldn't be necessary, and it shouldn't cause a problem. What is going on here?

Replies are listed 'Best First'.
Re: sysread blocking ??
by tybalt89 (Prior) on Nov 05, 2020 at 21:44 UTC

    sysread returning 0 means "connection was closed".
    sysread returning undef means "connection is still open but no data is available at this time, or some other error".

    Your code wasn't doing what you documented.

    #!/usr/bin/perl use diagnostics; use strict 'subs'; use strict 'refs'; use Socket; use IO::Handle; my $child; #filehandle to child process my $parent; #filehandle to parent process my $pid; #Process ID of child process # r e a d L i n e # This expects to read one line (\n-terminated, but the newline is cho +mped). # Returns the line, which will be empty if no characters are waiting t +o be read. sub readLine { my ($fh) = @_; my $buf = ''; my $offset = 0; my $cnt = 0; my $stat; do { $stat = sysread $fh, $buf, 4096-$offset, $offset; # die "sysread: $!\n" unless defined($stat); return '' unless defined($stat); ############ DOING WHAT YOU D +OCUMENTED $offset += $stat; } while ($stat and "\n" ne substr($buf, -1)); chomp $buf; #print STDERR time%100, " #readLine returning <$buf>\n"; return $buf; } #readLine() # w r i t e L i n e # Writes a buffer to the filehandle. sub writeLine { my ($fh, $buf) = @_; my $offset = 0; my $remaining = length($buf); do { my $stat = syswrite($fh, $buf, $remaining, $offset); die "syswrite: $!\n" unless defined($stat); $remaining -= $stat; $offset += $stat; } while (0 < $remaining); } #writeLine() socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; $child->autoflush(1); $parent->autoflush(1); $child->blocking(0) or die "blocking: $!\n"; ######### CHANGED #$parent->blocking(0) or die "blocking: $!\n"; if ($pid = fork()) { #parent close $parent or die "close: $!\n"; while (1) { sleep 1; print STDOUT time%100, ": polling child\n"; if (my $line = readLine($child)) { print STDOUT time%100, ": received <$line>\n"; if ('E_O_F' eq $line) { my $stat = wait; die "wait returned $stat\n" unless $stat == $pid; print STDOUT time%100, ": child reaped, parent exiting +\n"; exit 0; } } else { print STDOUT time%100, ": no input from child yet\n"; } } } else { #child die "cannot fork: $!" unless defined $pid; close $child or die "close: $!\n"; writeLine($parent, time%100 . ": child started\n"); sleep 6; writeLine($parent, time%100 . ": child wrote again\n"); sleep 2; writeLine($parent, "E_O_F\n"); #causes termination close $parent or die "close: $!\n"; print STDOUT time%100, ": child exiting\n"; exit; }

    Outputs:

    51: polling child 51: received <50: child started> 52: polling child 52: no input from child yet 53: polling child 53: no input from child yet 54: polling child 54: no input from child yet 55: polling child 55: no input from child yet 56: polling child 56: received <56: child wrote again> 57: polling child 57: no input from child yet 58: child exiting 58: polling child 58: received <E_O_F> 58: child reaped, parent exiting

    Is this what you wanted?

      Yes, that does what I want, and I thank you for the fixes.

      It's a mystery to me how you arrived at this, since the book simply says: "The sysread function returns undef on error." I never imagined that includes no data to read. I presume I have to look elsewhere to see if it is a real error or just no characters to read.

      It's also not clear to me why setting the parent to nonblocking causes an error.

        The error code is in $!.
        If a handle is set to non-blocking and there is no data present and a sysread returns undef, the error should be either EWOULDBLOCK or EAGAIN. Any thing else is a real error.
        The error text you were seeing "Resource temporarily unavailable" was from EAGAIN.
        sysread() ultimately calls the C function "read", see "man 2 read" for information about read errors includiung EAGAIN.

        Setting $parent to non-blocking does not cause an error, did you mean $child ?

        Another problem with your code is the sleep in the receiver section. If data arrives on the socket during that sleep, response will be delayed until that sleep completes. In general, there should be no sleeps in a receiver except for the timeout in can_read or select. This is why select() and IO::Select were invented!

        In general, any attempt to use non-blocking should be avoided, and should require exceptional justification before it is allowed.

        Following is a set of suggested "tweaks" for eliminating that problem and several others.

        #!/usr/bin/perl use diagnostics; use strict 'subs'; use strict 'refs'; use Socket; use IO::Handle; use IO::Select; my $child; # filehandle to child process my $parent; # filehandle to parent process my $pid; # Process ID of child process # w r i t e L i n e # Writes a buffer to the filehandle. sub writeLine { my ($fh, $buf) = @_; print $fh $buf; # while( length $buf ) # alternate to yours, but single print is the + same # { # my $stat = syswrite $fh, $buf; # $stat and substr $buf, 0, $stat, ''; # } } #writeLine() socketpair($child, $parent, AF_UNIX, SOCK_STREAM, PF_UNSPEC) or die "socketpair: $!"; $child->autoflush(1); $parent->autoflush(1); if ($pid = fork()) { #parent close $parent or die "close: $!\n"; my $sel = IO::Select->new($child); my @handles; my $buf = ''; while ($sel->count) { # print STDOUT time%100, ": polling child\n"; for my $fh ( @handles = $sel->can_read(1) ) { if( sysread $fh, $buf, 1e6, length $buf ) { print STDOUT time%100, ": received <$1>\n" # because you may + get more while $buf =~ s/(.*)\n//; # than one line a +t a time } else { $sel->remove($fh); } } @handles or print STDOUT time%100, ": no input from child at thi +s time\n"; } my $stat = wait; die "wait returned $stat\n" unless $stat == $pid; print STDOUT time%100, ": child reaped, parent exiting\n"; exit 0; } else { die "cannot fork: $!" unless defined $pid; close $child or die "close: $!\n"; writeLine($parent, time%100 . ": child started\n"); sleep 4; writeLine($parent, time%100 . ": child wrote again\nwith two lines +\n"); sleep 2; writeLine($parent, "E_O_F\n"); close $parent or die "close: $!\n"; #causes termination print STDOUT time%100, ": child exiting\n"; exit; }

        EDIT: removed overlooked ->blocking call.

        sysread is a thin wrapper around the read system call. It would help you to read its documentation (the read(2) man page). From the "Errors" section,

        EAGAIN or EWOULDBLOCK
        The file descriptor fd refers to a socket and has been marked nonblocking (O_NONBLOCK), and the read would block. POSIX.1-2001 allows either error to be returned for this case, and does not require these constants to have the same value, so a portable application should check for both possibilities.

        I would have used:

        if (!defined($stat)) { if $!{EAGAIN} || $!{EWOULDBLOCK}) { ... No data at this time ... } die "sysread: $!\n"; } if (!$stat) { ... EOF ... )

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://11123435]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others examining the Monastery: (7)
As of 2021-06-24 19:47 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    What does the "s" stand for in "perls"? (Whence perls)












    Results (132 votes). Check out past polls.

    Notices?