0

c++ win 32 application . VS 2013 I am making use of a 3rd party library. I want to call 3rd party library's function in a background thread. I then also want to eventually turn it off. I suspect I dont give third party enough time to properly shut itself down before I exist the application. How do I ensure the detached task I started on a separate thread is done before I exit the main().

//this class interfaces with the third part and runs on a separate thread
class ThirdParty
{

    void Start(std::string filename)
    {
        MyApplication application;
        FIX::SessionSettings settings(filename);
        FIX::FileStoreFactory storeFactory(settings);
        FIX::ScreenLogFactory logFactory(settings);
        FIX::SocketAcceptor acceptor(application, storeFactory, settings, logFactory);
        acceptor.start(); //this third party internally starts new threads and does stuff thats transparent to consumer like myself.
        while (m_runEngine) 
                {}


//this shutsdown a few things and cant execute instantaneously
//This does not finish execution and main() already ends.
        acceptor.stop();
    }
    void Stop()
    {
        m_runEngine = false;
    }
private:
bool m_runEngine{ true };

}

Here is my main() in a win32 application

int _tmain(int argc, _TCHAR* argv[])
{
    std::wstring arg = argv[1];
    std::string filename = std::string(arg.begin(), arg.end());
    ThirdParty myprocess;

    std::thread t(&ThirdParty::Start, &myprocess, filename);

    t.detach();

    while (true)
    {
        std::string value;
        std::cin >> value;
        if (value == "quit")
            break;
    }

    myprocess.Stop(); //This line will execute really fast and application will exit without allowing acceptor.stop() to properly finish execution
//How can I ensure acceptor.stop() has finished execution before I move on to the next line and finish the application  

    return 0;
}
bsobaid
  • 955
  • 1
  • 16
  • 36
  • 3
    Do not detach your thread. Instead, leave it joinable and join before exiting. Better yet, since you are on Windows, get native thread handle, wait for event on it with meaningful timeout, and join if event is triggered. Otherwise just report error and exit. – SergeyA May 09 '16 at 20:25
  • i get it. so instead of t.detach() I call t.joinable() and then in the end after myprocess.stop() I call t.join()? that does seem to work in my debugger. I will try the other option also. – bsobaid May 09 '16 at 20:28
  • No need to call t.joinable(), you know it is joinable. The problem with native join is that if your thread is unresponsive (stuck in a loop, locked on resource, etc) your application will be stuck as well. This is why it worth to give it some time to shut down, but if it can't, you just exit forcefuly (with error). – SergeyA May 09 '16 at 20:30
  • unrelated, but is there a way I can display messages on the main console window from another thread possible running from in another project of the same solution? to update status etc – bsobaid May 09 '16 at 20:52
  • You shoulld be able to simply print them? Sorry, I am not that familiar with Windows. – SergeyA May 09 '16 at 20:58

2 Answers2

1

Do not make you thread detached, so that you can wait for it to end using thread::join():

//t.detach()   do not detach thread
...
myprocess.Stop();
t.join();    // wait for t to end
shrike
  • 4,449
  • 2
  • 22
  • 38
  • though Sergey answered it first, but I can only upvote a comment, so i will check your correct answer as the answer. – bsobaid May 09 '16 at 21:06
  • I'm sure Sergey will blame neither me nor you :) – shrike May 09 '16 at 21:52
  • 1
    Unless you make the m_runEngine either an `std::atomic`, or guard it with a mutex, there's actually no guarantee that it will ever be read. The compiler is free to look at that code and say "oh... there's no way for this variable to change in this loop - optimize to while(true) {}". `std::atomic` will basically mean the variable is always read from the memory location, mutex is heavier weight, but may be closer to what people expect to read. – Charlie May 11 '16 at 01:06
  • @Charlie: you're right, I missed this point; would making `m_runEngine` `volatile` be an acceptable alternative to std::atomic ? – shrike May 11 '16 at 08:26
  • @shrike volatile doesn't quite do what you want in this case. Technically it'd be undefined behavior because volatile only has influence on the optimizer within a single thread. It's not meant for communication between two threads. (Generally, unless you're writing device drivers, `volatile` probably isn't what you want.) – Charlie May 13 '16 at 00:38
0

I think the following example illustrates the interesting aspects of thread join.

void pause_thread(int n, std::string lbl)
{
  std::this_thread::sleep_for (std::chrono::seconds(n));
  std::cout << lbl << " pause of " << n << " seconds ended" << std::endl;
}

int t403(void)  // in context of thread main
{
   std::cout << "Spawning 3 threads...\n" << std::flush;
   std::thread t1 (pause_thread, 3, "t1");
   std::thread t2 (pause_thread, 2, "t2");
   std::thread t3 (pause_thread, 1, "t3");
   std::cout << "Done spawning threads, "
      "Note that the threads finish out-of-order. \n"
      "Now 'main' thread waits for spawned threads to join:\n" << std::flush;

   t1.join(); std::cout << "join t1  " << std::flush;
   t2.join(); std::cout << "join t2  " << std::flush;
   t3.join(); std::cout << "join t3  " << std::flush;
   std::cout << "completed join \n"
      "note: \n - join sequence is in-order, but finish sequence is out-of-order\n"
      " - inference:  the threads waited in join main. "<< std::endl;

   return(0);
}

Note that the threads are spawned in order: t1, t2, t3.

Note that the threads end in a different order.

But the join is still in the launch order, because that is what main waits for.

Using 'std::flush()' presents the timeline that has been chosen slow enough for human vision.

2785528
  • 5,438
  • 2
  • 18
  • 20