| // Copyright 2024 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef V8_OBJECTS_WAITER_QUEUE_NODE_H_ |
| #define V8_OBJECTS_WAITER_QUEUE_NODE_H_ |
| |
| #include "src/base/platform/condition-variable.h" |
| |
| namespace v8 { |
| |
| namespace base { |
| class TimeDelta; |
| } // namespace base |
| |
| namespace internal { |
| |
| class Isolate; |
| |
| namespace detail { |
| |
| // To manage waiting threads inside JSSynchronizationPrimitives, there is a |
| // process-wide doubly-linked intrusive list per waiter (i.e. mutex or condition |
| // variable). There is a per-thread node allocated on the stack when the thread |
| // goes to sleep during waiting. |
| // |
| // When compressing pointers (including when sandboxing), the access to the |
| // on-stack node is indirected through the shared external pointer table. |
| // |
| // TODO(https://crbug.com/42202493): Unittest this. |
| class V8_NODISCARD WaiterQueueNode final { |
| public: |
| explicit WaiterQueueNode(Isolate* requester); |
| |
| ~WaiterQueueNode(); |
| |
| // Enqueues {new_tail}, mutating {head} to be the new head. |
| static void Enqueue(WaiterQueueNode** head, WaiterQueueNode* new_tail); |
| |
| using DequeueMatcher = std::function<bool(WaiterQueueNode*)>; |
| // Dequeues the first waiter for which {matcher} returns true and returns it; |
| // mutating {head} to be the new head. |
| // |
| // The queue lock must be held in the synchronization primitive that owns |
| // this waiter queue when calling this method. |
| static WaiterQueueNode* DequeueMatching(WaiterQueueNode** head, |
| const DequeueMatcher& matcher); |
| |
| static WaiterQueueNode* Dequeue(WaiterQueueNode** head); |
| |
| // Splits at most {count} nodes of the waiter list of into its own list and |
| // returns it, mutating {head} to be the head of the back list. |
| static WaiterQueueNode* Split(WaiterQueueNode** head, uint32_t count); |
| |
| // This method must be called from a known waiter queue head. Incorrectly |
| // encoded lists can cause this method to infinitely loop. |
| static int LengthFromHead(WaiterQueueNode* head); |
| |
| void Wait(); |
| |
| // Returns false if timed out, true otherwise. |
| bool WaitFor(const base::TimeDelta& rel_time); |
| |
| void Notify(); |
| |
| uint32_t NotifyAllInList(); |
| |
| private: |
| void SetNotInListForVerification(); |
| |
| Isolate* requester_; |
| // The queue wraps around, e.g. the head's prev is the tail, and the tail's |
| // next is the head. |
| WaiterQueueNode* next_ = nullptr; |
| WaiterQueueNode* prev_ = nullptr; |
| |
| bool should_wait_ = false; |
| |
| base::Mutex wait_lock_; |
| base::ConditionVariable wait_cond_var_; |
| void SbxCheckNotInList() const; |
| }; |
| |
| } // namespace detail |
| } // namespace internal |
| } // namespace v8 |
| |
| #endif // V8_OBJECTS_WAITER_QUEUE_NODE_H_ |