5

I have a simple client /server application the code of which is mentioned below. Please run the server in one shell and the client in another shell in linux. First start the server and then the client. When the server is done with it's work, it crashes with following exception:

terminate called after throwing an instance of 'std::system_error' what(): Resource deadlock avoided

This happens from the line m_thread->join() from inside the function Service::HandleClient I have no clue on what's going on.. Can someone please check the code.. I just want that the server application should also get closed correctly the way the client application got closed.

**Server Code : **

#include <boost/asio.hpp>

#include <thread>
#include <atomic>
#include <memory>
#include <iostream>

using namespace boost;

class Service {
public:
    Service(){}

    void StartHandligClient(
        std::shared_ptr<asio::ip::tcp::socket> sock) {

        m_thread.reset(new std::thread (([this, sock]() {
            HandleClient(sock);
        })) );

    }

private:
    void HandleClient(std::shared_ptr<asio::ip::tcp::socket> sock) {
        while(1)
        {
            try {
                asio::streambuf request;
                std::cout << "Waiting to read \n";
                asio::read_until(*sock.get(), request, '\n');

                std::string s( (std::istreambuf_iterator<char>(&request)), std::istreambuf_iterator<char>() );
                std::cout << "Server got : " << s << "\n";

                // Emulate request processing.
                int i = 0;
                while (i != 1000000)
                    i++;

                std::this_thread::sleep_for(
                        std::chrono::milliseconds(500));

                // Sending response.
                std::string response = "Response\n";
                asio::write(*sock.get(), asio::buffer(response));
            }
            catch (system::system_error &e) {
                boost::system::error_code ec = e.code();
                if(ec == asio::error::eof)
                {
                    std::cout << "Breaking loop \n";
                    break;
                }
                std::cout << "Error occured! Error code = "
                    << e.code() << ". Message: "
                    << e.what();
            }
        }

        m_thread->join();

        // Clean-up.
        delete this;
    }
    std::unique_ptr<std::thread> m_thread;
};

class Acceptor {
public:
    Acceptor(asio::io_service& ios, unsigned short port_num) :
        m_ios(ios),
        m_acceptor(m_ios,
        asio::ip::tcp::endpoint(
        asio::ip::address_v4::any(),
        port_num))
    {
        m_acceptor.listen();
    }

    void Accept() {
        std::cout << "Server Accept() \n" << std::flush;
        std::shared_ptr<asio::ip::tcp::socket>
            sock(new asio::ip::tcp::socket(m_ios));

        std::cout << "BEFORE calling acceptor's accept function \n" << std::flush;
        m_acceptor.accept(*sock.get());
        std::cout << "AFTER calling acceptor's accept function \n" << std::flush;


        (new Service)->StartHandligClient(sock);
    }

    void close()
    {
        std::cout << "Inside Acceptor.close() \n" << std::flush;
        m_acceptor.close();
    }

private:
    asio::io_service& m_ios;
    asio::ip::tcp::acceptor m_acceptor;
};

class Server {
public:
    Server() : m_stop(false) {}

    void Start(unsigned short port_num) {
        m_thread.reset(new std::thread([this, port_num]() {
            Run(port_num);
        }));
    }

    void Stop() {
        m_stop.store(true);
        m_thread->join();
    }

private:
    void Run(unsigned short port_num) {
        Acceptor acc(m_ios, port_num);

        while (!m_stop.load()) {
            std::cout << "Server accept\n" << std::flush;
            acc.Accept();
        }
        acc.close();
    }

    std::unique_ptr<std::thread> m_thread;
    std::atomic<bool> m_stop;
    asio::io_service m_ios;
};

int main()
{
    unsigned short port_num = 3333;

    try {
        Server srv;
        srv.Start(port_num);

        std::this_thread::sleep_for(std::chrono::seconds(4));

        srv.Stop();
    }
    catch (system::system_error &e) {
        std::cout << "Error occured! Error code = "
            << e.code() << ". Message: "
            << e.what();
    }

    return 0;
}

**Client Code : **

#include <boost/asio.hpp>
#include <iostream>

using namespace boost;

class SyncTCPClient {
public:
    SyncTCPClient(const std::string& raw_ip_address,
        unsigned short port_num) :
        m_ep(asio::ip::address::from_string(raw_ip_address),
        port_num),
        m_sock(m_ios) {

        m_sock.open(m_ep.protocol());
    }

    void connect() {
        m_sock.connect(m_ep);
    }

    void close() {
        m_sock.shutdown(
            boost::asio::ip::tcp::socket::shutdown_both);
        m_sock.close();
    }

