xref: /aosp_15_r20/external/abseil-cpp/absl/flags/internal/sequence_lock.h (revision 9356374a3709195abf420251b3e825997ff56c0f)
1*9356374aSAndroid Build Coastguard Worker //
2*9356374aSAndroid Build Coastguard Worker // Copyright 2020 The Abseil Authors.
3*9356374aSAndroid Build Coastguard Worker //
4*9356374aSAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
5*9356374aSAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
6*9356374aSAndroid Build Coastguard Worker // You may obtain a copy of the License at
7*9356374aSAndroid Build Coastguard Worker //
8*9356374aSAndroid Build Coastguard Worker //      https://www.apache.org/licenses/LICENSE-2.0
9*9356374aSAndroid Build Coastguard Worker //
10*9356374aSAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
11*9356374aSAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
12*9356374aSAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*9356374aSAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
14*9356374aSAndroid Build Coastguard Worker // limitations under the License.
15*9356374aSAndroid Build Coastguard Worker 
16*9356374aSAndroid Build Coastguard Worker #ifndef ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
17*9356374aSAndroid Build Coastguard Worker #define ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
18*9356374aSAndroid Build Coastguard Worker 
19*9356374aSAndroid Build Coastguard Worker #include <stddef.h>
20*9356374aSAndroid Build Coastguard Worker #include <stdint.h>
21*9356374aSAndroid Build Coastguard Worker 
22*9356374aSAndroid Build Coastguard Worker #include <atomic>
23*9356374aSAndroid Build Coastguard Worker #include <cassert>
24*9356374aSAndroid Build Coastguard Worker #include <cstring>
25*9356374aSAndroid Build Coastguard Worker 
26*9356374aSAndroid Build Coastguard Worker #include "absl/base/optimization.h"
27*9356374aSAndroid Build Coastguard Worker 
28*9356374aSAndroid Build Coastguard Worker namespace absl {
29*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_BEGIN
30*9356374aSAndroid Build Coastguard Worker namespace flags_internal {
31*9356374aSAndroid Build Coastguard Worker 
32*9356374aSAndroid Build Coastguard Worker // Align 'x' up to the nearest 'align' bytes.
AlignUp(size_t x,size_t align)33*9356374aSAndroid Build Coastguard Worker inline constexpr size_t AlignUp(size_t x, size_t align) {
34*9356374aSAndroid Build Coastguard Worker   return align * ((x + align - 1) / align);
35*9356374aSAndroid Build Coastguard Worker }
36*9356374aSAndroid Build Coastguard Worker 
37*9356374aSAndroid Build Coastguard Worker // A SequenceLock implements lock-free reads. A sequence counter is incremented
38*9356374aSAndroid Build Coastguard Worker // before and after each write, and readers access the counter before and after
39*9356374aSAndroid Build Coastguard Worker // accessing the protected data. If the counter is verified to not change during
40*9356374aSAndroid Build Coastguard Worker // the access, and the sequence counter value was even, then the reader knows
41*9356374aSAndroid Build Coastguard Worker // that the read was race-free and valid. Otherwise, the reader must fall back
42*9356374aSAndroid Build Coastguard Worker // to a Mutex-based code path.
43*9356374aSAndroid Build Coastguard Worker //
44*9356374aSAndroid Build Coastguard Worker // This particular SequenceLock starts in an "uninitialized" state in which
45*9356374aSAndroid Build Coastguard Worker // TryRead() returns false. It must be enabled by calling MarkInitialized().
46*9356374aSAndroid Build Coastguard Worker // This serves as a marker that the associated flag value has not yet been
47*9356374aSAndroid Build Coastguard Worker // initialized and a slow path needs to be taken.
48*9356374aSAndroid Build Coastguard Worker //
49*9356374aSAndroid Build Coastguard Worker // The memory reads and writes protected by this lock must use the provided
50*9356374aSAndroid Build Coastguard Worker // `TryRead()` and `Write()` functions. These functions behave similarly to
51*9356374aSAndroid Build Coastguard Worker // `memcpy()`, with one oddity: the protected data must be an array of
52*9356374aSAndroid Build Coastguard Worker // `std::atomic<uint64>`. This is to comply with the C++ standard, which
53*9356374aSAndroid Build Coastguard Worker // considers data races on non-atomic objects to be undefined behavior. See "Can
54*9356374aSAndroid Build Coastguard Worker // Seqlocks Get Along With Programming Language Memory Models?"[1] by Hans J.
55*9356374aSAndroid Build Coastguard Worker // Boehm for more details.
56*9356374aSAndroid Build Coastguard Worker //
57*9356374aSAndroid Build Coastguard Worker // [1] https://www.hpl.hp.com/techreports/2012/HPL-2012-68.pdf
58*9356374aSAndroid Build Coastguard Worker class SequenceLock {
59*9356374aSAndroid Build Coastguard Worker  public:
SequenceLock()60*9356374aSAndroid Build Coastguard Worker   constexpr SequenceLock() : lock_(kUninitialized) {}
61*9356374aSAndroid Build Coastguard Worker 
62*9356374aSAndroid Build Coastguard Worker   // Mark that this lock is ready for use.
MarkInitialized()63*9356374aSAndroid Build Coastguard Worker   void MarkInitialized() {
64*9356374aSAndroid Build Coastguard Worker     assert(lock_.load(std::memory_order_relaxed) == kUninitialized);
65*9356374aSAndroid Build Coastguard Worker     lock_.store(0, std::memory_order_release);
66*9356374aSAndroid Build Coastguard Worker   }
67*9356374aSAndroid Build Coastguard Worker 
68*9356374aSAndroid Build Coastguard Worker   // Copy "size" bytes of data from "src" to "dst", protected as a read-side
69*9356374aSAndroid Build Coastguard Worker   // critical section of the sequence lock.
70*9356374aSAndroid Build Coastguard Worker   //
71*9356374aSAndroid Build Coastguard Worker   // Unlike traditional sequence lock implementations which loop until getting a
72*9356374aSAndroid Build Coastguard Worker   // clean read, this implementation returns false in the case of concurrent
73*9356374aSAndroid Build Coastguard Worker   // calls to `Write`. In such a case, the caller should fall back to a
74*9356374aSAndroid Build Coastguard Worker   // locking-based slow path.
75*9356374aSAndroid Build Coastguard Worker   //
76*9356374aSAndroid Build Coastguard Worker   // Returns false if the sequence lock was not yet marked as initialized.
77*9356374aSAndroid Build Coastguard Worker   //
78*9356374aSAndroid Build Coastguard Worker   // NOTE: If this returns false, "dst" may be overwritten with undefined
79*9356374aSAndroid Build Coastguard Worker   // (potentially uninitialized) data.
TryRead(void * dst,const std::atomic<uint64_t> * src,size_t size)80*9356374aSAndroid Build Coastguard Worker   bool TryRead(void* dst, const std::atomic<uint64_t>* src, size_t size) const {
81*9356374aSAndroid Build Coastguard Worker     // Acquire barrier ensures that no loads done by f() are reordered
82*9356374aSAndroid Build Coastguard Worker     // above the first load of the sequence counter.
83*9356374aSAndroid Build Coastguard Worker     int64_t seq_before = lock_.load(std::memory_order_acquire);
84*9356374aSAndroid Build Coastguard Worker     if (ABSL_PREDICT_FALSE(seq_before & 1) == 1) return false;
85*9356374aSAndroid Build Coastguard Worker     RelaxedCopyFromAtomic(dst, src, size);
86*9356374aSAndroid Build Coastguard Worker     // Another acquire fence ensures that the load of 'lock_' below is
87*9356374aSAndroid Build Coastguard Worker     // strictly ordered after the RelaxedCopyToAtomic call above.
88*9356374aSAndroid Build Coastguard Worker     std::atomic_thread_fence(std::memory_order_acquire);
89*9356374aSAndroid Build Coastguard Worker     int64_t seq_after = lock_.load(std::memory_order_relaxed);
90*9356374aSAndroid Build Coastguard Worker     return ABSL_PREDICT_TRUE(seq_before == seq_after);
91*9356374aSAndroid Build Coastguard Worker   }
92*9356374aSAndroid Build Coastguard Worker 
93*9356374aSAndroid Build Coastguard Worker   // Copy "size" bytes from "src" to "dst" as a write-side critical section
94*9356374aSAndroid Build Coastguard Worker   // of the sequence lock. Any concurrent readers will be forced to retry
95*9356374aSAndroid Build Coastguard Worker   // until they get a read that does not conflict with this write.
96*9356374aSAndroid Build Coastguard Worker   //
97*9356374aSAndroid Build Coastguard Worker   // This call must be externally synchronized against other calls to Write,
98*9356374aSAndroid Build Coastguard Worker   // but may proceed concurrently with reads.
Write(std::atomic<uint64_t> * dst,const void * src,size_t size)99*9356374aSAndroid Build Coastguard Worker   void Write(std::atomic<uint64_t>* dst, const void* src, size_t size) {
100*9356374aSAndroid Build Coastguard Worker     // We can use relaxed instructions to increment the counter since we
101*9356374aSAndroid Build Coastguard Worker     // are extenally synchronized. The std::atomic_thread_fence below
102*9356374aSAndroid Build Coastguard Worker     // ensures that the counter updates don't get interleaved with the
103*9356374aSAndroid Build Coastguard Worker     // copy to the data.
104*9356374aSAndroid Build Coastguard Worker     int64_t orig_seq = lock_.load(std::memory_order_relaxed);
105*9356374aSAndroid Build Coastguard Worker     assert((orig_seq & 1) == 0);  // Must be initially unlocked.
106*9356374aSAndroid Build Coastguard Worker     lock_.store(orig_seq + 1, std::memory_order_relaxed);
107*9356374aSAndroid Build Coastguard Worker 
108*9356374aSAndroid Build Coastguard Worker     // We put a release fence between update to lock_ and writes to shared data.
109*9356374aSAndroid Build Coastguard Worker     // Thus all stores to shared data are effectively release operations and
110*9356374aSAndroid Build Coastguard Worker     // update to lock_ above cannot be re-ordered past any of them. Note that
111*9356374aSAndroid Build Coastguard Worker     // this barrier is not for the fetch_add above.  A release barrier for the
112*9356374aSAndroid Build Coastguard Worker     // fetch_add would be before it, not after.
113*9356374aSAndroid Build Coastguard Worker     std::atomic_thread_fence(std::memory_order_release);
114*9356374aSAndroid Build Coastguard Worker     RelaxedCopyToAtomic(dst, src, size);
115*9356374aSAndroid Build Coastguard Worker     // "Release" semantics ensure that none of the writes done by
116*9356374aSAndroid Build Coastguard Worker     // RelaxedCopyToAtomic() can be reordered after the following modification.
117*9356374aSAndroid Build Coastguard Worker     lock_.store(orig_seq + 2, std::memory_order_release);
118*9356374aSAndroid Build Coastguard Worker   }
119*9356374aSAndroid Build Coastguard Worker 
120*9356374aSAndroid Build Coastguard Worker   // Return the number of times that Write() has been called.
121*9356374aSAndroid Build Coastguard Worker   //
122*9356374aSAndroid Build Coastguard Worker   // REQUIRES: This must be externally synchronized against concurrent calls to
123*9356374aSAndroid Build Coastguard Worker   // `Write()` or `IncrementModificationCount()`.
124*9356374aSAndroid Build Coastguard Worker   // REQUIRES: `MarkInitialized()` must have been previously called.
ModificationCount()125*9356374aSAndroid Build Coastguard Worker   int64_t ModificationCount() const {
126*9356374aSAndroid Build Coastguard Worker     int64_t val = lock_.load(std::memory_order_relaxed);
127*9356374aSAndroid Build Coastguard Worker     assert(val != kUninitialized && (val & 1) == 0);
128*9356374aSAndroid Build Coastguard Worker     return val / 2;
129*9356374aSAndroid Build Coastguard Worker   }
130*9356374aSAndroid Build Coastguard Worker 
131*9356374aSAndroid Build Coastguard Worker   // REQUIRES: This must be externally synchronized against concurrent calls to
132*9356374aSAndroid Build Coastguard Worker   // `Write()` or `ModificationCount()`.
133*9356374aSAndroid Build Coastguard Worker   // REQUIRES: `MarkInitialized()` must have been previously called.
IncrementModificationCount()134*9356374aSAndroid Build Coastguard Worker   void IncrementModificationCount() {
135*9356374aSAndroid Build Coastguard Worker     int64_t val = lock_.load(std::memory_order_relaxed);
136*9356374aSAndroid Build Coastguard Worker     assert(val != kUninitialized);
137*9356374aSAndroid Build Coastguard Worker     lock_.store(val + 2, std::memory_order_relaxed);
138*9356374aSAndroid Build Coastguard Worker   }
139*9356374aSAndroid Build Coastguard Worker 
140*9356374aSAndroid Build Coastguard Worker  private:
141*9356374aSAndroid Build Coastguard Worker   // Perform the equivalent of "memcpy(dst, src, size)", but using relaxed
142*9356374aSAndroid Build Coastguard Worker   // atomics.
RelaxedCopyFromAtomic(void * dst,const std::atomic<uint64_t> * src,size_t size)143*9356374aSAndroid Build Coastguard Worker   static void RelaxedCopyFromAtomic(void* dst, const std::atomic<uint64_t>* src,
144*9356374aSAndroid Build Coastguard Worker                                     size_t size) {
145*9356374aSAndroid Build Coastguard Worker     char* dst_byte = static_cast<char*>(dst);
146*9356374aSAndroid Build Coastguard Worker     while (size >= sizeof(uint64_t)) {
147*9356374aSAndroid Build Coastguard Worker       uint64_t word = src->load(std::memory_order_relaxed);
148*9356374aSAndroid Build Coastguard Worker       std::memcpy(dst_byte, &word, sizeof(word));
149*9356374aSAndroid Build Coastguard Worker       dst_byte += sizeof(word);
150*9356374aSAndroid Build Coastguard Worker       src++;
151*9356374aSAndroid Build Coastguard Worker       size -= sizeof(word);
152*9356374aSAndroid Build Coastguard Worker     }
153*9356374aSAndroid Build Coastguard Worker     if (size > 0) {
154*9356374aSAndroid Build Coastguard Worker       uint64_t word = src->load(std::memory_order_relaxed);
155*9356374aSAndroid Build Coastguard Worker       std::memcpy(dst_byte, &word, size);
156*9356374aSAndroid Build Coastguard Worker     }
157*9356374aSAndroid Build Coastguard Worker   }
158*9356374aSAndroid Build Coastguard Worker 
159*9356374aSAndroid Build Coastguard Worker   // Perform the equivalent of "memcpy(dst, src, size)", but using relaxed
160*9356374aSAndroid Build Coastguard Worker   // atomics.
RelaxedCopyToAtomic(std::atomic<uint64_t> * dst,const void * src,size_t size)161*9356374aSAndroid Build Coastguard Worker   static void RelaxedCopyToAtomic(std::atomic<uint64_t>* dst, const void* src,
162*9356374aSAndroid Build Coastguard Worker                                   size_t size) {
163*9356374aSAndroid Build Coastguard Worker     const char* src_byte = static_cast<const char*>(src);
164*9356374aSAndroid Build Coastguard Worker     while (size >= sizeof(uint64_t)) {
165*9356374aSAndroid Build Coastguard Worker       uint64_t word;
166*9356374aSAndroid Build Coastguard Worker       std::memcpy(&word, src_byte, sizeof(word));
167*9356374aSAndroid Build Coastguard Worker       dst->store(word, std::memory_order_relaxed);
168*9356374aSAndroid Build Coastguard Worker       src_byte += sizeof(word);
169*9356374aSAndroid Build Coastguard Worker       dst++;
170*9356374aSAndroid Build Coastguard Worker       size -= sizeof(word);
171*9356374aSAndroid Build Coastguard Worker     }
172*9356374aSAndroid Build Coastguard Worker     if (size > 0) {
173*9356374aSAndroid Build Coastguard Worker       uint64_t word = 0;
174*9356374aSAndroid Build Coastguard Worker       std::memcpy(&word, src_byte, size);
175*9356374aSAndroid Build Coastguard Worker       dst->store(word, std::memory_order_relaxed);
176*9356374aSAndroid Build Coastguard Worker     }
177*9356374aSAndroid Build Coastguard Worker   }
178*9356374aSAndroid Build Coastguard Worker 
179*9356374aSAndroid Build Coastguard Worker   static constexpr int64_t kUninitialized = -1;
180*9356374aSAndroid Build Coastguard Worker   std::atomic<int64_t> lock_;
181*9356374aSAndroid Build Coastguard Worker };
182*9356374aSAndroid Build Coastguard Worker 
183*9356374aSAndroid Build Coastguard Worker }  // namespace flags_internal
184*9356374aSAndroid Build Coastguard Worker ABSL_NAMESPACE_END
185*9356374aSAndroid Build Coastguard Worker }  // namespace absl
186*9356374aSAndroid Build Coastguard Worker 
187*9356374aSAndroid Build Coastguard Worker #endif  // ABSL_FLAGS_INTERNAL_SEQUENCE_LOCK_H_
188