Non-blocking background thread

Hello,

for learning purposes I'm trying to connect to an IRC channel, which it works somehow, but there's a problem with the thread (Boost.Thread). I need a background thread since there's a GUI (wxWidgets) and I can't block the interface.

I have a button "Connect", which connects to the IRC server. The idea is keep connected to the server and always process the messages I read from the server, so .join() wouldn't be useful because there's no "end" for that thread.

I've tried .detach() which detaches (D'oh) the thread but it's like it never reaches the end, so I don't know what's going on with it.

This a log I have (this is what .join() let me get from the server, which it's what I expected. I cleaned it a little bit)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
.. more log...
:irc.ku.cx NOTICE AUTH :*** Looking up your hostname...

:irc.ku.cx NOTICE AUTH :*** Checking Ident

... more log...

Entre a HandleRecieve
The operation completed successfully
ERROR :Closing Link: XXXXXXXXXXXXXXXXXXX (Registration timed out)

 AUTH :*** Checking Ident

Entre a HandleRecieve
End of file


But when I use .detach() it gets "stuck" in this

1
2
3
4
5
6
7
8
9
10
 Entre a HandleResolve
The operation completed successfully
Entre a HandleConnect
The operation completed successfully
ап(
Entre a HandleRecieve
The operation completed successfully
:irc.ku.cx NOTICE AUTH :*** Looking up your hostname...

:irc.ku.cx NOTICE AUTH :*** Checking Ident


Since I'm not sending the values the server requests at some point it kicks me out as you can see with .join, but with .detach I never reach that point.

I have to keep that thread alive until the app ends and .join would block the app so it wouldn't work for this case, I think so.

The code of my app is a much more simplified version of these examples:
http://www.boost.org/doc/libs/1_50_0/doc/html/boost_asio/example/chat/chat_client.cpp
http://www.boost.org/doc/libs/1_41_0/doc/html/boost_asio/example/http/client/async_client.cpp

This is the code for the button (it doesn't say much)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void IRCBotGUIFrame::ConnectToChat(wxCommandEvent& event)
{
    try
    {
        boost::asio::io_service io_service;
        IRCChat irc(io_service, "irc.rizon.net", "6667");

        //io_service.run();
        boost::thread IRCThread(
                boost::bind(&boost::asio::io_service::run, &io_service));
        //IRCThread.detach();
        IRCThread.join();
    }
    catch(std::exception& e)
    {
        std::cout << "Exception : " << e.what() << std::endl;
    }
}



So I would like to know how can I make a background thread that keeps always communicating with the server, ping-pong with it and get all the messages it sends me without blocking the app (like every IRC client does but I can't find the way).

PS : Sorry for my english and for the extremely long post.
What if you won't use detach() and join()? Only creating new thread by new operator.
Thanks for answering, I tried it that way and it happens the same with .detach(), it doesn't block the app, the logging gets stuck in the first call to the server and when I try to close the app or try to do something else with it, it crashes with the message : "The app is not responding".
I second Konstantin2's approach. This is not a problem of join/detach but rather is a problem of scope. Your thread object's scope is not broad enough. Once your thread object goes out of scope, the thread will be automatically join()'d. To avoid that, you'll need to give it a broader scope (which can be done by putting it on the heap with new).

I tried it that way and it happens the same with .detach(), it doesn't block the app, the logging gets stuck in the first call to the server and when I try to close the app or try to do something else with it, it crashes with the message : "The app is not responding".


You must be doing something wrong, then. Can you post what you're doing when using new?
Sure

1
2
3
4
        boost::asio::io_service io_service;
        IRCChat irc(io_service, "irc.rizon.net", "6667");
        boost::thread* IRCThread =
            new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));


This produces the same result as this one :

1
2
3
4
5
        boost::asio::io_service io_service;
        IRCChat irc(io_service, "irc.rizon.net", "6667");
        boost::thread* IRCThread =
            new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
        IRCThread->detach();


Or this one :

1
2
3
4
        boost::asio::io_service io_service;
        IRCChat irc(io_service, "irc.rizon.net", "6667");
        boost::thread IRCThread (boost::bind(&boost::asio::io_service::run, &io_service));
        IRCThread.detach();


With any of these I lost the control of the thread.

To be more specific, I'll post the code inside wxButton, it's really simple

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void IRCBotGUIFrame::ConnectToChat(wxCommandEvent& event)
{
    std::ofstream file("log_irc.txt", std::ios_base::app);
    try
    {
        boost::asio::io_service io_service;
        IRCChat irc(io_service, "irc.rizon.net", "6667");
        boost::thread* IRCThread =
            new boost::thread(boost::bind(&boost::asio::io_service::run, &io_service));
    }
    catch(std::exception& e)
    {
        file << "ConnectToChat" << "\n";
        file << e.what() << "\n";
        std::cout << "Exception : " << e.what() << std::endl;
    }
    file.close();
}


And the relevants parts of the class IRCChat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//class def
class IRCChat
{
public:
    IRCChat(boost::asio::io_service& io_service,
            const std::string& server, const std::string& port) :
                resolver_(io_service), socket_(io_service)
    {
        boost::asio::ip::tcp::resolver::query query(boost::asio::ip::tcp::v4(), server, port);

        resolver_.async_resolve(query,
            boost::bind(&IRCChat::HandleResolve, this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::iterator));
    }
    ~IRCChat(){}

private:

    void HandleConnect(const boost::system::error_code&);
    void HandleRecieve(const boost::system::error_code&);
    void HandleResolve(const boost::system::error_code&, 
boost::asio::ip::tcp::resolver::iterator);
//... more irrelevant code 


And the class impl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
void IRCChat::HandleConnect(const boost::system::error_code& error) 
{
    std::ofstream file("log_irc.txt", std::ios_base::app);
    file << "Entre a HandleConnect" << "\n";
    file << error.message() << "\n";
    if (!error)
    {
        std::string pass = "PASS asdf12345\r\n";
        std::string nick = "NICK test123\r\n";
        std::string user = "USER test123 testhost 127.0.0.1 :test123\r\n";
        std::string join = "JOIN #test_channel\r\n";
        boost::asio::async_write(socket_, 
boost::asio::buffer(pass, pass.size()), boost::asio::transfer_all());
        boost::asio::async_write(socket_, 
boost::asio::buffer(nick, nick.size()), boost::asio::transfer_all());
        boost::asio::async_write(socket_, 
boost::asio::buffer(user, user.size()), boost::asio::transfer_all());
        boost::asio::async_write(socket_,
 boost::asio::buffer(join, join.size()), boost::asio::transfer_all());

        socket_.async_receive(boost::asio::buffer(buffer.data(), buffer.size()),
                boost::bind(&IRCChat::HandleRecieve, this, boost::asio::placeholders::error));
        file << buffer.data() << "\n";
    }
    else
    {
                file << error.message() << "\n";
      std::cout << "Error: " << error.message() << "\n";
    }
    file.close();
}

void IRCChat::HandleRecieve(const boost::system::error_code& error)
{
    std::ofstream file("log_irc.txt", std::ios_base::app);
    file << "Entre a HandleRecieve" << "\n";
    file << error.message() << "\n";
    if (!error)
    {
        socket_.async_receive(boost::asio::buffer(buffer.data(), buffer.size()),
                boost::bind(&IRCChat::HandleRecieve, this, boost::asio::placeholders::error));
        file << buffer.data() << "\n";
    }
    else
    {
        file << error.message() << "\n";
      std::cout << "Error: " << error.message() << "\n";
    }
    file.close();
}

void IRCChat::HandleResolve(const boost::system::error_code& error,
boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
    std::ofstream file("log_irc.txt", std::ios_base::app);
    file << "Entre a HandleResolve" << "\n";
    file << error.message() << "\n";
    if(!error)
    {
        boost::asio::ip::tcp::endpoint endpoint = *endpoint_iterator;
        socket_.async_connect(endpoint,
            boost::bind(&IRCChat::HandleConnect, this,
                boost::asio::placeholders::error));
    }
    else
    {
                file << error.message() << "\n";
        std::cout << "Error : " << error.message() << std::endl;
    }
        file.close();
}
Last edited on
Well, I finally did it, I have no idea how, just trying things. This is what it worked :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int TestRun()
{
    boost::asio::io_service io_service;
    IRCChat irc(io_service, "irc.rizon.net", "6667");
    io_service.run();
    return 0;
}

void IRCBotGUIFrame::ConnectToChat(wxCommandEvent& event)
{
    std::ofstream file("log_irc.txt", std::ios_base::app);
    try
    {
        boost::thread* IRCThread =
            new boost::thread(&TestRun);
    }
    catch(std::exception& e)
    {
        file << "ConnectToChat" << "\n";
        file << e.what() << "\n";
        std::cout << "Exception : " << e.what() << std::endl;
    }
    file.close();
}


The thread keeps listening and it doesn't block the app. I don't undersand what's the difference (besides the code obviously). Any ideas?

Thanks.
Topic archived. No new replies allowed.