Home > Software design >  In Win32 C , how to WaitForSingleObject and Detect Ctrl-C at the same time?
In Win32 C , how to WaitForSingleObject and Detect Ctrl-C at the same time?

Time:12-12

In Win32 C , How to WaitForSingleObject and Detect Ctrl-C at the same time?

I tried the following console application by compiling it in the Code::Blocks C compiler for windows.

Then, I tried pressing Control-C many times while running... it basically doesn't call the control-c handler while the main thread is in "WaitForSingleObject".

Is there a way to fix this?

Eventually, I want my Control-C handler to kill the secondary thread using TerminateThread and return control to mainthread breaking WaitForSingleObject... But, because of the wait the second thread is written i can't change any of the code...

#include <windows.h>
#include <stdio.h>
#include <iostream>
#include <atomic>

using namespace std;

BOOL  WINAPI fun1(DWORD id);
DWORD WINAPI fun2(void*);

atomic<DWORD> threadid {0};

int main()
{
    DWORD threadid1;
    cout << "Hello world!" << endl;
    SetConsoleCtrlHandler(fun1, TRUE);

    HANDLE H1 = CreateThread(NULL, 0, fun2, 0, 0, &threadid1);

    threadid = threadid1;

    WaitForSingleObject(H1, INFINITE);
    return 0;
}

BOOL WINAPI fun1(DWORD id)
{
    Beep(750, 300);
    printf("CtrlHandler:(%ld)\n", id);
    
    if (threadid != 0) {
        HANDLE H2 = OpenThread(THREAD_TERMINATE, FALSE, threadid);
        if (H2) {
            //TerminateThread(H2, 0);
            //threadid = 0;
            CloseHandle(H2);

        }
    }

    return TRUE;
}

DWORD WINAPI fun2(void*)
{
    // This thread will eventually do some work... 
    //   and I don't want to rewrite this code...
    //   to check for a flag from another thread...
    int count = 0;
    while(1) {
        printf("count: %d\n", count);
        Sleep(1000);
    }
    return 0;
}

CodePudding user response:

I cleaned up your code slightly and it seems like everything is working fine. When I type Ctrl C, I see that fun1 can run multiple times while the main thread is running WaitForSingleObject.

#include <windows.h>
#include <stdio.h>
#include <atomic>

std::atomic<DWORD> threadid {0};

BOOL WINAPI fun1(DWORD id) {
  printf("fun1: %ld\n", id);
  fflush(stdout);
  Beep(750, 100);

  if (threadid != 0) {
    HANDLE H2 = OpenThread(THREAD_TERMINATE, FALSE, threadid);
    if (H2) { CloseHandle(H2); }
  }
  return 1;
}

DWORD WINAPI fun2(void *) {
  unsigned int count = 0;
  while(1) {
    count  ;
    printf("count: %d\n", count);
    fflush(stdout);
    Sleep(4000);
  }
  return 0;
}

int main() {
  printf("Hello world!\n");
  fflush(stdout);
  SetConsoleCtrlHandler(fun1, TRUE);

  DWORD threadid1;
  HANDLE H1 = CreateThread(NULL, 0, fun2, 0, 0, &threadid1);
  threadid = threadid1;

  printf("Waiting for single oblect.\n");
  fflush(stdout);
  WaitForSingleObject(H1, INFINITE);
  printf("Done waiting for single oblect.\n");
  fflush(stdout);
  return 0;
}

Example output:

Hello world!
Waiting for single oblect.
count: 1
count: 2
fun1: 0
fun1: 0
fun1: 0
fun1: 0
fun1: 0
fun1: 0
fun1: 0
fun1: 0
fun1: 0
count: 3
count: 4

I compiled the code in MSYS2, targeting 64-bit Windows, with this command:

g   -std=gnu  20 -Wall -Wextra test.cpp

Your original code for fun1 was beeping before printing, and it wasn't flushing the stdout buffer, so maybe you thought the code wasn't actually running or that it was getting delayed.

CodePudding user response:

A SetConsoleCtrlHandler() handler gets run by the OS in its own thread. This is stated as much in the documentation:

https://docs.microsoft.com/en-us/windows/console/handlerroutine

An application-defined function used with the SetConsoleCtrlHandler function. A console process uses this function to handle control signals received by the process. When the signal is received, the system creates a new thread in the process to execute the function.

You need to have that signal thread notify your worker thread to terminate itself, you can't (safely) just terminate the thread directly (ie, DO NOT use TerminateThread()).

Try this:

#include <windows.h>
#include <cstdio>
#include <iostream>
#include <atomic>

using namespace std;

BOOL  WINAPI fun1(DWORD);
DWORD WINAPI fun2(void*);

atomic<bool> exitThread {false};

int main()
{
    cout << "Hello world!" << endl;
    SetConsoleCtrlHandler(fun1, TRUE);

    HANDLE H1 = CreateThread(NULL, 0, fun2, 0, 0, NULL);
    if (H1)
    {
        WaitForSingleObject(H1, INFINITE);
        CloseHandle(H1);
    }

    return 0;
}

BOOL WINAPI fun1(DWORD id)
{
    Beep(750, 300);
    printf("CtrlHandler:(%lu)\n", id);
    exitThread = true;
    return TRUE;
}

DWORD WINAPI fun2(void*)
{
    // This thread will eventually do some work...
    int count = 0;
    while (!static_cast<bool>(exitThread)) {
        printf("count: %d\n", count  );
        Sleep(1000);
    }
    return 0;
}

However, do note that creating a thread just to wait on it is a waste of a thread. You may as well just do your work in main() directly instead, eg:

#include <windows.h>
#include <cstdio>
#include <iostream>
#include <atomic>

using namespace std;

BOOL  WINAPI fun1(DWORD);

atomic<bool> exitApp {false};

int main()
{
    cout << "Hello world!" << endl;
    SetConsoleCtrlHandler(fun1, TRUE);

    // This will eventually do some work...
    int count = 0;
    while (!static_cast<bool>(exitApp)) {
        printf("count: %d\n", count  );
        Sleep(1000);
    }

    return 0;
}

BOOL WINAPI fun1(DWORD id)
{
    Beep(750, 300);
    printf("CtrlHandler:(%lu)\n", id);
    exitApp = true;
    return TRUE;
}
  • Related