1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6 
7 #pragma once
8 
9 #include <lib/user_copy/user_ptr.h>
10 #include <zircon/types.h>
11 #include <fbl/mutex.h>
12 #include <kernel/lockdep.h>
13 #include <object/futex_node.h>
14 
15 // FutexContext is a class that encapsulates support for futex operations.
16 // FutexContext uses a hash table keyed on the futex address (a pointer to integer in userspace)
17 // to contain all active futexes.
18 // A futex is considered active if there is one or more threads blocked on the futex.
19 // After no threads are left blocked on a futex it is removed from the hash table.
20 // The value in the futex hash table is the FutexNode object associated with the head
21 // of the list of threads blocked on the futex.
22 // To avoid memory allocation at futex operation time, a FutexNode is embedded in each
23 // ThreadDispatcher object.
24 // When the thread at the head of the futex's blocked thread list is resumed,
25 // The FutexNode for the new head of the blocked thread list is set as the hash table value
26 // for the futex.
27 class FutexContext {
28 public:
29     FutexContext();
30     ~FutexContext();
31 
32     enum class OwnerAction {
33         RELEASE,
34         ASSIGN_WOKEN,
35     };
36 
37     // FutexWait first verifies that the integer pointed to by |value_ptr|
38     // still equals |current_value|. If the test fails, FutexWait returns ZX_ERR_BAD_STATE.
39     // Otherwise it will block the current thread until the |slack|-adjusted |deadline| passes,
40     // or until the thread is woken by a FutexWake or FutexRequeue operation
41     // on the same |value_ptr| futex.
42     zx_status_t FutexWait(user_in_ptr<const zx_futex_t> value_ptr, zx_futex_t current_value,
43                           zx_handle_t new_futex_owner, zx_time_t deadline, TimerSlack slack);
44 
45     // FutexWake will wake up to |wake_count| number of threads blocked on the |value_ptr| futex.
46     //
47     // If owner_action is set to RELEASE, then the futex's owner will be set to nullptr in the
48     // process.  If the owner_action is set to ASSIGN_WOKEN, then the wake_count *must* be 1, and
49     // the futex's owner will be set to the thread which was woken during the operation, or nullptr
50     // if no thread was woken.
51     zx_status_t FutexWake(user_in_ptr<const zx_futex_t> value_ptr, uint32_t wake_count,
52                           OwnerAction owner_action);
53 
54     // FutexWait first verifies that the integer pointed to by |wake_ptr|
55     // still equals |current_value|. If the test fails, FutexWait returns ZX_ERR_BAD_STATE.
56     // Otherwise it will wake up to |wake_count| number of threads blocked on the |wake_ptr| futex.
57     // If any other threads remain blocked on on the |wake_ptr| futex, up to |requeue_count|
58     // of them will then be requeued to the tail of the list of threads
59     // blocked on the |requeue_ptr| futex.
60     //
61     // If owner_action is set to RELEASE, then the futex's owner will be set to nullptr in the
62     // process.  If the owner_action is set to ASSIGN_WOKEN, then the wake_count *must* be 1, and
63     // the futex's owner will be set to the thread which was woken during the operation, or nullptr
64     // if no thread was woken.
65     zx_status_t FutexRequeue(user_in_ptr<const zx_futex_t> wake_ptr,
66                              uint32_t wake_count,
67                              zx_futex_t current_value,
68                              OwnerAction owner_action,
69                              user_in_ptr<const zx_futex_t> requeue_ptr,
70                              uint32_t requeue_count,
71                              zx_handle_t new_requeue_owner);
72 
73     // Get the KOID of the current owner of the specified futex, if any, or ZX_KOID_INVALID if there
74     // is no known owner.
75     zx_status_t FutexGetOwner(user_in_ptr<const zx_futex_t> wake_ptr,
76                               user_out_ptr<zx_koid_t> koid);
77 
78 private:
79     FutexContext(const FutexContext&) = delete;
80     FutexContext& operator=(const FutexContext&) = delete;
81 
82     void QueueNodesLocked(FutexNode* head) TA_REQ(lock_);
83 
84     bool UnqueueNodeLocked(FutexNode* node) TA_REQ(lock_);
85 
86     // protects futex_table_
87     DECLARE_MUTEX(FutexContext) lock_;
88 
89     // Hash table for futexes in this context.
90     // Key is futex address, value is the FutexNode for the head of futex's blocked thread list.
91     FutexNode::HashTable futex_table_ TA_GUARDED(lock_);
92 };
93