1 | // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 | // for details. All rights reserved. Use of this source code is governed by a |
3 | // BSD-style license that can be found in the LICENSE file. |
4 | |
5 | #ifndef RUNTIME_VM_LOCKERS_H_ |
6 | #define RUNTIME_VM_LOCKERS_H_ |
7 | |
8 | #include "platform/assert.h" |
9 | #include "vm/allocation.h" |
10 | #include "vm/globals.h" |
11 | #include "vm/os_thread.h" |
12 | #include "vm/thread.h" |
13 | |
14 | namespace dart { |
15 | |
16 | const bool kNoSafepointScope = true; |
17 | const bool kDontAssertNoSafepointScope = false; |
18 | |
19 | /* |
20 | * Normal mutex locker : |
21 | * This locker abstraction should only be used when the enclosing code can |
22 | * not trigger a safepoint. In debug mode this class increments the |
23 | * no_safepoint_scope_depth variable for the current thread when the lock is |
24 | * taken and decrements it when the lock is released. NOTE: please do not use |
25 | * the passed in mutex object independent of the locker class, For example the |
26 | * code below will not assert correctly: |
27 | * { |
28 | * MutexLocker ml(m); |
29 | * .... |
30 | * m->Exit(); |
31 | * .... |
32 | * m->Enter(); |
33 | * ... |
34 | * } |
35 | * Always use the locker object even when the lock needs to be released |
36 | * temporarily, e.g: |
37 | * { |
38 | * MutexLocker ml(m); |
39 | * .... |
40 | * ml.Exit(); |
41 | * .... |
42 | * ml.Enter(); |
43 | * ... |
44 | * } |
45 | */ |
46 | class MutexLocker : public ValueObject { |
47 | public: |
48 | explicit MutexLocker(Mutex* mutex) |
49 | : |
50 | #if defined(DEBUG) |
51 | no_safepoint_scope_(true), |
52 | #endif |
53 | mutex_(mutex) { |
54 | ASSERT(mutex != nullptr); |
55 | #if defined(DEBUG) |
56 | Thread* thread = Thread::Current(); |
57 | if ((thread != nullptr) && |
58 | (thread->execution_state() != Thread::kThreadInNative)) { |
59 | thread->IncrementNoSafepointScopeDepth(); |
60 | } else { |
61 | no_safepoint_scope_ = false; |
62 | } |
63 | #endif |
64 | mutex_->Lock(); |
65 | } |
66 | |
67 | virtual ~MutexLocker() { |
68 | mutex_->Unlock(); |
69 | #if defined(DEBUG) |
70 | if (no_safepoint_scope_) { |
71 | Thread::Current()->DecrementNoSafepointScopeDepth(); |
72 | } |
73 | #endif |
74 | } |
75 | |
76 | void Lock() const { |
77 | #if defined(DEBUG) |
78 | if (no_safepoint_scope_) { |
79 | Thread::Current()->IncrementNoSafepointScopeDepth(); |
80 | } |
81 | #endif |
82 | mutex_->Lock(); |
83 | } |
84 | void Unlock() const { |
85 | mutex_->Unlock(); |
86 | #if defined(DEBUG) |
87 | if (no_safepoint_scope_) { |
88 | Thread::Current()->DecrementNoSafepointScopeDepth(); |
89 | } |
90 | #endif |
91 | } |
92 | |
93 | private: |
94 | DEBUG_ONLY(bool no_safepoint_scope_;) |
95 | Mutex* const mutex_; |
96 | |
97 | DISALLOW_COPY_AND_ASSIGN(MutexLocker); |
98 | }; |
99 | |
100 | /* |
101 | * Normal monitor locker : |
102 | * This locker abstraction should only be used when the enclosed code can |
103 | * not trigger a safepoint. In debug mode this class increments the |
104 | * no_safepoint_scope_depth variable for the current thread when the lock is |
105 | * taken and decrements it when the lock is released. NOTE: please do not use |
106 | * the passed in mutex object independent of the locker class, For example the |
107 | * code below will not assert correctly: |
108 | * { |
109 | * MonitorLocker ml(m); |
110 | * .... |
111 | * m->Exit(); |
112 | * .... |
113 | * m->Enter(); |
114 | * ... |
115 | * } |
116 | * Always use the locker object even when the lock needs to be released |
117 | * temporarily, e.g: |
118 | * { |
119 | * MonitorLocker ml(m); |
120 | * .... |
121 | * ml.Exit(); |
122 | * .... |
123 | * ml.Enter(); |
124 | * ... |
125 | * } |
126 | */ |
127 | class MonitorLocker : public ValueObject { |
128 | public: |
129 | explicit MonitorLocker(Monitor* monitor, bool no_safepoint_scope = true) |
130 | : monitor_(monitor), no_safepoint_scope_(no_safepoint_scope) { |
131 | ASSERT(monitor != NULL); |
132 | #if defined(DEBUG) |
133 | if (no_safepoint_scope_) { |
134 | Thread* thread = Thread::Current(); |
135 | if (thread != NULL) { |
136 | thread->IncrementNoSafepointScopeDepth(); |
137 | } else { |
138 | no_safepoint_scope_ = false; |
139 | } |
140 | } |
141 | #endif |
142 | monitor_->Enter(); |
143 | } |
144 | |
145 | virtual ~MonitorLocker() { |
146 | monitor_->Exit(); |
147 | #if defined(DEBUG) |
148 | if (no_safepoint_scope_) { |
149 | Thread::Current()->DecrementNoSafepointScopeDepth(); |
150 | } |
151 | #endif |
152 | } |
153 | |
154 | void Enter() const { |
155 | #if defined(DEBUG) |
156 | if (no_safepoint_scope_) { |
157 | Thread::Current()->IncrementNoSafepointScopeDepth(); |
158 | } |
159 | #endif |
160 | monitor_->Enter(); |
161 | } |
162 | void Exit() const { |
163 | monitor_->Exit(); |
164 | #if defined(DEBUG) |
165 | if (no_safepoint_scope_) { |
166 | Thread::Current()->DecrementNoSafepointScopeDepth(); |
167 | } |
168 | #endif |
169 | } |
170 | |
171 | Monitor::WaitResult Wait(int64_t millis = Monitor::kNoTimeout) { |
172 | return monitor_->Wait(millis); |
173 | } |
174 | |
175 | Monitor::WaitResult WaitWithSafepointCheck( |
176 | Thread* thread, |
177 | int64_t millis = Monitor::kNoTimeout); |
178 | |
179 | Monitor::WaitResult WaitMicros(int64_t micros = Monitor::kNoTimeout) { |
180 | return monitor_->WaitMicros(micros); |
181 | } |
182 | |
183 | void Notify() { monitor_->Notify(); } |
184 | |
185 | void NotifyAll() { monitor_->NotifyAll(); } |
186 | |
187 | private: |
188 | Monitor* const monitor_; |
189 | bool no_safepoint_scope_; |
190 | |
191 | DISALLOW_COPY_AND_ASSIGN(MonitorLocker); |
192 | }; |
193 | |
194 | // Leaves the given monitor during the scope of the object. |
195 | class MonitorLeaveScope : public ValueObject { |
196 | public: |
197 | explicit MonitorLeaveScope(MonitorLocker* monitor) |
198 | : monitor_locker_(monitor) { |
199 | monitor_locker_->Exit(); |
200 | } |
201 | |
202 | virtual ~MonitorLeaveScope() { monitor_locker_->Enter(); } |
203 | |
204 | private: |
205 | MonitorLocker* const monitor_locker_; |
206 | |
207 | DISALLOW_COPY_AND_ASSIGN(MonitorLeaveScope); |
208 | }; |
209 | |
210 | /* |
211 | * Safepoint mutex locker : |
212 | * This locker abstraction should be used when the enclosing code could |
213 | * potentially trigger a safepoint. |
214 | * This locker ensures that other threads that try to acquire the same lock |
215 | * will be marked as being at a safepoint if they get blocked trying to |
216 | * acquire the lock. |
217 | * NOTE: please do not use the passed in mutex object independent of the locker |
218 | * class, For example the code below will not work correctly: |
219 | * { |
220 | * SafepointMutexLocker ml(m); |
221 | * .... |
222 | * m->Exit(); |
223 | * .... |
224 | * m->Enter(); |
225 | * ... |
226 | * } |
227 | */ |
228 | class SafepointMutexLocker : public ValueObject { |
229 | public: |
230 | explicit SafepointMutexLocker(Mutex* mutex); |
231 | virtual ~SafepointMutexLocker() { mutex_->Unlock(); } |
232 | |
233 | private: |
234 | Mutex* const mutex_; |
235 | |
236 | DISALLOW_COPY_AND_ASSIGN(SafepointMutexLocker); |
237 | }; |
238 | |
239 | /* |
240 | * Safepoint monitor locker : |
241 | * This locker abstraction should be used when the enclosing code could |
242 | * potentially trigger a safepoint. |
243 | * This locker ensures that other threads that try to acquire the same lock |
244 | * will be marked as being at a safepoint if they get blocked trying to |
245 | * acquire the lock. |
246 | * NOTE: please do not use the passed in monitor object independent of the |
247 | * locker class, For example the code below will not work correctly: |
248 | * { |
249 | * SafepointMonitorLocker ml(m); |
250 | * .... |
251 | * m->Exit(); |
252 | * .... |
253 | * m->Enter(); |
254 | * ... |
255 | * } |
256 | */ |
257 | class SafepointMonitorLocker : public ValueObject { |
258 | public: |
259 | explicit SafepointMonitorLocker(Monitor* monitor); |
260 | virtual ~SafepointMonitorLocker() { monitor_->Exit(); } |
261 | |
262 | Monitor::WaitResult Wait(int64_t millis = Monitor::kNoTimeout); |
263 | |
264 | void NotifyAll() { monitor_->NotifyAll(); } |
265 | |
266 | private: |
267 | Monitor* const monitor_; |
268 | |
269 | DISALLOW_COPY_AND_ASSIGN(SafepointMonitorLocker); |
270 | }; |
271 | |
272 | class RwLock { |
273 | public: |
274 | RwLock() {} |
275 | ~RwLock() {} |
276 | |
277 | private: |
278 | friend class ReadRwLocker; |
279 | friend class WriteRwLocker; |
280 | |
281 | void EnterRead() { |
282 | MonitorLocker ml(&monitor_); |
283 | while (state_ == -1) { |
284 | ml.Wait(); |
285 | } |
286 | ++state_; |
287 | } |
288 | void LeaveRead() { |
289 | MonitorLocker ml(&monitor_); |
290 | ASSERT(state_ > 0); |
291 | if (--state_ == 0) { |
292 | ml.NotifyAll(); |
293 | } |
294 | } |
295 | |
296 | void EnterWrite() { |
297 | MonitorLocker ml(&monitor_); |
298 | while (state_ != 0) { |
299 | ml.Wait(); |
300 | } |
301 | state_ = -1; |
302 | } |
303 | void LeaveWrite() { |
304 | MonitorLocker ml(&monitor_); |
305 | ASSERT(state_ == -1); |
306 | state_ = 0; |
307 | ml.NotifyAll(); |
308 | } |
309 | |
310 | Monitor monitor_; |
311 | // [state_] > 0 : The lock is held by multiple readers. |
312 | // [state_] == 0 : The lock is free (no readers/writers). |
313 | // [state_] == -1: The lock is held by a single writer. |
314 | intptr_t state_ = 0; |
315 | }; |
316 | |
317 | class SafepointRwLock { |
318 | public: |
319 | SafepointRwLock() {} |
320 | ~SafepointRwLock() {} |
321 | |
322 | private: |
323 | friend class SafepointReadRwLocker; |
324 | friend class SafepointWriteRwLocker; |
325 | |
326 | void EnterRead() { |
327 | SafepointMonitorLocker ml(&monitor_); |
328 | while (state_ == -1) { |
329 | ml.Wait(); |
330 | } |
331 | ++state_; |
332 | } |
333 | void LeaveRead() { |
334 | SafepointMonitorLocker ml(&monitor_); |
335 | ASSERT(state_ > 0); |
336 | if (--state_ == 0) { |
337 | ml.NotifyAll(); |
338 | } |
339 | } |
340 | |
341 | void EnterWrite() { |
342 | SafepointMonitorLocker ml(&monitor_); |
343 | while (state_ != 0) { |
344 | ml.Wait(); |
345 | } |
346 | state_ = -1; |
347 | } |
348 | void LeaveWrite() { |
349 | SafepointMonitorLocker ml(&monitor_); |
350 | ASSERT(state_ == -1); |
351 | state_ = 0; |
352 | ml.NotifyAll(); |
353 | } |
354 | |
355 | Monitor monitor_; |
356 | // [state_] > 0 : The lock is held by multiple readers. |
357 | // [state_] == 0 : The lock is free (no readers/writers). |
358 | // [state_] == -1: The lock is held by a single writer. |
359 | intptr_t state_ = 0; |
360 | }; |
361 | |
362 | /* |
363 | * Locks a given [RwLock] for reading purposes. |
364 | * |
365 | * It will block while the lock is held by a writer. |
366 | * |
367 | * If this locker is long'jmped over (e.g. on a background compiler thread) the |
368 | * lock will be freed. |
369 | * |
370 | * NOTE: If the locking operation blocks (due to a writer) it will not check |
371 | * for a pending safepoint operation. |
372 | */ |
373 | class ReadRwLocker : public StackResource { |
374 | public: |
375 | ReadRwLocker(ThreadState* thread_state, RwLock* rw_lock) |
376 | : StackResource(thread_state), rw_lock_(rw_lock) { |
377 | rw_lock_->EnterRead(); |
378 | } |
379 | ~ReadRwLocker() { rw_lock_->LeaveRead(); } |
380 | |
381 | private: |
382 | RwLock* rw_lock_; |
383 | }; |
384 | |
385 | /* |
386 | * In addition to what [ReadRwLocker] does, this implementation also gets into a |
387 | * safepoint if necessary. |
388 | */ |
389 | class SafepointReadRwLocker : public StackResource { |
390 | public: |
391 | SafepointReadRwLocker(ThreadState* thread_state, SafepointRwLock* rw_lock) |
392 | : StackResource(thread_state), rw_lock_(rw_lock) { |
393 | rw_lock_->EnterRead(); |
394 | } |
395 | ~SafepointReadRwLocker() { rw_lock_->LeaveRead(); } |
396 | |
397 | private: |
398 | SafepointRwLock* rw_lock_; |
399 | }; |
400 | |
401 | /* |
402 | * Locks a given [RwLock] for writing purposes. |
403 | * |
404 | * It will block while the lock is held by one or more readers. |
405 | * |
406 | * If this locker is long'jmped over (e.g. on a background compiler thread) the |
407 | * lock will be freed. |
408 | * |
409 | * NOTE: If the locking operation blocks (due to a writer) it will not check |
410 | * for a pending safepoint operation. |
411 | */ |
412 | class WriteRwLocker : public StackResource { |
413 | public: |
414 | WriteRwLocker(ThreadState* thread_state, RwLock* rw_lock) |
415 | : StackResource(thread_state), rw_lock_(rw_lock) { |
416 | rw_lock_->EnterWrite(); |
417 | } |
418 | |
419 | ~WriteRwLocker() { rw_lock_->LeaveWrite(); } |
420 | |
421 | private: |
422 | RwLock* rw_lock_; |
423 | }; |
424 | |
425 | /* |
426 | * In addition to what [WriteRwLocker] does, this implementation also gets into a |
427 | * safepoint if necessary. |
428 | */ |
429 | class SafepointWriteRwLocker : public StackResource { |
430 | public: |
431 | SafepointWriteRwLocker(ThreadState* thread_state, SafepointRwLock* rw_lock) |
432 | : StackResource(thread_state), rw_lock_(rw_lock) { |
433 | rw_lock_->EnterWrite(); |
434 | } |
435 | |
436 | ~SafepointWriteRwLocker() { rw_lock_->LeaveWrite(); } |
437 | |
438 | private: |
439 | SafepointRwLock* rw_lock_; |
440 | }; |
441 | |
442 | } // namespace dart |
443 | |
444 | #endif // RUNTIME_VM_LOCKERS_H_ |
445 | |