Browse Source

Fixed EINTR handling when polling WebSocket TCP socket

Paul-Louis Ageneau 3 years ago
parent
commit
f0bd580345
1 changed files with 45 additions and 37 deletions
  1. 45 37
      src/impl/tcptransport.cpp

+ 45 - 37
src/impl/tcptransport.cpp

@@ -31,6 +31,7 @@
 namespace rtc::impl {
 namespace rtc::impl {
 
 
 using namespace std::chrono_literals;
 using namespace std::chrono_literals;
+using std::chrono::duration_cast;
 using std::chrono::milliseconds;
 using std::chrono::milliseconds;
 
 
 TcpTransport::TcpTransport(string hostname, string service, state_callback callback)
 TcpTransport::TcpTransport(string hostname, string service, state_callback callback)
@@ -196,40 +197,42 @@ void TcpTransport::connect(const sockaddr *addr, socklen_t addrlen) {
 			throw std::runtime_error(msg.str());
 			throw std::runtime_error(msg.str());
 		}
 		}
 
 
-		while (true) {
-			struct pollfd pfd[1];
-			pfd[0].fd = mSock;
-			pfd[0].events = POLLOUT;
-			milliseconds timeout = 10s; // TODO: Make the timeout configurable
-			int ret = ::poll(pfd, 1, int(timeout.count()));
-
-			if (ret < 0) {
-				if (sockerrno == SEINTR || sockerrno == SEAGAIN) // interrupted
-					continue;
-				else
-					throw std::runtime_error("Failed to wait for socket connection");
-			}
+		// Wait for connection
+		struct pollfd pfd[1];
+		pfd[0].fd = mSock;
+		pfd[0].events = POLLOUT;
 
 
-			if (!(pfd[0].revents & POLLOUT)) {
-				std::ostringstream msg;
-				msg << "TCP connection to " << node << ":" << serv << " timed out";
-				throw std::runtime_error(msg.str());
-			}
+		using clock = std::chrono::steady_clock;
+		auto end = clock::now() + 10s; // TODO: Make the timeout configurable
 
 
-			int error = 0;
-			socklen_t errorlen = sizeof(error);
-			if (::getsockopt(mSock, SOL_SOCKET, SO_ERROR, (char *)&error, &errorlen) != 0)
-				throw std::runtime_error("Failed to get socket error code");
+		do {
+			auto timeout = std::max(clock::duration::zero(), end - clock::now());
+			ret = ::poll(pfd, 1, duration_cast<milliseconds>(timeout).count());
 
 
-			if (error != 0) {
-				std::ostringstream msg;
-				msg << "TCP connection to " << node << ":" << serv << " failed, errno=" << error;
-				throw std::runtime_error(msg.str());
-			}
+		} while (ret < 0 && (sockerrno == SEINTR || sockerrno == SEAGAIN));
+
+		if (ret < 0)
+			throw std::runtime_error("Failed to wait for socket connection");
+
+		if (!(pfd[0].revents & POLLOUT)) {
+			std::ostringstream msg;
+			msg << "TCP connection to " << node << ":" << serv << " timed out";
+			throw std::runtime_error(msg.str());
+		}
+
+		int err = 0;
+		socklen_t errlen = sizeof(err);
+		if (::getsockopt(mSock, SOL_SOCKET, SO_ERROR, (char *)&err, &errlen) != 0)
+			throw std::runtime_error("Failed to get socket error code");
 
 
-			PLOG_DEBUG << "TCP connection to " << node << ":" << serv << " succeeded";
-			break;
+		if (err != 0) {
+			std::ostringstream msg;
+			msg << "TCP connection to " << node << ":" << serv << " failed, errno=" << err;
+			throw std::runtime_error(msg.str());
 		}
 		}
+
+		PLOG_DEBUG << "TCP connection to " << node << ":" << serv << " succeeded";
+
 	} catch (...) {
 	} catch (...) {
 		if (mSock != INVALID_SOCKET) {
 		if (mSock != INVALID_SOCKET) {
 			::closesocket(mSock);
 			::closesocket(mSock);
@@ -321,21 +324,26 @@ void TcpTransport::runLoop() {
 			pfd[0].fd = mSock;
 			pfd[0].fd = mSock;
 			pfd[0].events = !mSendQueue.empty() ? (POLLIN | POLLOUT) : POLLIN;
 			pfd[0].events = !mSendQueue.empty() ? (POLLIN | POLLOUT) : POLLIN;
 			mInterrupter.prepare(pfd[1]);
 			mInterrupter.prepare(pfd[1]);
-			milliseconds timeout = 10s;
+
+			using clock = std::chrono::steady_clock;
+			auto end = clock::now() + 10s;
+
+			int ret;
 			lock.unlock();
 			lock.unlock();
-			int ret = ::poll(pfd, 2, int(timeout.count()));
+			do {
+				auto timeout = std::max(clock::duration::zero(), end - clock::now());
+				ret = ::poll(pfd, 2, duration_cast<milliseconds>(timeout).count());
+
+			} while (ret < 0 && (sockerrno == SEINTR || sockerrno == SEAGAIN));
 			lock.lock();
 			lock.lock();
 
 
 			if (mSock == INVALID_SOCKET)
 			if (mSock == INVALID_SOCKET)
 				break;
 				break;
 
 
-			if (ret < 0) {
-				if (sockerrno == SEINTR || sockerrno == SEAGAIN) // interrupted
-					continue;
-				else
-					throw std::runtime_error("Failed to wait on socket");
+			if (ret < 0)
+				throw std::runtime_error("Failed to wait on socket");
 
 
-			} else if (ret == 0) {
+			if (ret == 0) {
 				PLOG_VERBOSE << "TCP is idle";
 				PLOG_VERBOSE << "TCP is idle";
 				lock.unlock(); // unlock now since the upper layer might send on incoming
 				lock.unlock(); // unlock now since the upper layer might send on incoming
 				incoming(make_message(0));
 				incoming(make_message(0));