41template <
typename R,
typename T>
43 std::move_constructible<R> &&
requires(R &r, std::span<T>
buffer) {
44 { r(
buffer) } -> std::same_as<std::optional<std::size_t>>;
52class acquire_accessor {
53 std::function<void()> halt_fn;
57 template <
typename Func>
58 explicit acquire_accessor(Func halt_func) : halt_fn(halt_func) {}
84constexpr auto slow_acq_sleep = std::chrono::milliseconds(10);
86template <
typename T,
typename Reader,
typename Downstream>
87 requires acquisition_reader<Reader, T> && processor<Downstream, bucket<T>>
90 std::shared_ptr<bucket_source<T>> bsource;
93 std::mutex halt_mutex;
94 std::condition_variable halt_cv;
97 Downstream downstream;
100 access_tracker<acquire_accessor> trk;
104 auto const lock = std::lock_guard(halt_mutex);
107 halt_cv.notify_one();
111 explicit acquire(Reader reader,
112 std::shared_ptr<bucket_source<T>> buffer_provider,
113 arg::batch_size<std::size_t> batch_size,
114 access_tracker<acquire_accessor> tracker,
115 Downstream downstream)
116 : reader(std::move(reader)), bsource(std::move(buffer_provider)),
117 bsize(batch_size.value), downstream(std::move(downstream)),
118 trk(std::move(tracker)) {
120 throw std::invalid_argument(
121 "acquire buffer_provider must not be null");
123 throw std::invalid_argument(
"acquire batch size must be positive");
125 trk.register_accessor_factory([](
auto &tracker) {
127 return acquire_accessor([self] { self->halt(); });
133 ~acquire() =
default;
135 acquire(acquire
const &) =
delete;
136 auto operator=(acquire
const &) =
delete;
138 acquire(acquire &&other) noexcept
139 : reader(std::move(other.reader)), bsource(std::move(other.bsource)),
140 bsize(other.bsize), halted(other.halted),
141 downstream(std::move(other.downstream)), trk(std::move(other.trk)) {}
143 auto operator=(acquire &&) =
delete;
145 [[nodiscard]]
auto introspect_node() const -> processor_info {
146 return processor_info(
this,
"acquire");
149 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
150 return downstream.introspect_graph().push_entry_point(
this);
155 bool reached_end =
false;
157 auto lock = std::unique_lock(halt_mutex);
160 auto const start_time = std::chrono::steady_clock::now();
162 b = bsource->bucket_of_size(bsize);
163 std::optional<std::size_t>
const read = reader(std::span(b));
170 downstream.handle(std::move(b));
175 halt_cv.wait_until(lock, start_time + slow_acq_sleep,
176 [&] {
return halted; });
183 throw acquisition_halted();
187template <
typename T,
typename Reader,
typename LiveDownstream,
188 typename BatchDownstream>
189 requires acquisition_reader<Reader, T> &&
190 processor<LiveDownstream, bucket<T const>> &&
191 processor<BatchDownstream, bucket<T>>
192class acquire_full_buckets {
194 std::shared_ptr<bucket_source<T>> bsource;
197 std::mutex halt_mutex;
198 std::condition_variable halt_cv;
201 LiveDownstream live_downstream;
202 BatchDownstream batch_downstream;
205 access_tracker<acquire_accessor> trk;
209 auto const lock = std::lock_guard(halt_mutex);
212 halt_cv.notify_one();
216 void emit_live(bucket<T> &b, std::size_t start, std::size_t
count) {
219 auto v = bsource->shared_view_of(b);
220 v.shrink(start,
count);
221 live_downstream.handle(std::move(v));
222 }
catch (end_of_processing
const &) {
223 b.shrink(0, start +
count);
224 batch_downstream.handle(std::move(b));
225 batch_downstream.flush();
231 void emit_batch(bucket<T> &&b) {
233 batch_downstream.handle(std::move(b));
234 }
catch (end_of_processing
const &) {
235 live_downstream.flush();
240 void flush_downstreams(bucket<T> &&b, std::size_t filled) {
241 std::exception_ptr end;
243 live_downstream.flush();
244 }
catch (end_of_processing
const &) {
245 end = std::current_exception();
247 if (not b.empty() && filled > 0) {
249 batch_downstream.handle(std::move(b));
251 batch_downstream.flush();
253 std::rethrow_exception(end);
257 explicit acquire_full_buckets(
258 Reader reader, std::shared_ptr<bucket_source<T>> buffer_provider,
259 arg::batch_size<std::size_t> batch_size,
260 access_tracker<acquire_accessor> tracker,
261 LiveDownstream live_downstream, BatchDownstream batch_downstream)
262 : reader(std::move(reader)), bsource(std::move(buffer_provider)),
263 bsize(batch_size.value), live_downstream(std::move(live_downstream)),
264 batch_downstream(std::move(batch_downstream)),
265 trk(std::move(tracker)) {
267 throw std::invalid_argument(
268 "acquire_full_buckets buffer_provider must not be null");
269 if constexpr (not std::is_same_v<LiveDownstream, internal::sink_all>) {
270 if (not bsource->supports_shared_views())
271 throw std::invalid_argument(
272 "acquire_full_buckets buffer_provider must support shared views");
275 throw std::invalid_argument(
276 "acquire_full_buckets batch size must be positive");
278 trk.register_accessor_factory([](
auto &tracker) {
281 return acquire_accessor([self] { self->halt(); });
287 ~acquire_full_buckets() =
default;
289 acquire_full_buckets(acquire_full_buckets
const &) =
delete;
290 auto operator=(acquire_full_buckets
const &) =
delete;
292 acquire_full_buckets(acquire_full_buckets &&other) noexcept
293 : reader(std::move(other.reader)), bsource(std::move(other.bsource)),
294 bsize(other.bsize), halted(other.halted),
295 live_downstream(std::move(other.live_downstream)),
296 batch_downstream(std::move(other.batch_downstream)),
297 trk(std::move(other.trk)) {}
299 auto operator=(acquire_full_buckets &&) =
delete;
301 [[nodiscard]]
auto introspect_node() const -> processor_info {
302 return processor_info(
this,
"acquire_full_buckets");
305 [[nodiscard]]
auto introspect_graph() const -> processor_graph {
307 live_downstream.introspect_graph().push_entry_point(
this),
308 batch_downstream.introspect_graph().push_entry_point(
this));
313 std::size_t filled = 0;
315 auto lock = std::unique_lock(halt_mutex);
318 auto const start_time = std::chrono::steady_clock::now();
320 b = bsource->bucket_of_size(bsize);
323 auto const unfilled = std::span(b).subspan(filled);
324 std::optional<std::size_t>
const read = reader(unfilled);
326 return flush_downstreams(std::move(b), filled);
327 if constexpr (not std::is_same_v<LiveDownstream,
329 emit_live(b, filled, *read);
331 if (filled == bsize) {
332 emit_batch(std::move(b));
336 if (filled < bsize) {
337 halt_cv.wait_until(lock, start_time + slow_acq_sleep,
338 [&] {
return halted; });
342 throw acquisition_halted();
386template <
typename T,
typename Reader,
typename Downstream>
390 return internal::acquire<T, Reader, Downstream>(
391 std::move(reader), std::move(buffer_provider), batch_size,
392 std::move(tracker), std::move(downstream));
451template <
typename T,
typename Reader,
typename LiveDownstream,
452 typename BatchDownstream>
457 LiveDownstream live_downstream,
458 BatchDownstream batch_downstream) {
459 return internal::acquire_full_buckets<T, Reader, LiveDownstream,
461 std::move(reader), std::move(buffer_provider), batch_size,
462 std::move(tracker), std::move(live_downstream),
463 std::move(batch_downstream));
473 auto operator()(std::span<T> ) -> std::optional<std::size_t> {
485 auto operator()(std::span<T> ) -> std::optional<std::size_t> {
#define LIBTCSPC_OBJECT_FROM_TRACKER(obj_type, tracker_field_name, tracker)
Recover the object address from a tcspc::access_tracker embedded in the object.
Definition context.hpp:255