blob: e95358ce3b942a1cba3f63b5ae4f5f4963f3c293 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#pragma once
#include <object/dispatcher.h>
#include <object/semaphore.h>
#include <object/state_observer.h>
#include <zircon/syscalls/port.h>
#include <zircon/types.h>
#include <fbl/canary.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/mutex.h>
#include <fbl/unique_ptr.h>
#include <sys/types.h>
// Important pointers diagram for PortObserver
//
// The diagrams below show the *relevant* pointers on different
// states of the system. The pure header view is really the
// union of all these pointer which can be confusing.
//
// rc = ref counted
// w = weak pointer
// o = owning pointer
//
// 1) Situation after handle_wait_async(port, handle) is issued:
//
//
// +----------+ +--------+
// | object | | Port |
// | | | |
// +----------+ +-----------+ +-+------+
// | state | w | Port | ^
// | tracker +------> | Observer | |
// +----------+ | | rc |
// | +---------+
// +-----------+
// | Port |
// | Packet |
// +-----------+
//
// State changes of the object are propagated from the object
// to the port via |w| --> observer --> |rc| calls.
//
// 2) Situation after the packet is queued in the one-shot case on
// signal match or the wait is canceled.
//
// +----------+ +--------+
// | object | | Port |
// | | | |
// +----------+ +-----------+ +-+---+--+
// | state | | Port | ^ |
// | tracker | | Observer | | |
// +----------+ | | rc | |
// +---> | +---------+ |
// | +-----------+ | list
// o1 | | Port | o2 |
// +-----| Packet | <-----------+
// +-----------+
//
// Note that the object no longer has a |w| to the observer
// but the observer still owns the port via |rc|.
//
// For repeating ports |w| is always valid until the wait is
// canceled.
//
// The |o1| pointer is used to destroy the port observer only
// when cancellation happens and the port still owns the packet.
//
class ExceptionPort;
class PortDispatcher;
class PortObserver;
struct PortPacket;
#define PKT_FLAG_EPHEMERAL 0x10000000u
#define PKT_FLAG_MASK 0x0FFFFFFFu
struct PortAllocator {
virtual ~PortAllocator() = default;
virtual PortPacket* Alloc() = 0;
virtual void Free(PortPacket* port_packet) = 0;
};
struct PortPacket final : public fbl::DoublyLinkedListable<PortPacket*> {
zx_port_packet_t packet;
const void* const handle;
PortObserver* observer;
PortAllocator* const allocator;
PortPacket(const void* handle, PortAllocator* allocator);
PortPacket(const PortPacket&) = delete;
void operator=(PortPacket) = delete;
uint64_t key() const { return packet.key; }
bool is_ephemeral() const { return allocator != nullptr; }
void Free() { allocator->Free(this); }
};
// Observers are weakly contained in state trackers until |remove_| member
// is false at the end of one of OnInitialize(), OnStateChange() or OnCancel()
// callbacks.
class PortObserver final : public StateObserver {
public:
PortObserver(uint32_t type, const Handle* handle, fbl::RefPtr<PortDispatcher> port,
uint64_t key, zx_signals_t signals);
~PortObserver() = default;
private:
PortObserver(const PortObserver&) = delete;
PortObserver& operator=(const PortObserver&) = delete;
// StateObserver overrides.
Flags OnInitialize(zx_signals_t initial_state, const StateObserver::CountInfo* cinfo) final;
Flags OnStateChange(zx_signals_t new_state) final;
Flags OnCancel(Handle* handle) final;
Flags OnCancelByKey(Handle* handle, const void* port, uint64_t key) final;
void OnRemoved() final;
// The following method can only be called from
// OnInitialize(), OnStateChange() and OnCancel().
Flags MaybeQueue(zx_signals_t new_state, uint64_t count);
const uint32_t type_;
const zx_signals_t trigger_;
PortPacket packet_;
fbl::RefPtr<PortDispatcher> const port_;
};
class PortDispatcher final : public Dispatcher {
public:
static void Init();
static PortAllocator* DefaultPortAllocator();
static zx_status_t Create(uint32_t options, fbl::RefPtr<Dispatcher>* dispatcher,
zx_rights_t* rights);
~PortDispatcher() final;
zx_obj_type_t get_type() const final { return ZX_OBJ_TYPE_PORT; }
void on_zero_handles() final;
zx_status_t Queue(PortPacket* port_packet, zx_signals_t observed, uint64_t count);
zx_status_t QueueUser(const zx_port_packet_t& packet);
zx_status_t Dequeue(zx_time_t deadline, zx_port_packet_t* packet);
// Decides who is going to destroy the observer. If it returns |true| it
// is the duty of the caller. If it is false it is the duty of the port.
bool CanReap(PortObserver* observer, PortPacket* port_packet);
// Called under the handle table lock.
zx_status_t MakeObserver(uint32_t options, Handle* handle, uint64_t key, zx_signals_t signals);
// Called under the handle table lock. Returns true if at least one packet was
// removed from the queue.
bool CancelQueued(const void* handle, uint64_t key);
private:
friend class ExceptionPort;
explicit PortDispatcher(uint32_t options);
// Adopts a RefPtr to |eport|, and adds it to |eports_|.
// Called by ExceptionPort.
void LinkExceptionPort(ExceptionPort* eport);
// Removes |eport| from |eports_|, dropping its RefPtr.
// Does nothing if |eport| is not on the list.
// Called by ExceptionPort.
void UnlinkExceptionPort(ExceptionPort* eport);
fbl::Canary<fbl::magic("PORT")> canary_;
fbl::Mutex lock_;
Semaphore sema_;
bool zero_handles_ TA_GUARDED(lock_);
fbl::DoublyLinkedList<PortPacket*> packets_ TA_GUARDED(lock_);
fbl::DoublyLinkedList<fbl::RefPtr<ExceptionPort>> eports_ TA_GUARDED(lock_);
};