blob: ef1df31335320b3514a8420eb4299fd7aff240a9 [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 <lib/user_copy/user_ptr.h>
#include <zircon/types.h>
#include <fbl/mutex.h>
#include <kernel/lockdep.h>
#include <object/futex_node.h>
// FutexContext is a class that encapsulates support for futex operations.
// FutexContext uses a hash table keyed on the futex address (a pointer to integer in userspace)
// to contain all active futexes.
// A futex is considered active if there is one or more threads blocked on the futex.
// After no threads are left blocked on a futex it is removed from the hash table.
// The value in the futex hash table is the FutexNode object associated with the head
// of the list of threads blocked on the futex.
// To avoid memory allocation at futex operation time, a FutexNode is embedded in each
// ThreadDispatcher object.
// When the thread at the head of the futex's blocked thread list is resumed,
// The FutexNode for the new head of the blocked thread list is set as the hash table value
// for the futex.
class FutexContext {
public:
FutexContext();
~FutexContext();
enum class OwnerAction {
RELEASE,
ASSIGN_WOKEN,
};
// FutexWait first verifies that the integer pointed to by |value_ptr|
// still equals |current_value|. If the test fails, FutexWait returns ZX_ERR_BAD_STATE.
// Otherwise it will block the current thread until the |deadline| passes, or until the thread
// is woken by a FutexWake or FutexRequeue operation on the same |value_ptr| futex.
zx_status_t FutexWait(user_in_ptr<const zx_futex_t> value_ptr, zx_futex_t current_value,
zx_handle_t new_futex_owner, const Deadline& deadline);
// FutexWake will wake up to |wake_count| number of threads blocked on the |value_ptr| futex.
//
// If owner_action is set to RELEASE, then the futex's owner will be set to nullptr in the
// process. If the owner_action is set to ASSIGN_WOKEN, then the wake_count *must* be 1, and
// the futex's owner will be set to the thread which was woken during the operation, or nullptr
// if no thread was woken.
zx_status_t FutexWake(user_in_ptr<const zx_futex_t> value_ptr, uint32_t wake_count,
OwnerAction owner_action);
// FutexWait first verifies that the integer pointed to by |wake_ptr|
// still equals |current_value|. If the test fails, FutexWait returns ZX_ERR_BAD_STATE.
// Otherwise it will wake up to |wake_count| number of threads blocked on the |wake_ptr| futex.
// If any other threads remain blocked on on the |wake_ptr| futex, up to |requeue_count|
// of them will then be requeued to the tail of the list of threads
// blocked on the |requeue_ptr| futex.
//
// If owner_action is set to RELEASE, then the futex's owner will be set to nullptr in the
// process. If the owner_action is set to ASSIGN_WOKEN, then the wake_count *must* be 1, and
// the futex's owner will be set to the thread which was woken during the operation, or nullptr
// if no thread was woken.
zx_status_t FutexRequeue(user_in_ptr<const zx_futex_t> wake_ptr,
uint32_t wake_count,
zx_futex_t current_value,
OwnerAction owner_action,
user_in_ptr<const zx_futex_t> requeue_ptr,
uint32_t requeue_count,
zx_handle_t new_requeue_owner);
// Get the KOID of the current owner of the specified futex, if any, or ZX_KOID_INVALID if there
// is no known owner.
zx_status_t FutexGetOwner(user_in_ptr<const zx_futex_t> wake_ptr,
user_out_ptr<zx_koid_t> koid);
private:
FutexContext(const FutexContext&) = delete;
FutexContext& operator=(const FutexContext&) = delete;
void QueueNodesLocked(FutexNode* head) TA_REQ(lock_);
bool UnqueueNodeLocked(FutexNode* node) TA_REQ(lock_);
// protects futex_table_
DECLARE_MUTEX(FutexContext) lock_;
// Hash table for futexes in this context.
// Key is futex address, value is the FutexNode for the head of futex's blocked thread list.
FutexNode::HashTable futex_table_ TA_GUARDED(lock_);
};