$search
00001 // 00002 // socket_select_interrupter.hpp 00003 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 00004 // 00005 // Copyright (c) 2003-2008 Christopher M. Kohlhoff (chris at kohlhoff dot com) 00006 // 00007 // Distributed under the Boost Software License, Version 1.0. (See accompanying 00008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 00009 // 00010 00011 #ifndef ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP 00012 #define ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP 00013 00014 #if defined(_MSC_VER) && (_MSC_VER >= 1200) 00015 # pragma once 00016 #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) 00017 00018 #include "asio/detail/push_options.hpp" 00019 00020 #include "asio/detail/push_options.hpp" 00021 #include <cstdlib> 00022 #include <boost/throw_exception.hpp> 00023 #include "asio/detail/pop_options.hpp" 00024 00025 #include "asio/error.hpp" 00026 #include "asio/system_error.hpp" 00027 #include "asio/detail/socket_holder.hpp" 00028 #include "asio/detail/socket_ops.hpp" 00029 #include "asio/detail/socket_types.hpp" 00030 00031 namespace asio { 00032 namespace detail { 00033 00034 class socket_select_interrupter 00035 { 00036 public: 00037 // Constructor. 00038 socket_select_interrupter() 00039 { 00040 asio::error_code ec; 00041 socket_holder acceptor(socket_ops::socket( 00042 AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); 00043 if (acceptor.get() == invalid_socket) 00044 { 00045 asio::system_error e(ec, "socket_select_interrupter"); 00046 boost::throw_exception(e); 00047 } 00048 00049 int opt = 1; 00050 socket_ops::setsockopt(acceptor.get(), 00051 SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt), ec); 00052 00053 using namespace std; // For memset. 00054 sockaddr_in4_type addr; 00055 std::size_t addr_len = sizeof(addr); 00056 memset(&addr, 0, sizeof(addr)); 00057 addr.sin_family = AF_INET; 00058 addr.sin_addr.s_addr = inet_addr("127.0.0.1"); 00059 addr.sin_port = 0; 00060 if (socket_ops::bind(acceptor.get(), (const socket_addr_type*)&addr, 00061 addr_len, ec) == socket_error_retval) 00062 { 00063 asio::system_error e(ec, "socket_select_interrupter"); 00064 boost::throw_exception(e); 00065 } 00066 00067 if (socket_ops::getsockname(acceptor.get(), (socket_addr_type*)&addr, 00068 &addr_len, ec) == socket_error_retval) 00069 { 00070 asio::system_error e(ec, "socket_select_interrupter"); 00071 boost::throw_exception(e); 00072 } 00073 00074 if (socket_ops::listen(acceptor.get(), 00075 SOMAXCONN, ec) == socket_error_retval) 00076 { 00077 asio::system_error e(ec, "socket_select_interrupter"); 00078 boost::throw_exception(e); 00079 } 00080 00081 socket_holder client(socket_ops::socket( 00082 AF_INET, SOCK_STREAM, IPPROTO_TCP, ec)); 00083 if (client.get() == invalid_socket) 00084 { 00085 asio::system_error e(ec, "socket_select_interrupter"); 00086 boost::throw_exception(e); 00087 } 00088 00089 if (socket_ops::connect(client.get(), (const socket_addr_type*)&addr, 00090 addr_len, ec) == socket_error_retval) 00091 { 00092 asio::system_error e(ec, "socket_select_interrupter"); 00093 boost::throw_exception(e); 00094 } 00095 00096 socket_holder server(socket_ops::accept(acceptor.get(), 0, 0, ec)); 00097 if (server.get() == invalid_socket) 00098 { 00099 asio::system_error e(ec, "socket_select_interrupter"); 00100 boost::throw_exception(e); 00101 } 00102 00103 ioctl_arg_type non_blocking = 1; 00104 if (socket_ops::ioctl(client.get(), FIONBIO, &non_blocking, ec)) 00105 { 00106 asio::system_error e(ec, "socket_select_interrupter"); 00107 boost::throw_exception(e); 00108 } 00109 00110 opt = 1; 00111 socket_ops::setsockopt(client.get(), 00112 IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); 00113 00114 non_blocking = 1; 00115 if (socket_ops::ioctl(server.get(), FIONBIO, &non_blocking, ec)) 00116 { 00117 asio::system_error e(ec, "socket_select_interrupter"); 00118 boost::throw_exception(e); 00119 } 00120 00121 opt = 1; 00122 socket_ops::setsockopt(server.get(), 00123 IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt), ec); 00124 00125 read_descriptor_ = server.release(); 00126 write_descriptor_ = client.release(); 00127 } 00128 00129 // Destructor. 00130 ~socket_select_interrupter() 00131 { 00132 asio::error_code ec; 00133 if (read_descriptor_ != invalid_socket) 00134 socket_ops::close(read_descriptor_, ec); 00135 if (write_descriptor_ != invalid_socket) 00136 socket_ops::close(write_descriptor_, ec); 00137 } 00138 00139 // Interrupt the select call. 00140 void interrupt() 00141 { 00142 char byte = 0; 00143 socket_ops::buf b; 00144 socket_ops::init_buf(b, &byte, 1); 00145 asio::error_code ec; 00146 socket_ops::send(write_descriptor_, &b, 1, 0, ec); 00147 } 00148 00149 // Reset the select interrupt. Returns true if the call was interrupted. 00150 bool reset() 00151 { 00152 char data[1024]; 00153 socket_ops::buf b; 00154 socket_ops::init_buf(b, data, sizeof(data)); 00155 asio::error_code ec; 00156 int bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); 00157 bool was_interrupted = (bytes_read > 0); 00158 while (bytes_read == sizeof(data)) 00159 bytes_read = socket_ops::recv(read_descriptor_, &b, 1, 0, ec); 00160 return was_interrupted; 00161 } 00162 00163 // Get the read descriptor to be passed to select. 00164 socket_type read_descriptor() const 00165 { 00166 return read_descriptor_; 00167 } 00168 00169 private: 00170 // The read end of a connection used to interrupt the select call. This file 00171 // descriptor is passed to select such that when it is time to stop, a single 00172 // byte will be written on the other end of the connection and this 00173 // descriptor will become readable. 00174 socket_type read_descriptor_; 00175 00176 // The write end of a connection used to interrupt the select call. A single 00177 // byte may be written to this to wake up the select which is waiting for the 00178 // other end to become readable. 00179 socket_type write_descriptor_; 00180 }; 00181 00182 } // namespace detail 00183 } // namespace asio 00184 00185 #include "asio/detail/pop_options.hpp" 00186 00187 #endif // ASIO_DETAIL_SOCKET_SELECT_INTERRUPTER_HPP