12

I'm developing a chat server and I have a question.

How to stop std::thread safely?

it is very easy problem, like this.

thread t(&func);
t.join();

but, if func is has infinite loop, join is not working.

this is my source.

void CServerSocket::AcceptRun(boost::asio::io_service &iosrv)
{
    while (true)
    {
        auto sock = std::make_shared<boost::asio::ip::tcp::socket>(iosrv);
        m_Acceptor->accept(*sock);

        m_SocketList.push_back(std::make_shared<CConnectionSocket>(this, sock));
    }
}

and

CServerSocket::~CServerSocket()
{
    CLogManager::WriteLog("Stopping Server...");
    m_Acceptor->close();
    m_Acceptor.reset();

    // m_AcceptThread.detach(); This is right?

    CLogManager::WriteLog("Server Stoped!");
}

I'm very wondering. Please help me. thank you.

ks1322
  • 33,961
  • 14
  • 109
  • 164
BombPenguin
  • 211
  • 2
  • 3
  • 10
  • 1
    `join` works quite well, actually. The fact that it has documented blocking behaviour doesn't mean that it's the thing not working. – chris Dec 19 '13 at 12:31
  • 1
    I'm fairly sure that closing the acceptor should cause the call to `accept` throw an exception and exit the loop. Don't delete the acceptor until after joining the thread. – Mike Seymour Dec 19 '13 at 12:33
  • Set a condition variable and join, the thread needs to check for the condition variable and cleanup and die if it gets set – Tim Seguine Dec 19 '13 at 12:34
  • Well, I also tried call join(), but there is an error occurred "Debug error! R6010 abort() has been called – BombPenguin Dec 19 '13 at 12:38
  • Mike Seymour// WOW thanks. join() is very Perfect!!! thanks for shared. – BombPenguin Dec 19 '13 at 12:48
  • @BombPenguin: You'll also need to catch the exception somewhere in the thread (but outside the loop), otherwise the program will terminate when it's thrown. – Mike Seymour Dec 19 '13 at 12:51
  • possible duplicate of [Boost::asio - how to interrupt a blocked tcp server thread?](http://stackoverflow.com/questions/11191028/boostasio-how-to-interrupt-a-blocked-tcp-server-thread) – Mike Seymour Dec 19 '13 at 13:15

3 Answers3

9

You can pass a suitable context to your thread(s) which can contain a flag indicating whether it is time to stop. The flag could be a std::atomic<bool>. Obviously, you' d also need to set up communication to not wait indefinitely for data so you have the chance to check the flag once in a while.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • thanks. but, m_Acceptor.accept() is block-mode. if i called that function, it is blocking. What should i do? – BombPenguin Dec 19 '13 at 12:42
  • @BombPenguin: Praying. Unlike managed languages running in a VM, C++ does not allow you to inject code; so if the behavior is not coded (either by you or the library you are using), then it will not happen. Since here you are using Boost, you should review the documentation of the acceptor and socket and check whether you can signal them to terminate all operations from another thread safely. – Matthieu M. Dec 19 '13 at 12:51
3

How to stop std::thread safely?

Stopping the thread safely means that you tell the thread function to stop processing (through a mechanism that is outside std::thread), then wait for the thread to stop.

The response from @DietmarKuhl tells you how to do this. Regarding the acccept being blocking, you must set an option on the socket/acceptor to expire on a timeout. When the accept call returns, you either break the loop (if your loop condition is false) or you call accept again, with a new timeout.

Your timeout value will be a compromise: a small timeout will be more computationally intensive (keep the CPU busy) while giving you a very responsive thread function (one that doesn't block much when you stop the thread).

Community
  • 1
  • 1
utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • 2
    Before going that route, you might want to check if it is just possible to close the socket/finalize the acceptor from another thread instead. – Matthieu M. Dec 19 '13 at 12:52
  • I hadn't even considered that (+1); It's a good solution, but I wouldn't consider it when looking at the code either as it is not explicit in any way (so I'd probably still implement this using a timeout on socket or accept). – utnapistim Dec 19 '13 at 12:54
  • A timeout in the socket is probably a good idea anyway, to avoid hanging indefinitely; the idea of closing/terminating is that it is (relatively) immediate. – Matthieu M. Dec 19 '13 at 13:02
3

I'm fairly sure that accept will exit cleanly, throwing an exception, when you close the acceptor. You should catch the exception so that the thread exits normally:

void CServerSocket::AcceptRun(boost::asio::io_service &iosrv)
try {
    // your loop here, unchanged
} catch (std::exception const & ex) {
    // perhaps log the message, ex.what()
}

and then join the thread after closing the acceptor, but before destroying it:

CServerSocket::~CServerSocket()
{
    CLogManager::WriteLog("Stopping Server...");
    m_Acceptor->close();
    m_AcceptThread.join();
    CLogManager::WriteLog("Server Stopped!");

    // No need to do anything else with m_Acceptor, assuming it's a smart pointer
}

Personally, I'd use asynchronous operations unless there were a compelling reason to use multiple threads. A single thread is much easier to deal with.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644