Boost:condition_variable.notify_one() causes segmentation fault 11 exception - thread-safety

I'm trying to run an example of websocket++ that consists in receive messages from websocket clients and broadcast to all connected clients, but i having problems with thread synchronization.
In the code example the method process_messages waits for message on a std:queue
boost::unique_lock<boost::mutex> lock(m_action_lock);
while(m_actions.empty()) {
m_action_cond.wait(lock);
}
And the on_message handler locks the queue before to push a new message received from client, but when it try to notify_one(), the program fail with an Segmentation fault 11.
void on_message(connection_hdl hdl, server::message_ptr msg) {
// queue message up for sending by processing thread
{
boost::unique_lock<boost::mutex> lock(m_action_lock);
m_actions.push(action(MESSAGE,msg));
lock.unlock();
}
m_action_cond.notify_one();
}
The only way that the program works is commenting the wait(lock) but i not sure if this is safe.
Some body could help me to find de segmentation fault cause?
The complete code is:
#include <websocketpp/config/asio_no_tls.hpp>
#include <websocketpp/server.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/condition_variable.hpp>
typedef websocketpp::server<websocketpp::config::asio> server;
using websocketpp::connection_hdl;
using websocketpp::lib::placeholders::_1;
using websocketpp::lib::placeholders::_2;
using websocketpp::lib::bind;
/* on_open insert connection_hdl into channel
* on_close remove connection_hdl from channel
* on_message queue send to all channels
*/
enum action_type {
SUBSCRIBE,
UNSUBSCRIBE,
MESSAGE
};
struct action {
action(action_type t, connection_hdl h) : type(t), hdl(h) {}
action(action_type t, server::message_ptr m) : type(t), msg(m) {}
action_type type;
websocketpp::connection_hdl hdl;
server::message_ptr msg;
};
class broadcast_server {
public:
broadcast_server() {
// Initialize Asio Transport
m_server.init_asio();
// Register handler callbacks
m_server.set_open_handler(bind(&broadcast_server::on_open,this,::_1));
m_server.set_close_handler(bind(&broadcast_server::on_close,this,::_1));
m_server.set_message_handler(bind(&broadcast_server::on_message,this,::_1,::_2));
}
void run(uint16_t port) {
// listen on specified port
m_server.listen(port);
// Start the server accept loop
m_server.start_accept();
// Start the ASIO io_service run loop
try {
m_server.run();
} catch (const std::exception & e) {
std::cout << e.what() << std::endl;
} catch (websocketpp::lib::error_code e) {
std::cout << e.message() << std::endl;
} catch (...) {
std::cout << "other exception" << std::endl;
}
}
void on_open(connection_hdl hdl) {
boost::unique_lock<boost::mutex> lock(m_action_lock);
//std::cout << "on_open" << std::endl;
m_actions.push(action(SUBSCRIBE,hdl));
lock.unlock();
m_action_cond.notify_one();
}
void on_close(connection_hdl hdl) {
boost::unique_lock<boost::mutex> lock(m_action_lock);
//std::cout << "on_close" << std::endl;
m_actions.push(action(UNSUBSCRIBE,hdl));
lock.unlock();
m_action_cond.notify_one();
}
void on_message(connection_hdl hdl, server::message_ptr msg) {
// queue message up for sending by processing thread
boost::unique_lock<boost::mutex> lock(m_action_lock);
//std::cout << "on_message" << std::endl;
m_actions.push(action(MESSAGE,msg));
lock.unlock();
m_action_cond.notify_one();
}
void process_messages() {
while(1) {
boost::unique_lock<boost::mutex> lock(m_action_lock);
while(m_actions.empty()) {
m_action_cond.wait(lock);
}
action a = m_actions.front();
m_actions.pop();
lock.unlock();
if (a.type == SUBSCRIBE) {
boost::unique_lock<boost::mutex> lock(m_connection_lock);
m_connections.insert(a.hdl);
} else if (a.type == UNSUBSCRIBE) {
boost::unique_lock<boost::mutex> lock(m_connection_lock);
m_connections.erase(a.hdl);
} else if (a.type == MESSAGE) {
boost::unique_lock<boost::mutex> lock(m_connection_lock);
con_list::iterator it;
for (it = m_connections.begin(); it != m_connections.end(); ++it) {
m_server.send(*it,a.msg);
}
} else {
// undefined.
}
}
}
private:
typedef std::set<connection_hdl,std::owner_less<connection_hdl>> con_list;
server m_server;
con_list m_connections;
std::queue<action> m_actions;
boost::mutex m_action_lock;
boost::mutex m_connection_lock;
boost::condition_variable m_action_cond;
};
int main() {
broadcast_server server;
// Start a thread to run the processing loop
boost::thread(bind(&broadcast_server::process_messages,&server));
// Run the asio loop with the main thread
server.run(9002);
}

