blob: 5f5926af586c5ef85a7fbcb068e9f8201f912184 [file] [log] [blame]
// Copyright 2017 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.
#pragma once
#include <zircon/compiler.h>
#include <zircon/types.h>
#include <lib/zx/channel.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/intrusive_wavl_tree.h>
#include <fbl/mutex.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#include <unistd.h>
#include <type_traits>
#include <dispatcher-pool/dispatcher-event-source.h>
namespace dispatcher {
// class Channel
//
// Channel is one of the EventSources in the dispatcher framework used to manage
// a zircon channel object.
//
// :: Handlers ::
//
// Two handlers are defined for channel event sources,
// one which becomes dispatched any time the channel has messages to read (the
// ProcessHandler), and one which becomes dispatched when a channel becomes
// closed (the ChannelClosedHandler).
//
// Returning an error from the process handler will cause the channel to
// automatically become deactivated.
//
// The ChannelClosedHandler is optional, but will be called from the execution
// domain context in the event that the peer's endpoint is closed, or if an
// error is returned during processing. It will not be called if the user's own
// code explicitly deactivates the channel, either by deactivating the channel
// object itself, or by deactivating the execution domain that the channel is
// bound to.
//
// :: Activation ::
//
// Two forms of the Activate method are provided. One requires that a valid
// zx::channel handle be supplied to the Channel object; this is the handle
// which will be monitored and dispatched by the Channel object. The other form
// attempts to create a channel pair and use one half of the pair while
// returning the other half to the caller via an out-parameter, presumably so
// that they can hand this handle off to some other process which needs to
// communicate with the user. A ProcessHandler must be provided during
// activation, a ChannelClosedHandler is optional.
//
// :: Reading/Writing ::
//
// Reading and writing on the channel is done using the Read and Write methods.
// Internally, the handle itself is protected with a lock making it safe to call
// Read or Write on a Channel object from any thread. Attempts to read or write
// a channel which either has not been activated yet, or which has been
// deactivated, will fail.
//
// :: A Simple Example of Activation ::
//
// class Thingy : public fbl::RefCounted<Thingy> {
// public:
// zx_status_t ConnectToClient(zx::channel ch_handle);
// ...
// private:
// fbl::RefPtr<dispatcher::ExecutionDomain> my_domain_;
// zx_status_t ProcessClientMessage(Channel* ch) TA_REQ(my_domain_.token());
// void ClientDisconnected(const Channel* ch) TA_REQ(my_domain_.token());
// ...
// };
//
// zx_status_t Thingy::ConnectToClient(zx::channel ch_handle) {
// fbl::RefPtr<Channel> ch = Channel::Create();
// if (!ch) return ZX_ERR_NO_MEMORY;
//
// Channel::ProcessHandler phandler(
// [thingy = fbl::MakeRefPtr(this)](Channel* ch) {
// OBTAIN_EXECUTION_DOMAIN_TOKEN(t, thingy->my_domain_->token());
// return thingy->ProcessClientMessage(ch);
// }
//
// Channel::ChannelCloseHandler chandler(
// [thingy = fbl::MakeRefPtr(this)](const Channel* ch) {
// OBTAIN_EXECUTION_DOMAIN_TOKEN(t, thingy->my_domain_->token());
// thingy->ClientDisconnected(ch);
// }
//
// return ch->Activate(std::move(ch_handle),
// my_domain_,
// std::move(phandler),
// std::move(dhandler));
// }
//
class Channel : public EventSource {
public:
// Definitions of process and deactivation handlers.
static constexpr size_t MAX_HANDLER_CAPTURE_SIZE = sizeof(void*) * 2;
using ProcessHandler =
fbl::InlineFunction<zx_status_t(Channel*), MAX_HANDLER_CAPTURE_SIZE>;
using ChannelClosedHandler =
fbl::InlineFunction<void(const Channel*), MAX_HANDLER_CAPTURE_SIZE>;
// Create
//
// General method handles construction/initialization of a Channel subclass.
// Given an implementation called 'MyChannel', invocation should look like:
//
// auto my_channel = Channel::Create<MyChannel>(arg1, arg2, ...);
//
// Note: Implementers are encouraged to keep their constructor/destructor
// protected/private. In order to do so, they should make sure to specify
// 'friend class Channel' and 'friend class fbl::RefPtr<T>'.
template <typename T = Channel, typename... ConstructorSignature>
static fbl::RefPtr<T> Create(ConstructorSignature&&... args) {
static_assert(std::is_base_of<Channel, T>::value, "Class must derive from Channel!");
fbl::AllocChecker ac;
auto ptr = fbl::AdoptRef(new (&ac) T(std::forward<ConstructorSignature>(args)...));
if (!ac.check()) {
return nullptr;
}
return ptr;
}
// Activate a channel, creating the channel pair and retuning the client's
// channel endpoint in the process.
//
// This operation binds the Channel object to an ExecutionDomain, a
// processing handler, and an optional deactivation handler. The operation
// will fail if the Channel has already been bound, or either the domain
// reference or processing handler is invalid.
zx_status_t Activate(zx::channel* client_channel_out,
fbl::RefPtr<ExecutionDomain> domain,
ProcessHandler process_handler,
ChannelClosedHandler channel_closed_handler = nullptr)
__TA_EXCLUDES(obj_lock_);
// Activate a channel, binding to the user supplied channel endpoint in the
// process.
zx_status_t Activate(zx::channel channel,
fbl::RefPtr<ExecutionDomain> domain,
ProcessHandler process_handler,
ChannelClosedHandler channel_closed_handler = nullptr)
__TA_EXCLUDES(obj_lock_);
void Deactivate() __TA_EXCLUDES(obj_lock_) override;
zx_status_t Read(void* buf,
uint32_t buf_len,
uint32_t* bytes_read_out,
zx::handle* rxed_handle = nullptr)
__TA_EXCLUDES(obj_lock_);
zx_status_t Write(const void* buf,
uint32_t buf_len,
zx::handle&& tx_handle = zx::handle())
__TA_EXCLUDES(obj_lock_);
protected:
Channel() : EventSource(ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED) { }
friend class fbl::RefPtr<Channel>;
void Dispatch(ExecutionDomain* domain) __TA_EXCLUDES(obj_lock_) override;
private:
zx_status_t ActivateLocked(zx::channel channel, fbl::RefPtr<ExecutionDomain> domain)
__TA_REQUIRES(obj_lock_);
ProcessHandler process_handler_;
ChannelClosedHandler channel_closed_handler_;
};
} // namespace dispatcher