blob: ab3ce056cbe4ee99f642f0a69e55a486136094a3 [file] [log] [blame]
// Copyright 2022 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
#ifndef ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_SHAREABLE_PROCESS_STATE_H_
#define ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_SHAREABLE_PROCESS_STATE_H_
#include <fbl/ref_counted.h>
#include <kernel/mutex.h>
#include <ktl/atomic.h>
#include <object/futex_context.h>
#include <object/handle_table.h>
#include <vm/vm_aspace.h>
// This class is logically private to ProcessDispatcher.
//
// |ShareableProcessState| contains all the state that can belong to more than one process.
//
// This class is logically private to ProcessDispatcher.
//
// The objects contained in this class have lifetimes that are decoupled from their lifecycle.
//
// A ShareableProcessState is always constructed with a process count of 1, meaning that the creator
// should issues a matching |DecrementShareCount| before the |ShareableProcessState| is destroyed.
class ShareableProcessState : public fbl::RefCounted<ShareableProcessState> {
public:
ShareableProcessState(const ShareableProcessState&) = delete;
ShareableProcessState& operator=(const ShareableProcessState&) = delete;
ShareableProcessState() = default;
~ShareableProcessState() {
DEBUG_ASSERT(!aspace_ || aspace_->is_destroyed());
DEBUG_ASSERT(process_count_.load(ktl::memory_order_relaxed) == 0);
}
// Shares this state with a process, effectively incrementing the number of calls to
// |RemoveFromProcess| that can be made before the shared resources are cleaned up.
//
// Returns whether or not the share count was incremented successfully. Can fail if the process
// state has been destroyed.
bool IncrementShareCount() {
uint64_t prev = process_count_.load(ktl::memory_order_relaxed);
do {
if (prev == 0) {
return false;
}
} while (!process_count_.compare_exchange_weak(prev, prev + 1, ktl::memory_order_relaxed));
return true;
}
// Removes this state from a process. If the state is not shared with any other processes, the
// shared resources are cleaned.
void DecrementShareCount() {
DEBUG_ASSERT(process_count_ > 0);
const uint64_t prev = process_count_.fetch_sub(1, ktl::memory_order_relaxed);
if (prev > 1) {
return;
}
handle_table_.Clean();
if (aspace_) {
zx_status_t result = aspace_->Destroy();
ASSERT_MSG(result == ZX_OK, "%d\n", result);
}
}
// Initializes the shared state.
//
// It is an error to call initialize on a shared state that has already been initialized, or one
// that has been destroyed.
bool Initialize(vaddr_t aspace_base, vaddr_t aspace_size, const char* aspace_name,
VmAspace::ShareOpt share_opt) {
DEBUG_ASSERT(!aspace_);
DEBUG_ASSERT(process_count_.load(ktl::memory_order_relaxed) == 1);
aspace_ =
VmAspace::Create(aspace_base, aspace_size, VmAspace::Type::User, aspace_name, share_opt);
return aspace_ != nullptr;
}
uint64_t share_count() const { return process_count_.load(ktl::memory_order_relaxed); }
HandleTable& handle_table() { return handle_table_; }
const HandleTable& handle_table() const { return handle_table_; }
FutexContext& futex_context() { return futex_context_; }
const FutexContext& futex_context() const { return futex_context_; }
VmAspace* aspace() { return aspace_.get(); }
const VmAspace* aspace() const { return aspace_.get(); }
private:
// The layout of the fields below is intended to eliminating padding.
//
// The number of processes currently sharing this state.
HandleTable handle_table_;
ktl::atomic<uint64_t> process_count_ = 1;
// This field is logically const and may not be changed after initialization. Resetting or
// assigning to this field post-initialization is a programming error.
fbl::RefPtr<VmAspace> aspace_;
FutexContext futex_context_;
};
#endif // ZIRCON_KERNEL_OBJECT_INCLUDE_OBJECT_SHAREABLE_PROCESS_STATE_H_