2

Below is a brief proof of concept I threw together from a larger project I'm working on. The project has a worker thread that is created using CreateThread that monitors a directory for changes. I have certain cleanup code that needs to run like closing handles and freeing some memory.

The application does not run as a background process or service. It is run via the command line and runs until the user either closes the command window or presses Ctrl-C.

Is there a better way to do this? I tried using atexit but that apparently is not being called when the process is killed.

Note that I'm using C, not C++ and am not using MFC, AFX or any other API other than the Windows API. Based on comments, I guess another part of this is how do I properly terminate the application in a multithreaded environment? Is this ok, or should I call ExitProcess within thread_func ?

UPDATE: I've updated with Luke's comment below as to setting a flag to indicate that the application is terminating. I've also changed it to use atexit to call the cleanup function instead of calling it directly.

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

HANDLE thread_handle = INVALID_HANDLE_VALUE;
BOOL time_to_quit = FALSE;

DWORD WINAPI thread_func(LPVOID lpvd)
{
  fwprintf(stdout, L"thread running...\n");
  while (!time_to_quit)
  {
    /* do stuff */
    Sleep(10);
  }

  return 0;
}

void cleanup()
{
  /* clean up code here that needs to be run at exit */
  fwprintf(stdout, L"cleaning up...\n");
  CloseHandle(thread_handle);
}

BOOL WINAPI console_handler(DWORD event)
{
    time_to_quit = TRUE;
    return FALSE;
}

int wmain(int argc, wchar_t* argv[])
{
  DWORD thread_id = 0;

  thread_handle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)thread_func, NULL, 0, &thread_id);
  if (thread_handle != NULL) 
  {
    atexit(cleanup);
    fwprintf(stdout, L"thread created...\n");
    SetConsoleCtrlHandler((PHANDLER_ROUTINE)console_handler, TRUE);
    WaitForSingleObject(thread_handle, INFINITE);
  }

  return 0;
}
Casey
  • 12,070
  • 18
  • 71
  • 107
  • As you have discovered, [`atexit` is only called when `exit` is called or the program returns from `main`](http://stackoverflow.com/questions/9402254/how-do-you-run-a-function-on-exit-in-c/9402278#9402278). Is `SetConsoleCtrlHandler` not working for you? – Seth Carnegie Mar 20 '12 at 23:38
  • CreateThread returns NULL on failure, not INVALID_HANDLE_VALUE. – Jim Rhodes Mar 20 '12 at 23:40
  • Ah yeah, NULL thanks. @Seth, Part of the problem is how I should exit the application. I would prefer not to have a bunch of calls to `cleanup` all over the place. I've updated the question with some clarifications. – Casey Mar 20 '12 at 23:43
  • 1
    @Casey it looks like you only have one call to `cleanup`. Where else were you calling it? Also your `switch` is way too big, you don't need `break` after a `return` and you can use fallthrough to avoid having to `return FALSE` on every one you want to do that on, and `return TRUE` can be in `default`. In fact, you really only need one `if`: `if (event == CTRL_C_EVENT) cleanup(); return FALSE;` – Seth Carnegie Mar 20 '12 at 23:45
  • @Seth thanks yeah, I updated the code. It can be even simpler than that. – Casey Mar 20 '12 at 23:54
  • I take it the way I'm going about doing this is a perfectly legitimate method on windows? I just want to ensure that my cleanup code is executed correctly. – Casey Mar 20 '12 at 23:54
  • There's no point in starting a thread and then waiting for it to exit. Just call thread_func() directly. – Hans Passant Mar 21 '12 at 00:34
  • Freeing memory is redundant, and closing handles almost always redundant, if you're about to exit the process anyway. – Harry Johnston Mar 21 '12 at 01:07
  • 1
    It's a common pattern. On startup you clear a flag (set when application is shutting down) and create your worker threads. Then you add your CTRL handler which sets the flag to indicate that the application should shut down. In your worker thread loop you occasionally check that flag, and when it is set you break out of your loop and return from the thread proc. Then your main thread waits for all of the worker threads to exit. – Luke Mar 21 '12 at 03:34
  • Luke..duh... that works well. Thanks! – Casey Mar 21 '12 at 03:43
  • Silly question: The OS will close files and free memory for you when you hit CTRL-C without your doing anything. Is there something else you need to be done on exit? – Larry Osterman Mar 21 '12 at 06:09
  • See e.g. [this old SO answer](http://stackoverflow.com/a/1641307/440558) how to catch `CTRL-C` in Windows. – Some programmer dude Mar 21 '12 at 07:02
  • Larry, just can't stand the idea of not cleaning up after myself... :-) – Casey Mar 21 '12 at 12:03

1 Answers1

0

In a general sense, you could might use signal()

#include <stdio.h>
#include <stdlib.h>    /* exit() */
#include <signal.h>    /* signal() */
#include <unistd.h>    /* sleep() */

void my_sigtrap(int sig)
{
    printf ("\n"
        "Signal %d == %d received.\n"
        "Bye bye\n",
        sig, SIGINT);
    exit(0);
}

int main(void)
{
    void (*orig_sig_fun)(int);

    orig_sig_fun = signal(SIGINT, my_sigtrap);

    if (orig_sig_fun == SIG_IGN) {
        printf("Ignored by default\n");
        signal(SIGINT, SIG_IGN);
    } else if (orig_sig_fun == SIG_DFL) {
        printf("Signal handled by default.\n");
    } else if (orig_sig_fun == SIG_ERR) {
        perror("Failed to set signal\nNo trap\n --");
    }

    setbuf(stdout, NULL); /* Turning off buffering */
    putchar('.');
    while (1) {
        putchar('.');
        sleep(1);
    }

    return 0;
}
Morpfh
  • 4,033
  • 18
  • 26