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

#1
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
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;
}
 
#6
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(...).
 

rconn

Administrator
Staff member
May 14, 2008
10,588
97
#7
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?
Yes -- use an exception handler, and ignore the return.

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(...).
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 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?​
Yes -- use an exception handler, and ignore the return.
I don't know what you mean. try {Command();} catch(INT i){} doesn't help. Command() still aborts when Ctrl-C is pressed.
 
#10
It's not a good idea to have an empty catch block -- the compiler tends to optimize it away.
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?
 
#12
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.
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?
 
#14
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.
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.
 

rconn

Administrator
Staff member
May 14, 2008
10,588
97
#15
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.
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
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.
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.]
 

rconn

Administrator
Staff member
May 14, 2008
10,588
97
#17
No.
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.]
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
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?
 

rconn

Administrator
Staff member
May 14, 2008
10,588
97
#19
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?
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
 

rconn

Administrator
Staff member
May 14, 2008
10,588
97
#20
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?
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
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.
I didn't realize you checked for exceptions in that many places. And I'd rather not re-write a bunch of plugins.