    std::string emulateLongComputationOp(
        unsigned int duration_sec) {

        std::string request = "EMULATE_LONG_COMP_OP "
            + std::to_string(duration_sec)
            + "\n";

        sendRequest(request);
        return receiveResponse();
    };

private:
    void sendRequest(const std::string& request)
    {
        std::cout << "Inside sendRequest : " << request << "\n";
        asio::write(m_sock, asio::buffer(request));
    }

    std::string receiveResponse() {
        asio::streambuf buf;
        asio::read_until(m_sock, buf, '\n');

        std::istream input(&buf);

        std::string response;
        std::getline(input, response);

        return response;
    }

private:
    asio::io_service m_ios;

    asio::ip::tcp::endpoint m_ep;
    asio::ip::tcp::socket m_sock;
};

int main()
{
    const std::string raw_ip_address = "127.0.0.1";
    const unsigned short port_num = 3333;

    try {
        SyncTCPClient client(raw_ip_address, port_num);

        // Sync connect.
        client.connect();

        std::cout << "Sending request to the server... " << std::endl;
        std::string response = client.emulateLongComputationOp(10);

        std::cout << "Response received: " << response << std::endl;
        sleep(2);

        // Close the connection and free resources.
        client.close();
    }
    catch (system::system_error &e) {
        std::cout << "Error occured! Error code = " << e.code()
            << ". Message: " << e.what();

        return e.code().value();
    }

    return 0;
}
Nishant Sharma
  • 341
  • 2
  • 5
  • 17
  • @rafix07 please undelete your fine response, as it contains important discussion. People will now simply waste time coming up with the exact same things you already diagnosed. – sehe Jan 03 '18 at 14:14
  • Re: [`_thanks @ rafix07.. no crashes any more.. But, I have to do Ctrl-C on the shell of server application to exit the program.. Why does it not exit ? May be because Acceptor::close() does not get called.. How to make that happen ? Pls suggest..._`](https://stackoverflow.com/questions/48076068/crash-terminate-called-after-throwing-an-instance-of-stdsystem-error-what#comment83129833_48076580) I will just suggest looking at the MANY samples online, instead of making people debug your code: https://stackoverflow.com/questions/46935025/boost-asio-single-tcp-server-many-clients/46935127#46935127 – sehe Jan 03 '18 at 14:17
  • @sehe .. can you run the code and let me know how to overcome the crash that I mentioned ? – Nishant Sharma Jan 03 '18 at 15:15
  • Actually @sehe ... I did look at some examples and most of them were asynchronous I/O related... What I posted above was for synchronous I/O using thread ... Request you to pls take time and help out.... – Nishant Sharma Jan 03 '18 at 15:30

1 Answers1

6

@sehe .. can you run the code and let me know how to overcome the crash that I mentioned ? – Nishant Sharma

Actually, no I won't. The problem has already been analyzed: you can't join the current thread (it would deadlock).

But I can do something better:

Grabbing my crystal ball, I can guess you got this example from a particular book, named Boost.Asio C++ Network Programming Cookbook¹, around page 139.

I recognized it after a while when I added up all the code smells (delete this and m_stop.load() tipped me over the edge).

The good news is, I reviewed that code before:

ASIO example code closing socket before it should

You can probably profit from the particular comments I made there.

¹ from packtpub: https://www.packtpub.com/application-development/boostasio-c-network-programming-cookbook

sehe
  • 374,641
  • 47
  • 450
  • 633
  • Thanks @sehe I still have 2 issues : a) I still see that the server application when run does not end and I have to press Ctrl-C to do that.. I expect the Server app to exit cleanly... b) Using detach() on thread is usually not a good practice.. Isn't it ? We should always join a thread at end.. So, can we get rid of that detach() ? – Nishant Sharma Jan 03 '18 at 23:36
  • The detach is not the problem (you can synchronize on the thread's completion using other methods than `join`). Having "runaway" threads or uncompleted threads at program exit is the problem. – sehe Jan 04 '18 at 01:31
  • About the shutdown, you don't have a way to cancel a synchronous accept. You might try (a) boost thread interruption points (don't think/know that will work) (b) connecting from the server itself to make sure the accept returns – sehe Jan 04 '18 at 01:32
  • Here's a proof-of-concept of that last ugly workaround: http://coliru.stacked-crooked.com/a/ef5cc6c3b0b494dc – sehe Jan 04 '18 at 01:46
  • The proof of concept looks good.. I like the idea of connecting to itself. The code has a problem on line 53 .. It should be under the condition of if(joinable()) because detached threads cannot be joined. Do you agree ? – Nishant Sharma Jan 04 '18 at 04:56
  • Also, @sehe, Regarding the comment "The detach is not the problem (you can synchronize on the thread's completion using other methods than join). ... " .... Can you please elaborate more on this ? How to handle uncompleted threads at program exit ? – Nishant Sharma Jan 04 '18 at 04:57