1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.

Plugin -> thread -> Command() -> Ctrl-C?

Discussion in 'Plugins' started by vefatica, Mar 16, 2013.

  1. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    If my plugin has created a thread, and that thread has called Command() (in my test, with the string "delay 5 & beep") and I press Ctrl-C while that command is being executed (and TCC is otherwise idle), TCC disappears. Rex, can you say exactly what's happening so I can program around it?
     
  2. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    I simplified my test to a bare minimum. If InitializePlugin() creates the thread below, then one of the first three Ctrl-Cs during the first five seconds after start-up will cause TCC to vanish. This happens with both v14 and v15.

    DWORD WINAPI Thread ( LPVOID pvoid )
    {
    WCHAR szCommand[4096] = L"delay 5 & beep";
    Command(szCommand, 0);
    return 0;
    }
     
  3. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    You're not handling exceptions. At a bare minimum, you should have a try / catch (...) there. When you're running in a separate thread, the TCC exception handling isn't going to save you.
     
  4. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    Save me from what?

    Apparently, DATEMONITOR has the same problem.
     
  5. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    Save you from crashing when you generate an exception (like ^C).

    You don't need to add exception handling if you:

    1) Never enter ^C
    2) Never enter ^Break
    3) Never call command() on a new thread
    4) Never have any errors in your code

    Otherwise, you'd better add try/catch around your code.
     
  6. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    OK, thanks. But is there a way to have my thread's Command() ignore Ctrl-C while Ctrl-C can be used normally in the user-interactive thread?

    Enlighten me please ... does TCC have a ConsoleCtrlHandler whch raises the exception or is that built-into C[++]? What could I catch (int, string, ...) after Ctrl-C, and with what values? I have only tried it with catch(...).
     
  7. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    Yes -- use an exception handler, and ignore the return.

    TCC will throw an int (3) when Windows calls the console control handler. Windows and C++ will throw other values as well; TCC has no control over that. If you don't know what you might get back (other than the int), you'd better have a (...) condition as well.
     
  8. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    vefatica said:
    OK, thanks. But is there a way to have my thread's Command() ignore Ctrl-C while Ctrl-C can be used normally in the user-interactive thread?​
    I don't know what you mean. try {Command();} catch(INT i){} doesn't help. Command() still aborts when Ctrl-C is pressed.
     
  9. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    It's not a good idea to have an empty catch block -- the compiler tends to optimize it away.
     
  10. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    I didn't think that'd work (even with my very limited knowledge of exception handling). With this (below) in place, my thread rarely even sees the exception ... Command() apparently gets it first and aborts. Is there some other strategy?
    Code:
    try
    {
        Command(szCommand, 0);
    }
    catch(INT i)
    {
        Printf(L"Got one!\r\n");
    }
    
    Here's the result of pressing Ctrl-C many timer during "delay 15 & beep". On which try my handler catches the exception is iffy. And in any case, Command() apparently aborts... that is, I never hear the beep, even if I catch the exception on the first and only Ctrl-C.
    Code:
    v:\> ^C
    v:\> ^C
    v:\> ^C
    Got one!
    ^C
    So back to an earlier question: Is there a way to have my thread's Command() ignore Ctrl-C while Ctrl-C can be used normally in the user-interactive thread?
     
  11. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    The ^C / ^Break are process-wide; they're coming from Windows in a separate thread, and apply to all threads running in TCC. Unless you've got the Windows source code & the desire to modify it, you cannot treat ^C differently in different threads.
     
  12. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    How's that going to work out when DATEMONITOR is using Command() in a background thread (which I suppose it does) and the interactive user presses Ctrl-C?
     
  13. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    Just like it does in every other monitoring command, which also use Command() in a background thread. There's nothing special about DATEMONITOR.

    If you don't want to abort your foreground & background threads, don't bang on ^C.
     
  14. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    It would seem quite reasonable to expect the execution of background stuff (monitoring commands) to be unaffected by what the user is doing interactively. There is occasionally a good reason to press Ctrl-C.
     
  15. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    Then you should pen a stiff note of complaint to Microsoft and demand they change their console exception architecture. But given that it's behaved that way for 20 years, you might not get much immediate satisfaction.
     
  16. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    You're not obliged to take any action upon CTRL_C_EVENT, are you? Command() has a reserved argument (reserved, at least from my POV). Couldn't it be (made to be) called in such a way that it ignores the INT 3 exception ... precisely for use by background threads? [I really don't know how it works so if I'm way off base, kindly scold me.]
     
  17. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    No.
    No. Here's the situation:

    First, there's no such thing as a "background" and "foreground" thread in the console process; they all have the same priority and the same privileges. The only differentiation is that one thread happened to be the one that at startup was responsible for accepting (but not necessarily executing) command line input. (But it is *not* the UI thread, which is another thread responsible for handling messages from Windows.)

    Second, what happens in your scenario when you get a ^C is this (vastly simplified; there are actually a number of additional threads):
    • Thread A is handling messages from Windows.
    • Thread B is taking command line input.
    • Thread C is handling tests for monitoring commands.
    • Threads D (and potentially E, F, G, etc.) are executing (reentrant) calls to Command() triggered from threads B and C (and occasionally A).
    • Threads G - Y are created by various internal commands spawned by threads A - G to handle internal tasks.
    • Thread Z is created (by Windows) to call the console control handler for the process when Windows detects a ^C. (Very little can be done in this thread because it has a limited stack size; pretty much just set a flag and exit.)
    • And TCC doesn't know or care what thread happened to call Command() when (somewhere in a deep and frequently reentrant stack) a ^C was detected and a throw was done. (Throws are passed from one catch to another, frequently unwinding nested 50+ function calls).
    You want TCC to automagically say "I got a ^C, and I in my ineffable mystery know that it is only relevant to thread B." Leaving aside how it could possible know that (and dodging for the moment complaints from users that they could never interrupt monitor commands), the ^C has already been processed in Windows (and may have broken out of a long-running Windows API.
    So to do what you want, TCC would have to:
    • Turn off all default ^C processing and disable the console control handler (which will also make it impossible to break out of a stuck API).
    • Check the keyboard input constantly for a ^C. (This will mean that sometimes you might not get a response to a ^C for a while; and possibly not at all depending on what TCC is doing.)
    • Modify (at least) 10K+ lines of code in TCC to only apply a ^C to thread B. Or sometimes threads started by thread B, depending on what B was doing. (Should only take a few months ...)
    • Ignore any frantic attempts by the user to abort threads C- Y.
    • Forbid anybody to ever attempt to press ^Break (an even worse situation than ^C handling).
    • Might be a good idea to ban plugins from creating threads, as they could never be stopped.
    • And finally, it'd be best to not allow anyone to press ^C when they're running Take Command, as that's another canister of annelids.
    Given all that, if you still want it and can convince a few thousand other users to support this request in the feedback forum, I'll consider it for a future version.
     
  18. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    Quote: "the ^C has already been processed in Windows (and may have broken out of a long-running Windows API".

    I didn't know that could happen. Do any API functions react at all to Ctrl-C? Can you name one or two?
     
  19. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    All of those problems and the only one you care about is the least significant one? Well, OK:

    CopyFileEx - used by all TCC file copy operations
    MoveFileEx - used by all TCC move & rename operations
    Many console APIs
    A couple dozen APIs that have callbacks
     
  20. rconn

    rconn Administrator
    Staff Member

    Joined:
    May 14, 2008
    Messages:
    9,868
    Likes Received:
    83
    It could, but then (1) I'd have to pass it on to all of the (several thousand) functions called by Command() that also check for ^C / ^Break, which (2) would take a loooong time to complete, and (3) would mean that every existing plugin would have to be rewritten & recompiled.
     
  21. vefatica

    Joined:
    May 20, 2008
    Messages:
    7,969
    Likes Received:
    30
    I didn't realize you checked for exceptions in that many places. And I'd rather not re-write a bunch of plugins.
     

Share This Page