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