Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invoking a function automatically on std::thread exit in C++11

I want to set up an invocation of a function (or lambda function) to happen automatically when the current thread exits, but I cannot see any way to do it that works with std::thread unless I take over the entire task of thread creation or manually ensure that every thread calls some particular function that I always provide as its very last operation.

Essentially, I want function whose prototype may resemble something like this:

on_thread_exit(const std::function<void()> &func);

Which would perform whatever setup was necessary to ensure that the given function was automatically called when the thread which called on_thread_exit eventually terminates, and without requiring any particular functions to be explicitly called at thread creation or termination.

like image 835
markt1964 Avatar asked Nov 21 '13 04:11

markt1964


People also ask

How do you exit a thread in C++?

The C++11 does not have direct method to terminate the threads. The std::future<void> can be used to the thread, and it should exit when value in future is available. If we want to send a signal to the thread, but does not send the actual value, we can pass void type object.

What happens when a thread calls an exit () function?

The pthread_exit() function terminates the calling thread, making its exit status available to any waiting threads. Normally, a thread terminates by returning from the start routine that was specified in the pthread_create() call which started it.

Does exit () terminate all threads?

Calling the exit subroutine terminates the entire process, including all its threads. In a multithreaded program, the exit subroutine should only be used when the entire process needs to be terminated; for example, in the case of an unrecoverable error.


3 Answers

You can do it using thread_local storage, since C++11 should call the destructors after the thread exits. You'll have to make sure you have a compiler that supports that properly.

#include <stack> 
#include <function>

void on_thread_exit(std::function<void()> func)
{
  class ThreadExiter
  {
    std::stack<std::function<void()>> exit_funcs;
  public:
    ThreadExiter() = default;
    ThreadExiter(ThreadExiter const&) = delete;
    void operator=(ThreadExiter const&) = delete;
    ~ThreadExiter()
    {
      while(!exit_funcs.empty())
      {
        exit_funcs.top()();
        exit_funcs.pop();
      }
    }
    void add(std::function<void()> func)
    {
      exit_funcs.push(std::move(func));
    }   
  };

  thread_local ThreadExiter exiter;
  exiter.add(std::move(func));
}

Basically, this function creates a thread_local object of the above class. This is basically static, except is destroyed when the thread exits. When called, it pushes the function onto the vector, and when its destroyed it runs the functions.

You can use it by calling on_thread_exit() from any thread, which will create the exit object, which will run your function in the reverse order that it was put into the thread queue (feel free to modify as you see fit).

like image 110
Dave S Avatar answered Oct 15 '22 22:10

Dave S


Here is a simplified/shortened version based on Dave S's solution that uses more C++11 features with the following properties:

  • Since ThreadExiter is only used once, we can combine the variable declaration, avoid public/private (class to struct) and remove the copy constructor/assignment operator
  • Replace std::stack with range-based for loop and std::deque
  • Add to the member variable directly instead of having an add() method

Note that this destructs the callback functions from the std::deque only after all have been called, which might or might not be what you want -- if you require the function object to be destroyed after it has been called (and before any other function objects are called), use a stack and pop elements like in Dave's solution).

Code:

#include <functional>
#include <deque>

void
on_thread_exit(std::function<void()> func)
{
    thread_local struct ThreadExiter {
        std::deque<std::function<void()>> callbacks;

        ~ThreadExiter() {
            for (auto &callback: callbacks) {
                callback();
            }
        }
    } exiter;

    exiter.callbacks.emplace_front(std::move(func));
}
like image 41
Thomas Perl Avatar answered Oct 15 '22 21:10

Thomas Perl


You may use BOOST_SCOPE_EXIT

For the the main thread:

void on_thread_exit();

void main( int argc, char** argv )
{
   BOOST_SCOPE_EXIT() // may be one arg, at least, is required 
   {
      on_thread_exit();
   }BOOST_SCOPE_EXIT_END

   ...
}

I leave you to extrapolate any thread !

like image 1
Jean Davy Avatar answered Oct 15 '22 23:10

Jean Davy