0
votes
    my $waitlist = IO::Select->new($self->{sock});
    while($datalen < 9)
    {
            ##timeout set as 50
            if($waitlist->can_read($timeout || 0)) {
                    print_message(LOGLEVEL_TRACE, "INSIDE IF....", LOG_TAG);
                    $templen = $self->{sock}->sysread($tempdata, 9 - $datalen);
            } else {
                    print_message(LOGLEVEL_TRACE, "INSIDE ELSE....", LOG_TAG);
                    print_message(LOGLEVEL_TRACE, "B4 ERROR $!", LOG_TAG);
                    $templen = 0;
                    $! = EWOULDBLOCK;
            }
           
            print_message(LOGLEVEL_TRACE, "ERROR $!", LOG_TAG);
    }

In the above code, "can_read" getting error out as Inappropriate ioctl for device. Like to know the reason and fix need to do.

1
Is sock properly initialized and does it refer to a socket in a valid state (open, no errors)? - rveerd

1 Answers

3
votes

$! is only meaningful after a system call indicates it set $!.

Problems:

  • You don't check if a system call set $! before using it.
  • You call print_message between the system call in which you're interested and the use of $!, and print_message can surely make system calls.

One thing to note is the way of checking if can_read returned an error is really weird.

Return an array of handles that are ready for reading. TIMEOUT is the maximum amount of time to wait before returning an empty list (with $! unchanged), in seconds, possibly fractional. If TIMEOUT is not given and any handles are registered then the call will block indefinitely. Upon error, an empty list is returned, with $! set to indicate the error. To distinguish between timeout and error, set $! to zero before calling this method, and check it after an empty list is returned.


Replace

my $waitlist = IO::Select->new($self->{sock});
while($datalen < 9)
{
     ##timeout set as 50
    if($waitlist->can_read($timeout || 0)) {
        print_message(LOGLEVEL_TRACE, "INSIDE IF....", LOG_TAG);
        $templen = $self->{sock}->sysread($tempdata, 9 - $datalen);
    } else {
        print_message(LOGLEVEL_TRACE, "INSIDE ELSE....", LOG_TAG);
        print_message(LOGLEVEL_TRACE, "B4 ERROR $!", LOG_TAG);
        $templen = 0;
        $! = EWOULDBLOCK;
    }
       
    print_message(LOGLEVEL_TRACE, "ERROR $!", LOG_TAG);
}

with

my $waitlist = IO::Select->new($self->{sock});
while (length($tempdata) < 9) {
    $! = 0;
    my @handles = $waitlist->can_read($timeout || 0);
    if ($!) { # Yuck, just about anywhere else, this would be wrong.
        print_message(LOGLEVEL_TRACE, "SELECT FAILED: $!", LOG_TAG);
        ...abort...
    }

    if (!@handles) {
        print_message(LOGLEVEL_TRACE, "SELECT FAILED: Timeout", LOG_TAG);
        ...abort...
    }

    my $bytes_read = $self->{sock}->sysread($tempdata, 9 - $datalen, length($tempdata));
    if (!defined($bytes_read)) {
        print_message(LOGLEVEL_TRACE, "SYSREAD FAILED: $!", LOG_TAG);
        ...abort...
    }

    if (!$bytes_read) {
        if (length($tempdata)) {
           print_message(LOGLEVEL_TRACE, "SYSREAD FAILED: Premature EOF", LOG_TAG);
           ...abort...
        }

        # Reached EOF without reading anything.
        last;
    }
}

Until you perform these changes, we don't know if you got an error, much less what it is.


Comments:

  • You were missing the final argument for sysread, which is necessary here.

  • Using select as you did above is totally useless if there's no timeout (because there's only one handle). sysread will already perform the necessary waiting.

  • If there is a timeout, it's not an absolute timeout. For example, say you have a timeout of 5 seconds, but you consistently get 1 byte every 4 seconds, the above will run for 36 seconds without timing out. (Ok, that's not a realistic example, but only because we're only reading 9 bytes.) For an absolute timeout, you'll need to keep track of how time has already gone by. (I'd use my $wait_til = time() + timeout(); up front, and pass $wait_til - time() to can_read, after checking if it's positive or not.)