Elvis Dukaj
Elvis Dukaj

Reputation: 7368

C++ - Best way to exit from main

In my main function there are some objects that do works in background until the destructor is called like in the following snippet:

#include <iostream>

class MyBackground {
public:
    MyBackground()
    {
        m_workerThread = std::thread(&MyBackground::work, this);
    }
    ~MyBackground()
    {
        g_exit = true;
        workerThread.join();
    }

private:
    void work()
    {
        while(!m_exit);
    }

private:
    std::atomic<bool> m_exit{false};
    std::thread m_workerThread;
};


int main(int argc, char* argv[])
{
    MyBackground object;

    // here ther's some async background work
    return EXIT_SUCCESS;

    // ~MyBackground -> here threads are stopped
}

I need a way to block main until some extern signal occurs. My first attempt was something like this:

#include <csignal>
#include <iostream>
#include <thread>

using namespace std

atomic<bool> g_Exit{false};

void signalExit(int)
{
    g_Exit = true;  
}

int main(int argc, char* argv[])
{
    signal(SIGINT, signalExit);
    signal(SIGTERM, signalExit);

    MyBackground object;

    while (!g_Exit)
        this_thread::sleep_for(chrono::seconds{1});

    // here ther's some async background work
    return EXIT_SUCCESS;

    // ~MyBackground -> here threads are stopped
}

But I'm not sure this works well. I thought is better use condition_variables like the following snippet:

#include <csignal>
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>

using namespace std

bool g_exitFlag = false;
condition_variable g_exitCondition;
mutex g_exitMutex;

using Lock = unique_lock<mutex>;

void signalExit(int)
{
    Lock lock{g_exitMutex};
    g_exitFlag = true;
    g_exitCondition.notify_one();
}

int main(int argc, char* argv[])
{
    signal(SIGINT, signalExit);
    signal(SIGTERM, signalExit);

    MyBackground object;

    Lock lock{g_exitMutex};
    g_exitCondition.wait(lock, [](){return g_exitFlag;});

    // here ther's some async background work
    return EXIT_SUCCESS;

    // ~MyBackground -> here threads are stopped
}

Which of the best implementations is correct. Are them correct? I'm not "experts" in multithreading programming.

Upvotes: 2

Views: 1320

Answers (2)

Some programmer dude
Some programmer dude

Reputation: 409166

Why not a slightly different design where your background worker thread isn't created and destroyed by the MyBackground class, but the thread is created in the main function? Then the main function can simply call join on the thread before it exits, and it will block until the background thread is done.

Something like

class MyBackground {
public:
    void work()
    {
        while(!m_exit)
        {
            // Do background work
        }
    }

private:
    std::atomic<bool> m_exit{false};
};

int main()
{
    MyBackground background;
    std::thread background_thread(&MyBackground::work, background);

    // Do foreground work

    background.join();  // Wait for background to exit

    // Implicit return
}

Another solution, if the thread is supposed to continue when the main function returns, is to detach the thread. Then it will work independent of the main thread, and the process will not actually exit until the thread has exited.

Note that this requires the main function to not just return or exit, as that will end the process including killing all threads. Instead you need to just exit the "main" thread. Unfortunately this is not possible using the standard C++ thread interface, but you have to use platform native functions. For example on POSIX systems (like macOS and Linux) you use pthread_exit.

Upvotes: 4

ObliteratedJillo
ObliteratedJillo

Reputation: 5156

You can simply use a std::weak_ptr in main() to check if the strong ref count is nil to terminate. Pass the only strong reference pointer to other threads to enable main() to exit smoothly. Use std::conditon_variable instead to avoid closing threads waiting on condition_variable to reduce CPU usage during polling for checking if the weak_ptr expires or not.

void worker_thread(std::shared_ptr<int>& pulse)
{
    // do something
    std::this_thread::sleep_for(std::chrono::seconds(2));

    pulse.reset(); // reset to terminate main() too from any other threads
  }

void main()
{
    std::shared_ptr<int> pulse = std::make_shared<int>(1);
    std::weak_ptr<int> weak_pulse(pulse);

    std::thread([&](){ worker_thread(pulse); }).detach();

    while (auto strong = weak_pulse.lock())
        std::this_thread::sleep_for(std::chrono::milliseconds(100));

}

Upvotes: 0

Related Questions