blob: 783d29910911318ae84731d9eabca41f416f471b [file] [log] [blame] [edit]
// 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.
#ifndef SRC_SYS_FUZZING_COMMON_BINDING_H_
#define SRC_SYS_FUZZING_COMMON_BINDING_H_
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/interface_handle.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/sync/completion.h>
#include <lib/syslog/cpp/macros.h>
#include <threads.h>
#include <zircon/status.h>
#include <mutex>
#include "src/lib/fxl/synchronization/thread_annotations.h"
#include "src/sys/fuzzing/common/dispatcher.h"
#include "src/sys/fuzzing/common/signal-coordinator.h"
namespace fuzzing {
// This class wraps |fidl::Binding| in an object that can be created and destroyed on both
// dispatcher and non-dispatcher threads, allowing for easier RAII-like semantics.
template <typename T>
class Binding {
public:
Binding(T* t) : binding_(t), dispatcher_(std::make_shared<Dispatcher>()) {}
// The HLCPP bindings are thread-hostile. In particular, they can only be safely unbound from the
// dispatcher thread, or run the risk of racing being unbound by a peer closure.
virtual ~Binding() { Unbind(); }
const std::shared_ptr<Dispatcher>& dispatcher() const { return dispatcher_; }
bool is_dispatcher_thread() const { return thrd_equal(dispatcher_->thrd(), thrd_current()); }
bool is_bound() const { return binding_.is_bound(); }
// Posts a task to the binding's associated FIDL dispatcher.
void PostTask(fit::closure&& task) {
dispatcher_->PostTask([task = std::move(task)]() { task(); });
}
// Creates a channel, binds it to this object, and returns the other end in an InterfaceHandle.
// This can be called from a non-dispatch thread. Does nothing if already bound.
fidl::InterfaceHandle<T> NewBinding() {
fidl::InterfaceHandle<T> client;
Bind(client.NewRequest());
return client;
}
// Binds the FIDL interface request to this object. This can be called from a non-dispatch thread.
void Bind(fidl::InterfaceRequest<T> request) { Bind(request.TakeChannel()); }
// Binds the channel to this object. This can be called from a non-dispatch thread.
void Bind(zx::channel channel) {
if (is_dispatcher_thread()) {
auto status = binding_.Bind(std::move(channel), dispatcher_->get());
FX_CHECK(status == ZX_OK) << "Bind: " << zx_status_get_string(status);
return;
}
SyncWait sync;
PostTask([this, &sync, channel = std::move(channel)]() mutable {
auto status = binding_.Bind(std::move(channel));
FX_CHECK(status == ZX_OK) << "Bind: " << zx_status_get_string(status);
sync.Signal();
});
sync.WaitFor("dispatcher to complete binding");
}
// Unbinds (and closes) the underlying channel from this object. This can be called from a
// non-dispatch thread. Does nothing if not bound.
void Unbind() {
if (!binding_.is_bound()) {
return;
}
if (is_dispatcher_thread()) {
binding_.Unbind();
return;
}
SyncWait sync;
PostTask([this, &sync]() {
binding_.Unbind();
sync.Signal();
});
sync.WaitFor("dispatcher to complete unbinding");
}
// Blocks until the underlying channel is unbound and closed.
zx_status_t AwaitClose() {
Waiter waiter = [this](zx::time deadline) {
const auto& channel = binding_.channel();
return channel.wait_one(ZX_CHANNEL_PEER_CLOSED, deadline, nullptr);
};
auto status = WaitFor("binding to close", &waiter);
switch (status) {
case ZX_ERR_BAD_HANDLE:
case ZX_ERR_CANCELED:
// The local end of the channel was closed before or during this call.
return ZX_OK;
default:
return status;
}
}
private:
fidl::Binding<T> binding_;
std::shared_ptr<Dispatcher> dispatcher_;
};
} // namespace fuzzing
#endif // SRC_SYS_FUZZING_COMMON_BINDING_H_