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

I'm writing a simple chess GUI in Tk. I would like to run an engine from Tk as a bidirectional pipe (writing commands to its STDIN and reading analysis results from its STDOUT). My problem is that sysread simply freezes when there is nothing to read. I've tried IO::Select, but it always returns an empty list for handles that are ready for reading. Even if I open a simple text file and add it to the selected handles it returns an empty list. Is this a Win32 specific problem, or am I doing something wrong?
-------
use IO::Select; open ENGINE_READ,"engine.exe |"; my $timeout=10; $s = IO::Select->new(); $s->add(\*ENGINE_READ); @ready = $s->can_read($timeout);
-------

@ready is empty no matter what

Replies are listed 'Best First'.
Re: bidirectional pipe freezing
by Anonymous Monk on Dec 21, 2011 at 13:42 UTC
      Is there any alternative way to work around this problem? Methods using 'alarm' in any shape or form as a timeout watcher also don't work on Windows.

      Multi threading could be an answer, but Tk is not thread safe.

      Currently I do the engine stuff in a separate multi threaded non-Tk perl program, which copies the output of the engine to an ordinary file which the main app can read. This way however I have to run two different programs.

        There is IPC::Run, but my approach to this has always been to do a (blocking) read from the pipe in a separate thread and stuff the lines into a Thread::Queue, from where the front end code (or whatever else) reads.

        Multi threading could be an answer, but Tk is not thread safe.

        Tk isn't "thread-safe", but it does run perfectly safely in a single thread.

        That is, you can have as many background threads running as you need in your TK app, provided that none of those other threads attempt to talk directly to the GUI.

        The basic method is that you start all your background threads runnung before you require Tk and build your GUI. And you then use Thread::Queue(s) to send commands from the GUI to the worker threads and another to receive back results. It is a simple, efficient mechanism that avoids all the fuss and bother of buffered pipes or sockets and select loops.


        With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
        Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
        "Science is about questioning the status quo. Questioning authority".
        In the absence of evidence, opinion is indistinguishable from prejudice.

        The start of some sanity?

Re: bidirectional pipe freezing
by zentara (Cardinal) on Dec 21, 2011 at 15:49 UTC
    Just some brainstorming :-)

    Instead of piped opens, you might want to try pipes with threads, for example Using Select and pipes to communicate with threads. That way you have more control over the pipes and any timer alarm threads you might setup. A way to run parallel threads, one as an alarm timer can be done, but it is frought with details about signal chains. On linux the signals are received by the parent thread first, then echo'd out to the child threads.

    #!/usr/bin/perl -w use strict; use threads; use threads::shared; my $timer_go:shared = 0; my $worker = threads->create(\&worker); my $timer = threads->create(\&timer,$worker)->detach(); print "hit enter to start\n"; <>; $timer_go=1; while( (scalar threads->list) > 0 ){ print scalar threads->list,"\n"; sleep 1; foreach my $thread (threads->list) { if( $thread->is_joinable ){ $thread->join;} } } print "worker joined, all done\n"; exit; sub timer { my $worker = shift; while(1){ if($timer_go){ my $count = 0; while(1){ $count++; if($count > 5){ print "timed out\n"; # Send a signal to a thread $worker->kill('INT'); return; # will destroy a detached thread } sleep 1; print "timing $count\n"; } }else{sleep 1} } } sub worker { $|++; $SIG{INT} = sub{ warn "Caught Zap!\n"; threads->exit() }; # threads->exit() will exit thread only while(1){sleep 1; next} return; }

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh