Merge branch 'master' into release

Change-Id: I8a6cfe28093bf3d60dc91bcbc4e98e641764c4c0
This commit is contained in:
Alexey Polyudov
2020-07-07 12:54:19 -07:00
29 changed files with 1034 additions and 234 deletions
+252 -31
View File
@@ -14,6 +14,7 @@
#include "platform_v2/impl/g3/wifi_lan.h"
#include <iostream>
#include <memory>
#include <string>
@@ -26,20 +27,45 @@ namespace location {
namespace nearby {
namespace g3 {
InputStream& WifiLanSocket::GetInputStream() {
WifiLanSocket::~WifiLanSocket() {
absl::MutexLock lock(&mutex_);
return pipe_.GetInputStream();
DoClose();
}
void WifiLanSocket::Connect(WifiLanSocket& other) {
absl::MutexLock lock(&mutex_);
remote_socket_ = &other;
input_ = other.output_;
}
InputStream& WifiLanSocket::GetInputStream() {
auto* remote_socket = GetRemoteSocket();
CHECK(remote_socket != nullptr);
return remote_socket->GetLocalInputStream();
}
OutputStream& WifiLanSocket::GetOutputStream() {
return GetLocalOutputStream();
}
WifiLanSocket* WifiLanSocket::GetRemoteSocket() {
absl::MutexLock lock(&mutex_);
return pipe_.GetOutputStream();
return remote_socket_;
}
bool WifiLanSocket::IsConnected() const {
absl::MutexLock lock(&mutex_);
return IsConnectedLocked();
}
bool WifiLanSocket::IsClosed() const {
absl::MutexLock lock(&mutex_);
return closed_;
}
Exception WifiLanSocket::Close() {
absl::MutexLock lock(&mutex_);
pipe_.GetOutputStream().Close();
pipe_.GetInputStream().Close();
DoClose();
return {Exception::kSuccess};
}
@@ -48,45 +74,215 @@ WifiLanService* WifiLanSocket::GetRemoteWifiLanService() {
return service_;
}
void WifiLanSocket::DoClose() {
if (!closed_) {
remote_socket_ = nullptr;
output_->GetOutputStream().Close();
output_->GetInputStream().Close();
if (IsConnectedLocked()) {
input_->GetOutputStream().Close();
input_->GetInputStream().Close();
}
closed_ = true;
}
}
bool WifiLanSocket::IsConnectedLocked() const { return input_ != nullptr; }
InputStream& WifiLanSocket::GetLocalInputStream() {
absl::MutexLock lock(&mutex_);
return output_->GetInputStream();
}
OutputStream& WifiLanSocket::GetLocalOutputStream() {
absl::MutexLock lock(&mutex_);
return output_->GetOutputStream();
}
std::unique_ptr<api::WifiLanSocket> WifiLanServerSocket::Accept() {
absl::MutexLock lock(&mutex_);
if (closed_) return {};
while (pending_sockets_.empty()) {
cond_.Wait(&mutex_);
if (closed_) break;
}
if (closed_) return {};
auto* remote_socket =
pending_sockets_.extract(pending_sockets_.begin()).value();
CHECK(remote_socket);
auto local_socket = std::make_unique<WifiLanSocket>();
local_socket->Connect(*remote_socket);
remote_socket->Connect(*local_socket);
cond_.SignalAll();
return local_socket;
}
bool WifiLanServerSocket::Connect(WifiLanSocket& socket) {
absl::MutexLock lock(&mutex_);
if (closed_) return false;
if (socket.IsConnected()) {
NEARBY_LOG(ERROR,
"Failed to connect to WifiLan server socket: already connected");
return true; // already connected.
}
// add client socket to the pending list
pending_sockets_.emplace(&socket);
cond_.SignalAll();
while (!socket.IsConnected()) {
cond_.Wait(&mutex_);
if (closed_) return false;
}
return true;
}
void WifiLanServerSocket::SetCloseNotifier(std::function<void()> notifier) {
absl::MutexLock lock(&mutex_);
close_notifier_ = std::move(notifier);
}
WifiLanServerSocket::~WifiLanServerSocket() {
absl::MutexLock lock(&mutex_);
DoClose();
}
Exception WifiLanServerSocket::Close() {
absl::MutexLock lock(&mutex_);
return DoClose();
}
Exception WifiLanServerSocket::DoClose() {
bool should_notify = !closed_;
closed_ = true;
if (should_notify) {
cond_.SignalAll();
if (close_notifier_) {
auto notifier = std::move(close_notifier_);
mutex_.Unlock();
// Notifier may contain calls to public API, and may cause deadlock, if
// mutex_ is held during the call.
notifier();
mutex_.Lock();
}
}
return {Exception::kSuccess};
}
WifiLanMedium::WifiLanMedium() {
service_.SetMedium(this);
auto& env = MediumEnvironment::Instance();
env.RegisterWifiLanMedium(*this);
env.RegisterWifiLanMedium(*this, service_);
}
WifiLanMedium::~WifiLanMedium() {
service_.SetMedium(nullptr);
auto& env = MediumEnvironment::Instance();
env.UnregisterWifiLanMedium(*this);
StopAdvertising(advertising_info_.service_id);
StopDiscovery(discovering_info_.service_id);
accept_loops_runner_.Shutdown();
NEARBY_LOG(INFO,
"WifiLanMedium dtor advertising_accept_thread_running_ = %d",
acceptance_thread_running_.load());
// If acceptance thread is still running, wait to finish.
if (acceptance_thread_running_) {
while (acceptance_thread_running_) {
CountDownLatch latch(1);
close_accept_loops_runner_.Execute([&latch]() { latch.CountDown(); });
latch.Await();
}
}
}
bool WifiLanMedium::StartAdvertising(
const std::string& service_id,
const std::string& wifi_lan_service_info_name) {
// TODO(edwinwu): Integrate medium_environment.
// steps:
// 1. create wifi_lan_service as the parameter to create wifi_lan_socket
// auto service = std::make_unique<WifiLanService>();
// auto socket = std::make_unique<WifiLanSocket>(service);
// 2. callback for accepting connection; otherwise don't callback if not
// accepted connection.
// accepted_connection_callback_.accepted_cb(socket, service_id);
NEARBY_LOG(INFO,
"G3 WifiLan StartAdvertising: service_id=%s, service_name=%s",
service_id.c_str(), wifi_lan_service_info_name.c_str());
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAdvertising(*this, service_, service_id, true);
absl::MutexLock lock(&mutex_);
if (server_socket_ != nullptr) server_socket_.release();
server_socket_ = std::make_unique<WifiLanServerSocket>();
acceptance_thread_running_.exchange(true);
accept_loops_runner_.Execute([&env, this, service_id]() mutable {
if (!accept_loops_runner_.InShutdown()) {
while (true) {
auto client_socket = server_socket_->Accept();
if (client_socket == nullptr) break;
env.CallWifiLanAcceptedConnectionCallback(*this, *client_socket,
service_id);
}
}
acceptance_thread_running_.exchange(false);
});
advertising_info_.service_id = service_id;
return true;
}
bool WifiLanMedium::StopAdvertising(const std::string& service_id) {
// TODO(edwinwu): Integrate medium_environment.
NEARBY_LOG(INFO, "G3 WifiLan StopAdvertising: service_id=%s",
service_id.c_str());
{
absl::MutexLock lock(&mutex_);
if (advertising_info_.Empty()) {
NEARBY_LOG(
INFO, "Can't stop advertising because we never started advertising.");
return false;
}
advertising_info_.Clear();
}
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAdvertising(*this, service_, service_id, false);
accept_loops_runner_.Shutdown();
if (server_socket_ == nullptr) {
NEARBY_LOG(ERROR, "Failed to find WifiLan Server socket: service_id=%s",
service_id.c_str());
// Fall through for server socket not found.
return true;
}
if (!server_socket_->Close().Ok()) {
NEARBY_LOG(INFO, "Failed to close WifiLan server socket for %s.",
service_id.c_str());
return false;
}
return true;
}
bool WifiLanMedium::StartDiscovery(const std::string& service_id,
DiscoveredServiceCallback callback) {
NEARBY_LOG(INFO, "G3 WifiLan StartDiscovery: service_id=%s",
service_id.c_str());
auto& env = MediumEnvironment::Instance();
NEARBY_LOG(INFO, "G3 StartDiscovery: service_id=%s", service_id.c_str());
env.UpdateWifiLanMediumForDiscovery(*this, service_, service_id,
std::move(callback), true);
{
absl::MutexLock lock(&mutex_);
discovering_info_.service_id = service_id;
}
return true;
}
bool WifiLanMedium::StopDiscovery(const std::string& service_id) {
NEARBY_LOG(INFO, "G3 WifiLan StopDiscovery: service_id=%s",
service_id.c_str());
{
absl::MutexLock lock(&mutex_);
if (discovering_info_.Empty()) {
NEARBY_LOG(
INFO, "Can't stop discovering because we never started discovering.");
return false;
}
discovering_info_.Clear();
}
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForDiscovery(*this, service_, service_id, {}, false);
return true;
@@ -94,33 +290,58 @@ bool WifiLanMedium::StopDiscovery(const std::string& service_id) {
bool WifiLanMedium::StartAcceptingConnections(
const std::string& service_id, AcceptedConnectionCallback callback) {
// TODO(edwinwu): Integrate medium_environment.
// steps:
NEARBY_LOG(INFO, "G3 WifiLan StartAcceptingConnections: service_id=%s",
service_id.c_str());
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_id, callback);
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_, service_id,
callback);
return true;
}
bool WifiLanMedium::StopAcceptingConnections(const std::string& service_id) {
// TODO(edwinwu): Integrate medium_environment.
NEARBY_LOG(INFO, "G3 WifiLan StopAcceptingConnections: service_id=%s",
service_id.c_str());
auto& env = MediumEnvironment::Instance();
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_id, {});
env.UpdateWifiLanMediumForAcceptedConnection(*this, service_, service_id, {});
return true;
}
std::unique_ptr<api::WifiLanSocket> WifiLanMedium::Connect(
api::WifiLanService& service, const std::string& service_id) {
api::WifiLanService& remote_service, const std::string& service_id) {
NEARBY_LOG(INFO, "G3 WifiLan Connect: medium=%p, service=%p, service_id=%s",
this, &service_, service_id.c_str());
// First, find an instance of remote medium, that exposed this service.
auto* medium = static_cast<WifiLanService&>(remote_service).GetMedium();
if (!medium) return {}; // Can't find medium. Bail out.
WifiLanServerSocket* server_socket = nullptr;
NEARBY_LOG(INFO,
"G3 WifiLan Connect [peer]: medium=%p, service=%p, service_id=%s",
medium, &remote_service, service_id.c_str());
// Then, find our server socket context in this medium.
{
absl::MutexLock medium_lock(&medium->mutex_);
server_socket = medium->server_socket_.get();
if (server_socket == nullptr) {
NEARBY_LOG(ERROR, "Failed to find WifiLan Server socket: service_id=%s",
service_id.c_str());
return {};
}
}
auto socket = std::make_unique<WifiLanSocket>();
NEARBY_LOG(INFO, "G3 Connect: medium=%p, service_id=%s", this,
service_id.c_str());
// Finally, Request to connect to this socket.
if (!server_socket->Connect(*socket)) {
NEARBY_LOG(
ERROR,
"Failed to connect to existing WifiLan Server socket: service_id=%s",
service_id.c_str());
return {};
}
NEARBY_LOG(INFO, "G3 WifiLan Connect: connected: socket=%p", socket.get());
return socket;
// TODO(edwinwu): Integrate medium_environment.
// steps:
// Request a connection, and block until the socket is provided via the
// callback.
// 1. connection = wifi_lan_service.requestConnection_();
// 2. create wifi_lan_socket with wifi_lan_service and connection
// return wifi_lan_socket;
}
} // namespace g3
+125 -8
View File
@@ -15,20 +15,25 @@
#ifndef PLATFORM_V2_IMPL_G3_WIFI_LAN_H_
#define PLATFORM_V2_IMPL_G3_WIFI_LAN_H_
#include <memory>
#include <string>
#include "platform_v2/api/wifi_lan.h"
#include "platform_v2/base/byte_array.h"
#include "platform_v2/base/input_stream.h"
#include "platform_v2/base/output_stream.h"
#include "platform_v2/impl/g3/multi_thread_executor.h"
#include "platform_v2/impl/g3/pipe.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/synchronization/mutex.h"
namespace location {
namespace nearby {
namespace g3 {
class WifiLanMedium;
// Opaque wrapper over a WifiLan service which contains encoded WifiLan service
// info name.
class WifiLanService : public api::WifiLanService {
@@ -39,19 +44,23 @@ class WifiLanService : public api::WifiLanService {
void SetName(std::string name) { name_ = std::move(name); }
std::string GetName() const override { return name_; }
void SetMedium(WifiLanMedium* medium) { medium_ = medium; }
WifiLanMedium* GetMedium() { return medium_; }
private:
std::string name_;
WifiLanMedium* medium_ = nullptr;
};
class WifiLanSocket : public api::WifiLanSocket {
public:
WifiLanSocket() = default;
explicit WifiLanSocket(WifiLanService* service) : service_(service) {}
~WifiLanSocket() override = default;
~WifiLanSocket() override;
// Connect to another WifiLanSocket, to form a functional low-level channel.
// from this point on, and until Close is called, connection exists.
void ConnectTo(WifiLanSocket* other) ABSL_LOCKS_EXCLUDED(mutex_);
void Connect(WifiLanSocket& other) ABSL_LOCKS_EXCLUDED(mutex_);
// Returns the InputStream of this connected WifiLanSocket.
InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
@@ -60,6 +69,15 @@ class WifiLanSocket : public api::WifiLanSocket {
// This stream is for local side to write.
OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_);
// Returns address of a remote WifiLanSocket or nullptr.
WifiLanSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_);
// Returns true if connection exists to the (possibly closed) remote socket.
bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_);
// Returns true if socket is closed.
bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_);
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_);
@@ -69,9 +87,75 @@ class WifiLanSocket : public api::WifiLanSocket {
ABSL_LOCKS_EXCLUDED(mutex_);
private:
Pipe pipe_;
WifiLanService* service_;
void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns true if connection exists to the (possibly closed) remote socket.
bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
// Returns InputStream of our side of a connection.
// This is what the remote side is supposed to read from.
// This is a helper for GetInputStream() method.
InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_);
// Returns OutputStream of our side of a connection.
// This is what the local size is supposed to write to.
// This is a helper for GetOutputStream() method.
OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_);
// Output pipe is initialized by constructor, it remains always valid, until
// it is closed. it represents output part of a local socket. Input part of a
// local socket comes from the peer socket, after connection.
std::shared_ptr<Pipe> output_ {new Pipe};
std::shared_ptr<Pipe> input_;
mutable absl::Mutex mutex_;
WifiLanService* service_;
WifiLanSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr;
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
};
class WifiLanServerSocket {
public:
~WifiLanServerSocket();
// Blocks until either:
// - at least one incoming connection request is available, or
// - ServerSocket is closed.
// On success, returns connected socket, ready to exchange data.
// Returns nullptr on error.
// Once error is reported, it is permanent, and ServerSocket has to be closed.
//
// Called by the server side of a connection.
// Returns WifiLanSocket to the server side.
// If not null, returned socket is connected to its remote (client-side) peer.
std::unique_ptr<api::WifiLanSocket> Accept() ABSL_LOCKS_EXCLUDED(mutex_);
// Blocks until either:
// - connection is available, or
// - server socket is closed, or
// - error happens.
//
// Called by the client side of a connection.
// Returns true, if socket is successfully connected.
bool Connect(WifiLanSocket& socket) ABSL_LOCKS_EXCLUDED(mutex_);
// Called by the server side of a connection before passing ownership of
// WifiLanServerSocker to user, to track validity of a pointer to this
// server socket,
void SetCloseNotifier(std::function<void()> notifier)
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
// Calls close_notifier if it was previously set, and marks socket as closed.
Exception Close() ABSL_LOCKS_EXCLUDED(mutex_);
private:
Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
absl::Mutex mutex_;
absl::CondVar cond_;
absl::flat_hash_set<WifiLanSocket*> pending_sockets_ ABSL_GUARDED_BY(mutex_);
std::function<void()> close_notifier_ ABSL_GUARDED_BY(mutex_);
bool closed_ ABSL_GUARDED_BY(mutex_) = false;
};
// Container of operations that can be performed over the WifiLan medium.
@@ -105,15 +189,48 @@ class WifiLanMedium : public api::WifiLanMedium {
bool StopAcceptingConnections(const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
// Returns a new WifiLanSocket. On Success, WifiLanSocket::IsValid()
// returns true.
// Connects to existing remote WifiLan service.
//
// On success, returns a new WifiLanSocket.
// On error, returns nullptr.
std::unique_ptr<api::WifiLanSocket> Connect(
api::WifiLanService& service, const std::string& service_id) override
ABSL_LOCKS_EXCLUDED(mutex_);
api::WifiLanService& remote_service,
const std::string& service_id) override ABSL_LOCKS_EXCLUDED(mutex_);
private:
static constexpr int kMaxConcurrentAcceptLoops = 5;
struct AdvertisingInfo {
bool Empty() const { return service_id.empty(); }
void Clear() { service_id.clear(); }
std::string service_id;
};
struct DiscoveringInfo {
bool Empty() const { return service_id.empty(); }
void Clear() { service_id.clear(); }
std::string service_id;
};
absl::Mutex mutex_;
WifiLanService service_{"wifi_lan_service_info_name"};
// A thread pool dedicated to running all the accept loops from
// StartAdvertising().
MultiThreadExecutor accept_loops_runner_{kMaxConcurrentAcceptLoops};
std::atomic_bool acceptance_thread_running_ = false;
// A thread pool dedicated to wait to complete the accept_loops_runner_.
MultiThreadExecutor close_accept_loops_runner_{kMaxConcurrentAcceptLoops};
// TODO(edwinwu): Extend it to hashmap to accept multiple sockets for multiple
// entrance.
// A server socket is established when start advertising.
std::unique_ptr<WifiLanServerSocket> server_socket_;
AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_);
DiscoveringInfo discovering_info_ ABSL_GUARDED_BY(mutex_);
};
} // namespace g3