Discussion:
[asio-users] A strange issue on Linux, but on Windows works perfectly
Dmitry Timoshenko
2011-06-05 23:39:52 UTC
Permalink
Hello,

The code is executed from within single thread. The io_service::poll()
is permanently called with an interval.
The code calls tcp::socket::async_connect(). Then, when connected it
calls tcp::socket::async_read_some().
It also calls boost::asio::async_write() on socket, when there is
something to send.
The code looks like standard asio use case.

But I have the strange thing on Linux. Specifically: Ubuntu 8.04 &
gcc4.2; Ubuntu 10.04 & gcc4.4 with Boost 1.45; 1.46.1.
The connection occurs properly, but there is no asio_write_handler()
called from within the io_service::poll(), I checked that
async_write() is actually called, but io_service::poll() is just silent.
It doesn't call the handlers.
When I close the socket, asio_read_handler() is not called with the
error, but It should have been in proper case.

The code is strictly checked and works fine on Windows.

But in the same time when the socket is accepted by the
tcp::acceptor::async_accept() everything works properly (handlers are
called in proper manner).

The code implements server and client, any code that reads and writes to
sockets for the server and the client is put into base entity, so
they (server&client) are only distinct by the async_connect() &
async_accept() related code.

May be I missed something?
Any help is appreciated, thank you.
Arash Partow
2011-06-06 00:43:02 UTC
Permalink
......
A cut-down working sample of your code demonstrating the problem would be helpful.
Dmitry Timoshenko
2011-06-30 13:47:39 UTC
Permalink
Hello, again

I told about the strange issue, I also told it happens only for
async_connect... I've little bit mistaken.

The new assertion is:
there is no difference how the connection is initiated. It may be client
(async_connect) or server (async_accept)
the issue occurs anyway. Accept and connect handlers are invoked, but
read and write handlers are NOT.
This means they are never called from within io_service::poll()
I didn't check io_service::run() and etc. I only checked io_service::poll().

I spent a lot of time to make this clear and this is what I found out.

I use asio in one of common ways. There is only one thread everything is
called from within.
io_service::poll() is permanently called, after it finishes the main
process() function is called doing all the work to handle incoming
data and put outgoing data into the out queue by calling async_read() &
async_write()
(I tried different variants provided by asio: async_read_some and etc.).

The code looks like this in simple way.

void main_loop(void)
{
while (is_working())
{
_io_service.poll();
process();
}
}

void handle_accept()
{
// first call to async_read()
start_read();
}

This pefectly works on Windows, but on Linux I get handlers specified to
async_read() & async_write() are not actually called
even if data (incoming or outgoing) exist (I strictly checked this).
There only accept handler is called on server side. io_service is just
silent.
Acceptor is also works wrong. In my case I only serve one connection, so
when the connection is accepted I close the listening socket.
When the connection is broken I reopen it. But it's not opened again! It
only can be opened if I restart the program.

There is a solution:
If boost::asio::io_service::work is used everything works as should have
been under both Windows and Linux.
I create work object before start main_loop() and destroy after
main_loop() exits.
After I did it, the issue disappeared. asio::acceptor also works properly.

If you need test project, or something else, please tell me.
Thank you.
Dmitry Timoshenko
2011-07-04 17:05:55 UTC
Permalink
Recently I've known people who encountered this issue too.

Does anyone encounter the issue?

In brief it looks like asio_async_read_handler and
asio_async_write_handler are not called from within io_service::poll()
and may be io_service::run(). But accept and connect handlers are
invoked. For me it happens only under Ubuntu, FreeBSD.
Under Windows everything is ok.

The solution is to use io_service::work class.

Thank you.
Igor R
2011-07-04 17:25:45 UTC
Permalink
Post by Dmitry Timoshenko
In brief it looks like asio_async_read_handler and
asio_async_write_handler are not called from within io_service::poll()
and may be io_service::run(). But accept and connect handlers are
invoked. For me it happens only under Ubuntu, FreeBSD.
Under Windows everything is ok.
The solution is to use io_service::work class.
If io_service::work helps, it means that otherwise io_service::run()
exits at some stage. So, check why and when it exits, and whether it's
the desired behavior.
Note that when io_service() runs out of work, you should call
io_service::reset(), prior to re-running it.
Dmitry Timoshenko
2011-07-04 17:59:15 UTC
Permalink
Thank you for the answer.

I my case io_service::poll() doesn't work.
I just head about the same issue but with io_service::run().

I do:

void main_loop(void)
{
while (is_working())
{
_io_service.poll();
process();
}
}

void handle_accept()
{
// first call to async_read()
start_read();
}


I have read handler never called even if incoming data exist (simple
test with telnet), but if I use io_service::work the "code" above works
fine.
Igor R
2011-07-04 19:21:53 UTC
Permalink
Post by Dmitry Timoshenko
I my case io_service::poll() doesn't work.
I just head about the same issue but with io_service::run().
void main_loop(void)
{
    while (is_working())
    {
      _io_service.poll();
      process();
    }
}
run() or poll() -- it doesn't matter. You get the situation, where
poll() *had* some work, which finished, and no new work arrived . In
such a situation you must call reset():
http://www.boost.org/doc/libs/1_46_0/doc/html/boost_asio/reference/io_service/reset.html

Of course, the above situation will never occur if you registered
"work" object OR if your handler queue is being filled quickly enough
(kind of race condition).
Dmitry Timoshenko
2011-07-05 23:16:50 UTC
Permalink
Post by Igor R
run() or poll() -- it doesn't matter. You get the situation, where
poll() *had* some work, which finished, and no new work arrived . In
I also do io_service::reset() explicitly. reset()/stop() works as
described by documentation.

The same code (without using io_service::work) works fine on Windows,
but on Linux the issue occurs. It only disappear if I use
io_service::work. In this case the code works for both Windows and Linux
properly.
Arash Partow
2011-07-08 16:04:54 UTC
Permalink
Dmitry,
Post by Igor R
run() or poll() -- it doesn't matter. You get the situation, where
poll() *had* some work, which finished, and no new work arrived . In
As Marat has mentioned to you twice already, and in order to increase the SNR in this list, please provide a cut-down working example that exhibits your problem completely, either attached to your post or perhaps accessible via the many online code snippet sites (eg: pastebin, codepad etc...), couple that with exact details of your OS version (installed SPs etc), compiler version (installed SPs etc), Boost version and ASIO version.
Marat Abrarov
2011-07-04 19:25:15 UTC
Permalink
Hi, Dmitry.

It seems than your problem is due to Asio bug.
Short working demo would be very helpful.

Regards,
Marat Abrarov.
Marat Abrarov
2011-07-07 06:24:05 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
The same code (without using io_service::work) works fine on Windows,
but on Linux the issue occurs. It only disappear if I use
io_service::work. In this case the code works for both Windows and Linux
properly.
Short working demo (located at one *.cpp file) would be very helpful.
I'm very interested to find solution of this problem.

Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-18 19:46:48 UTC
Permalink
Hello, Marat,

Thank you for the answer and your time,
sorry for the late response.

Sample is available through this link.
http://64.186.158.86:8400/asio-sample/socket_test.zip

May be it's not small enough. You are only interested in

server.hpp/cpp - main loop
session.hpp/cpp - socket handlers
Marat Abrarov
2011-07-19 04:29:12 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
Sample is available through this link.
http://64.186.158.86:8400/asio-sample/socket_test.zip
I see at detail.hpp: 19
#include <smart/event_dispatcher.hpp>
#include <smart/std_priority_queue_adapter.hpp>

These headers (especially event_dispatcher.hpp) aren't located in the ZIP.
Post by Dmitry Timoshenko
Thank you for the answer and your time,
sorry for the late response.
Not at all. I'm really interested to find solution or (probably) bug in Asio.

Regards,
Marat Abrarov.
Marat Abrarov
2011-07-19 05:03:23 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
Sample is available through this link.
http://64.186.158.86:8400/asio-sample/socket_test.zip
bool test::server::timeout_event_handler(const event_type& /*event*/)
{
// dispatch asio events
ioservice().poll();
// process clients
process_clients();
return true;
}
The run() function must not be called from a thread
that is currently calling one of run(), run_one(), poll() or poll_one()
on the same io_service object.
May be it is so for the poll() calling from a thread that is currently calling run(). I'll check this. If it so than it
really have to be included in Asio docs.

