blob: 112d16952e8a5254c32385ea3e38025d3870dcd2 [file] [log] [blame]
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "socket_logger.h"
#include <lib/async/cpp/task.h>
#include <lib/syslog/cpp/macros.h>
#include <future>
#include <src/lib/fsl/socket/socket_drainer.h>
#include "logger.h"
namespace {
// Posts a task to invoke |handler| and waits for it to complete.
//
// The current thread must not be a dispatcher thread, otherwise
// the call will deadlock.
zx_status_t PostTaskAndWait(async_dispatcher_t* dispatcher, fit::closure handler) {
std::promise<void> signal;
std::future<void> done = signal.get_future();
zx_status_t status = async::PostTask(dispatcher, [&handler, &signal]() {
handler();
signal.set_value();
});
if (status != ZX_OK) {
return status;
}
done.wait();
return ZX_OK;
}
} // namespace
class LogClient : public fsl::SocketDrainer::Client {
public:
explicit LogClient(Logger* logger) : logger_(logger) {}
void OnDataAvailable(const void* data, size_t num_bytes) override {
logger_->Write(std::string_view(static_cast<const char*>(data), num_bytes));
}
void OnDataComplete() override { logger_->Write("<guest serial connection closed>"); }
private:
Logger* logger_;
};
SocketLogger::SocketLogger(Logger* logger, zx::socket socket) {
client_ = std::make_unique<LogClient>(logger);
// Create a thread for the async loop.
zx_status_t status = loop_.StartThread("serial_logger_loop");
FX_CHECK(status == ZX_OK) << "Failed to start logging thread";
// SocketDrainer (and hence LogClient) is thread-hostile, and must be
// started and stopped on the remote thread.
status = PostTaskAndWait(loop_.dispatcher(), [this, socket = std::move(socket)]() mutable {
drainer_.emplace(client_.get(), loop_.dispatcher());
drainer_->Start(std::move(socket));
});
ZX_ASSERT(status == ZX_OK);
}
SocketLogger::~SocketLogger() {
// Destroy the drainer on the dispatcher's thread.
zx_status_t status = PostTaskAndWait(loop_.dispatcher(), [this]() { drainer_.reset(); });
ZX_ASSERT(status == ZX_OK);
}