blob: 033a75e79f48c5add7456f5843a87a174f6e2af0 [file] [log] [blame]
Austin Schuh745610d2015-09-06 18:19:50 -07001// -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*-
2/* Copyright (c) 2006, Google Inc.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * * Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above
12 * copyright notice, this list of conditions and the following disclaimer
13 * in the documentation and/or other materials provided with the
14 * distribution.
15 * * Neither the name of Google Inc. nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 *
31 * ---
32 * Author: Sanjay Ghemawat
33 */
34
35// SpinLock is async signal safe.
36// If used within a signal handler, all lock holders
37// should block the signal even outside the signal handler.
38
39#ifndef BASE_SPINLOCK_H_
40#define BASE_SPINLOCK_H_
41
42#include <config.h>
43#include "base/atomicops.h"
44#include "base/basictypes.h"
45#include "base/dynamic_annotations.h"
46#include "base/thread_annotations.h"
47
48class LOCKABLE SpinLock {
49 public:
50 SpinLock() : lockword_(kSpinLockFree) { }
51
52 // Special constructor for use with static SpinLock objects. E.g.,
53 //
54 // static SpinLock lock(base::LINKER_INITIALIZED);
55 //
56 // When intialized using this constructor, we depend on the fact
57 // that the linker has already initialized the memory appropriately.
58 // A SpinLock constructed like this can be freely used from global
59 // initializers without worrying about the order in which global
60 // initializers run.
61 explicit SpinLock(base::LinkerInitialized /*x*/) {
62 // Does nothing; lockword_ is already initialized
63 }
64
65 // Acquire this SpinLock.
66 // TODO(csilvers): uncomment the annotation when we figure out how to
67 // support this macro with 0 args (see thread_annotations.h)
68 inline void Lock() /*EXCLUSIVE_LOCK_FUNCTION()*/ {
69 if (base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree,
70 kSpinLockHeld) != kSpinLockFree) {
71 SlowLock();
72 }
73 ANNOTATE_RWLOCK_ACQUIRED(this, 1);
74 }
75
76 // Try to acquire this SpinLock without blocking and return true if the
77 // acquisition was successful. If the lock was not acquired, false is
78 // returned. If this SpinLock is free at the time of the call, TryLock
79 // will return true with high probability.
80 inline bool TryLock() EXCLUSIVE_TRYLOCK_FUNCTION(true) {
81 bool res =
82 (base::subtle::Acquire_CompareAndSwap(&lockword_, kSpinLockFree,
83 kSpinLockHeld) == kSpinLockFree);
84 if (res) {
85 ANNOTATE_RWLOCK_ACQUIRED(this, 1);
86 }
87 return res;
88 }
89
90 // Release this SpinLock, which must be held by the calling thread.
91 // TODO(csilvers): uncomment the annotation when we figure out how to
92 // support this macro with 0 args (see thread_annotations.h)
93 inline void Unlock() /*UNLOCK_FUNCTION()*/ {
94 ANNOTATE_RWLOCK_RELEASED(this, 1);
95 uint64 wait_cycles = static_cast<uint64>(
96 base::subtle::Release_AtomicExchange(&lockword_, kSpinLockFree));
97 if (wait_cycles != kSpinLockHeld) {
98 // Collect contentionz profile info, and speed the wakeup of any waiter.
99 // The wait_cycles value indicates how long this thread spent waiting
100 // for the lock.
101 SlowUnlock(wait_cycles);
102 }
103 }
104
105 // Determine if the lock is held. When the lock is held by the invoking
106 // thread, true will always be returned. Intended to be used as
107 // CHECK(lock.IsHeld()).
108 inline bool IsHeld() const {
109 return base::subtle::NoBarrier_Load(&lockword_) != kSpinLockFree;
110 }
111
112 static const base::LinkerInitialized LINKER_INITIALIZED; // backwards compat
113 private:
114 enum { kSpinLockFree = 0 };
115 enum { kSpinLockHeld = 1 };
116 enum { kSpinLockSleeper = 2 };
117
118 volatile Atomic32 lockword_;
119
120 void SlowLock();
121 void SlowUnlock(uint64 wait_cycles);
122 Atomic32 SpinLoop(int64 initial_wait_timestamp, Atomic32* wait_cycles);
123 inline int32 CalculateWaitCycles(int64 wait_start_time);
124
125 DISALLOW_COPY_AND_ASSIGN(SpinLock);
126};
127
128// Corresponding locker object that arranges to acquire a spinlock for
129// the duration of a C++ scope.
130class SCOPED_LOCKABLE SpinLockHolder {
131 private:
132 SpinLock* lock_;
133 public:
134 inline explicit SpinLockHolder(SpinLock* l) EXCLUSIVE_LOCK_FUNCTION(l)
135 : lock_(l) {
136 l->Lock();
137 }
138 // TODO(csilvers): uncomment the annotation when we figure out how to
139 // support this macro with 0 args (see thread_annotations.h)
140 inline ~SpinLockHolder() /*UNLOCK_FUNCTION()*/ { lock_->Unlock(); }
141};
142// Catch bug where variable name is omitted, e.g. SpinLockHolder (&lock);
143#define SpinLockHolder(x) COMPILE_ASSERT(0, spin_lock_decl_missing_var_name)
144
145
146#endif // BASE_SPINLOCK_H_