OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 the V8 project authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "src/futex-emulation.h" | |
6 | |
7 #include "src/base/macros.h" | |
8 #include "src/base/platform/time.h" | |
9 #include "src/conversions.h" | |
10 #include "src/handles-inl.h" | |
11 #include "src/isolate.h" | |
12 #include "src/list-inl.h" | |
13 | |
14 namespace v8 { | |
15 namespace internal { | |
16 | |
17 base::LazyMutex FutexEmulation::mutex_ = LAZY_MUTEX_INITIALIZER; | |
18 base::LazyInstance<FutexWaitList>::type FutexEmulation::wait_list_ = | |
19 LAZY_INSTANCE_INITIALIZER; | |
20 | |
21 | |
22 FutexWaitList::FutexWaitList() : head_(nullptr), tail_(nullptr) {} | |
23 | |
24 | |
25 void FutexWaitList::AddNode(FutexWaitListNode* node) { | |
26 DCHECK(node->prev_ == nullptr && node->next_ == nullptr); | |
27 if (tail_) { | |
28 tail_->next_ = node; | |
29 } else { | |
30 head_ = node; | |
31 } | |
32 | |
33 node->prev_ = tail_; | |
34 node->next_ = nullptr; | |
35 tail_ = node; | |
36 } | |
37 | |
38 | |
39 void FutexWaitList::RemoveNode(FutexWaitListNode* node) { | |
40 if (node->prev_) { | |
41 node->prev_->next_ = node->next_; | |
42 } else { | |
43 head_ = node->next_; | |
44 } | |
45 | |
46 if (node->next_) { | |
47 node->next_->prev_ = node->prev_; | |
48 } else { | |
49 tail_ = node->prev_; | |
50 } | |
51 | |
52 node->prev_ = node->next_ = nullptr; | |
53 } | |
54 | |
55 | |
56 FutexEmulation::Result FutexEmulation::Wait(Isolate* isolate, | |
57 Handle<JSArrayBuffer> array_buffer, | |
58 size_t addr, int32_t value) { | |
59 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); | |
60 | |
61 void* backing_store = array_buffer->backing_store(); | |
62 int32_t* p = | |
63 reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr); | |
64 | |
65 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); | |
66 | |
67 if (*p != value) { | |
68 return Result::kNotEqual; | |
69 } | |
70 | |
71 FutexWaitListNode* node = isolate->futex_wait_list_node(); | |
72 | |
73 node->backing_store_ = backing_store; | |
74 node->wait_addr_ = addr; | |
75 node->waiting_ = true; | |
76 | |
77 wait_list_.Pointer()->AddNode(node); | |
78 | |
79 while (node->waiting_) { | |
80 node->cond_.Wait(mutex_.Pointer()); | |
Jarin
2015/07/15 07:42:21
Benedikt pointed out that we cannot block the main
| |
81 } | |
82 | |
83 wait_list_.Pointer()->RemoveNode(node); | |
84 | |
85 return Result::kOk; | |
86 } | |
87 | |
88 | |
89 FutexEmulation::Result FutexEmulation::Wait( | |
90 Isolate* isolate, Handle<JSArrayBuffer> array_buffer, size_t addr, | |
91 int32_t value, const base::TimeDelta& rel_timeout) { | |
92 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); | |
93 | |
94 void* backing_store = array_buffer->backing_store(); | |
95 int32_t* p = | |
96 reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr); | |
97 | |
98 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); | |
99 | |
100 if (*p != value) { | |
101 return Result::kNotEqual; | |
102 } | |
103 | |
104 FutexWaitListNode* node = isolate->futex_wait_list_node(); | |
105 | |
106 node->backing_store_ = backing_store; | |
107 node->wait_addr_ = addr; | |
108 node->waiting_ = true; | |
109 | |
110 base::TimeDelta rel_time_left = rel_timeout; | |
111 | |
112 wait_list_.Pointer()->AddNode(node); | |
113 | |
114 Result result = Result::kOk; | |
115 | |
116 while (true) { | |
117 base::Time start_time = base::Time::NowFromSystemTime(); | |
118 if (!node->cond_.WaitFor(mutex_.Pointer(), rel_time_left)) { | |
119 result = Result::kTimedOut; | |
120 break; | |
121 } | |
122 | |
123 base::Time end_time = base::Time::NowFromSystemTime(); | |
124 | |
125 if (!node->waiting_) { | |
126 result = Result::kOk; | |
127 break; | |
128 } | |
129 | |
130 // Spurious wakeup. | |
131 base::TimeDelta waited_for = end_time - start_time; | |
132 rel_time_left -= waited_for; | |
133 } | |
134 | |
135 wait_list_.Pointer()->RemoveNode(node); | |
136 | |
137 return result; | |
138 } | |
139 | |
140 | |
141 int FutexEmulation::Wake(Isolate* isolate, Handle<JSArrayBuffer> array_buffer, | |
142 size_t addr, int num_waiters_to_wake) { | |
143 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); | |
144 | |
145 int waiters_woken = 0; | |
146 void* backing_store = array_buffer->backing_store(); | |
147 | |
148 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); | |
149 FutexWaitListNode* node = wait_list_.Pointer()->head_; | |
150 while (node && num_waiters_to_wake > 0) { | |
151 if (backing_store == node->backing_store_ && addr == node->wait_addr_) { | |
152 node->waiting_ = false; | |
153 node->cond_.NotifyOne(); | |
154 --num_waiters_to_wake; | |
155 waiters_woken++; | |
156 } | |
157 | |
158 node = node->next_; | |
159 } | |
160 | |
161 return waiters_woken; | |
162 } | |
163 | |
164 | |
165 int FutexEmulation::WakeOrRequeue(Isolate* isolate, | |
166 Handle<JSArrayBuffer> array_buffer, | |
167 size_t addr, int num_waiters_to_wake, | |
168 int32_t value, size_t addr2) { | |
169 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); | |
170 DCHECK(addr2 < NumberToSize(isolate, array_buffer->byte_length())); | |
171 | |
172 void* backing_store = array_buffer->backing_store(); | |
173 int32_t* p = | |
174 reinterpret_cast<int32_t*>(static_cast<int8_t*>(backing_store) + addr); | |
175 | |
176 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); | |
177 if (*p != value) { | |
178 return static_cast<int>(Result::kNotEqual); | |
179 } | |
180 | |
181 // Wake |num_waiters_to_wake| | |
182 int waiters_woken = 0; | |
183 FutexWaitListNode* node = wait_list_.Pointer()->head_; | |
184 while (node) { | |
185 if (backing_store == node->backing_store_ && addr == node->wait_addr_) { | |
186 if (num_waiters_to_wake > 0) { | |
187 node->waiting_ = false; | |
188 node->cond_.NotifyOne(); | |
189 --num_waiters_to_wake; | |
190 waiters_woken++; | |
191 } else { | |
192 node->wait_addr_ = addr2; | |
193 } | |
194 } | |
195 | |
196 node = node->next_; | |
197 } | |
198 | |
199 return waiters_woken; | |
200 } | |
201 | |
202 int FutexEmulation::NumWaitersForTesting(Isolate* isolate, | |
203 Handle<JSArrayBuffer> array_buffer, | |
204 size_t addr) { | |
205 DCHECK(addr < NumberToSize(isolate, array_buffer->byte_length())); | |
206 void* backing_store = array_buffer->backing_store(); | |
207 | |
208 base::LockGuard<base::Mutex> lock_guard(mutex_.Pointer()); | |
209 | |
210 int waiters = 0; | |
211 FutexWaitListNode* node = wait_list_.Pointer()->head_; | |
212 while (node) { | |
213 if (backing_store == node->backing_store_ && addr == node->wait_addr_) { | |
214 waiters++; | |
215 } | |
216 | |
217 node = node->next_; | |
218 } | |
219 | |
220 return waiters; | |
221 } | |
222 | |
223 } // namespace internal | |
224 } // namespace v8 | |
OLD | NEW |