Fixed WI-FI hotspot stuck issue during close

PiperOrigin-RevId: 551703821
This commit is contained in:
Guogang Li
2023-07-27 18:57:27 -07:00
committed by Copybara-Service
parent 0a13ce51f4
commit eb5f8aed10
2 changed files with 72 additions and 30 deletions
@@ -16,8 +16,8 @@
#define PLATFORM_IMPL_WINDOWS_WIFI_HOTSPOT_H_
// Windows headers
#include <winsock2.h>
#include <windows.h>
#include <winsock2.h>
#include <wlanapi.h>
// Standard C/C++ headers
@@ -27,6 +27,7 @@
#include <string>
// Nearby connections headers
#include "absl/strings/string_view.h"
#include "internal/platform/implementation/wifi_hotspot.h"
#include "internal/platform/implementation/windows/scheduled_executor.h"
#include "internal/platform/implementation/windows/submittable_executor.h"
@@ -123,7 +124,7 @@ class WifiHotspotSocket : public api::WifiHotspotSocket {
Exception Close() override;
private:
enum class SocketType {kWinRTSocket = 0, kWin32Socket};
enum class SocketType { kWinRTSocket = 0, kWin32Socket };
// A simple wrapper to handle input stream of socket
class SocketInputStream : public InputStream {
public:
@@ -204,6 +205,10 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket {
bool listen();
private:
static constexpr int kSocketEventsCount = 2;
static constexpr int kSocketEventListen = 0;
static constexpr int kSocketEventClose = 1;
// The listener is accepting incoming connections
fire_and_forget Listener_ConnectionReceived(
StreamSocketListener listener,
@@ -214,6 +219,8 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket {
// Retrieves hotspot IP address from local machine
std::string GetHotspotIpAddress() const;
void SocketErrorNotice(absl::string_view reason);
mutable absl::Mutex mutex_;
absl::CondVar cond_;
SubmittableExecutor submittable_executor_;
@@ -225,6 +232,13 @@ class WifiHotspotServerSocket : public api::WifiHotspotServerSocket {
std::deque<SOCKET> pending_client_sockets_ ABSL_GUARDED_BY(mutex_);
SOCKET listen_socket_ = INVALID_SOCKET;
SOCKET client_socket_ = INVALID_SOCKET;
// closesocket cannot trigger FD_CLOSE on listener socket. In order to avoid
// blocking in WSAWaitForMultipleEvents, we use a socket event to trigger
// WSAWaitForMultipleEvents safely.
// The socket_events_ has 2 events, the first one is to handle normal socket
// event, and the second one is to handle event to close the socket manually.
WSAEVENT socket_events_[kSocketEventsCount];
// Close notifier
absl::AnyInvocable<void()> close_notifier_ = nullptr;
@@ -36,7 +36,11 @@ namespace {
using ::winrt::Windows::Networking::Sockets::SocketQualityOfService;
} // namespace
WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) {}
WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) {
for (auto &it : socket_events_) {
it = WSA_INVALID_EVENT;
}
}
WifiHotspotServerSocket::~WifiHotspotServerSocket() { Close(); }
@@ -55,7 +59,7 @@ std::string WifiHotspotServerSocket::GetIPAddress() const {
std::string hotspot_ip_address = GetHotspotIpAddress();
NEARBY_LOGS(INFO) << __func__
<< ": Return hotspot IP address: " << hotspot_ip_address;
<< ": Return hotspot IP address: " << hotspot_ip_address;
return hotspot_ip_address;
}
@@ -126,18 +130,26 @@ Exception WifiHotspotServerSocket::Close() {
kEnableHotspotWin32Socket)) {
if (listen_socket_ != INVALID_SOCKET) {
NEARBY_LOGS(INFO) << ": Close listen_socket_: " << listen_socket_;
// Trigger close event manually
WSASetEvent(socket_events_[kSocketEventClose]);
shutdown(listen_socket_, 2);
shutdown(client_socket_, 2);
closesocket(listen_socket_);
closesocket(client_socket_);
listen_socket_ = INVALID_SOCKET;
client_socket_ = INVALID_SOCKET;
for (const auto &pending_socket : pending_client_sockets_) {
if (pending_socket != INVALID_SOCKET) closesocket(pending_socket);
}
submittable_executor_.Shutdown();
listen_socket_ = INVALID_SOCKET;
client_socket_ = INVALID_SOCKET;
for (auto &it : socket_events_) {
WSACloseEvent(it);
it = WSA_INVALID_EVENT;
}
WSACleanup();
pending_client_sockets_ = {};
}
submittable_executor_.Shutdown();
} else {
if (stream_socket_listener_ != nullptr) {
stream_socket_listener_.ConnectionReceived(listener_event_token_);
@@ -254,14 +266,16 @@ bool WifiHotspotServerSocket::SetupServerSocketWinRT() {
return false;
}
// Checks for SOCKET_ERROR, this error can come up when trying to bind, listen,
// Getsockname, WSACreateEvent, WSAEventSelect etc.
void SocketErrorNotice(SOCKET socket_to_close, const char *action) {
// const char *actionAttempted = action;
NEARBY_LOGS(WARNING) << "socket error. " << action
void WifiHotspotServerSocket::SocketErrorNotice(absl::string_view reason) {
NEARBY_LOGS(WARNING) << "socket error. " << reason
<< " failed with error: " << WSAGetLastError();
closesocket(socket_to_close);
for (auto &it : socket_events_) {
if (it != WSA_INVALID_EVENT) {
WSACloseEvent(it);
it = WSA_INVALID_EVENT;
}
}
closesocket(listen_socket_);
WSACleanup();
}
@@ -293,7 +307,7 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() {
sizeof(flag));
if (bind(listen_socket_, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) ==
SOCKET_ERROR) {
SocketErrorNotice(listen_socket_, "Bind");
SocketErrorNotice("Bind");
return false;
}
NEARBY_LOGS(INFO) << "Bind socket successful";
@@ -302,50 +316,63 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() {
memset(&serv_addr, 0, size);
if (getsockname(listen_socket_, (struct sockaddr *)&serv_addr, &size) ==
SOCKET_ERROR) {
SocketErrorNotice(listen_socket_, "Getsockname");
SocketErrorNotice("Getsockname");
return false;
}
port_ = ntohs(serv_addr.sin_port);
NEARBY_LOGS(INFO) << "Hotspot Server bound to port: " << port_;
socket_event = WSACreateEvent();
if (socket_event == nullptr) {
SocketErrorNotice(listen_socket_, "WSACreateEvent");
socket_events_[kSocketEventListen] = WSACreateEvent();
if (socket_events_[kSocketEventListen] == WSA_INVALID_EVENT) {
SocketErrorNotice("WSACreateEvent");
return false;
}
socket_events_[kSocketEventClose] = WSACreateEvent();
if (socket_events_[kSocketEventClose] == WSA_INVALID_EVENT) {
SocketErrorNotice("WSACreateEvent");
return false;
}
// Associate event types FD_ACCEPT and FD_CLOSE with the listen_socket_ and
// socket_event
if (WSAEventSelect(listen_socket_, socket_event, FD_ACCEPT | FD_CLOSE) ==
SOCKET_ERROR) {
SocketErrorNotice(listen_socket_, "WSAEventSelect");
if (WSAEventSelect(listen_socket_, socket_events_[kSocketEventListen],
FD_ACCEPT | FD_CLOSE) == SOCKET_ERROR) {
SocketErrorNotice("WSAEventSelect");
return false;
}
if (::listen(listen_socket_, SOMAXCONN) == SOCKET_ERROR) {
SocketErrorNotice(listen_socket_, "Listen");
SocketErrorNotice("Listen");
return false;
}
NEARBY_LOGS(INFO) << "Hotspot Server Socket " << listen_socket_
<< " started to listen with socket event: " << socket_event;
submittable_executor_.Execute([this, socket_event]() {
submittable_executor_.Execute([this]() {
DWORD index;
WSANETWORKEVENTS network_events;
// Wait for network events on all sockets
index =
WSAWaitForMultipleEvents(1, &socket_event, FALSE, WSA_INFINITE, FALSE);
index = WSAWaitForMultipleEvents(kSocketEventsCount, socket_events_, FALSE,
WSA_INFINITE, FALSE);
NEARBY_LOGS(INFO) << "Hotspot Server Socket " << listen_socket_
<< " received event: " << socket_event;
<< " received event index: " << index;
if (index == WSA_WAIT_TIMEOUT || index == WSA_WAIT_FAILED) {
NEARBY_LOGS(INFO) << "Hotspot Server Socket timout or failed ";
return false;
}
index = index - WSA_WAIT_EVENT_0;
if (index == kSocketEventClose) {
// the socket is closed by SDK
NEARBY_LOGS(INFO) << "listner socket is closed.";
return false;
}
// Iterate through all events and enumerate
if (WSAEnumNetworkEvents(listen_socket_, socket_event, &network_events) ==
SOCKET_ERROR) {
if (WSAEnumNetworkEvents(listen_socket_, socket_events_[index],
&network_events) == SOCKET_ERROR) {
NEARBY_LOGS(INFO) << "Iterate through all events failed";
return false;
}
@@ -361,7 +388,8 @@ bool WifiHotspotServerSocket::SetupServerSocketWinSock() {
return false;
}
if (WSAEventSelect(listen_socket_, socket_event, 0) == SOCKET_ERROR) {
if (WSAEventSelect(listen_socket_, socket_events_[kSocketEventListen],
0) == SOCKET_ERROR) {
NEARBY_LOGS(WARNING)
<< "Remove association between listen_socket_ and event failed: "
<< WSAGetLastError();