blob: 2cd35f2e0140d5ebcac25759cd4d899d33a19d21 [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 <kernel/thread.h>
#include <kernel/wait.h>
#include <list.h>
#include <zircon/types.h>
#include <fbl/intrusive_hash_table.h>
#include <fbl/mutex.h>
// Node for linked list of threads blocked on a futex
// Intended to be embedded within a ThreadDispatcher Instance
class FutexNode : public fbl::SinglyLinkedListable<FutexNode*> {
public:
using HashTable = fbl::HashTable<uintptr_t, FutexNode*>;
FutexNode();
~FutexNode();
FutexNode(const FutexNode &) = delete;
FutexNode& operator=(const FutexNode &) = delete;
bool IsInQueue() const;
void SetAsSingletonList();
// adds a list of nodes to our tail
void AppendList(FutexNode* head);
static FutexNode* RemoveNodeFromList(FutexNode* list_head, FutexNode* node);
static FutexNode* WakeThreads(FutexNode* node, uint32_t count,
uintptr_t old_hash_key);
static FutexNode* RemoveFromHead(FutexNode* list_head,
uint32_t count,
uintptr_t old_hash_key,
uintptr_t new_hash_key);
// This must be called with |mutex| held and returns without |mutex| held.
zx_status_t BlockThread(fbl::Mutex* mutex, zx_time_t deadline) TA_REL(mutex);
void set_hash_key(uintptr_t key) {
hash_key_ = key;
}
// Trait implementation for fbl::HashTable
uintptr_t GetKey() const { return hash_key_; }
static size_t GetHash(uintptr_t key) { return (key >> 3); }
private:
static void RelinkAsAdjacent(FutexNode* node1, FutexNode* node2);
static void SpliceNodes(FutexNode* node1, FutexNode* node2);
void WakeThread();
void MarkAsNotInQueue();
// hash_key_ contains the futex address. This field has two roles:
// * It is used by FutexWait() to determine which queue to remove the
// thread from when a wait operation times out.
// * Additionally, when this FutexNode is the head of a futex wait
// queue, this field is used by the HashTable (because it uses
// intrusive SinglyLinkedLists).
uintptr_t hash_key_;
// Used for waking the thread corresponding to the FutexNode.
WaitQueue wait_queue_;
// queue_prev_ and queue_next_ are used for maintaining a circular
// doubly-linked list of threads that are waiting on one futex address.
// * When the list contains only this node, queue_prev_ and
// queue_next_ both point back to this node.
// * When the thread is not waiting on a futex, queue_next_ is null.
FutexNode* queue_prev_ = nullptr;
FutexNode* queue_next_ = nullptr;
};