I can reproduce this behavior when Boost is compiled using g++ and libstdc++ but the program linking to it is compiled using clang and libc++. The libstdc++ and libc++ standard libraries are not ABI compatible, so you will need to build everything with one or everything with the other.
Details on how to compile Boost in C++11 mode with clang/libc++:
How to compile/link Boost with clang++/libc++?

Related

How to make a non blocking server

I want to have a non blocking server to read the incoming data in my application.
this is the workflow i want.
void main()
{
// create a socket server
CreateSocket();
while( true )
{
// keep doing the other tasks.
// if we receive a message than process it
}
}
this is the code for the Socket connection
#pragma once
//importing libraries
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
using namespace boost::asio;
using ip::tcp;
using std::cout;
using std::endl;
class con_handler : public boost::enable_shared_from_this<con_handler>
{
private:
tcp::socket sock;
std::string message = "Hello From Server";
enum { max_length = 2048};
char data[max_length];
public:
typedef boost::shared_ptr<con_handler> pointer;
con_handler(boost::asio::io_service& io_service) : sock( io_service) {}
// creating the pointer
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new con_handler(io_service));
}
// socket creation
tcp::socket& socket()
{
return sock;
}
void start()
{
sock.async_read_some(
boost::asio::buffer(data, max_length),
boost::bind(&con_handler::handle_read,
shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void handle_read(const boost::system::error_code& err, size_t bytes_transferred)
{
if (!err) {
cout << data << endl;
}
else {
std::cerr << "error: " << err.message() << std::endl;
sock.close();
}
}
void handle_write(const boost::system::error_code& err, size_t bytes_transferred)
{
if (!err) {
cout << "Server sent Hello message!" << endl;
}
else {
std::cerr << "error: " << err.message() << endl;
sock.close();
}
}
};
////////////////////////////////////////////////////////
#pragma once
#include "TCP_Server.h"
class Server
{
private:
tcp::acceptor acceptor_;
void start_accept()
{
// socket
con_handler::pointer connection = con_handler::create(acceptor_.get_io_service());
// asynchronous accept operation and wait for a new connection.
acceptor_.async_accept(connection->socket(),
boost::bind(&Server::handle_accept, this, connection,
boost::asio::placeholders::error));
}
public:
//constructor for accepting connection from client
Server(boost::asio::io_service& io_service) : acceptor_(io_service, tcp::endpoint(tcp::v4(), 1234))
{
start_accept();
}
void handle_accept(con_handler::pointer connection, const boost::system::error_code& err)
{
if (!err) {
connection->start();
}
start_accept();
}
};
Currently the socket connection is working but it is blocking the other process.
Do i need to have a separate thread for this workflow or i can do it in the same thread.
this is my main function
#include "pch.h"
#include <iostream>
#include "Server.h"
int main()
{
boost::asio::io_service io_service;
Server server(io_service);
io_service.run();
while (true)
{
std::cout << " function is running Running";
}
}
I am able to receive input messages but i never reach the while statement.
i would want while to keep printing and the server to receive message at the same time.

async_write_some callback not called after delay

My callback for async_write_some is not called after a one second sleep. If I am starting an io_service worker thread for every write, why is the callback not being called?
header
boost::system::error_code error_1;
boost::shared_ptr <boost::asio::io_service> io_service_1;
boost::shared_ptr <boost::asio::ip::tcp::socket> socket_1;
connect
void eth_socket::open_eth_socket (void)
{
// 1. reset io services
io_service_1.reset();
io_service_1 = boost::make_shared <boost::asio::io_service> ();
// 2. create endpoint
boost::asio::ip::tcp::endpoint remote_endpoint(
boost::asio::ip::address::from_string("10.0.0.3"),
socket_1_port
);
// 3. reset socket
socket_1.reset(new boost::asio::ip::tcp::socket(*io_service_1));
// 4. connect socket
socket_1->async_connect(remote_endpoint,
boost::bind(
&eth_socket::socket_1_connect_callback,
this, boost::asio::placeholders::error
)
);
// 5. start io_service_1 run thread after giving it work
boost::thread t(boost::bind(&boost::asio::io_service::run, *&io_service_1));
return;
}
write
void eth_socket::write_data (std::string data)
{
// 1. check socket status
if (!socket_1->is_open())
{
WARNING << "socket_1 is not open";
throw -3;
}
// 2. start asynchronous write
socket_1->async_write_some(
boost::asio::buffer(data.c_str(), data.size()),
boost::bind(
&eth_socket::socket_1_write_data_callback,
this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred
)
);
// 3. start io_service_1 run thread after giving it work
boost::thread t(boost::bind(&boost::asio::io_service::run, *&io_service_1));
return;
}
callback
void eth_socket::socket_1_write_data_callback (const boost::system::error_code& error, size_t bytes_transferred)
{
// 1. check for errors
if (error)
{
ERROR << "error.message() >> " << error.message().c_str();
return;
}
if (socket_1.get() == NULL || !socket_1->is_open())
{
WARNING << "serial_port_1 is not open";
return;
}
INFO << "data written to 10.0.0.3:1337 succeeded; bytes_transferred = " << bytes_transferred;
return;
}
test
open_eth_socket();
write_data("Hello"); // callback called
write_data("Hello"); // callback called
write_data("Hello"); // callback called
sleep(1);
write_data("Hello"); // callback not called after sleep
boost::thread t(boost::bind(&boost::asio::io_service::run, *&io_service_1));
That's weird for a number of reasons.
You should not "run" io_services for each operation. Instead, run them steadily while operations may be posted. Optionally use io_service::work to prevent run from returning.
You should not (have to) create threads for each operation. If anything, it's recipe for synchronization issues (Why do I need strand per connection when using boost::asio?)
When running io_service again after it returned (without error) you should call reset() first, as per documentation (Why must io_service::reset() be called?)
You destruct a non-detached thread - likely before it had completed. If you had used std::thread this would even have caused immediate abnormal program termination. It's bad practice to not-join non-detached threads (and I'd add it's iffy to use detached threads without explicit synchronization on thread termination). See Why is destructor of boost::thread detaching joinable thread instead of calling terminate() as standard suggests?
I'd add to these top-level concerns
the smell from using names like socket_1 (just call it socket_ and instantiate another object with a descriptive name to contain the other socket_). I'm not sure, but the question does raise suspicion these might even be global variables. (I hope that's not the case)
throw-ing raw integers, really?
You are risking full on data-races by destructing io_service while never checking that worker threads had completed.
More Undefined Behaviour here:
_sock.async_write_some(
ba::buffer(data.c_str(), data.size()),
You pass a reference to the parameter data which goes out of scope. When the async operation completes, it will be a dangling reference
There's some obvious copy/paste trouble going on here:
if (socket_1.get() == NULL || !socket_1->is_open())
{
WARNING << "serial_port_1 is not open";
return;
}
I'd actually say this stems from precisely the same source that lead to the variable names being serial_port_1 and socket_1
Some Cleanup
Simplify. There wasn't self-contained code, so nothing complete here, but at least see the many points of simplification:
Live On Coliru
#include <boost/asio.hpp>
#include <boost/thread.hpp>
#include <iostream>
namespace ba = boost::asio;
using ba::ip::tcp;
using boost::system::error_code;
#define ERROR std::cerr
#define WARNING std::cerr
#define INFO std::cerr
struct eth_socket {
~eth_socket() {
_work.reset();
if (_worker.joinable())
_worker.join(); // wait
}
void open(std::string address);
void write_data(std::string data);
private:
void connected(error_code error) {
if (error)
ERROR << "Connect failed: " << error << "\n";
else
INFO << "Connected to " << _sock.remote_endpoint() << "\n";
}
void written(error_code error, size_t bytes_transferred);
private:
ba::io_service _svc;
boost::optional<ba::io_service::work> _work{ _svc };
boost::thread _worker{ [this] { _svc.run(); } };
std::string _data;
unsigned short _port = 6767;
tcp::socket _sock{ _svc };
};
void eth_socket::open(std::string address) {
tcp::endpoint remote_endpoint(ba::ip::address::from_string(address), _port);
_sock.async_connect(remote_endpoint, boost::bind(&eth_socket::connected, this, _1));
}
void eth_socket::write_data(std::string data) {
_data = data;
_sock.async_write_some(ba::buffer(_data), boost::bind(&eth_socket::written, this, _1, _2));
}
void eth_socket::written(error_code error, size_t bytes_transferred) {
INFO << "data written to " << _sock.remote_endpoint() << " " << error.message() << ";"
<< "bytes_transferred = " << bytes_transferred << "\n";
}
int main() {
{
eth_socket s;
s.open("127.0.0.1");
s.write_data("Hello"); // callback called
s.write_data("Hello"); // callback called
s.write_data("Hello"); // callback called
boost::this_thread::sleep_for(boost::chrono::seconds(1));
s.write_data("Hello"); // callback not called after sleep
} // orderly worker thread join here
}
My problems are now fixed thanks to sehe's help and prayer.
This line in open_eth_socket:
boost::thread t(boost::bind(&boost::asio::io_service::run, *&io_service_1));
is now this:
boost::shared_ptr <boost::thread> io_service_1_thread; // in header
if (io_service_1_thread.get()) io_service_1_thread->interrupt();
io_service_1_thread.reset(new boost::thread (boost::bind(&eth_socket::run_io_service_1, this)));
I added this function:
void eth_socket::run_io_service_1 (void)
{
while (true) // work forever
{
boost::asio::io_service::work work(*io_service_1);
io_service_1->run();
io_service_1->reset(); // not sure if this will cause problems yet
INFO << "io_service_1 run complete";
boost::this_thread::sleep (boost::posix_time::milliseconds (100));
}
return;
}

Get notification in Asio if `dispatched` or `post` have finished

I want to know when dispatchhas finished with some specific work
service.dispatch(&some_work);
I want to know this because I need to restart some_work if it has finished.
struct work
{
std::shared_ptr<asio::io_service> io_service;
bool ready;
std::mutex m;
template <class F>
void do_some_work(F&& f)
{
if (io_service && ready) {
m.lock();
ready = false;
m.unlock();
io_service->dispatch([&f, this]() {
f();
m.lock();
ready = true;
m.unlock();
});
}
}
work(std::shared_ptr<asio::io_service> io_service)
: io_service(io_service)
, ready(true)
{
}
};
int
main()
{
auto service = std::make_shared<asio::io_service>();
auto w = std::make_shared<asio::io_service::work>(*service);
std::thread t1([&] { service->run(); });
work some_work{ service };
for (;;) {
some_work.do_some_work([] {
std::cout << "Start long draw on thread: " << std::this_thread::get_id()
<< std::endl;
std::this_thread::sleep_for(std::chrono::seconds(5));
std::cout << "End long draw on thread: " << std::this_thread::get_id()
<< std::endl;
});
}
w.reset();
t1.join();
}
There are some problems with the code, for example if some_workgoes out of scope, then the running taskwould still write to ready.
I am wondering if something like this already exists in Asio?
For lifetime issues, the common idiom is indeed to use shared pointers, examples:
Ensure no new wait is accepted by boost::deadline_timer unless previous wait is expired
Boost::Asio Async write failed
Other than that, the completion handler is already that event. So you would do:
void my_async_loop() {
auto This = shared_from_this();
socket_.async_read(buffer(m_buffer, ...,
[=,This](error_code ec, size_t transferred) {
if (!ec) {
// do something
my_async_loop();
}
}
);
}
This will re-schedule an (other?) async operation once the previous has completed.
On the subject of threadsafety, see Why do I need strand per connection when using boost::asio?

Using boost::asio::io_service::post()

First i asked this Running a function on the main thread from a boost thread and passing parameters to that function
so now i am trying this:
The following is a console c++ project where i perfectly simulated my big project
TestServicePost.cpp
#include "stdafx.h"
#include "SomeClass.h"
int _tmain(int argc, _TCHAR* argv[])
{
SomeClass* s = new SomeClass();
while(true)
{
s->update();
}
return 0;
}
SomeClass.h
#include <boost/thread.hpp>
#include <boost/asio.hpp>
#include <queue>
class ServiceNote
{
public:
std::string getType()
{
std::stringstream typeSS;
typeSS << "LamasaTech.MultiWall.PostNote." << (NoteType.compare("Normal") == 0 ? "Node" : "Header") << "." << Shape << "." << Colour;
return typeSS.str();
}
int Action;
int CNoteId;
std::string Colour;
int NoteId;
std::string NoteType;
int SessionId;
std::string Shape;
std::string Style;
std::string Text;
int X;
int Y;
};
class SomeClass
{
public:
SomeClass();
~SomeClass();
void update();
private:
std::queue<ServiceNote> pendingNotes;
void addToQueue(ServiceNote sn);
void pollService(boost::asio::io_service* svc);
int getMessage(boost::asio::io_service* svc, std::string sessionId, int messageId);
boost::thread servicePoller;
};
SomeClass.cpp
#include "stdafx.h"
#include "SomeClass.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/asio/signal_set.hpp>
#define POLL_SERVICE = 0;
#define POLLING_WAIT_TIME 1000
#define SAVE_SESSION_EVERY 1800000
SomeClass::SomeClass()
{
boost::asio::io_service io_servicePoller;
io_servicePoller.run();
servicePoller = boost::thread(boost::bind(&SomeClass::pollService, this, &io_servicePoller));
/*boost::asio::io_service io_sessionSaver;
boost::asio::signal_set signalsSaver(io_sessionSaver, SIGINT, SIGTERM);
signalsSaver.async_wait( boost::bind(&boost::asio::io_service::stop, &io_sessionSaver));
sessionSaver = boost::thread(&SomeClass::saveSessionEvery, io_sessionSaver);*/
}
SomeClass::~SomeClass()
{
}
void SomeClass::update()
{
while(!pendingNotes.empty())
{
ServiceNote sn = pendingNotes.front();
pendingNotes.pop();
}
}
void SomeClass::addToQueue(ServiceNote sn)
{
pendingNotes.push(sn);
}
void SomeClass::pollService(boost::asio::io_service* svc)
{
int messageId = 1;
while(true)
{
if(boost::this_thread::interruption_enabled() && boost::this_thread::interruption_requested())
return;
int currentId = messageId;
messageId = getMessage(svc, "49", messageId);
if(currentId == messageId)
boost::this_thread::sleep(boost::posix_time::milliseconds(POLLING_WAIT_TIME));
}
}
int SomeClass::getMessage(boost::asio::io_service* svc, std::string sessionId, int messageId)
{
try
{
boost::asio::io_service io_service;
// Get a list of endpoints corresponding to the server name.
boost::asio::ip::tcp::resolver resolver(io_service);
boost::asio::ip::tcp::resolver::query query("mw.rombus.com", "http");
boost::asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
// Try each endpoint until we successfully establish a connection.
boost::asio::ip::tcp::socket socket(io_service);
boost::asio::connect(socket, endpoint_iterator);
// Form the request. We specify the "Connection: close" header so that the
// server will close the socket after transmitting the response. This will
// allow us to treat all data up until the EOF as the content.
boost::asio::streambuf request;
std::ostream request_stream(&request);
request_stream << "GET " "/Service.svc/message/" << sessionId << "/" << messageId << " HTTP/1.0\r\n";
request_stream << "Host: " << "mw.rombus.com" << "\r\n";
request_stream << "Accept: */*\r\n";
request_stream << "Connection: close\r\n\r\n";
// Send the request.
boost::asio::write(socket, request);
// Read the response status line. The response streambuf will automatically
// grow to accommodate the entire line. The growth may be limited by passing
// a maximum size to the streambuf constructor.
boost::asio::streambuf response;
boost::asio::read_until(socket, response, "\r\n");
// Check that response is OK.
std::istream response_stream(&response);
std::string http_version;
response_stream >> http_version;
unsigned int status_code;
response_stream >> status_code;
std::string status_message;
std::getline(response_stream, status_message);
if (!response_stream || http_version.substr(0, 5) != "HTTP/")
{
//std::cout << "Invalid response\n";
return messageId;
}
if (status_code != 200)
{
//std::cout << "Response returned with status code " << status_code << "\n";
return messageId;
}
// Read the response headers, which are terminated by a blank line.
boost::asio::read_until(socket, response, "\r\n\r\n");
// Process the response headers.
std::string header;
std::string fullHeader = "";
while (std::getline(response_stream, header) && header != "\r")
fullHeader.append(header).append("\n");
// Write whatever content we already have to output.
std::string fullResponse = "";
if (response.size() > 0)
{
std::stringstream ss;
ss << &response;
fullResponse = ss.str();
try
{
boost::property_tree::ptree pt;
boost::property_tree::read_json(ss, pt);
ServiceNote sn;
sn.Action = pt.get<int>("Action");
sn.CNoteId = pt.get<int>("CNoteId");
sn.Colour = pt.get<std::string>("Colour");
sn.NoteId = pt.get<int>("NoteId");
sn.NoteType = pt.get<std::string>("NoteType");
sn.SessionId = pt.get<int>("SessionId");
sn.Shape = pt.get<std::string>("Shape");
sn.Style = pt.get<std::string>("Style");
sn.Text = pt.get<std::string>("Text");
sn.X = pt.get<int>("X");
sn.Y = pt.get<int>("Y");
svc->post(boost::bind(&SomeClass::addToQueue, this, sn));
//pendingNotes.push(sn);
}
catch (std::exception const& e)
{
std::string test = e.what();
//std::cerr << e.what() << std::endl;
}
messageId++;
}
// Read until EOF, writing data to output as we go.
std::string fullSth = "";
boost::system::error_code error;
while (boost::asio::read(socket, response,
boost::asio::transfer_at_least(1), error))
{
std::ostringstream ss;
ss << &response;
fullSth = ss.str();
}
if (error != boost::asio::error::eof)
throw boost::system::system_error(error);
}
catch (std::exception& e)
{
std::string test = e.what();
std::cout << "Exception: " << e.what() << "\n";
}
return messageId;
}
but i get Unhandled exception at 0x771215de in TestServicePost.exe: 0xC0000005: Access violation writing location 0xcccccce4., right after this line executes:
svc->post(boost::bind(&SomeClass::addToQueue, this, sn));
I couldn't define io_service as a class member so i can use it in the destructor ~SomeClass(), would appreciate help on that too
If io_service.post is not the best solution for me please recommend something, as you can see i have a constructor, destructor and an update method who is called every tick, i tried using this and the queue alone but it wasn't thread safe, is there an easy thread safe FIFO to use ?
In SomeClass constructor you actually do the following:
Define a local io_service instance.
Call its run() member-function, which returns immediately, because io_service has no work.
Pass an address of the local object to another thread.
This certainly won't work.
Note that io_service::run() is a kind of "message loop", so it should block the calling thread. Don't call it in object constructor.
I figured out how to declare io_service as a class member:
boost::shared_ptr< boost::asio::io_service > io_servicePoller;
and in the constructor i did the following:
SomeClass::SomeClass()
{
boost::shared_ptr< boost::asio::io_service > io_service(
new boost::asio::io_service
);
io_servicePoller = io_service;
servicePoller = boost::thread(boost::bind(&SomeClass::pollService, this, io_servicePoller));
}
Some cleanup
SomeClass::~SomeClass()
{
servicePoller.interrupt();
io_servicePoller->stop();
servicePoller.join();
}
and in update i called run which adds the stuff into the queue, then reads them in the while loop
void SomeClass::update()
{
io_servicePoller->run();
io_servicePoller->reset();
while(!pendingNotes.empty())
{
ServiceNote sn = pendingNotes.front();
pendingNotes.pop();
}
}
and changed my members signature to void SomeClass::pollService(boost::shared_ptr< boost::asio::io_service > svc)
So what happens is:
The app starts
inits my class
my class makes a service and starts the thread
the thread fetches items from the service
the main thread checks the io service queue and exuted it
then it uses the queue
Thanks to Igor R. i couldn't have done it without him
and also http://www.gamedev.net/blog/950/entry-2249317-a-guide-to-getting-started-with-boostasio?pg=4 where i got how to make the shared pointer

boost::asio doesn't work

With the following class
the header:
namespace msgSrv {
class endPoint {
public:
asio::ip::udp::endpoint ep;
endPoint(std::string ip, int port);
};
class msgSrv {
private:
asio::ip::udp::socket *asioSocket;
asio::io_service *asioIoService;
int listenPort;
boost::array<char, 1> rcvBuff;
asio::ip::udp::endpoint lastRcvdPcktEndp;
char * sbuff;
public:
boost::condition_variable cond;
boost::mutex mut;
msgSrv(int listenPort);
virtual ~msgSrv();
void start();
void pckRcvd(const asio::error_code& error, std::size_t bytes_transferred);
void sendTo(const char* buff, int len, endPoint ep);
void sendHnd(const asio::error_code& error, std::size_t bytes_transferred);
};
}
the .cpp
#include "msgSrv.h"
namespace msgSrv {
endPoint::endPoint(const std::string ip, int port) {
asio::ip::address addr = asio::ip::address::from_string(ip);
ep = asio::ip::udp::endpoint(addr, port);
}
msgSrv::msgSrv(int listenPort) {
// TODO Auto-generated constructor stub
this->listenPort = listenPort;
try {
asioIoService = new asio::io_service();
asioSocket = new asio::ip::udp::socket(*asioIoService,
asio::ip::udp::endpoint(asio::ip::udp::v4(), listenPort)); //new asio::ip::udp::socket_(*asioIoService, udp::endpoint(udp::v4(), listenPort));
} catch (std::exception &e) {
std::cerr << "Error initializing ioservice or socket:" << e.what();
}
asioIoService->run();
}
msgSrv::~msgSrv() {
// TODO Auto-generated destructor stub
delete asioIoService;
delete asioSocket;
}
void msgSrv::start() {
asioSocket->async_receive_from(asio::buffer(rcvBuff), lastRcvdPcktEndp,
boost::bind(&msgSrv::pckRcvd, this, asio::placeholders::error,
asio::placeholders::bytes_transferred));
}
void msgSrv::pckRcvd(const asio::error_code& error,
std::size_t bytes_transferred) {
std::cout << "Rcvd! " << lastRcvdPcktEndp.address().to_string() << ":"
<< lastRcvdPcktEndp.port() << "\n";
}
void msgSrv::sendTo(const char* buff, int len, endPoint ep) {
sbuff = new char[len];
mempcpy(sbuff, buff, len);
asioSocket->async_send_to(asio::buffer(sbuff, len), ep.ep, boost::bind(
&msgSrv::sendHnd, this, asio::placeholders::error,
asio::placeholders::bytes_transferred));
}
void msgSrv::sendHnd(const asio::error_code& error,
std::size_t bytes_transferred) {
std::cout << "Snt!\n";
delete sbuff;
}
}
and the following "main" file:
int main()
{
msgSrv::msgSrv aa(4450);
aa.start();
msgSrv::endPoint ep("127.0.0.1", 4450);
std::string a("Prova!");
int len = a.length();
aa.sendTo(a.c_str(), len, ep);
std::cout << "sent...\n";
std::cout << "notified...\n";
}
all I get is:
terminate called after throwing an instance of 'asio::system_error'
what(): mutex: Invalid argument
sent...
notified...
What's wrong?? I tried even to put a while(1) in the main, to see if something happens... I even tried to put a condition in the main that is unlocked by the receive handler... all remains locked... So what??? No idea!
I don't see you actually locking any muxtex, so that error is strange.
However your problem is to calling asioIoService->run() inside the constructor, witch fall in infinite loop. The solution is to create a new boost::thread, witch call asioIoService->run() itself. This thread will be processing all jobs. You may also call asio::io_service::run() with more then one thread, to get processing on more then one job at the same time.
m_thread = new boost::thread(boost::bind(&asio::io_service::run,asioIoService));
Calling asioIoService->stop() will force exit of asio::io_service::run(), thus closing the thread. You must join this thread to ensure that thread terminates before destroying asioIoService pointer in the destructor of your msgSrv class.

Resources