74template<std::size_t JobMailboxCapacity = 8,
75 std::size_t CompletionMailboxCapacity = 8,
76 typename DispatchCutpoint = SampleStreamAsyncNoopDispatchCutpoint>
78 struct ReservationRecord;
94 if (config.cache.scheduler_capacity == 0 ||
95 config.decode.worker_count == 0 ||
96 config.decode.source_capacity == 0 ||
97 config.decode.maximum_channels == 0 ||
98 config.decode.maximum_frames_per_job == 0 ||
99 config.decode.maximum_outstanding_jobs_per_source == 0 ||
100 config.decode.source_capacity >
101 std::numeric_limits<std::size_t>::max() /
102 config.decode.maximum_outstanding_jobs_per_source) {
106 const auto record_capacity =
107 static_cast<std::size_t
>(config.decode.source_capacity) *
108 config.decode.maximum_outstanding_jobs_per_source;
109 auto cache_config = config.cache;
110 cache_config.maximum_async_reservations_per_source =
111 config.decode.maximum_outstanding_jobs_per_source;
112 if (!cache_.prepare(cache_config)) {
116 if (!decode_.prepare(config.decode)) {
121 records_.resize(record_capacity);
122 retirements_.reserve(config.decode.source_capacity);
129 active_record_count_ = 0;
135 for (
auto& record : records_) {
137 (void) cache_.cancel_async_reservation(record.reservation);
138 clear_record(record);
143 retirements_.clear();
145 active_record_count_ = 0;
149 bool prepared() const noexcept {
return prepared_; }
154 if (!reader)
return {};
158 .read = [reader = std::move(reader)](
161 std::uint64_t frames,
163 return reader(start, destination, frames);
172 if (!prepared_ || !binding.
read)
return {};
174 [read = binding.
read](std::uint64_t start,
176 std::uint64_t frames) {
179 if (!decode_.add_source(config.token,
181 std::move(binding)).added()) {
184 const auto added = cache_.add_source(config, std::move(synchronous_reader));
185 if (!added.added()) {
186 (void) decode_.cancel_source(config.token);
187 (void) decode_.remove_idle_source(config.token);
195 return cache_.request_page(demand);
200 return cache_.cancel_pending_source_demands(source);
204 return cache_.cancel_requester(requester);
207 template<std::
size_t Capacity>
210 return cache_.drain_commands(inbox);
214 std::uint64_t completed)
noexcept {
215 return cache_.update_audio_generations(active, completed);
219 return cache_.begin_audio_page_read();
223 cache_.complete_audio_page_read(epoch);
228 const auto status = cache_.retire_source_after_asset_unpublish(source);
230 std::none_of(retirements_.begin(),
233 return candidate.source_id == source.source_id &&
234 candidate.source_generation ==
235 source.source_generation;
237 retirements_.push_back(source);
244 if (!prepared_ || !cache_.discard_unpublished_source(source))
246 return decode_.remove_idle_source(source);
250 for (
const auto source : retirements_) {
251 if (!cache_.retirement_watermark_reached(source))
continue;
252 (void) decode_.cancel_source(source);
253 (void) cache_.cancel_pending_source_demands(source);
254 for (
auto& record : records_) {
255 if (record.active && !record.submitted &&
256 record.reservation.source.source_id == source.source_id &&
257 record.reservation.source.source_generation ==
258 source.source_generation) {
259 (void) cache_.cancel_async_reservation(record.reservation);
260 clear_record(record);
263 (void) decode_.remove_idle_source(source);
266 const auto collected = cache_.collect_retired_sources();
268 return !cache_.contains_source(source);
274 if (!prepared_)
return {};
275 std::optional<SampleStreamAsyncDispatchResult> queue_full;
276 for (
auto& record : records_) {
277 if (!record.active || record.submitted)
continue;
282 if (has_earlier_unsubmitted_same_source(record))
continue;
283 const auto retried = submit_record(record);
287 queue_full = retried;
293 DispatchCutpoint::after_queue_full_before_reserve();
294 const auto reserved = cache_.reserve_async_page();
296 if (queue_full)
return *queue_full;
303 auto* record = free_record();
304 if (record ==
nullptr) {
305 (void) cache_.cancel_async_reservation(reserved.reservation);
310 record->active =
true;
311 record->submitted =
false;
312 record->reservation = reserved.reservation;
313 ++active_record_count_;
314 telemetry_.active_reservations_high_water = std::max<std::uint64_t>(
315 telemetry_.active_reservations_high_water, active_record_count_);
316 if (has_earlier_unsubmitted_same_source(*record) && queue_full)
318 return submit_record(*record);
323 ReservationRecord& record)
noexcept {
324 const auto& reservation = record.reservation;
327 clear_record(record);
332 const auto submitted = decode_.submit({
333 .source = reservation.source,
334 .reservation_serial = reservation.reservation_serial,
335 .start_frame = reservation.start_frame,
336 .frame_count = reservation.frame_count,
340 (void) cache_.cancel_async_reservation(reservation);
341 clear_record(record);
347 record.submitted =
true;
348 if (!cache_.commit_async_dispatch(reservation)) {
351 (void) decode_.cancel_source(reservation.source);
356 ++telemetry_.dispatches;
365 std::uint32_t worker_index)
noexcept {
366 return process_completion(decode_.try_pop_completion(worker_index));
370 std::uint32_t worker_index)
noexcept {
371 return process_completion(decode_.wait_pop_completion(worker_index));
375 if (!prepared_)
return;
376 bool made_progress =
false;
378 made_progress =
false;
379 for (std::uint32_t worker = 0; worker < decode_.worker_count(); ++worker) {
382 made_progress =
true;
385 }
while (made_progress);
391 return decode_.telemetry();
396 struct ReservationRecord {
399 bool submitted =
false;
402 ReservationRecord* free_record() noexcept {
403 for (
auto& record : records_) {
404 if (!record.active)
return &record;
409 void clear_record(ReservationRecord& record)
noexcept {
410 if (record.active && active_record_count_ != 0) --active_record_count_;
414 bool has_earlier_unsubmitted_same_source(
415 const ReservationRecord& candidate)
const noexcept {
417 records_.begin(), records_.end(),
418 [&candidate](
const ReservationRecord& record)
noexcept {
419 return record.active && !record.submitted &&
420 record.reservation.source.source_id ==
421 candidate.reservation.source.source_id &&
422 record.reservation.source.source_generation ==
423 candidate.reservation.source.source_generation &&
424 record.reservation.reservation_serial <
425 candidate.reservation.reservation_serial;
429 ReservationRecord* find_record(
430 const SampleStreamDecodeCompletion& completion)
noexcept {
431 for (
auto& record : records_) {
433 record.reservation.source.source_id == completion.source.source_id &&
434 record.reservation.source.source_generation ==
435 completion.source.source_generation &&
436 record.reservation.reservation_serial ==
437 completion.reservation_serial) {
445 std::optional<SampleStreamDecodeCompletionView> completion)
noexcept {
447 auto* record = find_record(completion->completion);
450 if (record !=
nullptr &&
452 completion->completion,
453 record->reservation.source,
454 record->reservation.reservation_serial) ==
456 completed = cache_.publish_async_reservation(record->reservation,
458 }
else if (record !=
nullptr) {
459 completed = cache_.cancel_async_reservation(record->reservation);
461 (void) decode_.release_completion(completion->completion);
462 if (record !=
nullptr) clear_record(*record);
465 ++telemetry_.completions_published;
470 ++telemetry_.completion_copy_failures;
473 ++telemetry_.completions_discarded;
477 SampleStreamCacheService cache_;
478 SampleStreamDecodePool<JobMailboxCapacity,
479 CompletionMailboxCapacity> decode_;
480 std::vector<ReservationRecord> records_;
481 std::vector<SampleStreamSourceToken> retirements_;
482 SampleStreamAsyncServiceTelemetry telemetry_{};
483 std::size_t active_record_count_ = 0;
484 bool prepared_ =
false;
Non-owning view over multi-channel audio sample data.
Definition buffer.hpp:30
Cooperative cancellation signal passed to a streaming frame reader.
Definition streaming_sample_source.hpp:45
SampleStreamAsyncPrepareResult prepare_checked(const SampleStreamAsyncServiceConfig &config)
Definition sample_stream_async_service.hpp:91
void release() noexcept
Definition sample_stream_async_service.hpp:133
SampleStreamScheduleStatus request_page(const SampleStreamPageDemand &demand) noexcept
Definition sample_stream_async_service.hpp:193
std::uint64_t begin_audio_page_read() const noexcept
Definition sample_stream_async_service.hpp:218
SampleStreamAsyncService & operator=(const SampleStreamAsyncService &)=delete
std::size_t cancel_pending_source_demands(SampleStreamSourceToken source) noexcept
Definition sample_stream_async_service.hpp:198
SampleStreamCacheServiceStats cache_stats() const noexcept
Definition sample_stream_async_service.hpp:389
std::size_t collect_retired_sources() noexcept
Definition sample_stream_async_service.hpp:249
SampleStreamAsyncDispatchResult dispatch_once() noexcept
Definition sample_stream_async_service.hpp:273
bool prepared() const noexcept
Definition sample_stream_async_service.hpp:149
SampleStreamDecodePoolTelemetry decode_telemetry() const noexcept
Definition sample_stream_async_service.hpp:390
SampleStreamAsyncServiceTelemetry telemetry() const noexcept
Definition sample_stream_async_service.hpp:393
bool update_audio_generations(std::uint64_t active, std::uint64_t completed) noexcept
Definition sample_stream_async_service.hpp:213
~SampleStreamAsyncService()
Definition sample_stream_async_service.hpp:82
SampleStreamSourceAddResult add_source(const SampleStreamCacheSourceConfig &config, FrameReaderBinding binding)
Definition sample_stream_async_service.hpp:169
SampleStreamAsyncService(const SampleStreamAsyncService &)=delete
SampleStreamAsyncService()=default
bool prepare(const SampleStreamAsyncServiceConfig &config)
Definition sample_stream_async_service.hpp:87
bool discard_unpublished_source(SampleStreamSourceToken source) noexcept
Definition sample_stream_async_service.hpp:242
void complete_audio_page_read(std::uint64_t epoch) noexcept
Definition sample_stream_async_service.hpp:222
void drain_completions() noexcept
Definition sample_stream_async_service.hpp:374
SampleStreamAsyncPollStatus try_process_completion(std::uint32_t worker_index) noexcept
Definition sample_stream_async_service.hpp:364
SampleStreamSourceAddResult add_source(const SampleStreamCacheSourceConfig &config, FrameReader reader)
Definition sample_stream_async_service.hpp:151
SampleStreamAsyncPollStatus wait_process_completion(std::uint32_t worker_index) noexcept
Definition sample_stream_async_service.hpp:369
std::size_t cancel_requester(SampleStreamRequesterToken requester) noexcept
Definition sample_stream_async_service.hpp:203
SampleStreamCommandDrainResult drain_commands(SampleStreamCommandInbox< Capacity > &inbox) noexcept
Definition sample_stream_async_service.hpp:208
const SampleStreamCacheService & cache_service() const noexcept
Definition sample_stream_async_service.hpp:388
SampleStreamSourceRetireStatus retire_source_after_asset_unpublish(SampleStreamSourceToken source) noexcept
Definition sample_stream_async_service.hpp:226
Bounded shared-page cache with deterministic caller-driven servicing.
Definition sample_stream_service.hpp:326
SampleStreamAsyncCompletionStatus cancel_async_reservation(const SampleStreamAsyncReservation &reservation) noexcept
bool async_reservation_has_interest(const SampleStreamAsyncReservation &reservation) const noexcept
Prepared bounded command port from one RT producer to the service owner.
Definition sample_stream_service.hpp:91
Definition analyzer_provider.hpp:12
@ Published
Definition sample_slot_bank.hpp:18
SampleStreamDecodeSubmitStatus
Definition sample_stream_decode_pool.hpp:111
@ SourceInFlight
Definition sample_stream_decode_pool.hpp:117
@ Queued
Definition sample_stream_decode_pool.hpp:112
@ QueueFull
Definition sample_stream_decode_pool.hpp:118
@ InvalidJob
Definition sample_stream_decode_pool.hpp:113
SampleStreamDecodeCompletionMatch match_sample_stream_decode_completion(const SampleStreamDecodeCompletion &completion, SampleStreamSourceToken expected_source, std::uint64_t expected_reservation_serial) noexcept
Definition sample_stream_decode_pool.hpp:74
SampleStreamAsyncPrepareStatus
Definition sample_stream_async_service.hpp:20
@ CachePrepareFailed
Definition sample_stream_async_service.hpp:23
@ Ok
Definition sample_stream_async_service.hpp:21
@ DecodePrepareFailed
Definition sample_stream_async_service.hpp:24
@ InvalidConfiguration
Definition sample_stream_async_service.hpp:22
@ MetadataAllocationFailed
Definition sample_stream_async_service.hpp:25
std::function< std::uint64_t(std::uint64_t start_frame, BufferView< float > dest, std::uint64_t frames)> FrameReader
Caller-supplied random-access frame reader.
Definition streaming_sample_source.hpp:73
@ None
Definition finite_time_stretch.hpp:26
SampleStreamScheduleStatus
Definition sample_stream_scheduler.hpp:35
@ Accepted
Definition sample_stream_decode_pool.hpp:65
@ Idle
Definition instrument_envelope.hpp:9
SampleStreamAsyncCompletionStatus
Definition sample_stream_service.hpp:289
@ CopyFailed
Definition sample_stream_service.hpp:295
@ StaleReservation
Definition sample_stream_service.hpp:294
@ PublishFailed
Definition sample_stream_service.hpp:296
@ Published
Definition sample_stream_service.hpp:290
SampleStreamAsyncDispatchStatus
Definition sample_stream_async_service.hpp:36
@ PoolRejected
Definition sample_stream_async_service.hpp:40
@ Deferred
Definition sample_stream_async_service.hpp:39
@ Queued
Definition sample_stream_async_service.hpp:37
@ Idle
Definition sample_stream_async_service.hpp:38
@ InvalidState
Definition sample_stream_async_service.hpp:41
SampleStreamAsyncPollStatus
Definition sample_stream_async_service.hpp:53
@ CopyFailed
Definition sample_stream_async_service.hpp:57
@ Discarded
Definition sample_stream_async_service.hpp:56
@ None
Definition sample_stream_async_service.hpp:54
@ Published
Definition sample_stream_async_service.hpp:55
@ Ok
Definition loop_types.hpp:34
SampleStreamSourceRetireStatus
Definition sample_stream_service.hpp:234
@ Scheduled
Definition sample_stream_service.hpp:235
@ JoinOnly
Definition streaming_sample_source.hpp:79
SampleStreamAsyncReserveStatus
Definition sample_stream_service.hpp:260
@ Reserved
Definition sample_stream_service.hpp:261
@ Idle
Definition sample_stream_service.hpp:262
@ InvalidConfiguration
Definition sample_heritage_live_cyclic.hpp:24
Definition streaming_sample_source.hpp:89
StoppableFrameReader read
Definition streaming_sample_source.hpp:90
Definition sample_stream_async_service.hpp:44
SampleStreamDecodeSubmitStatus submit_status
Definition sample_stream_async_service.hpp:49
SampleStreamAsyncDispatchStatus status
Definition sample_stream_async_service.hpp:45
SampleStreamAsyncReserveStatus reserve_status
Definition sample_stream_async_service.hpp:47
Definition sample_stream_async_service.hpp:68
static void after_queue_full_before_reserve() noexcept
Definition sample_stream_async_service.hpp:69
Definition sample_stream_async_service.hpp:28
SampleStreamAsyncPrepareStatus status
Definition sample_stream_async_service.hpp:29
constexpr bool prepared() const noexcept
Definition sample_stream_async_service.hpp:31
Definition sample_stream_service.hpp:272
Definition sample_stream_async_service.hpp:15
SampleStreamDecodePoolConfig decode
Definition sample_stream_async_service.hpp:17
SampleStreamCacheServiceConfig cache
Definition sample_stream_async_service.hpp:16
Definition sample_stream_async_service.hpp:60
std::uint64_t active_reservations_high_water
Definition sample_stream_async_service.hpp:65
std::uint64_t completions_published
Definition sample_stream_async_service.hpp:62
std::uint64_t dispatches
Definition sample_stream_async_service.hpp:61
std::uint64_t completion_copy_failures
Definition sample_stream_async_service.hpp:64
std::uint64_t completions_discarded
Definition sample_stream_async_service.hpp:63
Definition sample_stream_service.hpp:28
Definition sample_stream_service.hpp:299
Definition sample_stream_service.hpp:44
Definition sample_stream_service.hpp:143
Definition sample_stream_decode_pool.hpp:22
Definition sample_stream_decode_pool.hpp:130
Definition sample_stream_service.hpp:52
Definition sample_stream_service.hpp:23
Definition sample_stream_service.hpp:241
Definition sample_stream_service.hpp:18