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
14namespace dart {
15
16const bool kNoSafepointScope = true;
17const 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 */
46class 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 */
127class 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.
195class 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 */
228class 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 */
257class 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
272class 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
317class 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 */
373class 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 */
389class 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 */
412class 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 */
429class 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