#include "src/objects/waiter-queue-node.h"
#include "src/base/macros.h"
#include "src/base/platform/time.h"
#include "src/heap/local-heap-inl.h"
namespace v8 {
namespace internal {
namespace detail {
WaiterQueueNode::WaiterQueueNode(Isolate* requester)
: requester_(requester), should_wait_(true) {}
WaiterQueueNode::~WaiterQueueNode() {
SbxCheckNotInList();
}
void WaiterQueueNode::Enqueue(WaiterQueueNode** head,
WaiterQueueNode* new_tail) {
DCHECK_NOT_NULL(head);
new_tail->SbxCheckNotInList();
WaiterQueueNode* current_head = *head;
if (current_head == nullptr) {
new_tail->next_ = new_tail;
new_tail->prev_ = new_tail;
*head = new_tail;
} else {
WaiterQueueNode* current_tail = current_head->prev_;
current_tail->next_ = new_tail;
current_head->prev_ = new_tail;
new_tail->next_ = current_head;
new_tail->prev_ = current_tail;
}
}
WaiterQueueNode* WaiterQueueNode::DequeueMatching(
WaiterQueueNode** head, const DequeueMatcher& matcher) {
DCHECK_NOT_NULL(head);
DCHECK_NOT_NULL(*head);
WaiterQueueNode* const original_head = *head;
WaiterQueueNode* cur = *head;
do {
if (matcher(cur)) {
WaiterQueueNode* const next = cur->next_;
if (next == cur) {
DCHECK_EQ(cur, original_head);
*head = nullptr;
} else {
if (cur == original_head) {
WaiterQueueNode* tail = original_head->prev_;
next->prev_ = tail;
tail->next_ = next;
*head = next;
} else {
cur->prev_->next_ = next;
next->prev_ = cur->prev_;
}
}
cur->SetNotInListForVerification();
return cur;
}
cur = cur->next_;
} while (cur != original_head);
return nullptr;
}
WaiterQueueNode* WaiterQueueNode::Dequeue(WaiterQueueNode** head) {
return DequeueMatching(head, [](WaiterQueueNode* node) { return true; });
}
WaiterQueueNode* WaiterQueueNode::Split(WaiterQueueNode** head,
uint32_t count) {
DCHECK_GT(count, 0);
DCHECK_NOT_NULL(head);
DCHECK_NOT_NULL(*head);
WaiterQueueNode* front_head = *head;
WaiterQueueNode* back_head = front_head;
uint32_t actual_count = 0;
while (actual_count < count) {
back_head = back_head->next_;
if (back_head == front_head) {
*head = nullptr;
return front_head;
}
actual_count++;
}
WaiterQueueNode* front_tail = back_head->prev_;
WaiterQueueNode* back_tail = front_head->prev_;
back_head->prev_ = back_tail;
back_tail->next_ = back_head;
*head = back_head;
front_head->prev_ = front_tail;
front_tail->next_ = front_head;
return front_head;
}
int WaiterQueueNode::LengthFromHead(WaiterQueueNode* head) {
WaiterQueueNode* cur = head;
int len = 0;
do {
len++;
cur = cur->next_;
} while (cur != head);
return len;
}
void WaiterQueueNode::Wait() {
AllowGarbageCollection allow_before_parking;
requester_->main_thread_local_heap()->ExecuteWhileParked([this]() {
base::MutexGuard guard(&wait_lock_);
while (should_wait_) {
wait_cond_var_.Wait(&wait_lock_);
}
});
}
bool WaiterQueueNode::WaitFor(const base::TimeDelta& rel_time) {
bool result;
AllowGarbageCollection allow_before_parking;
requester_->main_thread_local_heap()->ExecuteWhileParked([this, rel_time,
&result]() {
base::MutexGuard guard(&wait_lock_);
base::TimeTicks current_time = base::TimeTicks::Now();
base::TimeTicks timeout_time = current_time + rel_time;
for (;;) {
if (!should_wait_) {
result = true;
return;
}
current_time = base::TimeTicks::Now();
if (current_time >= timeout_time) {
result = false;
return;
}
base::TimeDelta time_until_timeout = timeout_time - current_time;
bool wait_res = wait_cond_var_.WaitFor(&wait_lock_, time_until_timeout);
USE(wait_res);
}
});
return result;
}
void WaiterQueueNode::Notify() {
base::MutexGuard guard(&wait_lock_);
should_wait_ = false;
wait_cond_var_.NotifyOne();
SetNotInListForVerification();
}
uint32_t WaiterQueueNode::NotifyAllInList() {
WaiterQueueNode* cur = this;
uint32_t count = 0;
do {
WaiterQueueNode* next = cur->next_;
cur->Notify();
cur = next;
count++;
} while (cur != this);
return count;
}
void WaiterQueueNode::SbxCheckNotInList() const {
SBXCHECK_EQ(nullptr, next_);
SBXCHECK_EQ(nullptr, prev_);
}
void WaiterQueueNode::SetNotInListForVerification() {
next_ = prev_ = nullptr;
}
}
}
}