blob: 57fe728bb9dd7558be2ae8b425f1ff1a689e9609 [file] [log] [blame]
// Copyright 2016 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 <fbl/arena.h>
#include <fbl/atomic.h>
#include <fbl/intrusive_double_list.h>
#include <fbl/macros.h>
#include <fbl/mutex.h>
#include <fbl/ref_ptr.h>
#include <stdint.h>
#include <zircon/types.h>
class Dispatcher;
class Handle;
// HandleOwner wraps a Handle in a unique_ptr-like object that has single
// ownership of the Handle and deletes it whenever it falls out of scope.
class HandleOwner {
public:
HandleOwner() = default;
HandleOwner(decltype(nullptr)) : h_(nullptr) {}
explicit HandleOwner(Handle* h) : h_(h) {}
HandleOwner(const HandleOwner&) = delete;
HandleOwner& operator=(const HandleOwner&) = delete;
HandleOwner(HandleOwner&& other) : h_(other.release()) {}
HandleOwner& operator=(HandleOwner&& other) {
reset(other.release());
return *this;
}
~HandleOwner() {
Destroy();
}
Handle* operator->() const {
return h_;
}
Handle* get() const {
return h_;
}
Handle* release() {
Handle* h = h_;
h_ = nullptr;
return h;
}
void reset(Handle* h) {
Destroy();
h_ = h;
}
void swap(HandleOwner& other) {
Handle* h = h_;
h_ = other.h_;
other.h_ = h;
}
explicit operator bool() const { return h_ != nullptr; }
private:
// Defined inline below.
inline void Destroy();
Handle* h_ = nullptr;
};
// A Handle is how a specific process refers to a specific Dispatcher.
class Handle final : public fbl::DoublyLinkedListable<Handle*> {
public:
// Returns the Dispatcher to which this instance points.
const fbl::RefPtr<Dispatcher>& dispatcher() const { return dispatcher_; }
// Returns the process that owns this instance. Used to guarantee
// that one process may not access a handle owned by a different process.
zx_koid_t process_id() const {
return process_id_.load(fbl::memory_order_relaxed);
}
// Sets the value returned by process_id().
void set_process_id(zx_koid_t pid) {
process_id_.store(pid, fbl::memory_order_relaxed);
}
// Returns the |rights| parameter that was provided when this instance
// was created.
uint32_t rights() const {
return rights_;
}
// Returns true if this handle has all of the desired rights bits set.
bool HasRights(zx_rights_t desired) const {
return (rights_ & desired) == desired;
}
// Returns a value that can be decoded by Handle::FromU32() to derive a
// pointer to this instance. ProcessDispatcher will XOR this with its
// |handle_rand_| to create the zx_handle_t value that user space sees.
uint32_t base_value() const {
return base_value_;
}
// To be called once during bringup.
static void Init();
// Maps an integer obtained by Handle::base_value() back to a Handle.
static Handle* FromU32(uint32_t value);
// Get the number of outstanding handles for a given dispatcher.
static uint32_t Count(const fbl::RefPtr<const Dispatcher>&);
// Should only be called by diagnostics.cpp.
struct diagnostics {
// Dumps internal details of the handle table using printf().
static void DumpTableInfo();
// Returns the number of outstanding handles.
static size_t OutstandingHandles();
};
// Handle should never be created by anything other than Make or Dup.
static HandleOwner Make(
fbl::RefPtr<Dispatcher> dispatcher, zx_rights_t rights);
static HandleOwner Dup(Handle* source, zx_rights_t rights);
private:
DISALLOW_COPY_ASSIGN_AND_MOVE(Handle);
// Called only by Make.
Handle(fbl::RefPtr<Dispatcher> dispatcher,
zx_rights_t rights, uint32_t base_value);
// Called only by Dup.
Handle(Handle* rhs, zx_rights_t rights, uint32_t base_value);
// Private subroutines of Make and Dup.
static void* Alloc(const fbl::RefPtr<Dispatcher>&, const char* what,
uint32_t* base_value);
static uint32_t GetNewBaseValue(void* addr);
// Handle should never be destroyed by anything other than Delete,
// which uses TearDown to do the actual destruction.
~Handle() = default;
void TearDown() TA_EXCL(mutex_);
void Delete();
// Only HandleOwner is allowed to call Delete.
friend class HandleOwner;
// process_id_ is atomic because threads from different processes can
// access it concurrently, while holding different instances of
// handle_table_lock_.
fbl::atomic<zx_koid_t> process_id_;
fbl::RefPtr<Dispatcher> dispatcher_;
const zx_rights_t rights_;
const uint32_t base_value_;
// The handle arena and its mutex; also guards Dispatcher::handle_count_.
static fbl::Mutex mutex_;
static fbl::Arena TA_GUARDED(mutex_) arena_;
// NOTE! This can return an invalid address. It must be checked
// against the arena bounds before being cast to a Handle*.
static uintptr_t IndexToHandle(uint32_t index) TA_NO_THREAD_SAFETY_ANALYSIS {
return reinterpret_cast<uintptr_t>(arena_.start()) + index * sizeof(Handle);
}
static uint32_t HandleToIndex(Handle* handle) TA_NO_THREAD_SAFETY_ANALYSIS {
return static_cast<uint32_t>(
handle - reinterpret_cast<Handle*>(arena_.start()));
}
};
// This can't be defined direclty in the HandleOwner class definition
// because Handle is an incomplete type at that point.
inline void HandleOwner::Destroy() {
if (h_)
h_->Delete();
}