111 static constexpr std::size_t
kDepth = 32;
115 memberCount < CaptureFrameSet::kMaxMembers
117 : CaptureFrameSet::kMaxMembers}
121 std::size_t memberCount() const noexcept {
return m_members; }
125 void publish(
const int* slots,
const std::uint64_t* stampNs)
noexcept
127 const auto gen = m_generation.load(std::memory_order_relaxed) + 1;
128 auto& e = m_ring[gen %
kDepth];
133 e.seq.store(0, std::memory_order_relaxed);
134 std::atomic_thread_fence(std::memory_order_release);
136 e.set.generation = gen;
137 e.set.memberCount = m_members;
138 for(std::size_t i = 0; i < m_members; ++i)
143 e.set.slot[i] = slots[i] < int(kMaxReturnableSlot) ? slots[i] : -1;
144 e.set.stampNs[i] = stampNs ? stampNs[i] : 0;
147 e.seq.store(gen, std::memory_order_release);
149 const bool complete = e.set.complete();
152 const auto sk = e.set.skewNs();
153 if(sk > m_maxSkewNs.load(std::memory_order_relaxed))
154 m_maxSkewNs.store(sk, std::memory_order_relaxed);
158 m_incomplete.fetch_add(1, std::memory_order_relaxed);
164 m_generation.store(gen, std::memory_order_release);
166 m_newestComplete.store(gen, std::memory_order_release);
172 std::uint64_t generation{0};
173 std::uint64_t stampNs{0};
185 if(member >= m_members)
188 if(passId != m_pinnedPass)
190 m_pinnedPass = passId;
191 const auto gen = m_newestComplete.load(std::memory_order_acquire);
195 const auto newest = m_generation.load(std::memory_order_acquire);
196 if(gen == 0 || newest - gen >=
kDepth)
199 m_lapped.fetch_add(1, std::memory_order_relaxed);
210 if(m_pinnedGen != 0 && !snapshotPinned())
212 m_lapped.fetch_add(1, std::memory_order_relaxed);
216 m_pinnedIsNew = m_pinnedGen != 0 && m_pinnedGen != m_lastHandedOut;
221 m_lastHandedOut = m_pinnedGen;
222 for(std::size_t i = 0; i < m_members; ++i)
223 m_handedOutSlots[i] = m_pinnedSet.
slot[i];
236 out.slot = m_pinnedSet.
slot[member];
237 out.generation = m_pinnedSet.generation;
238 out.stampNs = m_pinnedSet.
stampNs[member];
239 out.fresh = m_pinnedIsNew;
256 if(member >= m_members)
258 return m_returns[member].exchange(0, std::memory_order_acquire);
264 return m_incomplete.load(std::memory_order_relaxed);
271 return m_lapped.load(std::memory_order_relaxed);
277 return m_maxSkewNs.load(std::memory_order_relaxed);
285 return m_stranded.load(std::memory_order_relaxed);
291 static constexpr std::size_t kMaxReturnableSlot = 32;
293 static constexpr std::size_t kRetireMax = 8;
298 int slot[CaptureFrameSet::kMaxMembers]{};
303 bool snapshotPinned() noexcept
305 const auto& e = m_ring[m_pinnedGen %
kDepth];
306 if(e.seq.load(std::memory_order_acquire) != m_pinnedGen)
309 std::atomic_thread_fence(std::memory_order_acquire);
310 return e.seq.load(std::memory_order_acquire) == m_pinnedGen;
321 void releaseSkipped() noexcept
323 for(std::uint64_t gen = m_lastHandedOut + 1; gen < m_pinnedGen; ++gen)
329 if(m_pinnedGen - gen >=
kDepth)
331 m_stranded.fetch_add(1, std::memory_order_relaxed);
335 const auto& e = m_ring[gen %
kDepth];
336 if(e.seq.load(std::memory_order_acquire) != gen)
338 m_stranded.fetch_add(1, std::memory_order_relaxed);
341 const CaptureFrameSet s = e.set;
342 std::atomic_thread_fence(std::memory_order_acquire);
343 if(e.seq.load(std::memory_order_acquire) != gen)
345 m_stranded.fetch_add(1, std::memory_order_relaxed);
349 for(std::size_t m = 0; m < m_members; ++m)
351 const int slot = s.slot[m];
352 if(slot < 0 || slot >=
int(kMaxReturnableSlot))
358 if(slot == m_pinnedSet.
slot[m])
360 m_returns[m].fetch_or(1u <<
unsigned(slot), std::memory_order_release);
369 void queueRetire() noexcept
371 if(m_lastHandedOut == 0)
373 if(m_retireN == kRetireMax)
378 releaseSlotsOf(m_retire[0]);
379 for(std::size_t i = 1; i < m_retireN; ++i)
380 m_retire[i - 1] = m_retire[i];
383 auto& r = m_retire[m_retireN++];
384 r.gen = m_lastHandedOut;
385 r.at = m_acquisitions + 1;
386 for(std::size_t m = 0; m < m_members; ++m)
387 r.slot[m] = m_handedOutSlots[m];
390 void ageRetired() noexcept
393 std::size_t keep = 0;
394 for(std::size_t i = 0; i < m_retireN; ++i)
396 if(m_acquisitions - m_retire[i].at < m_retireDepth)
398 m_retire[keep++] = m_retire[i];
401 releaseSlotsOf(m_retire[i]);
406 void releaseSlotsOf(
const Retired& r)
noexcept
411 for(std::size_t m = 0; m < m_members; ++m)
412 if(r.slot[m] >= 0 && r.slot[m] <
int(kMaxReturnableSlot))
413 m_returns[m].fetch_or(1u <<
unsigned(r.slot[m]), std::memory_order_release);
417 const std::size_t m_members;
419 CaptureRingEntry m_ring[
kDepth]{};
420 std::atomic<std::uint64_t> m_generation{0};
421 std::atomic<std::uint64_t> m_newestComplete{0};
423 std::atomic<std::uint64_t> m_incomplete{0};
424 std::atomic<std::uint64_t> m_lapped{0};
425 std::atomic<std::uint64_t> m_stranded{0};
426 std::atomic<std::uint64_t> m_maxSkewNs{0};
427 std::atomic<std::uint32_t> m_returns[CaptureFrameSet::kMaxMembers]{};
429 std::size_t m_retireDepth{1};
430 std::uint64_t m_acquisitions{0};
431 Retired m_retire[kRetireMax]{};
432 std::size_t m_retireN{0};
435 std::int64_t m_pinnedPass{-1};
436 std::uint64_t m_pinnedGen{0};
437 std::uint64_t m_lastHandedOut{0};
438 bool m_pinnedIsNew{
false};
439 CaptureFrameSet m_pinnedSet{};
440 int m_handedOutSlots[CaptureFrameSet::kMaxMembers]{};
Definition CaptureSyncGroup.hpp:94