Anyway, it is bad design decision to call io_service::poll (run, run_one, poll_one) from a handler being
executed by means of the same io_service instance (run, run_one, poll, poll_one).

So I need to see smart/event_dispatcher.hpp.
Post by Dmitry Timoshenko
subscribe_for_event(smart::code(timeout_event()), boost::bind(&server::timeout_event_handler, this, _1)); ).
Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-19 08:16:41 UTC
Permalink
Hi, Marat,
bool test::server::timeout_event_handler(const event_type& /*event*/)
{
// dispatch asio events
ioservice().poll();
// process clients
process_clients();
return true;
}
The run() function must not be called from a thread
that is currently calling one of run(), run_one(), poll() or poll_one()
on the same io_service object.
It my fault, I should have been provided small example, sorry.

test::server::timeout_event_handler() - is just called by the thread,
the event_dispatcher is just a boost::thread wrapper, it doesn't relate
to asio at all.

There is no subsequent io_service::run() call. The code just calls
poll(), then it calls process_clients() which performs all the logic to
process the data got by previous io_service::poll() call.

Please, take a look at README in the package or go there
http://sourceforge.net/projects/libsmart/ to get the necessary files.

Thank you

If it's still necessary I can provide less complicated example. Tell me
if you need it.
Marat Abrarov
2011-07-19 09:00:59 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
If it's still necessary I can provide less complicated example. Tell me
if you need it.
The working example demonstrating described problem (I can test it at virtual machine with Ubuntu) would be very
helpful. It'll be better for that example to support both Linux and Windows.

Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-19 17:09:28 UTC
Permalink
Hello, Marat,

The example is accessible here
http://64.186.158.86:8400/asio-sample/socket_test2.zip
use ./build.sh and .\build.cmd on Linux and Windows respectively to
build the sample.

I'm a little bit confused, the behavior is little bit changed. On
Windows everything is fine as before. On Linux it works only on first
connection attempt. Subsequent connects are not processed as wanted.

please, try

telnet localhost 5555
^]close
telnet localhost 5555

You should see that no handlers called on second connect, but 'netstat
-an --tcp' shows that connection is established.


