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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
cc / base / protected_sequence_synchronizer.h [blame]
// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CC_BASE_PROTECTED_SEQUENCE_SYNCHRONIZER_H_
#define CC_BASE_PROTECTED_SEQUENCE_SYNCHRONIZER_H_
#include <memory>
#include <utility>
namespace cc {
// ProtectedSequenceSynchronizer can be used to enforce thread safety of data
// that are owned and produced by an "owner" thread; and then passed by
// reference to another thread to use for a limited duration (a "protected
// sequence"). A protected sequence must be initiated on the owning thread, and
// it must be concluded on the non-owning thread. See the code comment above
// InProtectedSequence() for more on this requirement.
class ProtectedSequenceSynchronizer {
public:
ProtectedSequenceSynchronizer() = default;
ProtectedSequenceSynchronizer(const ProtectedSequenceSynchronizer&) = delete;
ProtectedSequenceSynchronizer& operator=(
const ProtectedSequenceSynchronizer&) = delete;
virtual ~ProtectedSequenceSynchronizer() = default;
// Returns true if the current thread is the owner and producer of these data.
virtual bool IsOwnerThread() const = 0;
// Returns true if a non-owner thread is currently running a protected
// sequence. The owner thread must be responsible for transitioning the return
// value from false to true, and the non-owner thread must be responsible for
// transitioning from true to false. Failure to adhere to these guidelines
// will likely cause race conditions and/or deadlock.
virtual bool InProtectedSequence() const = 0;
// Blocks execution of the owner thread until a non-owner thread finishes
// executing a protected sequence. It is an error for this to be called on a
// non-owner thread.
virtual void WaitForProtectedSequenceCompletion() const = 0;
};
// ProtectedSequenceForbidden values cannot be accessed for read or write by any
// non-owner thread. There are no restrictions on access by the owner thread.
template <typename T>
class ProtectedSequenceForbidden {
public:
template <typename... Args>
explicit ProtectedSequenceForbidden(Args&&... args)
: value_(std::forward<Args>(args)...) {}
ProtectedSequenceForbidden(const ProtectedSequenceForbidden&) = delete;
ProtectedSequenceForbidden& operator=(const ProtectedSequenceForbidden&) =
delete;
const T& Read(const ProtectedSequenceSynchronizer& synchronizer) const {
DCHECK(synchronizer.IsOwnerThread());
return value_;
}
T& Write(const ProtectedSequenceSynchronizer& synchronizer) {
DCHECK(synchronizer.IsOwnerThread());
return value_;
}
private:
T value_;
};
// ProtectedSequenceReadable values are...
// - readable by the owner thread at any time without blocking
// - writable by the owner thread, but not during a protected sequence
// - readable by a non-owner thread during a protected sequence
// - never writable by a non-owner thread
template <typename T>
class ProtectedSequenceReadable {
public:
template <typename... Args>
explicit ProtectedSequenceReadable(Args&&... args)
: value_(std::forward<Args>(args)...) {}
ProtectedSequenceReadable(const ProtectedSequenceReadable&) = delete;
ProtectedSequenceReadable& operator=(const ProtectedSequenceReadable&) =
delete;
const T& Read(const ProtectedSequenceSynchronizer& synchronizer) const {
DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
return value_;
}
T& Write(const ProtectedSequenceSynchronizer& synchronizer) {
DCHECK(synchronizer.IsOwnerThread());
synchronizer.WaitForProtectedSequenceCompletion();
return value_;
}
private:
T value_;
};
// ProtectedSequenceWritable values are...
// - readable by the owner thread, but not during a protected sequence
// - writable by the owner thread, but not during a protected sequence
// - readable by a non-owner thread during a protected sequence
// - writable by a non-owner thread during a protected sequence
//
// Note that it is not safe to use ProtectedSequenceWritable values concurrently
// on two or more non-owner threads.
template <typename T>
class ProtectedSequenceWritable {
public:
template <typename... Args>
explicit ProtectedSequenceWritable(Args&&... args)
: value_(std::forward<Args>(args)...) {}
ProtectedSequenceWritable(const ProtectedSequenceWritable&) = delete;
ProtectedSequenceWritable& operator=(const ProtectedSequenceWritable&) =
delete;
const T& Read(const ProtectedSequenceSynchronizer& synchronizer) const {
DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
if (synchronizer.IsOwnerThread())
synchronizer.WaitForProtectedSequenceCompletion();
return value_;
}
T& Write(const ProtectedSequenceSynchronizer& synchronizer) {
DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
if (synchronizer.IsOwnerThread())
synchronizer.WaitForProtectedSequenceCompletion();
return value_;
}
private:
T value_;
};
// Type specializations for various containers.
template <typename T>
class ProtectedSequenceForbidden<std::unique_ptr<T>> {
public:
template <typename... Args>
explicit ProtectedSequenceForbidden(Args&&... args)
: value_(std::forward<Args>(args)...) {}
ProtectedSequenceForbidden(const ProtectedSequenceForbidden&) = delete;
ProtectedSequenceForbidden& operator=(const ProtectedSequenceForbidden&) =
delete;
const T* Read(const ProtectedSequenceSynchronizer& synchronizer) const {
DCHECK(synchronizer.IsOwnerThread());
return value_.get();
}
std::unique_ptr<T>& Write(const ProtectedSequenceSynchronizer& synchronizer) {
DCHECK(synchronizer.IsOwnerThread());
return value_;
}
private:
std::unique_ptr<T> value_;
};
template <typename T>
class ProtectedSequenceReadable<std::unique_ptr<T>> {
public:
template <typename... Args>
explicit ProtectedSequenceReadable(Args&&... args)
: value_(std::forward<Args>(args)...) {}
ProtectedSequenceReadable(const ProtectedSequenceReadable&) = delete;
ProtectedSequenceReadable& operator=(const ProtectedSequenceReadable&) =
delete;
const T* Read(const ProtectedSequenceSynchronizer& synchronizer) const {
return value_.get();
}
std::unique_ptr<T>& Write(const ProtectedSequenceSynchronizer& synchronizer) {
DCHECK(synchronizer.IsOwnerThread());
synchronizer.WaitForProtectedSequenceCompletion();
return value_;
}
private:
std::unique_ptr<T> value_;
};
template <typename T>
class ProtectedSequenceWritable<std::unique_ptr<T>> {
public:
template <typename... Args>
explicit ProtectedSequenceWritable(Args&&... args)
: value_(std::forward<Args>(args)...) {}
ProtectedSequenceWritable(const ProtectedSequenceWritable&) = delete;
ProtectedSequenceWritable& operator=(const ProtectedSequenceWritable&) =
delete;
const T* Read(const ProtectedSequenceSynchronizer& synchronizer) const {
DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
if (synchronizer.IsOwnerThread())
synchronizer.WaitForProtectedSequenceCompletion();
return value_.get();
}
std::unique_ptr<T>& Write(const ProtectedSequenceSynchronizer& synchronizer) {
DCHECK(synchronizer.IsOwnerThread() || synchronizer.InProtectedSequence());
if (synchronizer.IsOwnerThread())
synchronizer.WaitForProtectedSequenceCompletion();
return value_;
}
private:
std::unique_ptr<T> value_;
};
} // namespace cc
#endif // CC_BASE_PROTECTED_SEQUENCE_SYNCHRONIZER_H_