-
Notifications
You must be signed in to change notification settings - Fork 1.3k
/
ParkingLot.h
180 lines (160 loc) · 8.55 KB
/
ParkingLot.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
* Copyright (C) 2015-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <wtf/Atomics.h>
#include <wtf/ScopedLambda.h>
#include <wtf/TimeWithDynamicClockType.h>
namespace WTF {
class Thread;
class ParkingLot {
WTF_MAKE_FAST_ALLOCATED;
ParkingLot() = delete;
ParkingLot(const ParkingLot&) = delete;
public:
// ParkingLot will accept any kind of time and convert it internally, but this typedef tells
// you what kind of time ParkingLot would be able to use without conversions. It's sad that
// this is WallTime not MonotonicTime, but that's just how OS wait functions work. However,
// because ParkingLot evaluates whether it should wait by checking if your time has passed
// using whatever clock you used, specifying timeouts in MonotonicTime is semantically better.
// For example, if the user sets his computer's clock back during the time that you wanted to
// wait for one second, and you specified the timeout using the MonotonicTime, then ParkingLot
// will be smart enough to know that your one second has elapsed.
typedef WallTime Time;
// Parks the thread in a queue associated with the given address, which cannot be null. The
// parking only succeeds if the validation function returns true while the queue lock is held.
//
// If validation returns false, it will unlock the internal parking queue and then it will
// return a null ParkResult (wasUnparked = false, token = 0) without doing anything else.
//
// If validation returns true, it will enqueue the thread, unlock the parking queue lock, call
// the beforeSleep function, and then it will sleep so long as the thread continues to be on the
// queue and the timeout hasn't fired. Finally, this returns wasUnparked = true if we actually
// got unparked or wasUnparked = false if the timeout was hit. When wasUnparked = true, the
// token will contain whatever token was returned from the callback to unparkOne(), or 0 if the
// thread was unparked using unparkAll() or the form of unparkOne() that doesn't take a
// callback.
//
// Note that beforeSleep is called with no locks held, so it's OK to do pretty much anything so
// long as you don't recursively call parkConditionally(). You can call unparkOne()/unparkAll()
// though. It's useful to use beforeSleep() to unlock some mutex in the implementation of
// Condition::wait().
struct ParkResult {
bool wasUnparked { false };
intptr_t token { 0 };
};
template<typename ValidationFunctor, typename BeforeSleepFunctor>
static ParkResult parkConditionally(
const void* address,
const ValidationFunctor& validation,
const BeforeSleepFunctor& beforeSleep,
const TimeWithDynamicClockType& timeout)
{
return parkConditionallyImpl(
address,
scopedLambdaRef<bool()>(validation),
scopedLambdaRef<void()>(beforeSleep),
timeout);
}
// Simple version of parkConditionally() that covers the most common case: you want to park
// indefinitely so long as the value at the given address hasn't changed.
template<typename T, typename U>
static ParkResult compareAndPark(const Atomic<T>* address, U expected)
{
return parkConditionally(
address,
[address, expected] () -> bool {
U value = address->load();
return value == expected;
},
[] () { },
Time::infinity());
}
// Unparking status given to you anytime you unparkOne().
struct UnparkResult {
// True if some thread was unparked.
bool didUnparkThread { false };
// True if there may be more threads on this address. This may be conservatively true.
bool mayHaveMoreThreads { false };
// This bit is randomly set to true indicating that it may be profitable to unlock the lock
// using a fair unlocking protocol. This is most useful when used in conjunction with
// unparkOne(address, callback).
bool timeToBeFair { false };
};
// Unparks one thread from the queue associated with the given address, which cannot be null.
// Returns true if there may still be other threads on that queue, or false if there definitely
// are no more threads on the queue.
WTF_EXPORT_PRIVATE static UnparkResult unparkOne(const void* address);
// This is an expert-mode version of unparkOne() that allows for really good thundering herd
// avoidance and eventual stochastic fairness in adaptive mutexes.
//
// Unparks one thread from the queue associated with the given address, and calls the given
// callback while the address is locked. Reports to the callback whether any thread got
// unparked, whether there may be any other threads still on the queue, and whether this may be
// a good time to do fair unlocking. The callback returns an intptr_t token, which is returned
// to the unparked thread via ParkResult::token.
//
// WTF::Lock and WTF::Condition both use this form of unparkOne() because it allows them to use
// the ParkingLot's internal queue lock to serialize some decision-making. For example, if
// UnparkResult::mayHaveMoreThreads is false inside the callback, then we know that at that
// moment nobody can add any threads to the queue because the queue lock is still held. Also,
// WTF::Lock uses the timeToBeFair and token mechanism to implement eventual fairness.
template<typename Callback>
static void unparkOne(const void* address, const Callback& callback)
{
unparkOneImpl(address, scopedLambdaRef<intptr_t(UnparkResult)>(callback));
}
WTF_EXPORT_PRIVATE static unsigned unparkCount(const void* address, unsigned count);
// Unparks every thread from the queue associated with the given address, which cannot be null.
WTF_EXPORT_PRIVATE static void unparkAll(const void* address);
// Locks the parking lot and walks all of the parked threads and the addresses they are waiting
// on. Threads that are on the same queue are guaranteed to be walked from first to last, but the
// queues may be randomly interleaved. For example, if the queue for address A1 has T1 and T2 and
// the queue for address A2 has T3 and T4, then you might see iteration orders like:
//
// A1,T1 A1,T2 A2,T3 A2,T4
// A2,T3 A2,T4 A1,T1 A1,T2
// A1,T1 A2,T3 A1,T2 A2,T4
// A1,T1 A2,T3 A2,T4 A1,T2
//
// As well as many other possible interleavings that all have T1 before T2 and T3 before T4 but are
// otherwise unconstrained. This method is useful primarily for debugging. It's also used by unit
// tests.
template<typename Func>
static void forEach(const Func& func)
{
forEachImpl(scopedLambdaRef<void(Thread&, const void*)>(func));
}
private:
WTF_EXPORT_PRIVATE static ParkResult parkConditionallyImpl(
const void* address,
const ScopedLambda<bool()>& validation,
const ScopedLambda<void()>& beforeSleep,
const TimeWithDynamicClockType& timeout);
WTF_EXPORT_PRIVATE static void unparkOneImpl(
const void* address, const ScopedLambda<intptr_t(UnparkResult)>& callback);
WTF_EXPORT_PRIVATE static void forEachImpl(const ScopedLambda<void(Thread&, const void*)>&);
};
} // namespace WTF
using WTF::ParkingLot;