The first sample (http://64.186.158.86:8400/asio-sample/socket_test.zip)
always produces the issue. I don't know why the behavior is different.
The samples in the essence are the same. The event_dispatcher is used in
the first sample as boost::thread wrapper and doesn't relate to the asio
at all. The "process()" is called from within the timeout_event_handler
which is periodically called by the thread. The same is done by the
second sample but in simple way.

I intentionally use same socket, because of I need only one connection
at a time.

Thank you, if you need something else, please let me know.
Marat Abrarov
2011-07-19 19:45:27 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
The example is accessible here
http://64.186.158.86:8400/asio-sample/socket_test2.zip
use ./build.sh and .\build.cmd on Linux and Windows respectively to
build the sample.
I think I found at least one serious error in your code.
main.cpp: 229

void asio_read_handler(const boost::system::error_code& error, size_t n)
{
SMART_DEBUG_OUT("[session]: Async read completed invoked");

_read_error = error;
if (error)
{
SMART_DEBUG_OUT("[session]: Read channel error; [" << error << "; " << error.message() << "]");
} else
{
// get data
BOOST_ASSERT(n <= _local_buffer.size() && " - Buffer overrun detected");

// at first, start next read operation anyway
start_read();

const size_t sz = std::min(n, _local_buffer.size());
const string s(_local_buffer.begin(), _local_buffer.begin() + sz);
_buffer += s;

SMART_DEBUG_OUT("[session]: Incoming data '" << s << "' = " << to_string(lexical_cast<string>(unsigned(sz))
+ " byte(s) ", _local_buffer.begin(), _local_buffer.begin() + sz));

start_write(s); // Oops! Are you sure that previous composed write operation was already completed?
}

SMART_DEBUG_OUT("[session]: Async read completed finished")
}
Post by Dmitry Timoshenko
The program must ensure that the stream performs no other write operations
(such as async_write, the stream's async_write_some function, or any other
composed operations that perform writes) until this operation completes.
The reason code works at Windows is IOCP - it's proactor so Asio's composed operation works rather good.
At Linux Asio uses epoll - it's reactor so Asio probably can't determine which composed-write-related send operation
completed when the send-complete event is fired.

It was discussed many times before: http://www.cyberforum.ru/archive/t-272413.html.

Best regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-20 09:20:50 UTC
Permalink
Hello, Marat,
Post by Marat Abrarov
I think I found at least one serious error in your code.
main.cpp: 229
I added a simple check before write to the socket, the behaviour is not
changed, the issue is present anyway.

The link is the same
http://64.186.158.86:8400/asio-sample/socket_test2.zip

To build the sample, please edit BOOST_ROOT located in build.sh or
build.cmd accordingly your boost libraries location. It should point to
the Jamroot file location.

Thank you,
Dmitry.
Marat Abrarov
2011-07-21 11:23:05 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
I added a simple check before write to the socket, the behaviour is not
changed, the issue is present anyway.
The link is the same
http://64.186.158.86:8400/asio-sample/socket_test2.zip
Really in your example async_accept's handler isn't called at the second connect (after the first connection was closed
from the client side).
Telnet connects but the handler isn't called.

This works fine (Ubuntu 10.10, g++ 4.4.5, Boost 1.46.0):
~~~~~~~~~~~~~~~~~~~~~~~~
#include <cstddef>
#include <iostream>
#include <boost/ref.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <boost/utility.hpp>
#include <boost/array.hpp>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/ptime.hpp>

#if defined (WIN32)
# include <tchar.h>
# include <conio.h>
#endif // defined (WIN32)

namespace {

typedef boost::asio::ip::tcp protocol_type;

const protocol_type::endpoint listen_endpoint(boost::asio::ip::address(), 5555);
const int listen_backlog = 6;
boost::array<int, 20> buffer;

void open(protocol_type::acceptor& acceptor,
const protocol_type::endpoint& endpoint, int backlog,
boost::system::error_code& ec)
{
boost::system::error_code error;

acceptor.open(endpoint.protocol(), error);
if (error)
{
ec = error;
return;
}

class acceptor_guard : private boost::noncopyable
{
public:
typedef protocol_type::acceptor guarded_type;

acceptor_guard(guarded_type& guarded)
: active_(true)
, guarded_(guarded)
{
}

~acceptor_guard()
{
if (active_)
{
boost::system::error_code ignored;
guarded_.close(ignored);
}
}

void release()
{
active_ = false;
}

private:
bool active_;
guarded_type& guarded_;
}; // class acceptor_guard

acceptor_guard closing_guard(acceptor);

acceptor.set_option(protocol_type::acceptor::reuse_address(true), error);
if (error)
{
ec = error;
return;
}

acceptor.bind(endpoint, error);
if (error)
{
ec = error;
return;
}

acceptor.listen(backlog, error);

if (!error)
{
closing_guard.release();
}

ec = error;
return;
}

void handle_accept(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error);

void handle_read_some(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error,
const std::size_t bytes_transferred);

void handle_write_some(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error,
const std::size_t bytes_transferred);

void handle_accept(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error)
{
if (error)
{
std::cerr << error.message() << std::endl;
return;
}

boost::system::error_code ignored;
acceptor.close(ignored);
if (ignored)
{
std::cerr << ignored.message() << std::endl;
}

socket.async_read_some(boost::asio::buffer(buffer),
boost::bind(handle_read_some, boost::ref(acceptor), boost::ref(socket),
boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()));
}

void handle_read_some(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error,
const std::size_t bytes_transferred)
{
if (error)
{
std::cerr << error.message() << std::endl;

boost::system::error_code ec;

socket.shutdown(protocol_type::socket::shutdown_both, ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
}

socket.close(ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
}

open(acceptor, listen_endpoint, listen_backlog, ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
return;
}

acceptor.async_accept(socket, boost::bind(handle_accept,
boost::ref(acceptor), boost::ref(socket),
boost::asio::placeholders::error()));

return;
}

boost::asio::async_write(socket,
boost::asio::buffer(buffer, bytes_transferred),
boost::bind(handle_write_some, boost::ref(acceptor), boost::ref(socket),
boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()));
}

void handle_write_some(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error,
const std::size_t /*bytes_transferred*/)
{
if (error)
{
std::cerr << error.message() << std::endl;

boost::system::error_code ec;

socket.shutdown(protocol_type::socket::shutdown_both, ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
}

socket.close(ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
}

open(acceptor, listen_endpoint, listen_backlog, ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
return;
}

acceptor.async_accept(socket, boost::bind(handle_accept,
boost::ref(acceptor), boost::ref(socket),
boost::asio::placeholders::error()));

return;
}

socket.async_read_some(boost::asio::buffer(buffer),
boost::bind(handle_read_some, boost::ref(acceptor), boost::ref(socket),
boost::asio::placeholders::error(),
boost::asio::placeholders::bytes_transferred()));
}

} // anonymous namespace

#if defined(WIN32)
int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
#else
int main(int /*argc*/, char* /*argv*/[])
#endif
{
boost::asio::io_service io_service;

protocol_type::acceptor acceptor(io_service);
protocol_type::socket socket(io_service);

boost::system::error_code error;
open(acceptor, listen_endpoint, listen_backlog, error);

if (error)
{
std::cerr << error.message() << std::endl;
return EXIT_FAILURE;
}

acceptor.async_accept(socket, boost::bind(handle_accept,
boost::ref(acceptor), boost::ref(socket),
boost::asio::placeholders::error()));

//io_service.run();
while (true)
{
io_service.poll();
boost::this_thread::sleep(boost::posix_time::milliseconds(0));
}

return EXIT_SUCCESS;
}
~~~~~~~~~~~~~~~~~~~~~~~~

Can you modify above listed code to reproduce described error in parallel with my researching of your sample?

Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-21 22:52:34 UTC
Permalink
Hello, Marat,
Post by Marat Abrarov
Can you modify above listed code to reproduce described error in parallel with my researching of your sample?
ok, I'll try.

Regards,
Dmitry
Dmitry Timoshenko
2011-07-25 10:22:30 UTC
Permalink
Hello, Marat,

I little bit changed your sample.
You are quite right.
The accept_handler() is not called by the io_service::poll() if second
and subsequent calls to async_accept() are done outside the
io_service::poll().

Here is the code:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#include <cstddef>
#include <iostream>
#include <boost/ref.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>
#include <boost/system/error_code.hpp>
#include <boost/utility.hpp>
#include <boost/array.hpp>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/ptime.hpp>

#if defined (WIN32)
# include <tchar.h>
# include <conio.h>
#endif // defined (WIN32)

namespace {

#ifdef BOOST_MSVC

boost::arg<1> error_placeholder(void) { return
boost::asio::placeholders::error; }
boost::arg<2> bytes_transferred_placeholder(void) { return
boost::asio::placeholders::bytes_transferred; }

#else

boost::arg<1> error_placeholder(void) { return
boost::asio::placeholders::error(); }
boost::arg<2> bytes_transferred_placeholder(void) { return
boost::asio::placeholders::bytes_transferred(); }

#endif // BOOST_MSVC

typedef boost::asio::ip::tcp protocol_type;

const protocol_type::endpoint
listen_endpoint(boost::asio::ip::address(), 5555);
const int listen_backlog = 6;
boost::array<int, 20> buffer;

boost::system::error_code _channel_error;

void open(protocol_type::acceptor& acceptor,
const protocol_type::endpoint& endpoint, int backlog,
boost::system::error_code& ec)
{
boost::system::error_code error;

acceptor.open(endpoint.protocol(), error);
if (error)
{
ec = error;
return;
}

class acceptor_guard : private boost::noncopyable
{
public:
typedef protocol_type::acceptor guarded_type;

acceptor_guard(guarded_type& guarded)
: active_(true)
, guarded_(guarded)
{
}

~acceptor_guard()
{
if (active_)
{
boost::system::error_code ignored;
guarded_.close(ignored);
}
}

void release()
{
active_ = false;
}

private:
bool active_;
guarded_type& guarded_;
}; // class acceptor_guard

acceptor_guard closing_guard(acceptor);

acceptor.set_option(protocol_type::acceptor::reuse_address(true),
error);
if (error)
{
ec = error;
return;
}

acceptor.bind(endpoint, error);
if (error)
{
ec = error;
return;
}

acceptor.listen(backlog, error);

if (!error)
{
closing_guard.release();
}

ec = error;
return;
}

//------------------------------------------------------------------
void handle_accept(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error);

void handle_read_some(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error,
const std::size_t bytes_transferred);

void handle_write_some(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error,
const std::size_t bytes_transferred);

//------------------------------------------------------------------
void start_accept(protocol_type::acceptor& acceptor,
protocol_type::socket& socket)
{
acceptor.async_accept(socket, boost::bind(handle_accept,
boost::ref(acceptor), boost::ref(socket),
error_placeholder()));
}

//------------------------------------------------------------------
void handle_accept(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error)
{
if (error)
{
std::cerr << error.message() << std::endl;
return;
}

boost::system::error_code ignored;
acceptor.close(ignored);
if (ignored)
{
std::cerr << ignored.message() << std::endl;
}

socket.async_read_some(boost::asio::buffer(buffer),
boost::bind(handle_read_some, boost::ref(acceptor),
boost::ref(socket),
error_placeholder(),
bytes_transferred_placeholder()));
}

//------------------------------------------------------------------
void handle_read_some(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error,
const std::size_t bytes_transferred)
{
if (_channel_error = error)
{
std::cerr << error.message() << std::endl;

boost::system::error_code ec;

socket.shutdown(protocol_type::socket::shutdown_both, ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
}

socket.close(ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
}

open(acceptor, listen_endpoint, listen_backlog, ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
return;
}

//start_accept(acceptor, socket);
// acceptor.async_accept(socket, boost::bind(handle_accept,
// boost::ref(acceptor), boost::ref(socket),
// error_placeholder()));

return;
}

boost::asio::async_write(socket,
boost::asio::buffer(buffer, bytes_transferred),
boost::bind(handle_write_some, boost::ref(acceptor),
boost::ref(socket),
error_placeholder(),
bytes_transferred_placeholder()));
}

//------------------------------------------------------------------
void handle_write_some(protocol_type::acceptor& acceptor,
protocol_type::socket& socket,
const boost::system::error_code& error,
const std::size_t /*bytes_transferred*/)
{
if (_channel_error = error)
{
std::cerr << error.message() << std::endl;

boost::system::error_code ec;

socket.shutdown(protocol_type::socket::shutdown_both, ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
}

socket.close(ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
}

open(acceptor, listen_endpoint, listen_backlog, ec);
if (ec)
{
std::cerr << ec.message() << std::endl;
return;
}

//start_accept(acceptor, socket);
// acceptor.async_accept(socket, boost::bind(handle_accept,
// boost::ref(acceptor), boost::ref(socket),
// error_placeholder()));

return;
}

socket.async_read_some(boost::asio::buffer(buffer),
boost::bind(handle_read_some, boost::ref(acceptor),
boost::ref(socket),
error_placeholder(),
bytes_transferred_placeholder()));
}

} // anonymous namespace

#if defined(WIN32)
int _tmain(int /*argc*/, _TCHAR* /*argv*/[])
#else
int main(int /*argc*/, char* /*argv*/[])
#endif
{
boost::asio::io_service io_service;

protocol_type::acceptor acceptor(io_service);
protocol_type::socket socket(io_service);

boost::system::error_code error;
open(acceptor, listen_endpoint, listen_backlog, error);

if (error)
{
std::cerr << error.message() << std::endl;
return EXIT_FAILURE;
}

acceptor.async_accept(socket, boost::bind(handle_accept,
boost::ref(acceptor), boost::ref(socket),
error_placeholder()));

//io_service.run();
while (true)
{
io_service.poll();

if (_channel_error)
{
_channel_error.clear();
start_accept(acceptor, socket);
}

boost::this_thread::sleep(boost::posix_time::milliseconds(0));
}

return EXIT_SUCCESS;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Thank you,
Dmitry
Marat Abrarov
2011-07-22 19:02:46 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
I added a simple check before write to the socket, the behaviour is not
changed, the issue is present anyway.
The link is the same
http://64.186.158.86:8400/asio-sample/socket_test2.zip
I was able to modify your code so that it works (Ubuntu 10.10, g++ 4.4.5, Boost 1.46.0):
~~~~~~~~~~~~~~~~~~~~~~~~
///////////////////////////////////////////////////////////////////////////////
// main.cpp
//
// Pos hardware project. Socket test.
//
// Entry point.
//
// 2011, (c) Dmitry Timoshenko.

#include "auto_sence.hpp"

#include <boost/asio.hpp>
#include <boost/assert.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>

#include <iostream>
#include <stdexcept>
#include <string>
#include <memory>
#include <vector>
#include <sstream>

#define SMART_DEBUG_OUT(expr) cout << std::dec << __FILE__ << "[" << __LINE__ << "]: " << expr << std::endl;

#if defined (POSHW_WIN)
# include <tchar.h>
# include <conio.h>
#elif defined (POSHW_UNIX)
# include <stdio.h>
# include <unistd.h>
#endif // POSHW_WIN

using std::cout;
using std::endl;
using std::string;

using boost::asio::ip::tcp;
using boost::asio::ip::address;
using boost::asio::io_service;
using boost::lexical_cast;
using boost::posix_time::milliseconds;

namespace
{

/** For debug purpose. */
template <typename IterT>
std::string to_string(const std::string& prefix, IterT first, IterT last)
{
std::stringstream ss;

ss << prefix << "[" << std::hex << std::showbase;

for ( ; first != last; )
{
ss << (static_cast<unsigned int>(*first++) & 0xff);
ss << (first == last? "]": ", ");
}

ss << std::dec;

return ss.str();
}

class server;

//////////////////////////////////////////////////////////////////////////
// session

class session
{
//////////////////////////////////////////////////////////////////////////
// interface
public:
/** Disconnect error. */
class disconnect_error : public std::runtime_error
{
public:
explicit disconnect_error(const std::string& what): std::runtime_error(what) { /* empty */ }
};

public:
server& _server;

/** Constructs a session object. */
explicit session(io_service& ioservice, server& the_server):
_socket(ioservice),
_connected(false),
_is_writing(false),
_server(the_server)
{
_local_buffer.assign(local_bufsz, 0);
}

/** Closes connection and destroys an object. */
~session(void) { disconnect(); }

/** Process outgoing messages and receives incoming if there are. */
void process(void)
{
// at first, process errors
process_errors();
}

/** Returns reference to internal asio socket. */
boost::asio::ip::tcp::socket& socket(void) { return _socket; }

/** Initializes the session. */
void initiate(void)
{
clear_state();
start_read();
connected(true);
}

/** Session remote endpoint. */
tcp::endpoint remote_endpoint(void) const
{
boost::system::error_code err;

return socket().remote_endpoint(err);
}

/** Session local endpoint. */
tcp::endpoint local_endpoint(void) const
{
boost::system::error_code err;

return socket().local_endpoint(err);
}

//////////////////////////////////////////////////////////////////////////
// private stuff
private:
enum { local_bufsz = 0x1000 };
typedef std::vector<char> local_buffer_type;
typedef std::string out_buffer_type;
typedef boost::shared_ptr<out_buffer_type> out_buffer_ptr;

private:
void disconnect(void)
{
if (connected())
{
connected(false);
boost::system::error_code err;
socket().shutdown(tcp::socket::shutdown_both, err);

SMART_DEBUG_OUT("[session]: SOCKET SHUTDOWN; [remote: " << remote_endpoint() << "; local: " << local_endpoint() <<
"; error_code: " << err << "; " << err.message() << "]");
}

close_socket();
}

//-----------------------------------------------------------------------------
void close_socket(void)
{
if (socket().is_open())
{
boost::system::error_code err;
socket().close(err);

SMART_DEBUG_OUT("[session]: SOCKET CLOSED; [remote: " << remote_endpoint() << "; local: " << local_endpoint() <<
"; error_code: " << err << "; " << err.message() << "]");
}
}

//-----------------------------------------------------------------------------
void clear_state(void)
{
connected(false);

_read_error.clear();
_write_error.clear();

_buffer.clear();
_local_buffer.assign(local_bufsz, 0);
}

//-----------------------------------------------------------------------------
void process_errors(void)
{
// handle any channel error
handle_ch_error();
}

//-----------------------------------------------------------------------------
void handle_ch_error(void)
{
if (_read_error || _write_error)
{
const boost::system::error_code error = _read_error? _read_error: _write_error;
_read_error.clear();
_write_error.clear();

SMART_DEBUG_OUT("[session]: Channel error; [" << error << ", " << error.message() << "]");

handle_disconnected(error, "Channel error");
}
}

//-----------------------------------------------------------------------------
void handle_disconnected(const boost::system::error_code& reason, const std::string& what);

//////////////////////////////////////////////////////////////////////////
// boost asio manipulators
void start_read(void)
{
_socket.async_read_some(boost::asio::buffer(_local_buffer),
boost::bind(&session::asio_read_handler, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}

//-----------------------------------------------------------------------------
void start_write(const string& s)
{
if (!is_writing())
{
is_writing(true);

BOOST_ASSERT(!s.empty() && " - Output buffer can't be empty");

out_buffer_ptr out_buf(new out_buffer_type(s));

// start asio async write
boost::asio::async_write(_socket, boost::asio::buffer(*out_buf),
boost::bind(&session::asio_write_handler, this, boost::asio::placeholders::error, out_buf));
}
}

//////////////////////////////////////////////////////////////////////////
// boost asio handlers
void asio_read_handler(const boost::system::error_code& error, size_t n)
{
SMART_DEBUG_OUT("[session]: Async read completed invoked");

_read_error = error;
if (error)
{
SMART_DEBUG_OUT("[session]: Read channel error; [" << error << "; " << error.message() << "]");
} else
{
// get data
BOOST_ASSERT(n <= _local_buffer.size() && " - Buffer overrun detected");

// at first, start next read operation anyway
start_read();

const size_t sz = std::min(n, _local_buffer.size());
const string s(_local_buffer.begin(), _local_buffer.begin() + sz);
_buffer += s;

SMART_DEBUG_OUT("[session]: Incoming data '" << s << "' = " << to_string(lexical_cast<string>(unsigned(sz))
+ " byte(s) ", _local_buffer.begin(), _local_buffer.begin() + sz));

start_write(s);
}

SMART_DEBUG_OUT("[session]: Async read completed finished");

process_errors();
}

//-----------------------------------------------------------------------------
void asio_write_handler(const boost::system::error_code& error, out_buffer_ptr buf)
{
SMART_DEBUG_OUT("[session]: Async write completed invoked");

_write_error = error;
is_writing(false);

if (error)
{
SMART_DEBUG_OUT("[session]: Write channel error; [" << error << "; " << error.message() << "]");
} else
{
SMART_DEBUG_OUT(to_string(string("[session]: Sent ") + lexical_cast<string>(unsigned(buf->size()))
+ " byte(s) ", buf->begin(), buf->end()));
}

SMART_DEBUG_OUT("[session]: Async write completed finished");

process_errors();
}

//-----------------------------------------------------------------------------
bool connected(void) const { return socket().is_open() && _connected; }
void connected(bool c) { _connected = c; }
const boost::asio::ip::tcp::socket& socket(void) const { return _socket; }
bool is_writing(void) const { return _is_writing; }
void is_writing(bool writing) { _is_writing = writing; }

private:
boost::asio::ip::tcp::socket _socket;
std::string _buffer;
local_buffer_type _local_buffer;
bool _connected;
bool _is_writing;

//////////////////////////////////////////////////////////////////////////
// error flags
boost::system::error_code _read_error;
boost::system::error_code _write_error;
}; // class session

//////////////////////////////////////////////////////////////////////////
// server

class server
{

//////////////////////////////////////////////////////////////////////////
// interface
public:
/** Constructs a server object. */
server(void):
_acceptor(ioservice()),
_session(ioservice(), *this)
{
create_listening_socket();
start_accept();
}

/** Serve iteration.
*
* Serves the underlying socket and process the incoming data.
*/
void serve(void) { process_iteration(); }

//////////////////////////////////////////////////////////////////////////
// private stuff
private:
// types
typedef std::auto_ptr<boost::asio::io_service::work> service_work_ptr;

private:
void process_iteration(void)
{
try
{
ioservice().poll();
_session.process();
}
catch (const session::disconnect_error& e)
{
SMART_DEBUG_OUT("[server]: Disconnect error exception; [what: " << e.what() << "]");

handle_disconnected(e.what());
}
catch (const std::exception& e)
{
SMART_DEBUG_OUT("[server]: An UNEXPECTED std::exception [what: " << e.what() << "]");

BOOST_ASSERT(!"An UNEXPECTED std::exception!");
}
catch (...)
{
SMART_DEBUG_OUT("[server]: An UNKNOWN UNEXPECTED exception!");

BOOST_ASSERT(!"An UNKNOWN UNEXPECTED exception!");
}
}

//-----------------------------------------------------------------------------
void start_accept(void)
{
acceptor().async_accept(_session.socket(),
boost::bind(&server::asio_accept_handler, this, boost::asio::placeholders::error));
}

//-----------------------------------------------------------------------------
void create_listening_socket(void)
{
if (!acceptor().is_open())
{
boost::system::error_code err;
acceptor().open(tcp::v4(), err);

SMART_DEBUG_OUT("[server]: Listening socket is opened " << "[" << err << "; " << err.message() << "]");

acceptor().bind(tcp::endpoint(address(), 5555), err);

SMART_DEBUG_OUT("[server]: Listening socket is bind " << acceptor().local_endpoint() << " [" << err << "; " <<
err.message() << "]");

acceptor().listen();
}
}

//-----------------------------------------------------------------------------
void destroy_listening_socket(void)
{
if (acceptor().is_open())
{
boost::system::error_code err;
acceptor().close(err);

SMART_DEBUG_OUT("[server]: Listening socket is closed [" << err << "; " << err.message() << "]");
}
}

//-----------------------------------------------------------------------------
void asio_accept_handler(const boost::system::error_code& error)
{
SMART_DEBUG_OUT("[server]: Asio accept handler invoked; [" << error << ", " << error.message() << "]");

if (error)
{
SMART_DEBUG_OUT("[server]: Accept error [" << error << ", " << error.message() << "]");
} else
{
// only one connection is allowed
destroy_listening_socket();

_session.initiate();

SMART_DEBUG_OUT("[server]: Client accepted; [remote ep = " << _session.remote_endpoint() << "; local ep = " <<
_session.local_endpoint() << "]");
}

SMART_DEBUG_OUT("[server]: Asio accept handler finished; [" << error << ", " << error.message() << "]");
}

public:
//-----------------------------------------------------------------------------
void handle_disconnected(const string& reason)
{
SMART_DEBUG_OUT("[server]: Disconnected handler invoked [" << reason << "]");

try
{
create_listening_socket();
start_accept();
}
catch (...)
{
SMART_DEBUG_OUT("[server]: Handle disconnected rose an UNEXPECTED exception");

BOOST_ASSERT(!"Handle disconnected rose an UNEXPECTED exception");
}
}

private:
//-----------------------------------------------------------------------------
boost::asio::ip::tcp::acceptor& acceptor(void) { return _acceptor; }
boost::asio::io_service& ioservice(void) { return _ioservice; }

private:
service_work_ptr _work;
io_service _ioservice;
tcp::acceptor _acceptor;
session _session;
}; // class server

//-----------------------------------------------------------------------------
void session::handle_disconnected(const boost::system::error_code& reason, const std::string& what)
{
SMART_DEBUG_OUT("[session]: Disconnected handler invoked; [" << reason << ", " << reason.message() << "]");

disconnect();

//throw disconnect_error(what + "[" + reason.message() + "]");
_server.handle_disconnected(what + "[" + reason.message() + "]");
}

//////////////////////////////////////////////////////////////////////////
// auxiliary

void print_help(void)
{
cout << "\nEcho test server, try telnet localhost 5555\n"
<< "press 'q' to quit (and strike enter on Linux)"
<< endl;
}

//------------------------------------------------------------------------
bool _is_working = true;
bool is_working(void) { return _is_working; }
void stop(void) { _is_working = false; }

//------------------------------------------------------------------------

#if defined (POSHW_WIN)

char get_char(void)
{
return char(_getch());
}

#elif defined (POSHW_UNIX)

char get_char(void)
{
return char(getchar());
}

#endif // POSHW_UNIX

//------------------------------------------------------------------------
void serving(void)
{
try
{
print_help();

server srv;

while (is_working())
{
srv.serve();
boost::this_thread::sleep(milliseconds(10));
}
} catch (const std::exception& e)
{
SMART_DEBUG_OUT("Error occurred: [" << e.what() << "]");
}
}

} // unnamed namespace

//------------------------------------------------------------------------
#if defined (POSHW_WIN)
int _tmain(int /*argc*/, _TCHAR* []/*argv[]*/)
#elif defined (POSHW_UNIX)
int main(int /*argc*/, char** /*argv*/)
#endif // POSHW_UNIX

{
boost::thread th(&serving);

for (char c = 0; c != 'q' && c != 'Q'; c = get_char())
{
;
}

stop();
th.join();

return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~

It seems that there is an issue with acceptor::async_accept in Boost.Asio (at least in Boost 1.46, try to check your
example with Boost 1.47). When the second async_accept is done within the completion handler (in io_service::poll) all
works fine (see above code). And when the second async_accept is called outside of the io_service::poll, accept
completion handler isn't called.

This issue really should be reported to the author of the Boost.Asio. Try to simplify your example and open an issue at
Asio bug tracker (http://sourceforge.net/tracker/?group_id=122478&atid=694037).

Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-23 10:50:26 UTC
Permalink
Hello, Marat,

I investigated the changes made.
Post by Marat Abrarov
It seems that there is an issue with acceptor::async_accept in Boost.Asio (at least in Boost 1.46, try to check your
example with Boost 1.47). When the second async_accept is done within the completion handler (in io_service::poll) all
works fine (see above code). And when the second async_accept is called outside of the io_service::poll, accept
completion handler isn't called.
May be I'm missing something, but I can't realize why you are talking
about the calling async_accept() from within the io_service::poll().

The differences are made leads that async_accept is called from within
session::process() and not from within io_service::poll().

The call graph is:

session::process()/
session::process_errors()/
session::handle_ch_error()/
session::handle_disconnected()/ <-- in original throws here
server::handle_disconnected()/
server::start_accept()/
boost::asio::tcp::acceptor::async_accept().

so, in original sample the async_connect is called from withing the
catch block.

The original call graph (in essence the same as above):

session::process()/
session::process_errors()/
session::handle_ch_error()/
session::handle_disconnected() => throw disconnect_error() =>
catch (const session::disconnect_error& e)
{
server::handle_disconnected()/
server::start_accept()/
boost::asio::tcp::acceptor::async_accept().
}

-----
void process_iteration(void)
{
try
{
ioservice().poll();
_session.process();
}
catch (const session::disconnect_error& e)
{
SMART_DEBUG_OUT("[server]: Disconnect error exception; [what: "
<< e.what() << "]");

handle_disconnected(e.what());
}
catch (const std::exception& e)
{
SMART_DEBUG_OUT("[server]: An UNEXPECTED std::exception [what: "
<< e.what() << "]");

BOOST_ASSERT(!"An UNEXPECTED std::exception!");
}
catch (...)
{
SMART_DEBUG_OUT("[server]: An UNKNOWN UNEXPECTED exception!");

BOOST_ASSERT(!"An UNKNOWN UNEXPECTED exception!");
}
}
-----

Many thanks for your time and answer.

Regards,
Dmitry
Marat Abrarov
2011-07-22 19:16:44 UTC
Permalink
Hi, Asio users.

BOOST_ASIO_DISABLE_EPOLL doesn't help - select-based reactor shows similar behavior.

Regards,
Marat Abrarov.
-----Original Message-----
Sent: Friday, July 22, 2011 11:03 PM
Subject: Re: [asio-users] A strange issue on Linux, but on Windows works perfectly
Hi, Dmitry.
Post by Dmitry Timoshenko
I added a simple check before write to the socket, the behaviour is not
changed, the issue is present anyway.
The link is the same
http://64.186.158.86:8400/asio-sample/socket_test2.zip
~~~~~~~~~~~~~~~~~~~~~~~~
///////////////////////////////////////////////////////////////////////////////
// main.cpp
//
// Pos hardware project. Socket test.
//
// Entry point.
//
// 2011, (c) Dmitry Timoshenko.
#include "auto_sence.hpp"
#include <boost/asio.hpp>
#include <boost/assert.hpp>
#include <boost/thread.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/date_time/posix_time/posix_time_duration.hpp>
#include <iostream>
#include <stdexcept>
#include <string>
#include <memory>
#include <vector>
#include <sstream>
#define SMART_DEBUG_OUT(expr) cout << std::dec << __FILE__ << "[" << __LINE__ << "]: " << expr <<
std::endl;
#if defined (POSHW_WIN)
# include <tchar.h>
# include <conio.h>
#elif defined (POSHW_UNIX)
# include <stdio.h>
# include <unistd.h>
#endif // POSHW_WIN
using std::cout;
using std::endl;
using std::string;
using boost::asio::ip::tcp;
using boost::asio::ip::address;
using boost::asio::io_service;
using boost::lexical_cast;
using boost::posix_time::milliseconds;
namespace
{
/** For debug purpose. */
template <typename IterT>
std::string to_string(const std::string& prefix, IterT first, IterT last)
{
std::stringstream ss;
ss << prefix << "[" << std::hex << std::showbase;
for ( ; first != last; )
{
ss << (static_cast<unsigned int>(*first++) & 0xff);
ss << (first == last? "]": ", ");
}
ss << std::dec;
return ss.str();
}
class server;
//////////////////////////////////////////////////////////////////////////
// session
class session
{
//////////////////////////////////////////////////////////////////////////
// interface
/** Disconnect error. */
class disconnect_error : public std::runtime_error
{
explicit disconnect_error(const std::string& what): std::runtime_error(what) { /* empty */ }
};
server& _server;
/** Constructs a session object. */
_socket(ioservice),
_connected(false),
_is_writing(false),
_server(the_server)
{
_local_buffer.assign(local_bufsz, 0);
}
/** Closes connection and destroys an object. */
~session(void) { disconnect(); }
/** Process outgoing messages and receives incoming if there are. */
void process(void)
{
// at first, process errors
process_errors();
}
/** Returns reference to internal asio socket. */
boost::asio::ip::tcp::socket& socket(void) { return _socket; }
/** Initializes the session. */
void initiate(void)
{
clear_state();
start_read();
connected(true);
}
/** Session remote endpoint. */
tcp::endpoint remote_endpoint(void) const
{
boost::system::error_code err;
return socket().remote_endpoint(err);
}
/** Session local endpoint. */
tcp::endpoint local_endpoint(void) const
{
boost::system::error_code err;
return socket().local_endpoint(err);
}
//////////////////////////////////////////////////////////////////////////
// private stuff
enum { local_bufsz = 0x1000 };
typedef std::vector<char> local_buffer_type;
typedef std::string out_buffer_type;
typedef boost::shared_ptr<out_buffer_type> out_buffer_ptr;
void disconnect(void)
{
if (connected())
{
connected(false);
boost::system::error_code err;
socket().shutdown(tcp::socket::shutdown_both, err);
SMART_DEBUG_OUT("[session]: SOCKET SHUTDOWN; [remote: " << remote_endpoint() << "; local: " <<
local_endpoint() <<
"; error_code: " << err << "; " << err.message() << "]");
}
close_socket();
}
//-----------------------------------------------------------------------------
void close_socket(void)
{
if (socket().is_open())
{
boost::system::error_code err;
socket().close(err);
SMART_DEBUG_OUT("[session]: SOCKET CLOSED; [remote: " << remote_endpoint() << "; local: " <<
local_endpoint() <<
"; error_code: " << err << "; " << err.message() << "]");
}
}
//-----------------------------------------------------------------------------
void clear_state(void)
{
connected(false);
_read_error.clear();
_write_error.clear();
_buffer.clear();
_local_buffer.assign(local_bufsz, 0);
}
//-----------------------------------------------------------------------------
void process_errors(void)
{
// handle any channel error
handle_ch_error();
}
//-----------------------------------------------------------------------------
void handle_ch_error(void)
{
if (_read_error || _write_error)
{
const boost::system::error_code error = _read_error? _read_error: _write_error;
_read_error.clear();
_write_error.clear();
SMART_DEBUG_OUT("[session]: Channel error; [" << error << ", " << error.message() << "]");
handle_disconnected(error, "Channel error");
}
}
//-----------------------------------------------------------------------------
void handle_disconnected(const boost::system::error_code& reason, const std::string& what);
//////////////////////////////////////////////////////////////////////////
// boost asio manipulators
void start_read(void)
{
_socket.async_read_some(boost::asio::buffer(_local_buffer),
boost::bind(&session::asio_read_handler, this,
boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
}
//-----------------------------------------------------------------------------
void start_write(const string& s)
{
if (!is_writing())
{
is_writing(true);
BOOST_ASSERT(!s.empty() && " - Output buffer can't be empty");
out_buffer_ptr out_buf(new out_buffer_type(s));
// start asio async write
boost::asio::async_write(_socket, boost::asio::buffer(*out_buf),
boost::bind(&session::asio_write_handler, this, boost::asio::placeholders::error, out_buf));
}
}
//////////////////////////////////////////////////////////////////////////
// boost asio handlers
void asio_read_handler(const boost::system::error_code& error, size_t n)
{
SMART_DEBUG_OUT("[session]: Async read completed invoked");
_read_error = error;
if (error)
{
SMART_DEBUG_OUT("[session]: Read channel error; [" << error << "; " << error.message() << "]");
} else
{
// get data
BOOST_ASSERT(n <= _local_buffer.size() && " - Buffer overrun detected");
// at first, start next read operation anyway
start_read();
const size_t sz = std::min(n, _local_buffer.size());
const string s(_local_buffer.begin(), _local_buffer.begin() + sz);
_buffer += s;
SMART_DEBUG_OUT("[session]: Incoming data '" << s << "' = " <<
to_string(lexical_cast<string>(unsigned(sz))
+ " byte(s) ", _local_buffer.begin(), _local_buffer.begin() + sz));
start_write(s);
}
SMART_DEBUG_OUT("[session]: Async read completed finished");
process_errors();
}
//-----------------------------------------------------------------------------
void asio_write_handler(const boost::system::error_code& error, out_buffer_ptr buf)
{
SMART_DEBUG_OUT("[session]: Async write completed invoked");
_write_error = error;
is_writing(false);
if (error)
{
SMART_DEBUG_OUT("[session]: Write channel error; [" << error << "; " << error.message() << "]");
} else
{
SMART_DEBUG_OUT(to_string(string("[session]: Sent ") + lexical_cast<string>(unsigned(buf-
Post by Dmitry Timoshenko
size()))
+ " byte(s) ", buf->begin(), buf->end()));
}
SMART_DEBUG_OUT("[session]: Async write completed finished");
process_errors();
}
//-----------------------------------------------------------------------------
bool connected(void) const { return socket().is_open() && _connected; }
void connected(bool c) { _connected = c; }
const boost::asio::ip::tcp::socket& socket(void) const { return _socket; }
bool is_writing(void) const { return _is_writing; }
void is_writing(bool writing) { _is_writing = writing; }
boost::asio::ip::tcp::socket _socket;
std::string _buffer;
local_buffer_type _local_buffer;
bool _connected;
bool _is_writing;
//////////////////////////////////////////////////////////////////////////
// error flags
boost::system::error_code _read_error;
boost::system::error_code _write_error;
}; // class session
//////////////////////////////////////////////////////////////////////////
// server
class server
{
//////////////////////////////////////////////////////////////////////////
// interface
/** Constructs a server object. */
_acceptor(ioservice()),
_session(ioservice(), *this)
{
create_listening_socket();
start_accept();
}
/** Serve iteration.
*
* Serves the underlying socket and process the incoming data.
*/
void serve(void) { process_iteration(); }
//////////////////////////////////////////////////////////////////////////
// private stuff
// types
typedef std::auto_ptr<boost::asio::io_service::work> service_work_ptr;
void process_iteration(void)
{
try
{
ioservice().poll();
_session.process();
}
catch (const session::disconnect_error& e)
{
SMART_DEBUG_OUT("[server]: Disconnect error exception; [what: " << e.what() << "]");
handle_disconnected(e.what());
}
catch (const std::exception& e)
{
SMART_DEBUG_OUT("[server]: An UNEXPECTED std::exception [what: " << e.what() << "]");
BOOST_ASSERT(!"An UNEXPECTED std::exception!");
}
catch (...)
{
SMART_DEBUG_OUT("[server]: An UNKNOWN UNEXPECTED exception!");
BOOST_ASSERT(!"An UNKNOWN UNEXPECTED exception!");
}
}
//-----------------------------------------------------------------------------
void start_accept(void)
{
acceptor().async_accept(_session.socket(),
boost::bind(&server::asio_accept_handler, this, boost::asio::placeholders::error));
}
//-----------------------------------------------------------------------------
void create_listening_socket(void)
{
if (!acceptor().is_open())
{
boost::system::error_code err;
acceptor().open(tcp::v4(), err);
SMART_DEBUG_OUT("[server]: Listening socket is opened " << "[" << err << "; " << err.message()
<< "]");
acceptor().bind(tcp::endpoint(address(), 5555), err);
SMART_DEBUG_OUT("[server]: Listening socket is bind " << acceptor().local_endpoint() << " [" <<
err << "; " <<
err.message() << "]");
acceptor().listen();
}
}
//-----------------------------------------------------------------------------
void destroy_listening_socket(void)
{
if (acceptor().is_open())
{
boost::system::error_code err;
acceptor().close(err);
SMART_DEBUG_OUT("[server]: Listening socket is closed [" << err << "; " << err.message() <<
"]");
}
}
//-----------------------------------------------------------------------------
void asio_accept_handler(const boost::system::error_code& error)
{
SMART_DEBUG_OUT("[server]: Asio accept handler invoked; [" << error << ", " << error.message() <<
"]");
if (error)
{
SMART_DEBUG_OUT("[server]: Accept error [" << error << ", " << error.message() << "]");
} else
{
// only one connection is allowed
destroy_listening_socket();
_session.initiate();
SMART_DEBUG_OUT("[server]: Client accepted; [remote ep = " << _session.remote_endpoint() << ";
local ep = " <<
_session.local_endpoint() << "]");
}
SMART_DEBUG_OUT("[server]: Asio accept handler finished; [" << error << ", " << error.message() <<
"]");
}
//-----------------------------------------------------------------------------
void handle_disconnected(const string& reason)
{
SMART_DEBUG_OUT("[server]: Disconnected handler invoked [" << reason << "]");
try
{
create_listening_socket();
start_accept();
}
catch (...)
{
SMART_DEBUG_OUT("[server]: Handle disconnected rose an UNEXPECTED exception");
BOOST_ASSERT(!"Handle disconnected rose an UNEXPECTED exception");
}
}
//-----------------------------------------------------------------------------
boost::asio::ip::tcp::acceptor& acceptor(void) { return _acceptor; }
boost::asio::io_service& ioservice(void) { return _ioservice; }
service_work_ptr _work;
io_service _ioservice;
tcp::acceptor _acceptor;
session _session;
}; // class server
//-----------------------------------------------------------------------------
void session::handle_disconnected(const boost::system::error_code& reason, const std::string& what)
{
SMART_DEBUG_OUT("[session]: Disconnected handler invoked; [" << reason << ", " << reason.message()
<< "]");
disconnect();
//throw disconnect_error(what + "[" + reason.message() + "]");
_server.handle_disconnected(what + "[" + reason.message() + "]");
}
//////////////////////////////////////////////////////////////////////////
// auxiliary
void print_help(void)
{
cout << "\nEcho test server, try telnet localhost 5555\n"
<< "press 'q' to quit (and strike enter on Linux)"
<< endl;
}
//------------------------------------------------------------------------
bool _is_working = true;
bool is_working(void) { return _is_working; }
void stop(void) { _is_working = false; }
//------------------------------------------------------------------------
#if defined (POSHW_WIN)
char get_char(void)
{
return char(_getch());
}
#elif defined (POSHW_UNIX)
char get_char(void)
{
return char(getchar());
}
#endif // POSHW_UNIX
//------------------------------------------------------------------------
void serving(void)
{
try
{
print_help();
server srv;
while (is_working())
{
srv.serve();
boost::this_thread::sleep(milliseconds(10));
}
} catch (const std::exception& e)
{
SMART_DEBUG_OUT("Error occurred: [" << e.what() << "]");
}
}
} // unnamed namespace
//------------------------------------------------------------------------
#if defined (POSHW_WIN)
int _tmain(int /*argc*/, _TCHAR* []/*argv[]*/)
#elif defined (POSHW_UNIX)
int main(int /*argc*/, char** /*argv*/)
#endif // POSHW_UNIX
{
boost::thread th(&serving);
for (char c = 0; c != 'q' && c != 'Q'; c = get_char())
{
;
}
stop();
th.join();
return 0;
}
~~~~~~~~~~~~~~~~~~~~~~~~
It seems that there is an issue with acceptor::async_accept in Boost.Asio (at least in Boost 1.46, try
to check your
example with Boost 1.47). When the second async_accept is done within the completion handler (in
io_service::poll) all
works fine (see above code). And when the second async_accept is called outside of the
io_service::poll, accept
completion handler isn't called.
This issue really should be reported to the author of the Boost.Asio. Try to simplify your example and
open an issue at
Asio bug tracker (http://sourceforge.net/tracker/?group_id=122478&atid=694037).
Regards,
Marat Abrarov.
------------------------------------------------------------------------------
10 Tips for Better Web Security
Web security, SSL, hacker attacks & Denial of Service (DoS), private keys,
security Microsoft Exchange, secure Instant Messaging, and much more.
http://www.accelacomm.com/jaw/sfnl/114/51426210/
_______________________________________________
asio-users mailing list
https://lists.sourceforge.net/lists/listinfo/asio-users
_______________________________________________
Using Asio? List your project at
http://think-async.com/Asio/WhoIsUsingAsio
Marat Abrarov
2011-07-22 20:27:53 UTC
Permalink
Post by Marat Abrarov
Hi, Dmitry.
I added a simple check before write to the socket, the behaviour is not
changed, the issue is present anyway.
The link is the same
http://64.186.158.86:8400/asio-sample/socket_test2.zip
Changing server::process_iteration in a such way helps too:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void process_iteration(void)
{
boost::asio::io_service::work work(ioservice());
try
{
ioservice().poll();
_session.process();
}
catch (const session::disconnect_error& e)
{
SMART_DEBUG_OUT("[server]: Disconnect error exception; [what: " << e.what() << "]");

handle_disconnected(e.what());

}
catch (const std::exception& e)
{
SMART_DEBUG_OUT("[server]: An UNEXPECTED std::exception [what: " << e.what() << "]");

BOOST_ASSERT(!"An UNEXPECTED std::exception!");
}
catch (...)
{
SMART_DEBUG_OUT("[server]: An UNKNOWN UNEXPECTED exception!");

BOOST_ASSERT(!"An UNKNOWN UNEXPECTED exception!");
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

It really looks like a bug in Asio.

Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-23 10:59:24 UTC
Permalink
Hello, Marat,

I also found out that boost::asio::io_service::work helps, however I
didn't strictly check this.
I have the same issue with boost::asio::serial_port (handlers are not
called).

When I started using boost::asio::io_service::work it helped with
sockets and serial_port both.
But unfortunately, after a few minutes (~5 - 10 min) using of serial_port
the issue occurs again (. Handlers just stop calling by the
io_service::poll() (sockets are not checked for this).

Thank you.
Marat Abrarov
2011-07-23 08:05:53 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
I added a simple check before write to the socket, the behaviour is not
changed, the issue is present anyway.
The link is the same
http://64.186.158.86:8400/asio-sample/socket_test2.zip
I found solution of your (and it seems it was mine too) problem - RTFM 8).
Changing server::process_iteration in a such way helps:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void process_iteration(void)
{
//boost::asio::io_service::work work(ioservice());
try
{
ioservice().reset();
ioservice().poll();
_session.process();
}
catch (const session::disconnect_error& e)
{
SMART_DEBUG_OUT("[server]: Disconnect error exception; [what: " << e.what() << "]");

handle_disconnected(e.what());

}
catch (const std::exception& e)
{
SMART_DEBUG_OUT("[server]: An UNEXPECTED std::exception [what: " << e.what() << "]");

BOOST_ASSERT(!"An UNEXPECTED std::exception!");
}
catch (...)
{
SMART_DEBUG_OUT("[server]: An UNKNOWN UNEXPECTED exception!");

BOOST_ASSERT(!"An UNKNOWN UNEXPECTED exception!");
}
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

After the first disconnect (initiated by client) we can see such stack trace:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
0 boost::asio::detail::task_io_service::stop_all_threads task_io_service.ipp 310 0x8052f52
1 boost::asio::detail::task_io_service::stop task_io_service.ipp 195 0x8052b9e
2 boost::asio::detail::task_io_service::work_finished task_io_service.hpp 86 0x80512d0
3 boost::asio::detail::task_io_service::work_finished_on_block_exit::~work_finished_on_block_exit
task_io_service.ipp 55
0x805280b
4 boost::asio::detail::task_io_service::do_one task_io_service.ipp 281 0x8052ea6
5 boost::asio::detail::task_io_service::poll task_io_service.ipp 170 0x8052b24
6 boost::asio::io_service::poll io_service.ipp 83 0x8053198
7 (anonymous namespace)::server::process_iteration main.cpp 341 0x804cb74
8 (anonymous namespace)::server::serve main.cpp 327 0x804cb57
9 (anonymous namespace)::serving main.cpp 496 0x804da32
10 boost::detail::thread_data<void (*)()>::run thread.hpp 61 0x805e35d
11 thread_proxy 0 0x806106c
12 start_thread /lib/i386-linux-gnu/libpthread.so.0 0 0x134e99
13 clone /lib/i386-linux-gnu/libc.so.6 0 0x34573e
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

This stack trace means that the reactor (asio::detail::task_io_service) used internally by asio::io_service is stopped
because it "run out of work". The only way to make this reactor continue work (execute completion handlers) is to call
asio::io_service::reset.

Ok. Let's look in Asio docs:
http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/reference/io_service/reset.html
http://www.boost.org/doc/libs/1_46_0/doc/html/boost_asio/reference/io_service/reset.html :
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This function (asio::io_service::reset) must be called prior to any second or later set of invocations of the run(),
run_one(), poll() or poll_one() functions when a previous invocation of these functions returned due to the io_service
being stopped or running out of work (it's our case!). This function allows the io_service to reset any internal state,
such as a "stopped" flag.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-23 11:10:13 UTC
Permalink
Hello, Marat,

Sounds hopefully, but are you sure that the call io_service::reset()
each time before io_service::poll() will not affect the right processing?

How the io_service::reset() deal with pending operations? Will they be
continued or may be dropped?
Post by Marat Abrarov
I found solution of your (and it seems it was mine too) problem - RTFM 8).
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void process_iteration(void)
{
//boost::asio::io_service::work work(ioservice());
try
{
ioservice().reset();
ioservice().poll();
_session.process();
}
catch (const session::disconnect_error& e)
{
SMART_DEBUG_OUT("[server]: Disconnect error exception; [what: "<< e.what()<< "]");
handle_disconnected(e.what());
}
catch (const std::exception& e)
{
SMART_DEBUG_OUT("[server]: An UNEXPECTED std::exception [what: "<< e.what()<< "]");
BOOST_ASSERT(!"An UNEXPECTED std::exception!");
}
catch (...)
{
SMART_DEBUG_OUT("[server]: An UNKNOWN UNEXPECTED exception!");
BOOST_ASSERT(!"An UNKNOWN UNEXPECTED exception!");
}
}
Thank you for your help.
Marat Abrarov
2011-07-23 11:34:56 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
Sounds hopefully, but are you sure that the call io_service::reset()
each time before io_service::poll() will not affect the right processing?
No it will not affect. Your code doesn't use io_service::stopped() (new Asio feature in Boost 1.47) to check if process
loop have to stop - your example has an additional "stop" flag instead.
Post by Dmitry Timoshenko
How the io_service::reset() deal with pending operations?
Will they be continued or may be dropped?
It only resets internal io_service's "stop" flag. It doesn't cancel any pending operations.
Also note that if io_service has any pending operations right after the exit from io_service::poll then "stop" flag will
have "false" value because there are some pending (but yet not complete - so there is no handlers ready to be executed)
operations.

Regards,
Marat Abrarov.
Marat Abrarov
2011-07-25 10:42:35 UTC
Permalink
Hi, Dmitry.
Post by Dmitry Timoshenko
I little bit changed your sample.
You are quite right.
I had the same result converting your example to process events within completion handlers. Didn't I post it?

A little advice. It's really better to use io_service::work to prevent io_service instance going out of work.
io_service::reset works with mutex so it'll generate additional (!) mutex-acquisition/mutex-release operations (with
probable memory barriers).

Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-25 18:13:37 UTC
Permalink
Hello, Marat,
Post by Marat Abrarov
I had the same result converting your example to process events within completion handlers. Didn't I post it?
Sorry, I should have used merge tool immediately...

Thank you for your time.

Wanted to ask.
I have similar issue with asio::serial_port, unfortunately...
If I don't use either io_sevice::work or io_service::reset() the
handlers (read and write) are not called at all. But if I add
io_sevice::work or io_service::reset() it start working, but after a
short period of time issue appears anyway. It happens on Ubuntu 10.04,
gcc 4.4.3, boost 1.47.0

So, I thought, may be io_service::run() can work better? May be it's
used more frequently and tested better?

Thank you.
Arash Partow
2011-07-26 02:16:43 UTC
Permalink
Post by Dmitry Timoshenko
So, I thought, may be io_service::run() can work better? May be it's
used more frequently and tested better?
Can you add some debug to the method io_service::~io_service() found in asio/impl/io_service.ipp line 49, recompile/build.
Dmitry Timoshenko
2011-07-27 18:40:49 UTC
Permalink
Post by Arash Partow
Can you add some debug to the method io_service::~io_service() found in asio/impl/io_service.ipp line 49, recompile/build.
Yes, give it and I'll do it.
Marat Abrarov
2011-07-26 05:10:39 UTC
Permalink
Post by Dmitry Timoshenko
So, I thought, may be io_service::run() can work better? May be it's
used more frequently and tested better?
I think it is not implemented and tested better. poll/poll_one/run/run_one are implemented in the same terms (do_one).
So they are very similar.
Serial port? At Windows VSPE (emulator) gives me eof even there are more data to be send. What do you use at Linux to
emulate serial port (I'm interested to test nmea_client from asio-samples)?

Regards,
Marat Abrarov.
Dmitry Timoshenko
2011-07-27 19:09:47 UTC
Permalink
Hello, Marat,

I change the code (working with serial ports) to use io_service::run()
instead of io_service::poll() and it started working properly,
fortunately. I'm happy.
Post by Marat Abrarov
I think it is not implemented and tested better. poll/poll_one/run/run_one are implemented in the same terms (do_one).
So they are very similar.
Serial port? At Windows VSPE (emulator) gives me eof even there are more data to be send. What do you use at Linux to
emulate serial port (I'm interested to test nmea_client from asio-samples)?
I'm not sure what you exactly mean, emulate serial port. I'm using
hardware uart located on the mainboard. I also couldn't find nmea_client
in asio examples (looked for by the name nmea_client).

Thank you.
Dmitry Timoshenko
2011-07-29 16:13:41 UTC
Permalink
Hello, Marat and everybody who concerned this post.

I eventually found out that the bug with serial ports is not asio bug,
It seems this is the Ubuntu 10.04 issue. I replaced the asio code
working with serial ports with the native code and I still have the same
issue, unfortunately, I also found mention of the same issue on Ubuntu
forums.

Thank you everybody.
Arash Partow
2011-07-30 05:00:00 UTC
Permalink
Post by Dmitry Timoshenko
I eventually found out that the bug with serial ports is not asio bug,
It seems this is the Ubuntu 10.04 issue.
Please post the relevant links/bug reports regarding the issue.

Continue reading on narkive:
Loading...