Thread: [asio-users] Problem with deadline timer and async connect interaction
Brought to you by:
chris_kohlhoff
From: Zachary T. <div...@gm...> - 2009-08-03 19:03:01
|
Hello, I've looked at the sample but it's slightly different than what I'm trying to do so if anyone could offer some advice I'd appreciate it. I have a server that waits for connections from clients. I've implemented a timeout on the async_accept because at any point the server might be stopped manually by an administrator. This triggers a global variable to be set, and so every 3 seconds I want to check the value of this global variable and shutdown. Problem is that this was originally designed without boost::asio according to a one-connection / one-thread request model. It's too much work to change this architecture completely so I have to stick with it for now. So when I get a new connection I want to spawn a new thread and pass the socket and the corresponding io_service to the thread. What is happening is that when I get a new connection, I try to post a message to the main thread to accept more connections. And at this point my deadline_timer starts erroring out saying "The I/O operation has been aborted because of either a thread exit or an application request." Here's the relevant code: class accept_handler { typedef boost::shared_ptr<io_service> service_ptr; typedef boost::shared_ptr<tcp::socket> socket_ptr ; public: accept_handler(io_service& service) : service_(service), timer_(service), acceptor_(service, tcp::endpoint_v4(g_addr), g_port) { accept_connections(); } private: void accept_connections() { service_ptr peer_service(new io_service()); socket_ptr peer_socket(new tcp::socket(*peer_service)); acceptor_.async_accept(*peer_socket, boost::bind(&accept_handler::handle_accept, this, peer_service, peer_socket, placeholders::error)); timer_.expires_from_now(boost::posix_time::seconds(3)); timer_.async_wait(boost::bind(&accept_handler::timer_expired, this, placeholders::error)); } void handle_accept(service_ptr service, socket_ptr socket, boost::system::error_code ec) { if (ec || g_is_server_shutting_down) close(); else { connections_.create_thread(boost::bind(&accept_handler::run_connection_thread, this, service, socket)); service_.post(boost::bind(&accept_handler::accept_connetions, this)); } } void timer_expired(boost::system::error_code ec) { if (ec || g_is_server_shutting_down) close(); else { timer_.expires_from_now(boost::posix_time::seconds(3)); timer_.async_wait(boost::bind(&accept_handler::timer_expired, this, placehodlers::error)); } } io_service& service_; deadline_timer timer_; tcp::acceptor acceptor_; boost::thread_group connections_; }; A few minor irrelevant code snippets are omitted but this is the important stuff. As soon as a connection occurs and handle_accept() finishes executing, a new thread runs the request as shown above in the line to connections_.create_thread(). When this thread finishes, it exits and immediately handle_accept is called with an error of 995, which on windows is as described at the top of this post. I don't understand this behavior though, why does the child thread exiting have any relation to what happens on the main thread's io_service? I was careful to make the correct distinction (as far as I know) between different instances of io_service: 'acceptor_' and 'timer_' get created with the io_service instance for the main thread, and child threads get a private instance of io_service. For the record, io_service::run() is never being called on the child thread with the child's private io_service instance, currently it just uses synchronous read/write methods of the socket. |
From: Christopher K. <ch...@ko...> - 2009-08-10 12:10:08
|
Zachary Turner wrote: > void accept_connections() > { > service_ptr peer_service(new io_service()); > socket_ptr peer_socket(new tcp::socket(*peer_service)); > > acceptor_.async_accept(*peer_socket, > boost::bind(&accept_handler::handle_accept, this, peer_service, > peer_socket, placeholders::error)); > > timer_.expires_from_now(boost::posix_time::seconds(3)); When you call expires_from_now(), it will cancel any existing timer operations. They will complete with asio::error::operation_aborted. As written, your timer_expired() function will simply shut everything down. > timer_.async_wait(boost::bind(&accept_handler::timer_expired, > this, placeholders::error)); > } Maybe it would be cleaner to have two distinct chains of async operations: accept_connections() -> async_accept() --> handle_accept() -> accept_connections() -> async_accept() --> handle_accept() ... and async_wait() --> timer_expired() -> async_wait() --> timer_expired() ... both started from the constructor. That is, don't touch the timer at all in the first chain. Cheers, Chris |