Search code examples
perlfreetds

Perl select function hanging indefinitely


I am sorry for this really basic question but I do not seem to find root cause of my issue.

Environment details: Perl 5.14.1 Connecting to MS SQL Server via freeTDS tsql Linux x64

Here is my code:

#!/usr/bin/perl -w
use IO::Select;
use strict;

my $query = "";
my $s = IO::Select->new();
$s->add(\*STDIN);
my @STD_IN = ();
if ($s->can_read(.5)) {
      @STD_IN = <STDIN>;
        }
$query .= "@STD_IN\n@ARGV";

my $myTmpFile = `mktemp /tmp/$ENV{USER}QueryXXXX`; chomp($myTmpFile);
`echo "use testDB\n$dbQuery\ngo\nquit" > $myTmpFile`;

print(`/usr/bin/tsql -H myhost -p 9999 -U myuser -P mypass -o q < $myTmpFile`);

When I run this script, like this

$>./myscript "select * from mytable"

it works fine sometimes but often I see the script hanging indefninitly.

I did some debugging by running ps -ef here is how it looks:

kedar 24659 24574  0 05:50 ttyp3    00:00:00 /usr/bin/perl -w /home/kedar/myscript select * from mytable

What can be the cause for hang here? I do not understand yet - it is a very simple script.

I checked the perl documentation for IO::Select and this is what it says -

can_read    

$s->can_read([timeout])
Returns array of handles that are ready for reading. timeout is the maximum amount of time to wait before returning an empty list. If timeout is not given, and any handles are registered, then the call blocks.

But I do have timeout in my script.

Any idea? Please help as I am stuck with issue

PS: Some variables, filenames and other things have been modified from original due to few constraints. Also, this script is written by someone else previously and I need to fix the hang first. So if you can think of alternate and clean way of doing this - it would be great !


Solution

  • @STD_IN = <STDIN> reads from the filehandle STDIN in list context, meaning it will block until it receives an eof on the filehandle. The functions of IO::Select, on the other hand, will just tell you if there is any input on the selected filehandle, not whether it contains all the input.

    So in this case you want to read STDIN in scalar context(*). It could look something like this:

    if ($s->can_read(.5)) {
        push @STD_IN, scalar <STDIN>;
        # check if there is more than one line of input ready ...
        while ($s->can_read(0)) {
            push @STD_IN, scalar <STDIN>;
        }
    }
    

    (*) - scalar context might not be sufficient here, either. Scalar context will read from the filehandle up until the next end-of-line character or character sequence. For buffered input or unstructured input, IO::Select might tell you that input is available but that input might not contain a new line, and a scalar <STDIN> call will block. In that case, you may have to resort to loading input one character at a time (with getc, or read/sysread) or, if your OS supports it, setting up a non-blocking filehandle.