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

Hallo Monks...

It would be convenient for me to be able to interrupt a Perl script by pressing some key, which would pause execution and let me modify a few variable via a subroutine, and then resume execution... that is, pass in parameters on the fly, like a 'mainloop' would allow for a GUI system.

All the simple ways I can think of to do this are pretty kludgy (i.e. a seperate script that modifies a file that gets periodically checked, etc.). Would pipes be involved?

Ideally I would like to avoid forks... I could make a forking system work for my application, but it would be overkill and wouldn't generalize well.

Anybody have suggestions? Simpler is always better!

Thanks

~evan

Replies are listed 'Best First'.
Re: GUI-like behavior
by jdporter (Paladin) on Jul 17, 2003 at 18:46 UTC
    If the point of this is to help you during the development of the program, but won't be used when the program has been deployed, I'd suggest trying the perl debugger. It lets you set break points, single-step, and lots of other things, besides breaking and twiddling values.

    On the other hand, if this is a long-lived program, which should be re-configurable on the fly at any arbitrary time, then you could do the traditional UNIXy thing, and have the program trap, and reload its configuration, on the HUP signal.

    jdporter
    The 6th Rule of Perl Club is -- There is no Rule #6.

Re: GUI-like behavior (vague)
by tye (Sage) on Jul 17, 2003 at 19:15 UTC

    It would probably help if you were at least a little less vague about what you are doing. What the program will be trying to do when a keypress comes in can make a huge difference in what approaches make sense. Also, more details on what type of user interface you would like after you press the key would help.

    But my first idea is to have a subroutine that does a non-blocking read (or peek) to see if there is any input (probably via Term::ReadKey) and if there is none, then it immediately returns. If there is some, then it prompts for what you want to do.

    Then you sprinkle calls to this subroutine all over your code, making sure that there aren't any long-running sections where the routine will never be called.

    Depending on what your code is doing, this sprinkle might be reduced to just a single call.

    You could also teach this routine to be smart enough to track how long it was since the last time it was called and notify you if it ever takes more than Xms between calls so that you can know that more sprinkling is required. (and in such cases report the call stack to you so you can figure out what just ran that took so long w/o sprinkles)

                    - tye
Re: GUI-like behavior
by simonm (Vicar) on Jul 17, 2003 at 19:23 UTC
    In a POSIX environment, causing a long-running process such as a daemon to stop and load the latest configuration information is often associated with the HUP signal.

    Perhaps you could put in a signal handler that would allow you to eval some new code in, and then continue, along the following (untested) lines:

    my $code_file = '/tmp/myscript.cmds'; $SIG{HUP} = sub { unless ( -e $code_file ) { warn "Received HUP signal, but no command file."; return; } eval { require $code_file; unlink $code_file; }; if ( ! $@ ) { warn "Caught HUP signal and processed command file"; } else { warn "Caught HUP signal but unable to process commands: $@"; } };

    While your script is running interactively, you can use another session to write commands to this file and HUP your running Perl process.

    > echo "$foo = 27" >> /tmp/myscript.cmds > killall -HUP perl

    A major caveat: signal handlers are notoriously finicky and idiosyncratic, so this approach might not be particularly reliable or portable.

Re: GUI-like behavior
by BrowserUk (Patriarch) on Jul 17, 2003 at 23:51 UTC

    Did you get an answer that you like?

    A non-blocking read would work, but the need to spread checks throughout the code to test for user input is a pain.

    Using signals is a possibility, but from what I've read, the problems associated with signals in perl, even on fully complient POSIX systems, mean that getting this right can be a hit and miss affair even using 5.8's safe signals.

    If your target is Win32, and you don't need portability, then there is a way that might work. The idea is that you would start a thread to monitor the keyboard waiting for the user to indicate they want to interact. Your main script would be running on another thread. When you recieve user input, you can use a couple of Win32 native apis; SuspendThread & ResumeThread to pause and restart the thread running the main code whilst you make your changes. This would neatly avoid the need to litter tests throughout the main code, but is obviously very OS specific.

    The only real problem I see in implementing this is that that the API's require a native 'thread handle' rather than the perl 'thread id'. This is stored and used internally by the threads module, but not exposed. It would require a small modification to the module to gain access to this, perhaps if the $thread->tid method is called in a list context, then it would return both.

    I'd be willing to take a crack at making this work if anyone would find it useful. It would probably need to be based around 5.8.1 as this is the first version in which threads have begun to act in a resonably sane manner. (Just ripe for me to come along and screw them up:)


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller

Re: GUI-like behavior
by eweaverp (Scribe) on Jul 17, 2003 at 18:44 UTC
    UPDATE: This isn't meant to be a debugging tool... I would like the functionality in production-level code. Thus things like Komodo won't help.

    Thanks to jeffa for the tip though.

    UPDATE 2: I don't need to execute arbitrary code; just prompt the user to change a few integers.

    ~evan

      If, as you say, this is for production code, then I really don't think you want the user to be able to change any arbitrary variable at any arbitrary time. There are (I must think) certain variables that it makes sense for them to alter, and certain specific times at which it makes sense for them to be able to do it.

      Thus tye's suggestion is probably reasonable, given that your application is console-based. Then you don't have to (as he says) sprinkle the key checks throughout your program; just at the certain places where it makes sense. I wince at the idea of sprinkling things like that throughout a production system. And if the specific times at which it makes sense to alter the variables are known, and are rather infrequent, you could take the approach of always stopping to ask the user for values, and timing out the request with an alarm. I've done this in the past with some success.

      jdporter
      The 6th Rule of Perl Club is -- There is no Rule #6.