From d41d2d15e91d090bd083f94682cb8b20fa7731e6 Mon Sep 17 00:00:00 2001 From: Christopher Kohlhoff Date: Wed, 2 Mar 2011 08:27:32 +0000 Subject: [PATCH] * Add support for the fork() system call. Programs that use fork must call io_service.notify_fork() at the appropriate times. Two new examples have been added showing how to use this feature. Refs #3238, #4162. * Clean up the handling of errors reported by the close() system call. In particular, assume that most operating systems won't have close() fail with EWOULDBLOCK, but if it does then set blocking mode and restart the call. If any other error occurs we assume the descriptor is closed. Refs #3307. * EV_ONESHOT seems to cause problems on some versions of Mac OS X, with the io_service destructor getting stuck inside the close() system call. Use EV_CLEAR instead. Refs #5021. * Include function name in exception what() messages. * Fix insufficient initialisers warning with MinGW. * Make the shutdown_service() member functions private. * Add archetypes for testing socket option functions. * Add missing lock in signal_set_service::cancel(). * Fix copy/paste error in SignalHandler example. * The signal header needs to be included in signal_set_service.hpp so that we can use constants like NSIG and SIGRTMAX. * Don't use Boost.Thread's convenience header. Use the header file that is specifically for the boost::thread class instead. [SVN r69467] --- doc/Jamfile.v2 | 2 +- doc/examples.qbk | 14 ++ example/chat/chat_client.cpp | 2 +- example/echo/blocking_tcp_echo_server.cpp | 2 +- example/fork/Jamfile | 45 ++++ example/fork/Jamfile.v2 | 53 +++++ example/fork/daemon.cpp | 189 +++++++++++++++++ example/fork/process_per_connection.cpp | 151 ++++++++++++++ example/http/server2/io_service_pool.cpp | 2 +- example/http/server3/server.cpp | 2 +- example/local/connect_pair.cpp | 2 +- example/services/logger_service.hpp | 2 +- example/timeouts/async_tcp_client.cpp | 3 +- example/timeouts/blocking_tcp_client.cpp | 3 +- example/timeouts/server.cpp | 3 +- example/tutorial/timer5/timer.cpp | 2 +- include/boost/asio/basic_datagram_socket.hpp | 16 +- include/boost/asio/basic_deadline_timer.hpp | 14 +- include/boost/asio/basic_raw_socket.hpp | 16 +- .../boost/asio/basic_seq_packet_socket.hpp | 6 +- include/boost/asio/basic_serial_port.hpp | 24 +-- include/boost/asio/basic_signal_set.hpp | 20 +- include/boost/asio/basic_socket.hpp | 48 ++--- include/boost/asio/basic_socket_acceptor.hpp | 40 ++-- include/boost/asio/basic_socket_streambuf.hpp | 4 +- include/boost/asio/basic_stream_socket.hpp | 12 +- .../boost/asio/datagram_socket_service.hpp | 12 +- include/boost/asio/deadline_timer_service.hpp | 12 +- .../boost/asio/detail/dev_poll_reactor.hpp | 13 ++ include/boost/asio/detail/epoll_reactor.hpp | 9 + .../detail/eventfd_select_interrupter.hpp | 9 + .../boost/asio/detail/impl/descriptor_ops.ipp | 21 +- .../asio/detail/impl/dev_poll_reactor.ipp | 89 +++++++- .../boost/asio/detail/impl/epoll_reactor.ipp | 96 ++++++++- .../impl/eventfd_select_interrupter.ipp | 20 ++ .../boost/asio/detail/impl/kqueue_reactor.ipp | 106 ++++++++-- .../detail/impl/pipe_select_interrupter.ipp | 20 ++ .../impl/reactive_descriptor_service.ipp | 11 +- .../impl/reactive_socket_service_base.ipp | 13 +- .../detail/impl/resolver_service_base.ipp | 19 ++ .../boost/asio/detail/impl/select_reactor.ipp | 15 ++ .../asio/detail/impl/service_registry.ipp | 30 +++ .../asio/detail/impl/signal_set_service.ipp | 192 ++++++++++++------ include/boost/asio/detail/impl/socket_ops.ipp | 51 +++-- .../detail/impl/socket_select_interrupter.ipp | 20 ++ include/boost/asio/detail/kqueue_reactor.hpp | 9 + .../asio/detail/pipe_select_interrupter.hpp | 9 + .../asio/detail/resolver_service_base.hpp | 3 + include/boost/asio/detail/select_reactor.hpp | 8 + .../boost/asio/detail/service_registry.hpp | 3 + .../boost/asio/detail/signal_set_service.hpp | 10 + .../asio/detail/socket_select_interrupter.hpp | 9 + .../boost/asio/detail/win_static_mutex.hpp | 2 +- include/boost/asio/impl/connect.hpp | 8 +- include/boost/asio/impl/io_service.ipp | 9 + include/boost/asio/impl/read.hpp | 8 +- include/boost/asio/impl/read_at.hpp | 8 +- include/boost/asio/impl/read_until.hpp | 8 +- include/boost/asio/impl/write.hpp | 8 +- include/boost/asio/impl/write_at.hpp | 8 +- include/boost/asio/io_service.hpp | 73 ++++++- include/boost/asio/ip/basic_resolver.hpp | 4 +- include/boost/asio/ip/resolver_service.hpp | 18 +- include/boost/asio/local/connect_pair.hpp | 2 +- include/boost/asio/posix/basic_descriptor.hpp | 20 +- .../asio/posix/basic_stream_descriptor.hpp | 4 +- .../asio/posix/stream_descriptor_service.hpp | 12 +- include/boost/asio/raw_socket_service.hpp | 12 +- .../boost/asio/seq_packet_socket_service.hpp | 12 +- include/boost/asio/serial_port_service.hpp | 12 +- include/boost/asio/signal_set_service.hpp | 18 +- .../boost/asio/socket_acceptor_service.hpp | 12 +- include/boost/asio/ssl/context_service.hpp | 10 +- include/boost/asio/ssl/stream_service.hpp | 10 +- include/boost/asio/stream_socket_service.hpp | 12 +- include/boost/asio/windows/basic_handle.hpp | 8 +- .../windows/basic_random_access_handle.hpp | 4 +- .../asio/windows/basic_stream_handle.hpp | 4 +- .../windows/random_access_handle_service.hpp | 12 +- .../asio/windows/stream_handle_service.hpp | 12 +- test/archetypes/gettable_socket_option.hpp | 54 +++++ test/archetypes/settable_socket_option.hpp | 49 +++++ test/deadline_timer.cpp | 2 +- test/io_service.cpp | 2 +- test/ip/tcp.cpp | 24 ++- test/ip/udp.cpp | 25 ++- test/strand.cpp | 2 +- 87 files changed, 1595 insertions(+), 379 deletions(-) create mode 100644 example/fork/Jamfile create mode 100644 example/fork/Jamfile.v2 create mode 100644 example/fork/daemon.cpp create mode 100644 example/fork/process_per_connection.cpp create mode 100644 test/archetypes/gettable_socket_option.hpp create mode 100644 test/archetypes/settable_socket_option.hpp diff --git a/doc/Jamfile.v2 b/doc/Jamfile.v2 index 4d1aef6c..68bd119b 100644 --- a/doc/Jamfile.v2 +++ b/doc/Jamfile.v2 @@ -25,7 +25,7 @@ install images html/boost_asio ; -local example-names = allocation buffers chat echo http/client http/server +local example-names = allocation buffers chat echo fork http/client http/server http/server2 http/server3 http/server4 icmp invocation iostreams local multicast nonblocking porthopper serialization services socks4 ssl timeouts timers windows ; diff --git a/doc/examples.qbk b/doc/examples.qbk index d61d7d0a..04288495 100644 --- a/doc/examples.qbk +++ b/doc/examples.qbk @@ -53,6 +53,20 @@ and asynchronous operations. * [@boost_asio/example/echo/blocking_udp_echo_server.cpp] +[heading Fork] + +These POSIX-specific examples show how to use Boost.Asio in conjunction with the +`fork()` system call. The first example illustrates the steps required to start +a daemon process: + +* [@boost_asio/example/fork/daemon.cpp] + +The second example demonstrates how it is possible to fork a process from +within a completion handler. + +* [@boost_asio/example/fork/process_per_connection.cpp] + + [heading HTTP Client] Example programs implementing simple HTTP 1.0 clients. These examples show how diff --git a/example/chat/chat_client.cpp b/example/chat/chat_client.cpp index 6a0d70d4..dfda97d1 100644 --- a/example/chat/chat_client.cpp +++ b/example/chat/chat_client.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include "chat_message.hpp" using boost::asio::ip::tcp; diff --git a/example/echo/blocking_tcp_echo_server.cpp b/example/echo/blocking_tcp_echo_server.cpp index d67aee4c..c52a6d7b 100644 --- a/example/echo/blocking_tcp_echo_server.cpp +++ b/example/echo/blocking_tcp_echo_server.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include using boost::asio::ip::tcp; diff --git a/example/fork/Jamfile b/example/fork/Jamfile new file mode 100644 index 00000000..343315f2 --- /dev/null +++ b/example/fork/Jamfile @@ -0,0 +1,45 @@ +# +# Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# + +subproject libs/asio/example/fork ; + +project boost : $(BOOST_ROOT) ; + +if $(UNIX) +{ + switch $(JAMUNAME) + { + case SunOS* : + { + SOCKET_LIBS = socket nsl ; + } + } +} + +exe daemon + : @boost/libs/system/build/boost_system + daemon.cpp + : $(BOOST_ROOT) + ../../../.. + BOOST_ALL_NO_LIB=1 + multi + <*>ws2_32 + <*>mswsock + $(SOCKET_LIBS) + ; + +exe process_per_connection + : @boost/libs/system/build/boost_system + process_per_connection.cpp + : $(BOOST_ROOT) + ../../../.. + BOOST_ALL_NO_LIB=1 + multi + <*>ws2_32 + <*>mswsock + $(SOCKET_LIBS) + ; diff --git a/example/fork/Jamfile.v2 b/example/fork/Jamfile.v2 new file mode 100644 index 00000000..916d9bff --- /dev/null +++ b/example/fork/Jamfile.v2 @@ -0,0 +1,53 @@ +# +# Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +# +# Distributed under the Boost Software License, Version 1.0. (See accompanying +# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +# + +import os ; + +if [ os.name ] = SOLARIS +{ + lib socket ; + lib nsl ; +} +else if [ os.name ] = NT +{ + lib ws2_32 ; + lib mswsock ; +} +else if [ os.name ] = HPUX +{ + lib ipv6 ; +} + +exe daemon + : daemon.cpp + /boost/system//boost_system + : BOOST_ALL_NO_LIB=1 + multi + SOLARIS:socket + SOLARIS:nsl + NT:_WIN32_WINNT=0x0501 + NT,gcc:ws2_32 + NT,gcc:mswsock + NT,gcc-cygwin:__USE_W32_SOCKETS + HPUX,gcc:_XOPEN_SOURCE_EXTENDED + HPUX:ipv6 + ; + +exe process_per_connection + : process_per_connection.cpp + /boost/system//boost_system + : BOOST_ALL_NO_LIB=1 + multi + SOLARIS:socket + SOLARIS:nsl + NT:_WIN32_WINNT=0x0501 + NT,gcc:ws2_32 + NT,gcc:mswsock + NT,gcc-cygwin:__USE_W32_SOCKETS + HPUX,gcc:_XOPEN_SOURCE_EXTENDED + HPUX:ipv6 + ; diff --git a/example/fork/daemon.cpp b/example/fork/daemon.cpp new file mode 100644 index 00000000..c9e44a57 --- /dev/null +++ b/example/fork/daemon.cpp @@ -0,0 +1,189 @@ +// +// daemon.cpp +// ~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using boost::asio::ip::udp; + +class udp_daytime_server +{ +public: + udp_daytime_server(boost::asio::io_service& io_service) + : socket_(io_service, udp::endpoint(udp::v4(), 13)) + { + start_receive(); + } + +private: + void start_receive() + { + socket_.async_receive_from( + boost::asio::buffer(recv_buffer_), remote_endpoint_, + boost::bind(&udp_daytime_server::handle_receive, this, _1)); + } + + void handle_receive(const boost::system::error_code& ec) + { + if (!ec || ec == boost::asio::error::message_size) + { + using namespace std; // For time_t, time and ctime; + time_t now = time(0); + std::string message = ctime(&now); + + boost::system::error_code ignored_ec; + socket_.send_to(boost::asio::buffer(message), + remote_endpoint_, 0, ignored_ec); + } + + start_receive(); + } + + udp::socket socket_; + udp::endpoint remote_endpoint_; + boost::array recv_buffer_; +}; + +int main() +{ + try + { + boost::asio::io_service io_service; + + // Initialise the server before becoming a daemon. If the process is + // started from a shell, this means any errors will be reported back to the + // user. + udp_daytime_server server(io_service); + + // Register signal handlers so that the daemon may be shut down. You may + // also want to register for other signals, such as SIGHUP to trigger a + // re-read of a configuration file. + boost::asio::signal_set signals(io_service, SIGINT, SIGTERM); + signals.async_wait( + boost::bind(&boost::asio::io_service::stop, &io_service)); + + // Inform the io_service that we are about to become a daemon. The + // io_service cleans up any internal resources, such as threads, that may + // interfere with forking. + io_service.notify_fork(boost::asio::io_service::fork_prepare); + + // Fork the process and have the parent exit. If the process was started + // from a shell, this returns control to the user. Forking a new process is + // also a prerequisite for the subsequent call to setsid(). + if (pid_t pid = fork()) + { + if (pid > 0) + { + // We're in the parent process and need to exit. + // + // When the exit() function is used, the program terminates without + // invoking local variables' destructors. Only global variables are + // destroyed. As the io_service object is a local variable, this means + // we do not have to call: + // + // io_service.notify_fork(boost::asio::io_service::fork_parent); + // + // However, this line should be added before each call to exit() if + // using a global io_service object. An additional call: + // + // io_service.notify_fork(boost::asio::io_service::fork_prepare); + // + // should also precede the second fork(). + exit(0); + } + else + { + syslog(LOG_ERR | LOG_USER, "First fork failed: %m"); + return 1; + } + } + + // Make the process a new session leader. This detaches it from the + // terminal. + setsid(); + + // A process inherits its working directory from its parent. This could be + // on a mounted filesystem, which means that the running daemon would + // prevent this filesystem from being unmounted. Changing to the root + // directory avoids this problem. + chdir("/"); + + // The file mode creation mask is also inherited from the parent process. + // We don't want to restrict the permissions on files created by the + // daemon, so the mask is cleared. + umask(0); + + // A second fork ensures the process cannot acquire a controlling terminal. + if (pid_t pid = fork()) + { + if (pid > 0) + { + exit(0); + } + else + { + syslog(LOG_ERR | LOG_USER, "Second fork failed: %m"); + return 1; + } + } + + // Close the standard streams. This decouples the daemon from the terminal + // that started it. + close(0); + close(1); + close(2); + + // We don't want the daemon to have any standard input. + if (open("/dev/null", O_RDONLY) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to open /dev/null: %m"); + return 1; + } + + // Send standard output to a log file. + const char* output = "/tmp/asio.daemon.out"; + const int flags = O_WRONLY | O_CREAT | O_APPEND; + const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + if (open(output, flags, mode) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to open output file %s: %m", output); + return 1; + } + + // Also send standard error to the same log file. + if (dup(1) < 0) + { + syslog(LOG_ERR | LOG_USER, "Unable to dup output descriptor: %m"); + return 1; + } + + // Inform the io_service that we have finished becoming a daemon. The + // io_service uses this opportunity to create any internal file descriptors + // that need to be private to the new process. + io_service.notify_fork(boost::asio::io_service::fork_child); + + // The io_service can now be used normally. + syslog(LOG_INFO | LOG_USER, "Daemon started"); + io_service.run(); + syslog(LOG_INFO | LOG_USER, "Daemon stopped"); + } + catch (std::exception& e) + { + syslog(LOG_ERR | LOG_USER, "Exception: %s", e.what()); + std::cerr << "Exception: " << e.what() << std::endl; + } +} diff --git a/example/fork/process_per_connection.cpp b/example/fork/process_per_connection.cpp new file mode 100644 index 00000000..52c70f1d --- /dev/null +++ b/example/fork/process_per_connection.cpp @@ -0,0 +1,151 @@ +// +// process_per_connection.cpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using boost::asio::ip::tcp; + +class server +{ +public: + server(boost::asio::io_service& io_service, unsigned short port) + : io_service_(io_service), + signal_(io_service, SIGCHLD), + acceptor_(io_service, tcp::endpoint(tcp::v4(), port)), + socket_(io_service) + { + start_signal_wait(); + start_accept(); + } + +private: + void start_signal_wait() + { + signal_.async_wait(boost::bind(&server::handle_signal_wait, this)); + } + + void handle_signal_wait() + { + // Reap completed child processes so that we don't end up with zombies. + int status = 0; + while (waitpid(-1, &status, WNOHANG) > 0) {} + + start_signal_wait(); + } + + void start_accept() + { + acceptor_.async_accept(socket_, + boost::bind(&server::handle_accept, this, _1)); + } + + void handle_accept(const boost::system::error_code& ec) + { + if (!ec) + { + // Inform the io_service that we are about to fork. The io_service cleans + // up any internal resources, such as threads, that may interfere with + // forking. + io_service_.notify_fork(boost::asio::io_service::fork_prepare); + + if (fork() == 0) + { + // Inform the io_service that the fork is finished and that this is the + // child process. The io_service uses this opportunity to create any + // internal file descriptors that must be private to the new process. + io_service_.notify_fork(boost::asio::io_service::fork_child); + + // The child won't be accepting new connections, so we can close the + // acceptor. It remains open in the parent. + acceptor_.close(); + + start_read(); + } + else + { + // Inform the io_service that the fork is finished (or failed) and that + // this is the parent process. The io_service uses this opportunity to + // recreate any internal resources that were cleaned up during + // preparation for the fork. + io_service_.notify_fork(boost::asio::io_service::fork_parent); + + socket_.close(); + start_accept(); + } + } + else + { + std::cerr << "Accept error: " << ec.message() << std::endl; + } + } + + void start_read() + { + socket_.async_read_some(boost::asio::buffer(data_), + boost::bind(&server::handle_read, this, _1, _2)); + } + + void handle_read(const boost::system::error_code& ec, std::size_t length) + { + if (!ec) + start_write(length); + } + + void start_write(std::size_t length) + { + boost::asio::async_write(socket_, boost::asio::buffer(data_, length), + boost::bind(&server::handle_write, this, _1)); + } + + void handle_write(const boost::system::error_code& ec) + { + if (!ec) + start_read(); + } + + boost::asio::io_service& io_service_; + boost::asio::signal_set signal_; + tcp::acceptor acceptor_; + tcp::socket socket_; + boost::array data_; +}; + +int main(int argc, char* argv[]) +{ + try + { + if (argc != 2) + { + std::cerr << "Usage: process_per_connection \n"; + return 1; + } + + boost::asio::io_service io_service; + + using namespace std; // For atoi. + server s(io_service, atoi(argv[1])); + + io_service.run(); + } + catch (std::exception& e) + { + std::cerr << "Exception: " << e.what() << std::endl; + } +} diff --git a/example/http/server2/io_service_pool.cpp b/example/http/server2/io_service_pool.cpp index 4bd8303a..4fcc5728 100644 --- a/example/http/server2/io_service_pool.cpp +++ b/example/http/server2/io_service_pool.cpp @@ -10,7 +10,7 @@ #include "server.hpp" #include -#include +#include #include #include diff --git a/example/http/server3/server.cpp b/example/http/server3/server.cpp index 5f65c838..ef9bef51 100644 --- a/example/http/server3/server.cpp +++ b/example/http/server3/server.cpp @@ -9,7 +9,7 @@ // #include "server.hpp" -#include +#include #include #include #include diff --git a/example/local/connect_pair.cpp b/example/local/connect_pair.cpp index a53cb1f3..148b3027 100644 --- a/example/local/connect_pair.cpp +++ b/example/local/connect_pair.cpp @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include #include diff --git a/example/services/logger_service.hpp b/example/services/logger_service.hpp index 53176268..78d353fa 100644 --- a/example/services/logger_service.hpp +++ b/example/services/logger_service.hpp @@ -12,7 +12,7 @@ #define SERVICES_LOGGER_SERVICE_HPP #include -#include +#include #include #include #include diff --git a/example/timeouts/async_tcp_client.cpp b/example/timeouts/async_tcp_client.cpp index b248a981..a84acffa 100644 --- a/example/timeouts/async_tcp_client.cpp +++ b/example/timeouts/async_tcp_client.cpp @@ -110,7 +110,8 @@ public: void stop() { stopped_ = true; - socket_.close(); + boost::system::error_code ignored_ec; + socket_.close(ignored_ec); deadline_.cancel(); heartbeat_timer_.cancel(); } diff --git a/example/timeouts/blocking_tcp_client.cpp b/example/timeouts/blocking_tcp_client.cpp index 48c5d7bc..5347e10a 100644 --- a/example/timeouts/blocking_tcp_client.cpp +++ b/example/timeouts/blocking_tcp_client.cpp @@ -178,7 +178,8 @@ private: // The deadline has passed. The socket is closed so that any outstanding // asynchronous operations are cancelled. This allows the blocked // connect(), read_line() or write_line() functions to return. - socket_.close(); + boost::system::error_code ignored_ec; + socket_.close(ignored_ec); // There is no longer an active deadline. The expiry is set to positive // infinity so that the actor takes no action until a new deadline is set. diff --git a/example/timeouts/server.cpp b/example/timeouts/server.cpp index e4a18939..5d0e40f2 100644 --- a/example/timeouts/server.cpp +++ b/example/timeouts/server.cpp @@ -177,7 +177,8 @@ private: { channel_.leave(shared_from_this()); - socket_.close(); + boost::system::error_code ignored_ec; + socket_.close(ignored_ec); input_deadline_.cancel(); non_empty_output_queue_.cancel(); output_deadline_.cancel(); diff --git a/example/tutorial/timer5/timer.cpp b/example/tutorial/timer5/timer.cpp index 9e38e5e6..303e1763 100644 --- a/example/tutorial/timer5/timer.cpp +++ b/example/tutorial/timer5/timer.cpp @@ -10,7 +10,7 @@ #include #include -#include +#include #include #include diff --git a/include/boost/asio/basic_datagram_socket.hpp b/include/boost/asio/basic_datagram_socket.hpp index 26463b00..f6e2a54d 100644 --- a/include/boost/asio/basic_datagram_socket.hpp +++ b/include/boost/asio/basic_datagram_socket.hpp @@ -159,7 +159,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.send(this->implementation, buffers, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send"); return s; } @@ -187,7 +187,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send"); return s; } @@ -335,7 +335,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send_to( this->implementation, buffers, destination, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send_to"); return s; } @@ -362,7 +362,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send_to( this->implementation, buffers, destination, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send_to"); return s; } @@ -510,7 +510,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive"); return s; } @@ -539,7 +539,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive"); return s; } @@ -688,7 +688,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive_from( this->implementation, buffers, sender_endpoint, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive_from"); return s; } @@ -715,7 +715,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive_from( this->implementation, buffers, sender_endpoint, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive_from"); return s; } diff --git a/include/boost/asio/basic_deadline_timer.hpp b/include/boost/asio/basic_deadline_timer.hpp index 75d5dedd..297d56d5 100644 --- a/include/boost/asio/basic_deadline_timer.hpp +++ b/include/boost/asio/basic_deadline_timer.hpp @@ -162,7 +162,7 @@ public: { boost::system::error_code ec; this->service.expires_at(this->implementation, expiry_time, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "expires_at"); } /// Constructor to set a particular expiry time relative to now. @@ -181,7 +181,7 @@ public: { boost::system::error_code ec; this->service.expires_from_now(this->implementation, expiry_time, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "expires_from_now"); } /// Cancel any asynchronous operations that are waiting on the timer. @@ -210,7 +210,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.cancel(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "cancel"); return s; } @@ -269,7 +269,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.cancel_one(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "cancel_one"); return s; } @@ -339,7 +339,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.expires_at( this->implementation, expiry_time, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "expires_at"); return s; } @@ -408,7 +408,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.expires_from_now( this->implementation, expiry_time, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "expires_from_now"); return s; } @@ -452,7 +452,7 @@ public: { boost::system::error_code ec; this->service.wait(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "wait"); } /// Perform a blocking wait on the timer. diff --git a/include/boost/asio/basic_raw_socket.hpp b/include/boost/asio/basic_raw_socket.hpp index ee6ad5e5..efcc9bb5 100644 --- a/include/boost/asio/basic_raw_socket.hpp +++ b/include/boost/asio/basic_raw_socket.hpp @@ -158,7 +158,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.send(this->implementation, buffers, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send"); return s; } @@ -185,7 +185,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send"); return s; } @@ -330,7 +330,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send_to( this->implementation, buffers, destination, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send_to"); return s; } @@ -357,7 +357,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send_to( this->implementation, buffers, destination, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send_to"); return s; } @@ -505,7 +505,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive"); return s; } @@ -534,7 +534,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive"); return s; } @@ -683,7 +683,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive_from( this->implementation, buffers, sender_endpoint, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive_from"); return s; } @@ -710,7 +710,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive_from( this->implementation, buffers, sender_endpoint, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive_from"); return s; } diff --git a/include/boost/asio/basic_seq_packet_socket.hpp b/include/boost/asio/basic_seq_packet_socket.hpp index 8a468632..26580adb 100644 --- a/include/boost/asio/basic_seq_packet_socket.hpp +++ b/include/boost/asio/basic_seq_packet_socket.hpp @@ -166,7 +166,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send"); return s; } @@ -276,7 +276,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, 0, out_flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive"); return s; } @@ -323,7 +323,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, in_flags, out_flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive"); return s; } diff --git a/include/boost/asio/basic_serial_port.hpp b/include/boost/asio/basic_serial_port.hpp index 9a0ac3d5..d7ed20c8 100644 --- a/include/boost/asio/basic_serial_port.hpp +++ b/include/boost/asio/basic_serial_port.hpp @@ -88,7 +88,7 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, device, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); } /// Construct and open a basic_serial_port. @@ -108,7 +108,7 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, device, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); } /// Construct a basic_serial_port on an existing native serial port. @@ -129,7 +129,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, native_serial_port, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Get a reference to the lowest layer. @@ -172,7 +172,7 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, device, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); } /// Open the serial port using the specified device name. @@ -202,7 +202,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, native_serial_port, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Assign an existing native serial port to the serial port. @@ -237,7 +237,7 @@ public: { boost::system::error_code ec; this->service.close(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "close"); } /// Close the serial port. @@ -288,7 +288,7 @@ public: { boost::system::error_code ec; this->service.cancel(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "cancel"); } /// Cancel all asynchronous operations associated with the serial port. @@ -315,7 +315,7 @@ public: { boost::system::error_code ec; this->service.send_break(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send_break"); } /// Send a break sequence to the serial port. @@ -350,7 +350,7 @@ public: { boost::system::error_code ec; this->service.set_option(this->implementation, option, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "set_option"); } /// Set an option on the serial port. @@ -396,7 +396,7 @@ public: { boost::system::error_code ec; this->service.get_option(this->implementation, option, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "get_option"); } /// Get an option from the serial port. @@ -454,7 +454,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.write_some(this->implementation, buffers, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_some"); return s; } @@ -561,7 +561,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.read_some(this->implementation, buffers, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_some"); return s; } diff --git a/include/boost/asio/basic_signal_set.hpp b/include/boost/asio/basic_signal_set.hpp index 8b588258..f32924f3 100644 --- a/include/boost/asio/basic_signal_set.hpp +++ b/include/boost/asio/basic_signal_set.hpp @@ -125,7 +125,7 @@ public: { boost::system::error_code ec; this->service.add(this->implementation, signal_number_1, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "add"); } /// Construct a signal set and add two signals. @@ -150,9 +150,9 @@ public: { boost::system::error_code ec; this->service.add(this->implementation, signal_number_1, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "add"); this->service.add(this->implementation, signal_number_2, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "add"); } /// Construct a signal set and add three signals. @@ -180,11 +180,11 @@ public: { boost::system::error_code ec; this->service.add(this->implementation, signal_number_1, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "add"); this->service.add(this->implementation, signal_number_2, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "add"); this->service.add(this->implementation, signal_number_3, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "add"); } /// Add a signal to a signal_set. @@ -200,7 +200,7 @@ public: { boost::system::error_code ec; this->service.add(this->implementation, signal_number, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "add"); } /// Add a signal to a signal_set. @@ -234,7 +234,7 @@ public: { boost::system::error_code ec; this->service.remove(this->implementation, signal_number, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "remove"); } /// Remove a signal from a signal_set. @@ -268,7 +268,7 @@ public: { boost::system::error_code ec; this->service.clear(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "clear"); } /// Remove all signals from a signal_set. @@ -310,7 +310,7 @@ public: { boost::system::error_code ec; this->service.cancel(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "cancel"); } /// Cancel all operations associated with the signal set. diff --git a/include/boost/asio/basic_socket.hpp b/include/boost/asio/basic_socket.hpp index 9322ed88..c1d8d8f7 100644 --- a/include/boost/asio/basic_socket.hpp +++ b/include/boost/asio/basic_socket.hpp @@ -87,7 +87,7 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, protocol, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); } /// Construct a basic_socket, opening it and binding it to the given local @@ -111,9 +111,9 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, endpoint.protocol(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); this->service.bind(this->implementation, endpoint, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "bind"); } /// Construct a basic_socket on an existing native socket. @@ -135,7 +135,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, protocol, native_socket, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Get a reference to the lowest layer. @@ -184,7 +184,7 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, protocol, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); } /// Open the socket using the specified protocol. @@ -227,7 +227,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, protocol, native_socket, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Assign an existing native socket to the socket. @@ -259,7 +259,8 @@ public: * or connect operations will be cancelled immediately, and will complete * with the boost::asio::error::operation_aborted error. * - * @throws boost::system::system_error Thrown on failure. + * @throws boost::system::system_error Thrown on failure. Note that, even if + * the function indicates an error, the underlying descriptor is closed. * * @note For portable behaviour with respect to graceful closure of a * connected socket, call shutdown() before closing the socket. @@ -268,7 +269,7 @@ public: { boost::system::error_code ec; this->service.close(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "close"); } /// Close the socket. @@ -277,7 +278,8 @@ public: * or connect operations will be cancelled immediately, and will complete * with the boost::asio::error::operation_aborted error. * - * @param ec Set to indicate what error occurred, if any. + * @param ec Set to indicate what error occurred, if any. Note that, even if + * the function indicates an error, the underlying descriptor is closed. * * @par Example * @code @@ -366,7 +368,7 @@ public: { boost::system::error_code ec; this->service.cancel(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "cancel"); } /// Cancel all asynchronous operations associated with the socket. @@ -429,7 +431,7 @@ public: { boost::system::error_code ec; bool b = this->service.at_mark(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "at_mark"); return b; } @@ -462,7 +464,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.available(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "available"); return s; } @@ -503,7 +505,7 @@ public: { boost::system::error_code ec; this->service.bind(this->implementation, endpoint, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "bind"); } /// Bind the socket to the given local endpoint. @@ -564,10 +566,10 @@ public: if (!is_open()) { this->service.open(this->implementation, peer_endpoint.protocol(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "connect"); } this->service.connect(this->implementation, peer_endpoint, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "connect"); } /// Connect the socket to the specified endpoint. @@ -716,7 +718,7 @@ public: { boost::system::error_code ec; this->service.set_option(this->implementation, option, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "set_option"); } /// Set an option on the socket. @@ -805,7 +807,7 @@ public: { boost::system::error_code ec; this->service.get_option(this->implementation, option, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "get_option"); } /// Get an option from the socket. @@ -882,7 +884,7 @@ public: { boost::system::error_code ec; this->service.io_control(this->implementation, command, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "io_control"); } /// Perform an IO control command on the socket. @@ -952,7 +954,7 @@ public: { boost::system::error_code ec; this->service.non_blocking(this->implementation, mode, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "non_blocking"); } /// Sets the non-blocking mode of the socket. @@ -1152,7 +1154,7 @@ public: { boost::system::error_code ec; this->service.native_non_blocking(this->implementation, mode, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "native_non_blocking"); } /// Sets the non-blocking mode of the native socket implementation. @@ -1265,7 +1267,7 @@ public: { boost::system::error_code ec; endpoint_type ep = this->service.local_endpoint(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "local_endpoint"); return ep; } @@ -1314,7 +1316,7 @@ public: { boost::system::error_code ec; endpoint_type ep = this->service.remote_endpoint(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "remote_endpoint"); return ep; } @@ -1365,7 +1367,7 @@ public: { boost::system::error_code ec; this->service.shutdown(this->implementation, what, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "shutdown"); } /// Disable sends or receives on the socket. diff --git a/include/boost/asio/basic_socket_acceptor.hpp b/include/boost/asio/basic_socket_acceptor.hpp index b25b264f..33300cfa 100644 --- a/include/boost/asio/basic_socket_acceptor.hpp +++ b/include/boost/asio/basic_socket_acceptor.hpp @@ -102,7 +102,7 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, protocol, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); } /// Construct an acceptor opened on the given endpoint. @@ -138,18 +138,18 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, endpoint.protocol(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); if (reuse_addr) { this->service.set_option(this->implementation, socket_base::reuse_address(true), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "set_option"); } this->service.bind(this->implementation, endpoint, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "bind"); this->service.listen(this->implementation, socket_base::max_connections, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "listen"); } /// Construct a basic_socket_acceptor on an existing native acceptor. @@ -173,7 +173,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, protocol, native_acceptor, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Open the acceptor using the specified protocol. @@ -195,7 +195,7 @@ public: { boost::system::error_code ec; this->service.open(this->implementation, protocol, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "open"); } /// Open the acceptor using the specified protocol. @@ -239,7 +239,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, protocol, native_acceptor, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Assigns an existing native acceptor to the acceptor. @@ -286,7 +286,7 @@ public: { boost::system::error_code ec; this->service.bind(this->implementation, endpoint, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "bind"); } /// Bind the acceptor to the given local endpoint. @@ -331,7 +331,7 @@ public: { boost::system::error_code ec; this->service.listen(this->implementation, backlog, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "listen"); } /// Place the acceptor into the state where it will listen for new @@ -375,7 +375,7 @@ public: { boost::system::error_code ec; this->service.close(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "close"); } /// Close the acceptor. @@ -439,7 +439,7 @@ public: { boost::system::error_code ec; this->service.cancel(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "cancel"); } /// Cancel all asynchronous operations associated with the acceptor. @@ -481,7 +481,7 @@ public: { boost::system::error_code ec; this->service.set_option(this->implementation, option, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "set_option"); } /// Set an option on the acceptor. @@ -544,7 +544,7 @@ public: { boost::system::error_code ec; this->service.get_option(this->implementation, option, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "get_option"); } /// Get an option from the acceptor. @@ -606,7 +606,7 @@ public: { boost::system::error_code ec; this->service.io_control(this->implementation, command, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "io_control"); } /// Perform an IO control command on the acceptor. @@ -674,7 +674,7 @@ public: { boost::system::error_code ec; this->service.non_blocking(this->implementation, mode, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "non_blocking"); } /// Sets the non-blocking mode of the acceptor. @@ -734,7 +734,7 @@ public: { boost::system::error_code ec; this->service.native_non_blocking(this->implementation, mode, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "native_non_blocking"); } /// Sets the non-blocking mode of the native acceptor implementation. @@ -777,7 +777,7 @@ public: { boost::system::error_code ec; endpoint_type ep = this->service.local_endpoint(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "local_endpoint"); return ep; } @@ -831,7 +831,7 @@ public: { boost::system::error_code ec; this->service.accept(this->implementation, peer, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "accept"); } /// Accept a new connection. @@ -944,7 +944,7 @@ public: { boost::system::error_code ec; this->service.accept(this->implementation, peer, &peer_endpoint, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "accept"); } /// Accept a new connection and obtain the endpoint of the peer diff --git a/include/boost/asio/basic_socket_streambuf.hpp b/include/boost/asio/basic_socket_streambuf.hpp index 13fedbfe..e6d4330b 100644 --- a/include/boost/asio/basic_socket_streambuf.hpp +++ b/include/boost/asio/basic_socket_streambuf.hpp @@ -223,7 +223,7 @@ public: boost::system::error_code ec; timer_service_->expires_at(timer_implementation_, expiry_time, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "expires_at"); start_timer(); } @@ -252,7 +252,7 @@ public: boost::system::error_code ec; timer_service_->expires_from_now(timer_implementation_, expiry_time, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "expires_from_now"); start_timer(); } diff --git a/include/boost/asio/basic_stream_socket.hpp b/include/boost/asio/basic_stream_socket.hpp index 0c20cf60..fa0a4256 100644 --- a/include/boost/asio/basic_stream_socket.hpp +++ b/include/boost/asio/basic_stream_socket.hpp @@ -164,7 +164,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send"); return s; } @@ -202,7 +202,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.send( this->implementation, buffers, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "send"); return s; } @@ -359,7 +359,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.receive(this->implementation, buffers, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive"); return s; } @@ -400,7 +400,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.receive( this->implementation, buffers, flags, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "receive"); return s; } @@ -560,7 +560,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.send(this->implementation, buffers, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_some"); return s; } @@ -667,7 +667,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.receive(this->implementation, buffers, 0, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_some"); return s; } diff --git a/include/boost/asio/datagram_socket_service.hpp b/include/boost/asio/datagram_socket_service.hpp index ba73c1d1..81551efa 100644 --- a/include/boost/asio/datagram_socket_service.hpp +++ b/include/boost/asio/datagram_socket_service.hpp @@ -90,12 +90,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new datagram socket implementation. void construct(implementation_type& impl) { @@ -340,6 +334,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/deadline_timer_service.hpp b/include/boost/asio/deadline_timer_service.hpp index aca59f04..a37ba596 100644 --- a/include/boost/asio/deadline_timer_service.hpp +++ b/include/boost/asio/deadline_timer_service.hpp @@ -72,12 +72,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new timer implementation. void construct(implementation_type& impl) { @@ -143,6 +137,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/detail/dev_poll_reactor.hpp b/include/boost/asio/detail/dev_poll_reactor.hpp index 51829357..407419be 100644 --- a/include/boost/asio/detail/dev_poll_reactor.hpp +++ b/include/boost/asio/detail/dev_poll_reactor.hpp @@ -64,6 +64,10 @@ public: // Destroy all user-defined handler objects owned by the service. BOOST_ASIO_DECL void shutdown_service(); + // Recreate internal descriptors following a fork. + BOOST_ASIO_DECL void fork_service( + boost::asio::io_service::fork_event event); + // Initialise the task. BOOST_ASIO_DECL void init_task(); @@ -98,6 +102,11 @@ public: BOOST_ASIO_DECL void deregister_descriptor(socket_type descriptor, per_descriptor_data&, bool closing); + // Cancel any operations that are running against the descriptor and remove + // its registration from the reactor. + BOOST_ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data&); + // Add a new timer queue to the reactor. template void add_timer_queue(timer_queue& queue); @@ -148,6 +157,10 @@ private: BOOST_ASIO_DECL void cancel_ops_unlocked(socket_type descriptor, const boost::system::error_code& ec); + // Helper class used to reregister descriptors after a fork. + class fork_helper; + friend class fork_helper; + // Add a pending event entry for the given descriptor. BOOST_ASIO_DECL ::pollfd& add_pending_event_change(int descriptor); diff --git a/include/boost/asio/detail/epoll_reactor.hpp b/include/boost/asio/detail/epoll_reactor.hpp index d7bdbb0e..d1b08540 100644 --- a/include/boost/asio/detail/epoll_reactor.hpp +++ b/include/boost/asio/detail/epoll_reactor.hpp @@ -52,6 +52,7 @@ public: friend class epoll_reactor; friend class object_pool_access; mutex mutex_; + int descriptor_; op_queue op_queue_[max_ops]; bool shutdown_; descriptor_state* next_; @@ -70,6 +71,10 @@ public: // Destroy all user-defined handler objects owned by the service. BOOST_ASIO_DECL void shutdown_service(); + // Recreate internal descriptors following a fork. + BOOST_ASIO_DECL void fork_service( + boost::asio::io_service::fork_event event); + // Initialise the task. BOOST_ASIO_DECL void init_task(); @@ -107,6 +112,10 @@ public: BOOST_ASIO_DECL void deregister_descriptor(socket_type descriptor, per_descriptor_data& descriptor_data, bool closing); + // Remote the descriptor's registration from the reactor. + BOOST_ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data& descriptor_data); + // Add a new timer queue to the reactor. template void add_timer_queue(timer_queue& timer_queue); diff --git a/include/boost/asio/detail/eventfd_select_interrupter.hpp b/include/boost/asio/detail/eventfd_select_interrupter.hpp index 954fe790..22926e8a 100644 --- a/include/boost/asio/detail/eventfd_select_interrupter.hpp +++ b/include/boost/asio/detail/eventfd_select_interrupter.hpp @@ -35,6 +35,9 @@ public: // Destructor. BOOST_ASIO_DECL ~eventfd_select_interrupter(); + // Recreate the interrupter's descriptors. Used after a fork. + BOOST_ASIO_DECL void recreate(); + // Interrupt the select call. BOOST_ASIO_DECL void interrupt(); @@ -48,6 +51,12 @@ public: } private: + // Open the descriptors. Throws on error. + BOOST_ASIO_DECL void open_descriptors(); + + // Close the descriptors. + BOOST_ASIO_DECL void close_descriptors(); + // The read end of a connection used to interrupt the select call. This file // descriptor is passed to select such that when it is time to stop, a single // 64bit value will be written on the other end of the connection and this diff --git a/include/boost/asio/detail/impl/descriptor_ops.ipp b/include/boost/asio/detail/impl/descriptor_ops.ipp index c2a69dd3..ca2222c4 100644 --- a/include/boost/asio/detail/impl/descriptor_ops.ipp +++ b/include/boost/asio/detail/impl/descriptor_ops.ipp @@ -43,8 +43,19 @@ int close(int d, state_type& state, boost::system::error_code& ec) int result = 0; if (d != -1) { - if (state & internal_non_blocking) + errno = 0; + result = error_wrapper(::close(d), ec); + + if (result != 0 + && (ec == boost::asio::error::would_block + || ec == boost::asio::error::try_again)) { + // According to UNIX Network Programming Vol. 1, it is possible for + // close() to fail with EWOULDBLOCK under certain circumstances. What + // isn't clear is the state of the descriptor after this error. The one + // current OS where this behaviour is seen, Windows, says that the socket + // remains open. Therefore we'll put the descriptor back into blocking + // mode and have another attempt at closing it. #if defined(__SYMBIAN32__) int flags = ::fcntl(d, F_GETFL, 0); if (flags >= 0) @@ -53,11 +64,11 @@ int close(int d, state_type& state, boost::system::error_code& ec) ioctl_arg_type arg = 0; ::ioctl(d, FIONBIO, &arg); #endif // defined(__SYMBIAN32__) - state &= ~internal_non_blocking; - } + state &= ~non_blocking; - errno = 0; - result = error_wrapper(::close(d), ec); + errno = 0; + result = error_wrapper(::close(d), ec); + } } if (result == 0) diff --git a/include/boost/asio/detail/impl/dev_poll_reactor.ipp b/include/boost/asio/detail/impl/dev_poll_reactor.ipp index 0a5cbb82..7713ad88 100644 --- a/include/boost/asio/detail/impl/dev_poll_reactor.ipp +++ b/include/boost/asio/detail/impl/dev_poll_reactor.ipp @@ -19,6 +19,7 @@ #if defined(BOOST_ASIO_HAS_DEV_POLL) +#include #include #include #include @@ -38,7 +39,7 @@ dev_poll_reactor::dev_poll_reactor(boost::asio::io_service& io_service) shutdown_(false) { // Add the interrupter's descriptor to /dev/poll. - ::pollfd ev = { 0 }; + ::pollfd ev = { 0, 0, 0 }; ev.fd = interrupter_.read_descriptor(); ev.events = POLLIN | POLLERR; ev.revents = 0; @@ -65,6 +66,64 @@ void dev_poll_reactor::shutdown_service() timer_queues_.get_all_timers(ops); } +// Helper class to re-register all descriptors with /dev/poll. +class dev_poll_reactor::fork_helper +{ +public: + fork_helper(dev_poll_reactor* reactor, short events) + : reactor_(reactor), events_(events) + { + } + + bool set(int descriptor) + { + ::pollfd& ev = reactor_->add_pending_event_change(descriptor); + ev.events = events_; + return true; + } + +private: + dev_poll_reactor* reactor_; + short events_; +}; + +void dev_poll_reactor::fork_service(boost::asio::io_service::fork_event event) +{ + if (event == boost::asio::io_service::fork_child) + { + detail::mutex::scoped_lock lock(mutex_); + + if (dev_poll_fd_ != -1) + ::close(dev_poll_fd_); + dev_poll_fd_ = -1; + dev_poll_fd_ = do_dev_poll_create(); + + interrupter_.recreate(); + + // Add the interrupter's descriptor to /dev/poll. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = interrupter_.read_descriptor(); + ev.events = POLLIN | POLLERR; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + + // Re-register all descriptors with /dev/poll. The changes will be written + // to the /dev/poll descriptor the next time the reactor is run. + op_queue ops; + fork_helper read_op_helper(this, POLLERR | POLLHUP | POLLIN); + op_queue_[read_op].get_descriptors(read_op_helper, ops); + fork_helper write_op_helper(this, POLLERR | POLLHUP | POLLOUT); + op_queue_[write_op].get_descriptors(write_op_helper, ops); + fork_helper except_op_helper(this, POLLERR | POLLHUP | POLLPRI); + op_queue_[except_op].get_descriptors(except_op_helper, ops); + interrupter_.interrupt(); + + // The ops op_queue will always be empty because the fork_helper's set() + // member function never returns false. + BOOST_ASSERT(ops.empty()); + } +} + void dev_poll_reactor::init_task() { io_service_.init_task(); @@ -163,6 +222,26 @@ void dev_poll_reactor::deregister_descriptor(socket_type descriptor, cancel_ops_unlocked(descriptor, boost::asio::error::operation_aborted); } +void dev_poll_reactor::deregister_internal_descriptor( + socket_type descriptor, dev_poll_reactor::per_descriptor_data&) +{ + boost::asio::detail::mutex::scoped_lock lock(mutex_); + + // Remove the descriptor from /dev/poll. Since this function is only called + // during a fork, we can apply the change immediately. + ::pollfd ev = { 0, 0, 0 }; + ev.fd = descriptor; + ev.events = POLLREMOVE; + ev.revents = 0; + ::write(dev_poll_fd_, &ev, sizeof(ev)); + + // Destroy all operations associated with the descriptor. + op_queue ops; + boost::system::error_code ec; + for (int i = 0; i < max_ops; ++i) + op_queue_[i].cancel_operations(descriptor, ops, ec); +} + void dev_poll_reactor::run(bool block, op_queue& ops) { boost::asio::detail::mutex::scoped_lock lock(mutex_); @@ -199,8 +278,8 @@ void dev_poll_reactor::run(bool block, op_queue& ops) lock.unlock(); // Block on the /dev/poll descriptor. - ::pollfd events[128] = { { 0 } }; - ::dvpoll dp = { 0 }; + ::pollfd events[128] = { { 0, 0, 0 } }; + ::dvpoll dp = { 0, 0, 0 }; dp.dp_fds = events; dp.dp_nfds = 128; dp.dp_timeout = timeout; @@ -248,7 +327,7 @@ void dev_poll_reactor::run(bool block, op_queue& ops) // The poll operation can produce POLLHUP or POLLERR events when there // is no operation pending, so if we do not remove the descriptor we // can end up in a tight polling loop. - ::pollfd ev = { 0 }; + ::pollfd ev = { 0, 0, 0 }; ev.fd = descriptor; ev.events = POLLREMOVE; ev.revents = 0; @@ -256,7 +335,7 @@ void dev_poll_reactor::run(bool block, op_queue& ops) } else { - ::pollfd ev = { 0 }; + ::pollfd ev = { 0, 0, 0 }; ev.fd = descriptor; ev.events = POLLERR | POLLHUP; if (more_reads) diff --git a/include/boost/asio/detail/impl/epoll_reactor.ipp b/include/boost/asio/detail/impl/epoll_reactor.ipp index e2c68e02..7c0952d9 100644 --- a/include/boost/asio/detail/impl/epoll_reactor.ipp +++ b/include/boost/asio/detail/impl/epoll_reactor.ipp @@ -62,7 +62,8 @@ epoll_reactor::epoll_reactor(boost::asio::io_service& io_service) epoll_reactor::~epoll_reactor() { - close(epoll_fd_); + if (epoll_fd_ != -1) + close(epoll_fd_); if (timer_fd_ != -1) close(timer_fd_); } @@ -86,6 +87,57 @@ void epoll_reactor::shutdown_service() timer_queues_.get_all_timers(ops); } +void epoll_reactor::fork_service(boost::asio::io_service::fork_event event) +{ + if (event == boost::asio::io_service::fork_child) + { + if (epoll_fd_ != -1) + ::close(epoll_fd_); + epoll_fd_ = -1; + epoll_fd_ = do_epoll_create(); + + if (timer_fd_ != -1) + ::close(timer_fd_); + timer_fd_ = -1; + timer_fd_ = do_timerfd_create(); + + interrupter_.recreate(); + + // Add the interrupter's descriptor to epoll. + epoll_event ev = { 0, { 0 } }; + ev.events = EPOLLIN | EPOLLERR | EPOLLET; + ev.data.ptr = &interrupter_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, interrupter_.read_descriptor(), &ev); + interrupter_.interrupt(); + + // Add the timer descriptor to epoll. + if (timer_fd_ != -1) + { + ev.events = EPOLLIN | EPOLLERR; + ev.data.ptr = &timer_fd_; + epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, timer_fd_, &ev); + } + + update_timeout(); + + // Re-register all descriptors with epoll. + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + for (descriptor_state* state = registered_descriptors_.first(); + state != 0; state = state->next_) + { + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET; + ev.data.ptr = state; + int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, state->descriptor_, &ev); + if (result != 0) + { + boost::system::error_code ec(errno, + boost::asio::error::get_system_category()); + boost::asio::detail::throw_error(ec, "epoll re-registration"); + } + } + } +} + void epoll_reactor::init_task() { io_service_.init_task(); @@ -97,6 +149,7 @@ int epoll_reactor::register_descriptor(socket_type descriptor, mutex::scoped_lock lock(registered_descriptors_mutex_); descriptor_data = registered_descriptors_.alloc(); + descriptor_data->descriptor_ = descriptor; descriptor_data->shutdown_ = false; lock.unlock(); @@ -118,20 +171,14 @@ int epoll_reactor::register_internal_descriptor( mutex::scoped_lock lock(registered_descriptors_mutex_); descriptor_data = registered_descriptors_.alloc(); + descriptor_data->descriptor_ = descriptor; descriptor_data->shutdown_ = false; descriptor_data->op_queue_[op_type].push(op); lock.unlock(); epoll_event ev = { 0, { 0 } }; - ev.events = EPOLLERR | EPOLLHUP | EPOLLET; - switch (op_type) - { - case read_op: ev.events |= EPOLLIN; break; - case write_op: ev.events |= EPOLLOUT; break; - case except_op: ev.events |= EPOLLPRI; break; - default: break; - }; + ev.events = EPOLLIN | EPOLLERR | EPOLLHUP | EPOLLOUT | EPOLLPRI | EPOLLET; ev.data.ptr = descriptor_data; int result = epoll_ctl(epoll_fd_, EPOLL_CTL_ADD, descriptor, &ev); if (result != 0) @@ -243,6 +290,7 @@ void epoll_reactor::deregister_descriptor(socket_type descriptor, } } + descriptor_data->descriptor_ = -1; descriptor_data->shutdown_ = true; descriptor_lock.unlock(); @@ -256,6 +304,36 @@ void epoll_reactor::deregister_descriptor(socket_type descriptor, } } +void epoll_reactor::deregister_internal_descriptor(socket_type descriptor, + epoll_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + + if (!descriptor_data->shutdown_) + { + epoll_event ev = { 0, { 0 } }; + epoll_ctl(epoll_fd_, EPOLL_CTL_DEL, descriptor, &ev); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + ops.push(descriptor_data->op_queue_[i]); + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + registered_descriptors_.free(descriptor_data); + descriptor_data = 0; + + descriptors_lock.unlock(); + } +} + void epoll_reactor::run(bool block, op_queue& ops) { // Calculate a timeout only if timerfd is not used. diff --git a/include/boost/asio/detail/impl/eventfd_select_interrupter.ipp b/include/boost/asio/detail/impl/eventfd_select_interrupter.ipp index 7adfcd00..e931efff 100644 --- a/include/boost/asio/detail/impl/eventfd_select_interrupter.ipp +++ b/include/boost/asio/detail/impl/eventfd_select_interrupter.ipp @@ -39,6 +39,11 @@ namespace asio { namespace detail { eventfd_select_interrupter::eventfd_select_interrupter() +{ + open_descriptors(); +} + +void eventfd_select_interrupter::open_descriptors() { #if __GLIBC__ == 2 && __GLIBC_MINOR__ < 8 write_descriptor_ = read_descriptor_ = syscall(__NR_eventfd, 0); @@ -88,6 +93,11 @@ eventfd_select_interrupter::eventfd_select_interrupter() } eventfd_select_interrupter::~eventfd_select_interrupter() +{ + close_descriptors(); +} + +void eventfd_select_interrupter::close_descriptors() { if (write_descriptor_ != -1 && write_descriptor_ != read_descriptor_) ::close(write_descriptor_); @@ -95,6 +105,16 @@ eventfd_select_interrupter::~eventfd_select_interrupter() ::close(read_descriptor_); } +void eventfd_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = -1; + read_descriptor_ = -1; + + open_descriptors(); +} + void eventfd_select_interrupter::interrupt() { uint64_t counter(1UL); diff --git a/include/boost/asio/detail/impl/kqueue_reactor.ipp b/include/boost/asio/detail/impl/kqueue_reactor.ipp index 7a03241a..deaa2925 100644 --- a/include/boost/asio/detail/impl/kqueue_reactor.ipp +++ b/include/boost/asio/detail/impl/kqueue_reactor.ipp @@ -47,9 +47,9 @@ kqueue_reactor::kqueue_reactor(boost::asio::io_service& io_service) interrupter_(), shutdown_(false) { - // The interrupter is put into a permanently readable state. Whenever we - // want to interrupt the blocked kevent call we register a one-shot read - // operation against the descriptor. + // The interrupter is put into a permanently readable state. Whenever we want + // to interrupt the blocked kevent call we register a read operation against + // the descriptor. interrupter_.interrupt(); } @@ -77,17 +77,57 @@ void kqueue_reactor::shutdown_service() timer_queues_.get_all_timers(ops); } +void kqueue_reactor::fork_service(boost::asio::io_service::fork_event event) +{ + if (event == boost::asio::io_service::fork_child) + { + // The kqueue descriptor is automatically closed in the child. + kqueue_fd_ = -1; + kqueue_fd_ = do_kqueue_create(); + + interrupter_.recreate(); + + // Re-register all descriptors with kqueue. + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + for (descriptor_state* state = registered_descriptors_.first(); + state != 0; state = state->next_) + { + struct kevent events[2]; + int num_events = 0; + + if (!state->op_queue_[read_op].empty()) + BOOST_ASIO_KQUEUE_EV_SET(&events[num_events++], state->descriptor_, + EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, state); + else if (!state->op_queue_[except_op].empty()) + BOOST_ASIO_KQUEUE_EV_SET(&events[num_events++], state->descriptor_, + EVFILT_READ, EV_ADD | EV_CLEAR, EV_OOBAND, 0, state); + + if (!state->op_queue_[write_op].empty()) + BOOST_ASIO_KQUEUE_EV_SET(&events[num_events++], state->descriptor_, + EVFILT_WRITE, EV_ADD | EV_CLEAR, 0, 0, state); + + if (num_events && ::kevent(kqueue_fd_, events, num_events, 0, 0, 0) == -1) + { + boost::system::error_code error(errno, + boost::asio::error::get_system_category()); + boost::asio::detail::throw_error(error); + } + } + } +} + void kqueue_reactor::init_task() { io_service_.init_task(); } -int kqueue_reactor::register_descriptor(socket_type, +int kqueue_reactor::register_descriptor(socket_type descriptor, kqueue_reactor::per_descriptor_data& descriptor_data) { mutex::scoped_lock lock(registered_descriptors_mutex_); descriptor_data = registered_descriptors_.alloc(); + descriptor_data->descriptor_ = descriptor; descriptor_data->shutdown_ = false; return 0; @@ -100,6 +140,7 @@ int kqueue_reactor::register_internal_descriptor( mutex::scoped_lock lock(registered_descriptors_mutex_); descriptor_data = registered_descriptors_.alloc(); + descriptor_data->descriptor_ = descriptor; descriptor_data->shutdown_ = false; descriptor_data->op_queue_[op_type].push(op); @@ -108,15 +149,15 @@ int kqueue_reactor::register_internal_descriptor( { case read_op: BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); break; case write_op: BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); break; case except_op: BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); + EV_ADD | EV_CLEAR, EV_OOBAND, 0, descriptor_data); break; } ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); @@ -170,17 +211,17 @@ void kqueue_reactor::start_op(int op_type, socket_type descriptor, { case read_op: BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); break; case write_op: BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); break; case except_op: if (!descriptor_data->op_queue_[read_op].empty()) return; // Already registered for read events. BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); + EV_ADD | EV_CLEAR, EV_OOBAND, 0, descriptor_data); break; } @@ -255,6 +296,7 @@ void kqueue_reactor::deregister_descriptor(socket_type descriptor, } } + descriptor_data->descriptor_ = -1; descriptor_data->shutdown_ = true; descriptor_lock.unlock(); @@ -268,6 +310,40 @@ void kqueue_reactor::deregister_descriptor(socket_type descriptor, } } +void kqueue_reactor::deregister_internal_descriptor(socket_type descriptor, + kqueue_reactor::per_descriptor_data& descriptor_data) +{ + if (!descriptor_data) + return; + + mutex::scoped_lock descriptor_lock(descriptor_data->mutex_); + mutex::scoped_lock descriptors_lock(registered_descriptors_mutex_); + + if (!descriptor_data->shutdown_) + { + struct kevent events[2]; + BOOST_ASIO_KQUEUE_EV_SET(&events[0], descriptor, + EVFILT_READ, EV_DELETE, 0, 0, 0); + BOOST_ASIO_KQUEUE_EV_SET(&events[1], descriptor, + EVFILT_WRITE, EV_DELETE, 0, 0, 0); + ::kevent(kqueue_fd_, events, 2, 0, 0, 0); + + op_queue ops; + for (int i = 0; i < max_ops; ++i) + ops.push(descriptor_data->op_queue_[i]); + + descriptor_data->descriptor_ = -1; + descriptor_data->shutdown_ = true; + + descriptor_lock.unlock(); + + registered_descriptors_.free(descriptor_data); + descriptor_data = 0; + + descriptors_lock.unlock(); + } +} + void kqueue_reactor::run(bool block, op_queue& ops) { mutex::scoped_lock lock(mutex_); @@ -290,7 +366,7 @@ void kqueue_reactor::run(bool block, op_queue& ops) if (ptr == &interrupter_) { // No need to reset the interrupter since we're leaving the descriptor - // in a ready-to-read state and relying on one-shot notifications. + // in a ready-to-read state and relying on edge-triggered notifications. } else { @@ -339,17 +415,17 @@ void kqueue_reactor::run(bool block, op_queue& ops) case EVFILT_READ: if (!descriptor_data->op_queue_[read_op].empty()) BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); else if (!descriptor_data->op_queue_[except_op].empty()) BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_READ, - EV_ADD | EV_ONESHOT, EV_OOBAND, 0, descriptor_data); + EV_ADD | EV_CLEAR, EV_OOBAND, 0, descriptor_data); else continue; break; case EVFILT_WRITE: if (!descriptor_data->op_queue_[write_op].empty()) BOOST_ASIO_KQUEUE_EV_SET(&event, descriptor, EVFILT_WRITE, - EV_ADD | EV_ONESHOT, 0, 0, descriptor_data); + EV_ADD | EV_CLEAR, 0, 0, descriptor_data); else continue; break; @@ -381,7 +457,7 @@ void kqueue_reactor::interrupt() { struct kevent event; BOOST_ASIO_KQUEUE_EV_SET(&event, interrupter_.read_descriptor(), - EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, &interrupter_); + EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, &interrupter_); ::kevent(kqueue_fd_, &event, 1, 0, 0, 0); } diff --git a/include/boost/asio/detail/impl/pipe_select_interrupter.ipp b/include/boost/asio/detail/impl/pipe_select_interrupter.ipp index 957f3638..59aa0537 100644 --- a/include/boost/asio/detail/impl/pipe_select_interrupter.ipp +++ b/include/boost/asio/detail/impl/pipe_select_interrupter.ipp @@ -37,6 +37,11 @@ namespace asio { namespace detail { pipe_select_interrupter::pipe_select_interrupter() +{ + open_descriptors(); +} + +void pipe_select_interrupter::open_descriptors() { int pipe_fds[2]; if (pipe(pipe_fds) == 0) @@ -60,6 +65,11 @@ pipe_select_interrupter::pipe_select_interrupter() } pipe_select_interrupter::~pipe_select_interrupter() +{ + close_descriptors(); +} + +void pipe_select_interrupter::close_descriptors() { if (read_descriptor_ != -1) ::close(read_descriptor_); @@ -67,6 +77,16 @@ pipe_select_interrupter::~pipe_select_interrupter() ::close(write_descriptor_); } +void pipe_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = -1; + read_descriptor_ = -1; + + open_descriptors(); +} + void pipe_select_interrupter::interrupt() { char byte = 0; diff --git a/include/boost/asio/detail/impl/reactive_descriptor_service.ipp b/include/boost/asio/detail/impl/reactive_descriptor_service.ipp index e9c41270..324b8aad 100644 --- a/include/boost/asio/detail/impl/reactive_descriptor_service.ipp +++ b/include/boost/asio/detail/impl/reactive_descriptor_service.ipp @@ -97,8 +97,15 @@ boost::system::error_code reactive_descriptor_service::close( (impl.state_ & descriptor_ops::possible_dup) == 0); } - if (descriptor_ops::close(impl.descriptor_, impl.state_, ec) == 0) - construct(impl); + descriptor_ops::close(impl.descriptor_, impl.state_, ec); + + // The descriptor is closed by the OS even if close() returns an error. + // + // (Actually, POSIX says the state of the descriptor is unspecified. On + // Linux the descriptor is apparently closed anyway; e.g. see + // http://lkml.org/lkml/2005/9/10/129 + // We'll just have to assume that other OSes follow the same behaviour.) + construct(impl); return ec; } diff --git a/include/boost/asio/detail/impl/reactive_socket_service_base.ipp b/include/boost/asio/detail/impl/reactive_socket_service_base.ipp index 0077acf5..a4230840 100644 --- a/include/boost/asio/detail/impl/reactive_socket_service_base.ipp +++ b/include/boost/asio/detail/impl/reactive_socket_service_base.ipp @@ -72,8 +72,17 @@ boost::system::error_code reactive_socket_service_base::close( (impl.state_ & socket_ops::possible_dup) == 0); } - if (socket_ops::close(impl.socket_, impl.state_, true, ec) == 0) - construct(impl); + socket_ops::close(impl.socket_, impl.state_, false, ec); + + // The descriptor is closed by the OS even if close() returns an error. + // + // (Actually, POSIX says the state of the descriptor is unspecified. On + // Linux the descriptor is apparently closed anyway; e.g. see + // http://lkml.org/lkml/2005/9/10/129 + // We'll just have to assume that other OSes follow the same behaviour. The + // known exception is when Windows's closesocket() function fails with + // WSAEWOULDBLOCK, but this case is handled inside socket_ops::close(). + construct(impl); return ec; } diff --git a/include/boost/asio/detail/impl/resolver_service_base.ipp b/include/boost/asio/detail/impl/resolver_service_base.ipp index 7b0435da..0b8461a0 100644 --- a/include/boost/asio/detail/impl/resolver_service_base.ipp +++ b/include/boost/asio/detail/impl/resolver_service_base.ipp @@ -65,6 +65,25 @@ void resolver_service_base::shutdown_service() } } +void resolver_service_base::fork_service( + boost::asio::io_service::fork_event event) +{ + if (work_thread_) + { + if (event == boost::asio::io_service::fork_prepare) + { + work_io_service_->stop(); + work_thread_->join(); + } + else + { + work_io_service_->reset(); + work_thread_.reset(new boost::asio::detail::thread( + work_io_service_runner(*work_io_service_))); + } + } +} + void resolver_service_base::construct( resolver_service_base::implementation_type& impl) { diff --git a/include/boost/asio/detail/impl/select_reactor.ipp b/include/boost/asio/detail/impl/select_reactor.ipp index e5121a14..0e07cf3e 100644 --- a/include/boost/asio/detail/impl/select_reactor.ipp +++ b/include/boost/asio/detail/impl/select_reactor.ipp @@ -84,6 +84,12 @@ void select_reactor::shutdown_service() timer_queues_.get_all_timers(ops); } +void select_reactor::fork_service(boost::asio::io_service::fork_event event) +{ + if (event == boost::asio::io_service::fork_child) + interrupter_.recreate(); +} + void select_reactor::init_task() { io_service_.init_task(); @@ -138,6 +144,15 @@ void select_reactor::deregister_descriptor(socket_type descriptor, cancel_ops_unlocked(descriptor, boost::asio::error::operation_aborted); } +void select_reactor::deregister_internal_descriptor( + socket_type descriptor, select_reactor::per_descriptor_data&) +{ + boost::asio::detail::mutex::scoped_lock lock(mutex_); + op_queue ops; + for (int i = 0; i < max_ops; ++i) + op_queue_[i].cancel_operations(descriptor, ops); +} + void select_reactor::run(bool block, op_queue& ops) { boost::asio::detail::mutex::scoped_lock lock(mutex_); diff --git a/include/boost/asio/detail/impl/service_registry.ipp b/include/boost/asio/detail/impl/service_registry.ipp index 291b0134..ff19d327 100644 --- a/include/boost/asio/detail/impl/service_registry.ipp +++ b/include/boost/asio/detail/impl/service_registry.ipp @@ -17,6 +17,7 @@ #include #include +#include #include #include @@ -52,6 +53,35 @@ service_registry::~service_registry() } } +void service_registry::notify_fork(boost::asio::io_service::fork_event event) +{ + // Make a copy of all of the services while holding the lock. We don't want + // to hold the lock while calling into each service, as it may try to call + // back into this class. + std::vector services; + { + boost::asio::detail::mutex::scoped_lock lock(mutex_); + boost::asio::io_service::service* service = first_service_; + while (service) + { + services.push_back(service); + service = service->next_; + } + } + + // If processing the fork_prepare event, we want to go in reverse order of + // service registration, which happens to be the existing order of the + // services in the vector. For the other events we want to go in the other + // direction. + std::size_t num_services = services.size(); + if (event == boost::asio::io_service::fork_prepare) + for (std::size_t i = 0; i < num_services; ++i) + services[i]->fork_service(event); + else + for (std::size_t i = num_services; i > 0; --i) + services[i - 1]->fork_service(event); +} + void service_registry::init_key(boost::asio::io_service::service::key& key, const boost::asio::io_service::id& id) { diff --git a/include/boost/asio/detail/impl/signal_set_service.ipp b/include/boost/asio/detail/impl/signal_set_service.ipp index 5820975a..d350f6a7 100644 --- a/include/boost/asio/detail/impl/signal_set_service.ipp +++ b/include/boost/asio/detail/impl/signal_set_service.ipp @@ -17,9 +17,9 @@ #include -#include #include #include +#include #include #include @@ -40,6 +40,9 @@ struct signal_state // The write end of the pipe used for signal notifications. int write_descriptor_; + // Whether the signal state has been prepared for a fork. + bool fork_prepared_; + // The head of a linked list of all signal_set_service instances. class signal_set_service* service_list_; @@ -50,7 +53,7 @@ struct signal_state signal_state* get_signal_state() { static signal_state state = { - BOOST_ASIO_STATIC_MUTEX_INIT, -1, -1, 0, { 0 } }; + BOOST_ASIO_STATIC_MUTEX_INIT, -1, -1, false, 0, { 0 } }; return &state; } @@ -69,6 +72,37 @@ void asio_signal_handler(int signal_number) #endif // !defined(BOOST_ASIO_HAS_SIGACTION) } +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +class signal_set_service::pipe_read_op : public reactor_op +{ +public: + pipe_read_op() + : reactor_op(&pipe_read_op::do_perform, pipe_read_op::do_complete) + { + } + + static bool do_perform(reactor_op*) + { + signal_state* state = get_signal_state(); + + int fd = state->read_descriptor_; + int signal_number = 0; + while (::read(fd, &signal_number, sizeof(int)) == sizeof(int)) + if (signal_number >= 0 && signal_number < max_signal_number) + signal_set_service::deliver_signal(signal_number); + + return false; + } + + static void do_complete(io_service_impl* /*owner*/, operation* base, + boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) + { + pipe_read_op* o(static_cast(base)); + delete o; + } +}; +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + signal_set_service::signal_set_service( boost::asio::io_service& io_service) : io_service_(boost::asio::use_service(io_service)), @@ -112,6 +146,43 @@ void signal_set_service::shutdown_service() } } +void signal_set_service::fork_service(boost::asio::io_service::fork_event event) +{ +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + switch (event) + { + case boost::asio::io_service::fork_prepare: + reactor_.deregister_internal_descriptor( + state->read_descriptor_, reactor_data_); + state->fork_prepared_ = true; + break; + case boost::asio::io_service::fork_parent: + state->fork_prepared_ = false; + reactor_.register_internal_descriptor(reactor::read_op, + state->read_descriptor_, reactor_data_, new pipe_read_op); + break; + case boost::asio::io_service::fork_child: + if (state->fork_prepared_) + { + boost::asio::detail::signal_blocker blocker; + close_descriptors(); + open_descriptors(); + state->fork_prepared_ = false; + } + reactor_.register_internal_descriptor(reactor::read_op, + state->read_descriptor_, reactor_data_, new pipe_read_op); + break; + default: + break; + } +#else // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + (void)event; +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +} + void signal_set_service::construct( signal_set_service::implementation_type& impl) { @@ -323,11 +394,16 @@ boost::system::error_code signal_set_service::cancel( BOOST_ASIO_HANDLER_OPERATION(("signal_set", &impl, "cancel")); op_queue ops; - while (signal_op* op = impl.queue_.front()) { - op->ec_ = boost::asio::error::operation_aborted; - impl.queue_.pop(); - ops.push(op); + signal_state* state = get_signal_state(); + static_mutex::scoped_lock lock(state->mutex_); + + while (signal_op* op = impl.queue_.front()) + { + op->ec_ = boost::asio::error::operation_aborted; + impl.queue_.pop(); + ops.push(op); + } } io_service_.post_deferred_completions(ops); @@ -372,37 +448,6 @@ void signal_set_service::deliver_signal(int signal_number) } } -#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) -class signal_set_service::pipe_read_op : public reactor_op -{ -public: - pipe_read_op() - : reactor_op(&pipe_read_op::do_perform, pipe_read_op::do_complete) - { - } - - static bool do_perform(reactor_op*) - { - signal_state* state = get_signal_state(); - - int fd = state->read_descriptor_; - int signal_number = 0; - while (::read(fd, &signal_number, sizeof(int)) == sizeof(int)) - if (signal_number >= 0 && signal_number < max_signal_number) - signal_set_service::deliver_signal(signal_number); - - return false; - } - - static void do_complete(io_service_impl* /*owner*/, operation* base, - boost::system::error_code /*ec*/, std::size_t /*bytes_transferred*/) - { - pipe_read_op* o(static_cast(base)); - delete o; - } -}; -#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) - void signal_set_service::add_service(signal_set_service* service) { signal_state* state = get_signal_state(); @@ -411,28 +456,7 @@ void signal_set_service::add_service(signal_set_service* service) #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) // If this is the first service to be created, open a new pipe. if (state->service_list_ == 0) - { - int pipe_fds[2]; - if (::pipe(pipe_fds) == 0) - { - state->read_descriptor_ = pipe_fds[0]; - ::fcntl(state->read_descriptor_, F_SETFL, O_NONBLOCK); - - state->write_descriptor_ = pipe_fds[1]; - ::fcntl(state->write_descriptor_, F_SETFL, O_NONBLOCK); - -#if defined(FD_CLOEXEC) - ::fcntl(state->read_descriptor_, F_SETFD, FD_CLOEXEC); - ::fcntl(state->write_descriptor_, F_SETFD, FD_CLOEXEC); -#endif // defined(FD_CLOEXEC) - } - else - { - boost::system::error_code ec(errno, - boost::asio::error::get_system_category()); - boost::asio::detail::throw_error(ec, "signal_set_service pipe"); - } - } + open_descriptors(); #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) // Insert service into linked list of all services. @@ -475,16 +499,54 @@ void signal_set_service::remove_service(signal_set_service* service) #if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) // If this is the last service to be removed, close the pipe. if (state->service_list_ == 0) - { - ::close(state->read_descriptor_); - state->read_descriptor_ = -1; - ::close(state->write_descriptor_); - state->write_descriptor_ = -1; - } + close_descriptors(); #endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) } } +void signal_set_service::open_descriptors() +{ +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + + int pipe_fds[2]; + if (::pipe(pipe_fds) == 0) + { + state->read_descriptor_ = pipe_fds[0]; + ::fcntl(state->read_descriptor_, F_SETFL, O_NONBLOCK); + + state->write_descriptor_ = pipe_fds[1]; + ::fcntl(state->write_descriptor_, F_SETFL, O_NONBLOCK); + +#if defined(FD_CLOEXEC) + ::fcntl(state->read_descriptor_, F_SETFD, FD_CLOEXEC); + ::fcntl(state->write_descriptor_, F_SETFD, FD_CLOEXEC); +#endif // defined(FD_CLOEXEC) + } + else + { + boost::system::error_code ec(errno, + boost::asio::error::get_system_category()); + boost::asio::detail::throw_error(ec, "signal_set_service pipe"); + } +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +} + +void signal_set_service::close_descriptors() +{ +#if !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) + signal_state* state = get_signal_state(); + + if (state->read_descriptor_ != -1) + ::close(state->read_descriptor_); + state->read_descriptor_ = -1; + + if (state->write_descriptor_ != -1) + ::close(state->write_descriptor_); + state->write_descriptor_ = -1; +#endif // !defined(BOOST_WINDOWS) && !defined(__CYGWIN__) +} + void signal_set_service::start_wait_op( signal_set_service::implementation_type& impl, signal_op* op) { diff --git a/include/boost/asio/detail/impl/socket_ops.ipp b/include/boost/asio/detail/impl/socket_ops.ipp index 7ec92c98..45f358aa 100644 --- a/include/boost/asio/detail/impl/socket_ops.ipp +++ b/include/boost/asio/detail/impl/socket_ops.ipp @@ -278,28 +278,9 @@ int close(socket_type s, state_type& state, int result = 0; if (s != invalid_socket) { -#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) - if ((state & non_blocking) && (state & user_set_linger)) - { - ioctl_arg_type arg = 0; - ::ioctlsocket(s, FIONBIO, &arg); - state &= ~non_blocking; - } -#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - if (state & non_blocking) - { -#if defined(__SYMBIAN32__) - int flags = ::fcntl(s, F_GETFL, 0); - if (flags >= 0) - ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK); -#else // defined(__SYMBIAN32__) - ioctl_arg_type arg = 0; - ::ioctl(s, FIONBIO, &arg); -#endif // defined(__SYMBIAN32__) - state &= ~non_blocking; - } -#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) - + // We don't want the destructor to block, so set the socket to linger in + // the background. If the user doesn't like this behaviour then they need + // to explicitly close the socket. if (destruction && (state & user_set_linger)) { ::linger opt; @@ -316,6 +297,32 @@ int close(socket_type s, state_type& state, #else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) result = error_wrapper(::close(s), ec); #endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + + if (result != 0 + && (ec == boost::asio::error::would_block + || ec == boost::asio::error::try_again)) + { + // According to UNIX Network Programming Vol. 1, it is possible for + // close() to fail with EWOULDBLOCK under certain circumstances. What + // isn't clear is the state of the descriptor after this error. The one + // current OS where this behaviour is seen, Windows, says that the socket + // remains open. Therefore we'll put the descriptor back into blocking + // mode and have another attempt at closing it. +#if defined(BOOST_WINDOWS) || defined(__CYGWIN__) + ioctl_arg_type arg = 0; + ::ioctlsocket(s, FIONBIO, &arg); +#else // defined(BOOST_WINDOWS) || defined(__CYGWIN__) +# if defined(__SYMBIAN32__) + int flags = ::fcntl(s, F_GETFL, 0); + if (flags >= 0) + ::fcntl(s, F_SETFL, flags & ~O_NONBLOCK); +# else // defined(__SYMBIAN32__) + ioctl_arg_type arg = 0; + ::ioctl(s, FIONBIO, &arg); +# endif // defined(__SYMBIAN32__) +#endif // defined(BOOST_WINDOWS) || defined(__CYGWIN__) + state &= ~non_blocking; + } } if (result == 0) diff --git a/include/boost/asio/detail/impl/socket_select_interrupter.ipp b/include/boost/asio/detail/impl/socket_select_interrupter.ipp index 3b64771f..533bafd8 100644 --- a/include/boost/asio/detail/impl/socket_select_interrupter.ipp +++ b/include/boost/asio/detail/impl/socket_select_interrupter.ipp @@ -35,6 +35,11 @@ namespace asio { namespace detail { socket_select_interrupter::socket_select_interrupter() +{ + open_descriptors(); +} + +void socket_select_interrupter::open_descriptors() { boost::system::error_code ec; socket_holder acceptor(socket_ops::socket( @@ -109,6 +114,11 @@ socket_select_interrupter::socket_select_interrupter() } socket_select_interrupter::~socket_select_interrupter() +{ + close_descriptors(); +} + +void socket_select_interrupter::close_descriptors() { boost::system::error_code ec; socket_ops::state_type state = socket_ops::internal_non_blocking; @@ -118,6 +128,16 @@ socket_select_interrupter::~socket_select_interrupter() socket_ops::close(write_descriptor_, state, true, ec); } +void socket_select_interrupter::recreate() +{ + close_descriptors(); + + write_descriptor_ = invalid_socket; + read_descriptor_ = invalid_socket; + + open_descriptors(); +} + void socket_select_interrupter::interrupt() { char byte = 0; diff --git a/include/boost/asio/detail/kqueue_reactor.hpp b/include/boost/asio/detail/kqueue_reactor.hpp index be3aab89..37817cb0 100644 --- a/include/boost/asio/detail/kqueue_reactor.hpp +++ b/include/boost/asio/detail/kqueue_reactor.hpp @@ -63,6 +63,7 @@ public: friend class kqueue_reactor; friend class object_pool_access; mutex mutex_; + int descriptor_; op_queue op_queue_[max_ops]; bool shutdown_; descriptor_state* next_; @@ -81,6 +82,10 @@ public: // Destroy all user-defined handler objects owned by the service. BOOST_ASIO_DECL void shutdown_service(); + // Recreate internal descriptors following a fork. + BOOST_ASIO_DECL void fork_service( + boost::asio::io_service::fork_event event); + // Initialise the task. BOOST_ASIO_DECL void init_task(); @@ -118,6 +123,10 @@ public: BOOST_ASIO_DECL void deregister_descriptor(socket_type descriptor, per_descriptor_data& descriptor_data, bool closing); + // Remote the descriptor's registration from the reactor. + BOOST_ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data& descriptor_data); + // Add a new timer queue to the reactor. template void add_timer_queue(timer_queue& queue); diff --git a/include/boost/asio/detail/pipe_select_interrupter.hpp b/include/boost/asio/detail/pipe_select_interrupter.hpp index ad32736c..56321246 100644 --- a/include/boost/asio/detail/pipe_select_interrupter.hpp +++ b/include/boost/asio/detail/pipe_select_interrupter.hpp @@ -37,6 +37,9 @@ public: // Destructor. BOOST_ASIO_DECL ~pipe_select_interrupter(); + // Recreate the interrupter's descriptors. Used after a fork. + BOOST_ASIO_DECL void recreate(); + // Interrupt the select call. BOOST_ASIO_DECL void interrupt(); @@ -50,6 +53,12 @@ public: } private: + // Open the descriptors. Throws on error. + BOOST_ASIO_DECL void open_descriptors(); + + // Close the descriptors. + BOOST_ASIO_DECL void close_descriptors(); + // The read end of a connection used to interrupt the select call. This file // descriptor is passed to select such that when it is time to stop, a single // byte will be written on the other end of the connection and this diff --git a/include/boost/asio/detail/resolver_service_base.hpp b/include/boost/asio/detail/resolver_service_base.hpp index 60e9e09f..6302c505 100644 --- a/include/boost/asio/detail/resolver_service_base.hpp +++ b/include/boost/asio/detail/resolver_service_base.hpp @@ -48,6 +48,9 @@ public: // Destroy all user-defined handler objects owned by the service. BOOST_ASIO_DECL void shutdown_service(); + // Perform any fork-related housekeeping. + BOOST_ASIO_DECL void fork_service(boost::asio::io_service::fork_event event); + // Construct a new resolver implementation. BOOST_ASIO_DECL void construct(implementation_type& impl); diff --git a/include/boost/asio/detail/select_reactor.hpp b/include/boost/asio/detail/select_reactor.hpp index efcc193d..96233dae 100644 --- a/include/boost/asio/detail/select_reactor.hpp +++ b/include/boost/asio/detail/select_reactor.hpp @@ -73,6 +73,10 @@ public: // Destroy all user-defined handler objects owned by the service. BOOST_ASIO_DECL void shutdown_service(); + // Recreate internal descriptors following a fork. + BOOST_ASIO_DECL void fork_service( + boost::asio::io_service::fork_event event); + // Initialise the task, but only if the reactor is not in its own thread. BOOST_ASIO_DECL void init_task(); @@ -107,6 +111,10 @@ public: BOOST_ASIO_DECL void deregister_descriptor(socket_type descriptor, per_descriptor_data&, bool closing); + // Remote the descriptor's registration from the reactor. + BOOST_ASIO_DECL void deregister_internal_descriptor( + socket_type descriptor, per_descriptor_data& descriptor_data); + // Add a new timer queue to the reactor. template void add_timer_queue(timer_queue& queue); diff --git a/include/boost/asio/detail/service_registry.hpp b/include/boost/asio/detail/service_registry.hpp index a247ea81..9d0279cf 100644 --- a/include/boost/asio/detail/service_registry.hpp +++ b/include/boost/asio/detail/service_registry.hpp @@ -58,6 +58,9 @@ public: // Destructor. BOOST_ASIO_DECL ~service_registry(); + // Notify all services of a fork event. + BOOST_ASIO_DECL void notify_fork(boost::asio::io_service::fork_event event); + // Get the service object corresponding to the specified service type. Will // create a new service object automatically if no such object already // exists. Ownership of the service object is not transferred to the caller. diff --git a/include/boost/asio/detail/signal_set_service.hpp b/include/boost/asio/detail/signal_set_service.hpp index bbfa9bc2..56a2ae0d 100644 --- a/include/boost/asio/detail/signal_set_service.hpp +++ b/include/boost/asio/detail/signal_set_service.hpp @@ -17,6 +17,7 @@ #include +#include #include #include #include @@ -117,6 +118,9 @@ public: // Destroy all user-defined handler objects owned by the service. BOOST_ASIO_DECL void shutdown_service(); + // Perform fork-related housekeeping. + BOOST_ASIO_DECL void fork_service(boost::asio::io_service::fork_event event); + // Construct a new signal_set implementation. BOOST_ASIO_DECL void construct(implementation_type& impl); @@ -166,6 +170,12 @@ private: // Helper function to remove a service from the global signal state. BOOST_ASIO_DECL static void remove_service(signal_set_service* service); + // Helper function to create the pipe descriptors. + BOOST_ASIO_DECL static void open_descriptors(); + + // Helper function to close the pipe descriptors. + BOOST_ASIO_DECL static void close_descriptors(); + // Helper function to start a wait operation. BOOST_ASIO_DECL void start_wait_op(implementation_type& impl, signal_op* op); diff --git a/include/boost/asio/detail/socket_select_interrupter.hpp b/include/boost/asio/detail/socket_select_interrupter.hpp index 6d68d5a3..70e60847 100644 --- a/include/boost/asio/detail/socket_select_interrupter.hpp +++ b/include/boost/asio/detail/socket_select_interrupter.hpp @@ -38,6 +38,9 @@ public: // Destructor. BOOST_ASIO_DECL ~socket_select_interrupter(); + // Recreate the interrupter's descriptors. Used after a fork. + BOOST_ASIO_DECL void recreate(); + // Interrupt the select call. BOOST_ASIO_DECL void interrupt(); @@ -51,6 +54,12 @@ public: } private: + // Open the descriptors. Throws on error. + BOOST_ASIO_DECL void open_descriptors(); + + // Close the descriptors. + BOOST_ASIO_DECL void close_descriptors(); + // The read end of a connection used to interrupt the select call. This file // descriptor is passed to select such that when it is time to stop, a single // byte will be written on the other end of the connection and this diff --git a/include/boost/asio/detail/win_static_mutex.hpp b/include/boost/asio/detail/win_static_mutex.hpp index b347e870..89c9bfa6 100644 --- a/include/boost/asio/detail/win_static_mutex.hpp +++ b/include/boost/asio/detail/win_static_mutex.hpp @@ -55,7 +55,7 @@ struct win_static_mutex ::CRITICAL_SECTION crit_section_; }; -#define BOOST_ASIO_WIN_STATIC_MUTEX_INIT { false, { 0 } } +#define BOOST_ASIO_WIN_STATIC_MUTEX_INIT { false, { 0, 0, 0, 0, 0, 0 } } } // namespace detail } // namespace asio diff --git a/include/boost/asio/impl/connect.hpp b/include/boost/asio/impl/connect.hpp index 75111ce0..2e927be0 100644 --- a/include/boost/asio/impl/connect.hpp +++ b/include/boost/asio/impl/connect.hpp @@ -45,7 +45,7 @@ Iterator connect(basic_socket& s, Iterator begin) { boost::system::error_code ec; Iterator result = connect(s, begin, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "connect"); return result; } @@ -62,7 +62,7 @@ Iterator connect(basic_socket& s, { boost::system::error_code ec; Iterator result = connect(s, begin, end, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "connect"); return result; } @@ -80,7 +80,7 @@ Iterator connect(basic_socket& s, { boost::system::error_code ec; Iterator result = connect(s, begin, connect_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "connect"); return result; } @@ -100,7 +100,7 @@ Iterator connect(basic_socket& s, { boost::system::error_code ec; Iterator result = connect(s, begin, end, connect_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "connect"); return result; } diff --git a/include/boost/asio/impl/io_service.ipp b/include/boost/asio/impl/io_service.ipp index 2119bfda..60ad28c8 100644 --- a/include/boost/asio/impl/io_service.ipp +++ b/include/boost/asio/impl/io_service.ipp @@ -118,6 +118,11 @@ void io_service::reset() impl_.reset(); } +void io_service::notify_fork(boost::asio::io_service::fork_event event) +{ + service_registry_->notify_fork(event); +} + io_service::service::service(boost::asio::io_service& owner) : owner_(owner), next_(0) @@ -128,6 +133,10 @@ io_service::service::~service() { } +void io_service::service::fork_service(boost::asio::io_service::fork_event) +{ +} + service_already_exists::service_already_exists() : std::logic_error("Service already exists.") { diff --git a/include/boost/asio/impl/read.hpp b/include/boost/asio/impl/read.hpp index d1072f30..032b1b3f 100644 --- a/include/boost/asio/impl/read.hpp +++ b/include/boost/asio/impl/read.hpp @@ -59,7 +59,7 @@ inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers) { boost::system::error_code ec; std::size_t bytes_transferred = read(s, buffers, transfer_all(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read"); return bytes_transferred; } @@ -77,7 +77,7 @@ inline std::size_t read(SyncReadStream& s, const MutableBufferSequence& buffers, { boost::system::error_code ec; std::size_t bytes_transferred = read(s, buffers, completion_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read"); return bytes_transferred; } @@ -112,7 +112,7 @@ inline std::size_t read(SyncReadStream& s, { boost::system::error_code ec; std::size_t bytes_transferred = read(s, b, transfer_all(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read"); return bytes_transferred; } @@ -132,7 +132,7 @@ inline std::size_t read(SyncReadStream& s, { boost::system::error_code ec; std::size_t bytes_transferred = read(s, b, completion_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read"); return bytes_transferred; } diff --git a/include/boost/asio/impl/read_at.hpp b/include/boost/asio/impl/read_at.hpp index ebcd2287..1085e3e5 100644 --- a/include/boost/asio/impl/read_at.hpp +++ b/include/boost/asio/impl/read_at.hpp @@ -63,7 +63,7 @@ inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::system::error_code ec; std::size_t bytes_transferred = read_at( d, offset, buffers, transfer_all(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_at"); return bytes_transferred; } @@ -84,7 +84,7 @@ inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::system::error_code ec; std::size_t bytes_transferred = read_at( d, offset, buffers, completion_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_at"); return bytes_transferred; } @@ -121,7 +121,7 @@ inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::system::error_code ec; std::size_t bytes_transferred = read_at( d, offset, b, transfer_all(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_at"); return bytes_transferred; } @@ -142,7 +142,7 @@ inline std::size_t read_at(SyncRandomAccessReadDevice& d, boost::system::error_code ec; std::size_t bytes_transferred = read_at( d, offset, b, completion_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_at"); return bytes_transferred; } diff --git a/include/boost/asio/impl/read_until.hpp b/include/boost/asio/impl/read_until.hpp index 411d4611..8759f494 100644 --- a/include/boost/asio/impl/read_until.hpp +++ b/include/boost/asio/impl/read_until.hpp @@ -39,7 +39,7 @@ inline std::size_t read_until(SyncReadStream& s, { boost::system::error_code ec; std::size_t bytes_transferred = read_until(s, b, delim, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_until"); return bytes_transferred; } @@ -95,7 +95,7 @@ inline std::size_t read_until(SyncReadStream& s, { boost::system::error_code ec; std::size_t bytes_transferred = read_until(s, b, delim, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_until"); return bytes_transferred; } @@ -195,7 +195,7 @@ inline std::size_t read_until(SyncReadStream& s, { boost::system::error_code ec; std::size_t bytes_transferred = read_until(s, b, expr, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_until"); return bytes_transferred; } @@ -315,7 +315,7 @@ inline std::size_t read_until(SyncReadStream& s, { boost::system::error_code ec; std::size_t bytes_transferred = read_until(s, b, match_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_until"); return bytes_transferred; } diff --git a/include/boost/asio/impl/write.hpp b/include/boost/asio/impl/write.hpp index 44e65954..6c0caa55 100644 --- a/include/boost/asio/impl/write.hpp +++ b/include/boost/asio/impl/write.hpp @@ -57,7 +57,7 @@ inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers) { boost::system::error_code ec; std::size_t bytes_transferred = write(s, buffers, transfer_all(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write"); return bytes_transferred; } @@ -75,7 +75,7 @@ inline std::size_t write(SyncWriteStream& s, const ConstBufferSequence& buffers, { boost::system::error_code ec; std::size_t bytes_transferred = write(s, buffers, completion_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write"); return bytes_transferred; } @@ -98,7 +98,7 @@ inline std::size_t write(SyncWriteStream& s, { boost::system::error_code ec; std::size_t bytes_transferred = write(s, b, transfer_all(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write"); return bytes_transferred; } @@ -118,7 +118,7 @@ inline std::size_t write(SyncWriteStream& s, { boost::system::error_code ec; std::size_t bytes_transferred = write(s, b, completion_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write"); return bytes_transferred; } diff --git a/include/boost/asio/impl/write_at.hpp b/include/boost/asio/impl/write_at.hpp index a0e588eb..c7fd6dca 100644 --- a/include/boost/asio/impl/write_at.hpp +++ b/include/boost/asio/impl/write_at.hpp @@ -61,7 +61,7 @@ inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::system::error_code ec; std::size_t bytes_transferred = write_at( d, offset, buffers, transfer_all(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_at"); return bytes_transferred; } @@ -82,7 +82,7 @@ inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::system::error_code ec; std::size_t bytes_transferred = write_at( d, offset, buffers, completion_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_at"); return bytes_transferred; } @@ -106,7 +106,7 @@ inline std::size_t write_at(SyncRandomAccessWriteDevice& d, { boost::system::error_code ec; std::size_t bytes_transferred = write_at(d, offset, b, transfer_all(), ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_at"); return bytes_transferred; } @@ -127,7 +127,7 @@ inline std::size_t write_at(SyncRandomAccessWriteDevice& d, boost::system::error_code ec; std::size_t bytes_transferred = write_at( d, offset, b, completion_condition, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_at"); return bytes_transferred; } diff --git a/include/boost/asio/io_service.hpp b/include/boost/asio/io_service.hpp index 2ada9b35..ecc4f3f6 100644 --- a/include/boost/asio/io_service.hpp +++ b/include/boost/asio/io_service.hpp @@ -68,9 +68,12 @@ namespace detail { typedef task_io_service io_service_impl; } * * @par Thread Safety * @e Distinct @e objects: Safe.@n - * @e Shared @e objects: Safe, with the exception that calling reset() while - * there are unfinished run(), run_one(), poll() or poll_one() calls results in - * undefined behaviour. + * @e Shared @e objects: Safe, with the specific exceptions of the reset() and + * notify_fork() functions. Calling reset() while there are unfinished run(), + * run_one(), poll() or poll_one() calls results in undefined behaviour. The + * notify_fork() function should not be called while any io_service function, + * or any function on an I/O object that is associated with the io_service, is + * being called in another thread. * * @par Concepts: * Dispatcher. @@ -495,6 +498,61 @@ public: #endif wrap(Handler handler); + /// Fork-related event notifications. + enum fork_event + { + /// Notify the io_service that the process is about to fork. + fork_prepare, + + /// Notify the io_service that the process has forked and is the parent. + fork_parent, + + /// Notify the io_service that the process has forked and is the child. + fork_child + }; + + /// Notify the io_service of a fork-related event. + /** + * This function is used to inform the io_service that the process is about + * to fork, or has just forked. This allows the io_service, and the services + * it contains, to perform any necessary housekeeping to ensure correct + * operation following a fork. + * + * This function must not be called while any other io_service function, or + * any function on an I/O object associated with the io_service, is being + * called in another thread. It is, however, safe to call this function from + * within a completion handler, provided no other thread is accessing the + * io_service. + * + * @param event A fork-related event. + * + * @throws boost::system::system_error Thrown on failure. If the notification + * fails the io_service object should no longer be used and should be + * destroyed. + * + * @par Example + * The following code illustrates how to incorporate the notify_fork() + * function: + * @code my_io_service.notify_fork(boost::asio::io_service::fork_prepare); + * if (fork() == 0) + * { + * // This is the child process. + * my_io_service.notify_fork(boost::asio::io_service::fork_child); + * } + * else + * { + * // This is the parent process. + * my_io_service.notify_fork(boost::asio::io_service::fork_parent); + * } @endcode + * + * @note For each service object @c svc in the io_service set, performs + * svc->fork_service();. When processing the fork_prepare event, + * services are visited in reverse order of the beginning of service object + * lifetime. Otherwise, services are visited in order of the beginning of + * service object lifetime. + */ + BOOST_ASIO_DECL void notify_fork(boost::asio::io_service::fork_event event); + /// Obtain the service object corresponding to the given type. /** * This function is used to locate a service object that corresponds to @@ -635,6 +693,15 @@ private: /// Destroy all user-defined handler objects owned by the service. virtual void shutdown_service() = 0; + /// Handle notification of a fork-related event to perform any necessary + /// housekeeping. + /** + * This function is not a pure virtual so that services only have to + * implement it if necessary. The default implementation does nothing. + */ + BOOST_ASIO_DECL virtual void fork_service( + boost::asio::io_service::fork_event event); + friend class boost::asio::detail::service_registry; struct key { diff --git a/include/boost/asio/ip/basic_resolver.hpp b/include/boost/asio/ip/basic_resolver.hpp index d0695c4a..fc678dda 100644 --- a/include/boost/asio/ip/basic_resolver.hpp +++ b/include/boost/asio/ip/basic_resolver.hpp @@ -100,7 +100,7 @@ public: { boost::system::error_code ec; iterator i = this->service.resolve(this->implementation, q, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "resolve"); return i; } @@ -186,7 +186,7 @@ public: { boost::system::error_code ec; iterator i = this->service.resolve(this->implementation, e, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "resolve"); return i; } diff --git a/include/boost/asio/ip/resolver_service.hpp b/include/boost/asio/ip/resolver_service.hpp index db0554b0..bb3e38b2 100644 --- a/include/boost/asio/ip/resolver_service.hpp +++ b/include/boost/asio/ip/resolver_service.hpp @@ -77,12 +77,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new resolver implementation. void construct(implementation_type& impl) { @@ -132,6 +126,18 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + + // Perform any fork-related housekeeping. + void fork_service(boost::asio::io_service::fork_event event) + { + service_impl_.fork_service(event); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/local/connect_pair.hpp b/include/boost/asio/local/connect_pair.hpp index 9ef6cd3f..3cafae71 100644 --- a/include/boost/asio/local/connect_pair.hpp +++ b/include/boost/asio/local/connect_pair.hpp @@ -52,7 +52,7 @@ inline void connect_pair( { boost::system::error_code ec; connect_pair(socket1, socket2, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "connect_pair"); } template diff --git a/include/boost/asio/posix/basic_descriptor.hpp b/include/boost/asio/posix/basic_descriptor.hpp index fc6b68df..2d7ba00d 100644 --- a/include/boost/asio/posix/basic_descriptor.hpp +++ b/include/boost/asio/posix/basic_descriptor.hpp @@ -88,7 +88,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, native_descriptor, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Get a reference to the lowest layer. @@ -131,7 +131,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, native_descriptor, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Assign an existing native descriptor to the descriptor. @@ -160,13 +160,14 @@ public: * write operations will be cancelled immediately, and will complete with the * boost::asio::error::operation_aborted error. * - * @throws boost::system::system_error Thrown on failure. + * @throws boost::system::system_error Thrown on failure. Note that, even if + * the function indicates an error, the underlying descriptor is closed. */ void close() { boost::system::error_code ec; this->service.close(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "close"); } /// Close the descriptor. @@ -175,7 +176,8 @@ public: * write operations will be cancelled immediately, and will complete with the * boost::asio::error::operation_aborted error. * - * @param ec Set to indicate what error occurred, if any. + * @param ec Set to indicate what error occurred, if any. Note that, even if + * the function indicates an error, the underlying descriptor is closed. */ boost::system::error_code close(boost::system::error_code& ec) { @@ -232,7 +234,7 @@ public: { boost::system::error_code ec; this->service.cancel(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "cancel"); } /// Cancel all asynchronous operations associated with the descriptor. @@ -275,7 +277,7 @@ public: { boost::system::error_code ec; this->service.io_control(this->implementation, command, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "io_control"); } /// Perform an IO control command on the descriptor. @@ -345,7 +347,7 @@ public: { boost::system::error_code ec; this->service.non_blocking(this->implementation, mode, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "non_blocking"); } /// Sets the non-blocking mode of the descriptor. @@ -405,7 +407,7 @@ public: { boost::system::error_code ec; this->service.native_non_blocking(this->implementation, mode, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "native_non_blocking"); } /// Sets the non-blocking mode of the native descriptor implementation. diff --git a/include/boost/asio/posix/basic_stream_descriptor.hpp b/include/boost/asio/posix/basic_stream_descriptor.hpp index 4bd5dec9..d38333eb 100644 --- a/include/boost/asio/posix/basic_stream_descriptor.hpp +++ b/include/boost/asio/posix/basic_stream_descriptor.hpp @@ -124,7 +124,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.write_some(this->implementation, buffers, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_some"); return s; } @@ -231,7 +231,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.read_some(this->implementation, buffers, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_some"); return s; } diff --git a/include/boost/asio/posix/stream_descriptor_service.hpp b/include/boost/asio/posix/stream_descriptor_service.hpp index fb8e0079..3edd94b6 100644 --- a/include/boost/asio/posix/stream_descriptor_service.hpp +++ b/include/boost/asio/posix/stream_descriptor_service.hpp @@ -78,12 +78,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new stream descriptor implementation. void construct(implementation_type& impl) { @@ -210,6 +204,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/raw_socket_service.hpp b/include/boost/asio/raw_socket_service.hpp index 8ae06f95..6285307d 100644 --- a/include/boost/asio/raw_socket_service.hpp +++ b/include/boost/asio/raw_socket_service.hpp @@ -90,12 +90,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new raw socket implementation. void construct(implementation_type& impl) { @@ -340,6 +334,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/seq_packet_socket_service.hpp b/include/boost/asio/seq_packet_socket_service.hpp index a939e93b..cf628ad4 100644 --- a/include/boost/asio/seq_packet_socket_service.hpp +++ b/include/boost/asio/seq_packet_socket_service.hpp @@ -92,12 +92,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new sequenced packet socket implementation. void construct(implementation_type& impl) { @@ -307,6 +301,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/serial_port_service.hpp b/include/boost/asio/serial_port_service.hpp index c10e2c3e..9b45ae20 100644 --- a/include/boost/asio/serial_port_service.hpp +++ b/include/boost/asio/serial_port_service.hpp @@ -84,12 +84,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new serial port implementation. void construct(implementation_type& impl) { @@ -204,6 +198,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/signal_set_service.hpp b/include/boost/asio/signal_set_service.hpp index 34b2eac9..60764a0a 100644 --- a/include/boost/asio/signal_set_service.hpp +++ b/include/boost/asio/signal_set_service.hpp @@ -54,12 +54,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new signal set implementation. void construct(implementation_type& impl) { @@ -108,6 +102,18 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + + // Perform any fork-related housekeeping. + void fork_service(boost::asio::io_service::fork_event event) + { + service_impl_.fork_service(event); + } + // The platform-specific implementation. detail::signal_set_service service_impl_; }; diff --git a/include/boost/asio/socket_acceptor_service.hpp b/include/boost/asio/socket_acceptor_service.hpp index 9aa893b6..bebb9883 100644 --- a/include/boost/asio/socket_acceptor_service.hpp +++ b/include/boost/asio/socket_acceptor_service.hpp @@ -90,12 +90,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new socket acceptor implementation. void construct(implementation_type& impl) { @@ -246,6 +240,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/ssl/context_service.hpp b/include/boost/asio/ssl/context_service.hpp index 50b7b576..5062d225 100644 --- a/include/boost/asio/ssl/context_service.hpp +++ b/include/boost/asio/ssl/context_service.hpp @@ -62,11 +62,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - } - /// Return a null context implementation. impl_type null() const { @@ -161,6 +156,11 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + // The service that provides the platform-specific implementation. service_impl_type& service_impl_; }; diff --git a/include/boost/asio/ssl/stream_service.hpp b/include/boost/asio/ssl/stream_service.hpp index 92f7ee36..8f7e08dc 100644 --- a/include/boost/asio/ssl/stream_service.hpp +++ b/include/boost/asio/ssl/stream_service.hpp @@ -62,11 +62,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - } - /// Return a null stream implementation. impl_type null() const { @@ -171,6 +166,11 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + } + // The service that provides the platform-specific implementation. service_impl_type& service_impl_; }; diff --git a/include/boost/asio/stream_socket_service.hpp b/include/boost/asio/stream_socket_service.hpp index 6b93856d..09d57ebc 100644 --- a/include/boost/asio/stream_socket_service.hpp +++ b/include/boost/asio/stream_socket_service.hpp @@ -90,12 +90,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new stream socket implementation. void construct(implementation_type& impl) { @@ -303,6 +297,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/windows/basic_handle.hpp b/include/boost/asio/windows/basic_handle.hpp index 749bb3aa..609030fd 100644 --- a/include/boost/asio/windows/basic_handle.hpp +++ b/include/boost/asio/windows/basic_handle.hpp @@ -84,7 +84,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, handle, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Get a reference to the lowest layer. @@ -127,7 +127,7 @@ public: { boost::system::error_code ec; this->service.assign(this->implementation, handle, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "assign"); } /// Assign an existing native handle to the handle. @@ -162,7 +162,7 @@ public: { boost::system::error_code ec; this->service.close(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "close"); } /// Close the handle. @@ -212,7 +212,7 @@ public: { boost::system::error_code ec; this->service.cancel(this->implementation, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "cancel"); } /// Cancel all asynchronous operations associated with the handle. diff --git a/include/boost/asio/windows/basic_random_access_handle.hpp b/include/boost/asio/windows/basic_random_access_handle.hpp index a7a8f7ec..fab149c0 100644 --- a/include/boost/asio/windows/basic_random_access_handle.hpp +++ b/include/boost/asio/windows/basic_random_access_handle.hpp @@ -124,7 +124,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.write_some_at( this->implementation, offset, buffers, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_some_at"); return s; } @@ -240,7 +240,7 @@ public: boost::system::error_code ec; std::size_t s = this->service.read_some_at( this->implementation, offset, buffers, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_some_at"); return s; } diff --git a/include/boost/asio/windows/basic_stream_handle.hpp b/include/boost/asio/windows/basic_stream_handle.hpp index 41b9451f..5ad7d56c 100644 --- a/include/boost/asio/windows/basic_stream_handle.hpp +++ b/include/boost/asio/windows/basic_stream_handle.hpp @@ -121,7 +121,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.write_some(this->implementation, buffers, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "write_some"); return s; } @@ -228,7 +228,7 @@ public: { boost::system::error_code ec; std::size_t s = this->service.read_some(this->implementation, buffers, ec); - boost::asio::detail::throw_error(ec); + boost::asio::detail::throw_error(ec, "read_some"); return s; } diff --git a/include/boost/asio/windows/random_access_handle_service.hpp b/include/boost/asio/windows/random_access_handle_service.hpp index 605bd797..c8ca4925 100644 --- a/include/boost/asio/windows/random_access_handle_service.hpp +++ b/include/boost/asio/windows/random_access_handle_service.hpp @@ -81,12 +81,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new random-access handle implementation. void construct(implementation_type& impl) { @@ -171,6 +165,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/include/boost/asio/windows/stream_handle_service.hpp b/include/boost/asio/windows/stream_handle_service.hpp index f00c51e4..924a0ccd 100644 --- a/include/boost/asio/windows/stream_handle_service.hpp +++ b/include/boost/asio/windows/stream_handle_service.hpp @@ -78,12 +78,6 @@ public: { } - /// Destroy all user-defined handler objects owned by the service. - void shutdown_service() - { - service_impl_.shutdown_service(); - } - /// Construct a new stream handle implementation. void construct(implementation_type& impl) { @@ -168,6 +162,12 @@ public: } private: + // Destroy all user-defined handler objects owned by the service. + void shutdown_service() + { + service_impl_.shutdown_service(); + } + // The platform-specific implementation. service_impl_type service_impl_; }; diff --git a/test/archetypes/gettable_socket_option.hpp b/test/archetypes/gettable_socket_option.hpp new file mode 100644 index 00000000..aa8bb155 --- /dev/null +++ b/test/archetypes/gettable_socket_option.hpp @@ -0,0 +1,54 @@ +// +// gettable_socket_option.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ARCHETYPES_GETTABLE_SOCKET_OPTION_HPP +#define ARCHETYPES_GETTABLE_SOCKET_OPTION_HPP + +#include + +namespace archetypes { + +template +class gettable_socket_option +{ +public: + template + int level(const Protocol&) const + { + return 0; + } + + template + int name(const Protocol&) const + { + return 0; + } + + template + PointerType* data(const Protocol&) + { + return 0; + } + + template + std::size_t size(const Protocol&) const + { + return 0; + } + + template + void resize(const Protocol&, std::size_t) + { + } +}; + +} // namespace archetypes + +#endif // ARCHETYPES_GETTABLE_SOCKET_OPTION_HPP diff --git a/test/archetypes/settable_socket_option.hpp b/test/archetypes/settable_socket_option.hpp new file mode 100644 index 00000000..dbef2df0 --- /dev/null +++ b/test/archetypes/settable_socket_option.hpp @@ -0,0 +1,49 @@ +// +// settable_socket_option.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2003-2011 Christopher M. Kohlhoff (chris at kohlhoff dot com) +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef ARCHETYPES_SETTABLE_SOCKET_OPTION_HPP +#define ARCHETYPES_SETTABLE_SOCKET_OPTION_HPP + +#include + +namespace archetypes { + +template +class settable_socket_option +{ +public: + template + int level(const Protocol&) const + { + return 0; + } + + template + int name(const Protocol&) const + { + return 0; + } + + template + const PointerType* data(const Protocol&) const + { + return 0; + } + + template + std::size_t size(const Protocol&) const + { + return 0; + } +}; + +} // namespace archetypes + +#endif // ARCHETYPES_SETTABLE_SOCKET_OPTION_HPP diff --git a/test/deadline_timer.cpp b/test/deadline_timer.cpp index 8b6a49ea..e4a62699 100644 --- a/test/deadline_timer.cpp +++ b/test/deadline_timer.cpp @@ -16,7 +16,7 @@ // Test that header file is self-contained. #include -#include +#include #include #include #include diff --git a/test/io_service.cpp b/test/io_service.cpp index 1fb5a9d7..5f49267d 100644 --- a/test/io_service.cpp +++ b/test/io_service.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include "unit_test.hpp" diff --git a/test/ip/tcp.cpp b/test/ip/tcp.cpp index 00a8ffa7..31d3f008 100644 --- a/test/ip/tcp.cpp +++ b/test/ip/tcp.cpp @@ -26,7 +26,9 @@ #include #include #include "../unit_test.hpp" +#include "../archetypes/gettable_socket_option.hpp" #include "../archetypes/io_control_command.hpp" +#include "../archetypes/settable_socket_option.hpp" //------------------------------------------------------------------------------ @@ -156,6 +158,12 @@ void test() const char const_char_buffer[128] = ""; socket_base::message_flags in_flags = 0; socket_base::keep_alive socket_option; + archetypes::settable_socket_option settable_socket_option1; + archetypes::settable_socket_option settable_socket_option2; + archetypes::settable_socket_option settable_socket_option3; + archetypes::gettable_socket_option gettable_socket_option1; + archetypes::gettable_socket_option gettable_socket_option2; + archetypes::gettable_socket_option gettable_socket_option3; archetypes::io_control_command io_control_command; boost::system::error_code ec; @@ -233,11 +241,19 @@ void test() socket1.async_connect(ip::tcp::endpoint(ip::tcp::v4(), 0), connect_handler); socket1.async_connect(ip::tcp::endpoint(ip::tcp::v6(), 0), connect_handler); - socket1.set_option(socket_option); - socket1.set_option(socket_option, ec); + socket1.set_option(settable_socket_option1); + socket1.set_option(settable_socket_option1, ec); + socket1.set_option(settable_socket_option2); + socket1.set_option(settable_socket_option2, ec); + socket1.set_option(settable_socket_option3); + socket1.set_option(settable_socket_option3, ec); - socket1.get_option(socket_option); - socket1.get_option(socket_option, ec); + socket1.get_option(gettable_socket_option1); + socket1.get_option(gettable_socket_option1, ec); + socket1.get_option(gettable_socket_option2); + socket1.get_option(gettable_socket_option2, ec); + socket1.get_option(gettable_socket_option3); + socket1.get_option(gettable_socket_option3, ec); socket1.io_control(io_control_command); socket1.io_control(io_control_command, ec); diff --git a/test/ip/udp.cpp b/test/ip/udp.cpp index 0cff5143..8202f32c 100644 --- a/test/ip/udp.cpp +++ b/test/ip/udp.cpp @@ -21,7 +21,9 @@ #include #include #include "../unit_test.hpp" +#include "../archetypes/gettable_socket_option.hpp" #include "../archetypes/io_control_command.hpp" +#include "../archetypes/settable_socket_option.hpp" //------------------------------------------------------------------------------ @@ -55,7 +57,12 @@ void test() char mutable_char_buffer[128] = ""; const char const_char_buffer[128] = ""; socket_base::message_flags in_flags = 0; - socket_base::keep_alive socket_option; + archetypes::settable_socket_option settable_socket_option1; + archetypes::settable_socket_option settable_socket_option2; + archetypes::settable_socket_option settable_socket_option3; + archetypes::gettable_socket_option gettable_socket_option1; + archetypes::gettable_socket_option gettable_socket_option2; + archetypes::gettable_socket_option gettable_socket_option3; archetypes::io_control_command io_control_command; boost::system::error_code ec; @@ -133,11 +140,19 @@ void test() socket1.async_connect(ip::udp::endpoint(ip::udp::v4(), 0), connect_handler); socket1.async_connect(ip::udp::endpoint(ip::udp::v6(), 0), connect_handler); - socket1.set_option(socket_option); - socket1.set_option(socket_option, ec); + socket1.set_option(settable_socket_option1); + socket1.set_option(settable_socket_option1, ec); + socket1.set_option(settable_socket_option2); + socket1.set_option(settable_socket_option2, ec); + socket1.set_option(settable_socket_option3); + socket1.set_option(settable_socket_option3, ec); - socket1.get_option(socket_option); - socket1.get_option(socket_option, ec); + socket1.get_option(gettable_socket_option1); + socket1.get_option(gettable_socket_option1, ec); + socket1.get_option(gettable_socket_option2); + socket1.get_option(gettable_socket_option2, ec); + socket1.get_option(gettable_socket_option3); + socket1.get_option(gettable_socket_option3, ec); socket1.io_control(io_control_command); socket1.io_control(io_control_command, ec); diff --git a/test/strand.cpp b/test/strand.cpp index d429e2c1..61681a9d 100644 --- a/test/strand.cpp +++ b/test/strand.cpp @@ -17,7 +17,7 @@ #include #include -#include +#include #include #include #include