catch hitting crtl-c twice

Discussion in 'Perl Misc' started by Michael Goerz, Nov 6, 2006.

  1. Hi,

    is there a way to catch if an interrupt (ctrl-c) is called twice in a
    certain time window? Catching the first one is done by

    $SIG{INT} = \&refresh;

    but now I want to detect if the user hits ctrl-c again while that
    handler is running (there can be a 'sleep 1' inside the handler to wait
    for this), so that the program can exit in that case. As I observed, the
    event is not triggered again while the handler is running.

    Any suggestions?

    Thanks,
    Michael Goerz
    Michael Goerz, Nov 6, 2006
    #1
    1. Advertising

  2. Michael Goerz

    Sisyphus Guest

    "Michael Goerz" <4ward.com> wrote in message
    news:...
    > Hi,
    >
    > is there a way to catch if an interrupt (ctrl-c) is called twice in a
    > certain time window? Catching the first one is done by
    >
    > $SIG{INT} = \&refresh;
    >
    > but now I want to detect if the user hits ctrl-c again while that
    > handler is running (there can be a 'sleep 1' inside the handler to wait
    > for this), so that the program can exit in that case. As I observed, the
    > event is not triggered again while the handler is running.
    >
    > Any suggestions?
    >


    A simplistic approach:

    use warnings;
    use strict;

    $SIG{INT} = \&refresh;
    my $global = 1;

    while(1) {}

    sub refresh {
    print "Ctrl-C caught\n";
    $global *= -1;
    sleep(1);
    if($global > 0) {
    print "Another Ctrl-C detected\n";
    exit(0);
    }
    else {
    print "Continuing on\n";
    $global *= -1;
    }
    }

    __END__

    Whenever I hit Ctrl-C, the program calls refresh() and then continues on -
    unless I hit Ctrl-C a second time during the the sleep(1) period - in which
    case the program exits.

    (This is perl 5.8.8, Windows 2000.)

    Cheers,
    Rob
    Sisyphus, Nov 6, 2006
    #2
    1. Advertising

  3. Sisyphus wrote:
    > "Michael Goerz" <4ward.com> wrote in message
    > news:...
    >> Hi,
    >>
    >> is there a way to catch if an interrupt (ctrl-c) is called twice in a
    >> certain time window?

    > A simplistic approach:
    >
    > use warnings;
    > use strict;
    >
    > $SIG{INT} = \&refresh;
    > my $global = 1;
    >
    > while(1) {}
    >
    > sub refresh {
    > print "Ctrl-C caught\n";
    > $global *= -1;
    > sleep(1);
    > if($global > 0) {
    > print "Another Ctrl-C detected\n";
    > exit(0);
    > }
    > else {
    > print "Continuing on\n";
    > $global *= -1;
    > }
    > }
    >
    > __END__
    >
    > Whenever I hit Ctrl-C, the program calls refresh() and then continues on -
    > unless I hit Ctrl-C a second time during the the sleep(1) period - in which
    > case the program exits.
    >
    > (This is perl 5.8.8, Windows 2000.)
    >
    > Cheers,
    > Rob

    Yes, thanks, that works indeed! I tried something similar, which didn't
    work for some reason...

    Thanks a lot,
    Michael
    Michael Goerz, Nov 6, 2006
    #3
  4. Michael Goerz wrote:
    > Sisyphus wrote:
    >> "Michael Goerz" <4ward.com> wrote in message
    >> news:...
    >>> Hi,
    >>>
    >>> is there a way to catch if an interrupt (ctrl-c) is called twice in a
    >>> certain time window?

    >> A simplistic approach:
    >>
    >> use warnings;
    >> use strict;
    >>
    >> $SIG{INT} = \&refresh;
    >> my $global = 1;
    >>
    >> while(1) {}
    >>
    >> sub refresh {
    >> print "Ctrl-C caught\n";
    >> $global *= -1;
    >> sleep(1);
    >> if($global > 0) {
    >> print "Another Ctrl-C detected\n";
    >> exit(0);
    >> }
    >> else {
    >> print "Continuing on\n";
    >> $global *= -1;
    >> }
    >> }
    >>
    >> __END__
    >>
    >> Whenever I hit Ctrl-C, the program calls refresh() and then continues on -
    >> unless I hit Ctrl-C a second time during the the sleep(1) period - in which
    >> case the program exits.
    >>
    >> (This is perl 5.8.8, Windows 2000.)
    >>
    >> Cheers,
    >> Rob

    > Yes, thanks, that works indeed! I tried something similar, which didn't
    > work for some reason...
    >
    > Thanks a lot,
    > Michael

    Hmmm... on second try, it *doesn't* work. I always end up in the else
    block (running your script verbatim). Is this an inconsistency in Perl?
    I'm running perl 5.8.7 on Suse Linux 10.0.

    Confused,
    Michael Goerz
    Michael Goerz, Nov 6, 2006
    #4
  5. Michael Goerz

    Sisyphus Guest

    "Michael Goerz" <4ward.com> wrote in message
    news:...

    > use warnings;
    > use strict;
    >
    > $SIG{INT} = \&refresh;
    > my $global = 1;
    >
    > while(1) {}
    >
    > sub refresh {
    > print "Ctrl-C caught\n";
    > $global *= -1;
    > sleep(1);
    > if($global > 0) {
    > print "Another Ctrl-C detected\n";
    > exit(0);
    > }
    > else {
    > print "Continuing on\n";
    > $global *= -1;
    > }
    > }
    >
    > __END__

    ..
    ..
    ..
    > Hmmm... on second try, it *doesn't* work. I always end up in the else
    > block (running your script verbatim). Is this an inconsistency in Perl?
    > I'm running perl 5.8.7 on Suse Linux 10.0.
    >


    Probably a difference between operating systems.
    The script works for me as intended on Windows 2000 (perl 5.8.8) but I find
    the same behaviour as you on Mandrake-9.1 linux (also perl 5.8.8).

    Seems that on Win32, the second instance of refresh() is run as soon as the
    second Ctrl-C is hit. But on linux, the second instance of refresh() does
    not get run until the first instance has finished running .... which is not
    what we want if the script is going to perform as intended.

    Perhaps on linux refresh() needs to fork() the process that modifies $global
    and sleeps.

    (Note - I've never used fork() so my suggestion may not even be sane, faik
    :)

    Cheers,
    Rob
    Sisyphus, Nov 6, 2006
    #5
  6. Michael Goerz

    Ben Morrow Guest

    Quoth "Sisyphus" <>:

    [invoking a signal handler reentrantly]

    > Probably a difference between operating systems.
    > The script works for me as intended on Windows 2000 (perl 5.8.8)


    [pressing ctrl-c while $SIG{INT} is still running reenters $SIG{INT}]

    > but I find
    > the same behaviour as you on Mandrake-9.1 linux (also perl 5.8.8).


    [the second SIGINT is apparently not delivered until just after
    $SIG{INT} returns]

    > Seems that on Win32, the second instance of refresh() is run as soon as the
    > second Ctrl-C is hit. But on linux, the second instance of refresh() does
    > not get run until the first instance has finished running .... which is not
    > what we want if the script is going to perform as intended.


    General recommendation when using signal handlers is to do nothing in
    the actual handler except set a global, which you then check in your
    main loop. However, if you really want the behaviour you describe, you
    can get it as follows:

    use POSIX qw/sigaction SIGINT SA_NODEFER/;

    sigaction
    SIGINT,
    POSIX::SigAction->new(
    sub {
    warn "start INT handler";
    sleep 2;
    warn "end INT handler";
    },
    POSIX::SigSet->new,
    SA_NODEFER,
    ),
    or die "sigaction for SIGINT failed: $!";

    Note that this defeats the 5.8 'safe signals' mechanism (for this signal
    only), which means there is a chance of a segfault if the signal arrives
    while perl is in the middle of something non-reentrant. You can
    reinstate it with the ->safe method on the POSIX::SigAction object, but
    then you lose the reentrancy again. See sigaction and POSIX::SigAction
    in the POSIX.pm manpage.

    > Perhaps on linux refresh() needs to fork() the process that modifies $global
    > and sleeps.
    >
    > (Note - I've never used fork() so my suggestion may not even be sane, faik
    > :)


    Er, no. For a start, a forked child cannot affect $global in the parent
    (that's kinda the point :) ).

    Ben

    --
    You poor take courage, you rich take care:
    The Earth was made a common treasury for everyone to share
    All things in common, all people one. []
    'We come in peace'---the order came to cut them down.
    Ben Morrow, Nov 6, 2006
    #6
  7. Michael Goerz

    Ch Lamprecht Guest

    Michael Goerz wrote:
    > Hi,
    >
    > is there a way to catch if an interrupt (ctrl-c) is called twice in a
    > certain time window? Catching the first one is done by
    >
    > $SIG{INT} = \&refresh;
    >
    > but now I want to detect if the user hits ctrl-c again while that
    > handler is running (there can be a 'sleep 1' inside the handler to wait
    > for this), so that the program can exit in that case. As I observed, the
    > event is not triggered again while the handler is running.
    >
    > Any suggestions?
    >
    > Thanks,
    > Michael Goerz



    How about this:

    use warnings;
    use strict;

    $SIG{INT} = \&refresh;
    my $last_time= 0 ;

    while(1) {}

    sub refresh {
    print "Ctrl-C caught\n";
    if (time() - $last_time< 2){
    print "Another Ctrl-C detected\n";
    exit(0);
    }
    else {
    print "Continuing on\n";
    $last_time = time();
    }
    }

    __END__

    Christoph
    --

    perl -e "print scalar reverse q//"
    Ch Lamprecht, Nov 6, 2006
    #7
  8. Ben Morrow wrote:
    > Quoth "Sisyphus" <>:
    >
    > [invoking a signal handler reentrantly]
    >
    >> Probably a difference between operating systems.
    >> The script works for me as intended on Windows 2000 (perl 5.8.8)

    >
    > [pressing ctrl-c while $SIG{INT} is still running reenters $SIG{INT}]
    >
    >> but I find
    >> the same behaviour as you on Mandrake-9.1 linux (also perl 5.8.8).

    >
    > [the second SIGINT is apparently not delivered until just after
    > $SIG{INT} returns]
    >
    >> Seems that on Win32, the second instance of refresh() is run as soon as the
    >> second Ctrl-C is hit. But on linux, the second instance of refresh() does
    >> not get run until the first instance has finished running .... which is not
    >> what we want if the script is going to perform as intended.

    >
    > General recommendation when using signal handlers is to do nothing in
    > the actual handler except set a global, which you then check in your
    > main loop. However, if you really want the behaviour you describe, you
    > can get it as follows:
    >
    > use POSIX qw/sigaction SIGINT SA_NODEFER/;
    >
    > sigaction
    > SIGINT,
    > POSIX::SigAction->new(
    > sub {
    > warn "start INT handler";
    > sleep 2;
    > warn "end INT handler";
    > },
    > POSIX::SigSet->new,
    > SA_NODEFER,
    > ),
    > or die "sigaction for SIGINT failed: $!";
    >
    > Note that this defeats the 5.8 'safe signals' mechanism (for this signal
    > only), which means there is a chance of a segfault if the signal arrives
    > while perl is in the middle of something non-reentrant. You can
    > reinstate it with the ->safe method on the POSIX::SigAction object, but
    > then you lose the reentrancy again. See sigaction and POSIX::SigAction
    > in the POSIX.pm manpage.
    >


    Interesting, I thought the 'safe' signal semantics of 5.8+ would enable
    you to get away with I/O and other potentially non-reentrant calls as
    documented in perlipc IIUC:

    ... Then at strategic "safe" points in the Perl interpreter (e.g.
    when it is about to execute a new opcode) the flags are checked
    and the Perl level handler from %SIG is executed. The "deferred"
    scheme allows much more flexibility in the coding of signal
    handlers as we know Perl interpreter is in a safe state, and that
    we are not in a system library function when the handler is called...

    But here, it looks as though 'safe' means deferring receipt until the
    handler returns entirely (except for Win32).

    --
    Charles DeRykus
    Charles DeRykus, Nov 6, 2006
    #8
  9. Michael Goerz

    Guest

    Charles DeRykus <> wrote:
    >
    > Interesting, I thought the 'safe' signal semantics of 5.8+ would enable
    > you to get away with I/O and other potentially non-reentrant calls as
    > documented in perlipc IIUC:
    >
    > ... Then at strategic "safe" points in the Perl interpreter (e.g.
    > when it is about to execute a new opcode) the flags are checked
    > and the Perl level handler from %SIG is executed. The "deferred"
    > scheme allows much more flexibility in the coding of signal
    > handlers as we know Perl interpreter is in a safe state, and that
    > we are not in a system library function when the handler is called...
    >
    > But here, it looks as though 'safe' means deferring receipt until the
    > handler returns entirely (except for Win32).


    I think there are two different aspects of "safety" here. At the perl
    level, you don't want to interrupt perl ops and cause bad things to inside
    the black box (e.g. seg faults) . At the Perl level, you don't want to
    interrupt signal handlers with other signal handlers, which can cause bad
    things to happen outside the black box.

    Xho

    --
    -------------------- http://NewsReader.Com/ --------------------
    Usenet Newsgroup Service $9.95/Month 30GB
    , Nov 6, 2006
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. bg
    Replies:
    0
    Views:
    268
  2. Kiuhnm

    twice(twice(x))

    Kiuhnm, Apr 1, 2006, in forum: C++
    Replies:
    2
    Views:
    384
    Kiuhnm
    Apr 1, 2006
  3. Scott

    sending "crtl z" to serial port

    Scott, Apr 24, 2007, in forum: ASP .Net
    Replies:
    2
    Views:
    415
    Scott
    Apr 25, 2007
  4. bg
    Replies:
    0
    Views:
    93
  5. bg

    can not catch the page unless twice post?

    bg, Dec 28, 2004, in forum: ASP .Net Web Controls
    Replies:
    1
    Views:
    102
Loading...

Share This Page