SeqAn3 3.2.0-rc.1
The Modern C++ library for sequence analysis.
algorithm_executor_blocking.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
4// This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5// shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6// -----------------------------------------------------------------------------------------------------
7
13#pragma once
14
15#include <functional>
16#include <optional>
17#include <seqan3/std/ranges>
18#include <type_traits>
19
22
23namespace seqan3::detail
24{
25
54template <std::ranges::viewable_range resource_t,
55 std::semiregular algorithm_t,
56 std::semiregular algorithm_result_t,
57 typename execution_handler_t = execution_handler_sequential>
59 requires std::ranges::forward_range<resource_t> &&
60 std::invocable<algorithm_t, std::ranges::range_reference_t<resource_t>,
61 std::function<void(algorithm_result_t)>>
64{
65private:
70 using resource_type = std::views::all_t<resource_t>;
72 using resource_iterator_type = std::ranges::iterator_t<resource_type>;
76
83 using bucket_iterator_type = std::ranges::iterator_t<bucket_type>;
87 using buffer_iterator_type = std::ranges::iterator_t<buffer_type>;
89
92 {
96 };
97
98public:
107
126 algorithm_executor_blocking{std::move(other), other.resource_position()}
127 {}
128
131
135 {
136 auto old_resource_position = other.resource_position();
137
138 resource = std::move(other.resource);
139 move_initialise(std::move(other), old_resource_position);
140 return *this;
141 }
142
145
161 algorithm_t algorithm,
162 algorithm_result_t const SEQAN3_DOXYGEN_ONLY(result) = algorithm_result_t{},
163 execution_handler_t && exec_handler = execution_handler_t{}) :
164 exec_handler{std::move(exec_handler)},
165 resource{std::views::all(resource)},
166 resource_it{std::ranges::begin(this->resource)},
167 algorithm{std::move(algorithm)}
168 {
169 if constexpr (std::same_as<execution_handler_t, execution_handler_parallel>)
170 buffer_size = static_cast<size_t>(std::ranges::distance(this->resource));
171
173 buffer_it = buffer.end();
175 }
177
194 {
195 fill_status status;
196 // Each invocation of the algorithm might produce zero results (e.g. a search might not find a query)
197 // this repeats the algorithm until it produces the first result or the input resource was consumed.
198 do { status = fill_buffer(); } while (status == fill_status::empty_buffer);
199
200 if (status == fill_status::end_of_resource)
201 return {std::nullopt};
202
203 assert(status == fill_status::non_empty_buffer);
204 assert(bucket_it != buffer_it->end());
205
206 std::optional<algorithm_result_t> result = std::ranges::iter_move(bucket_it);
207 go_to_next_result(); // Go to next buffered result.
208 return result;
209 }
210
212 bool is_eof() noexcept
213 {
214 return resource_it == std::ranges::end(resource);
215 }
216
217private:
224 resource_difference_type old_resource_position) noexcept :
225 resource{std::move(other.resource)}
226 {
227 move_initialise(std::move(other), old_resource_position);
228 }
229
237 {
238 // Get the old resource position.
239 auto position = std::ranges::distance(std::ranges::begin(resource), resource_it);
240 assert (position >= 0);
241 return position;
242 }
243
246 {
247 if (!is_buffer_empty()) // Not everything consumed yet.
248 return fill_status::non_empty_buffer;
249
250 if (is_eof()) // Case: reached end of resource.
251 return fill_status::end_of_resource;
252
253 // Reset the buckets and the buffer iterator.
254 reset_buffer();
255
256 // Execute the algorithm (possibly asynchronous) and fill the buckets in this pre-assigned order.
258 {
259 exec_handler.execute(algorithm, *resource_it, [target_buffer_it = buffer_end_it] (auto && algorithm_result)
260 {
261 target_buffer_it->push_back(std::move(algorithm_result));
262 });
263 }
264
265 exec_handler.wait();
266
267 // Move the results iterator to the next available result. (This skips empty results of the algorithm)
269
270 if (is_buffer_empty())
271 return fill_status::empty_buffer;
272
273 return fill_status::non_empty_buffer;
274 }
275
279 bool is_buffer_empty() const
280 {
281 return buffer_it == buffer_end_it;
282 }
283
293 {
294 // Clear all buckets
295 for (auto & bucket : buffer)
296 bucket.clear();
297
298 // Reset the iterator over the buckets.
300 }
301
311 {
312 assert(buffer_it <= buffer_end_it);
313 // find first buffered bucket that contains at least one element
315 {
316 return !buffer.empty();
317 });
318
320 bucket_it = buffer_it->begin();
321 }
322
331 {
332 if (++bucket_it == buffer_it->end())
333 {
334 ++buffer_it;
336 }
337 }
338
340 void move_initialise(algorithm_executor_blocking && other, resource_difference_type old_resource_position) noexcept
341 {
342 algorithm = std::move(other.algorithm);
343 buffer_size = std::move(other.buffer_size);
344 exec_handler = std::move(other.exec_handler);
345 // Move the resource and set the iterator state accordingly.
346 resource_it = std::ranges::next(std::ranges::begin(resource), old_resource_position);
347
348 // Get the old buffer and bucket iterator positions.
349 auto buffer_it_position = other.buffer_it - other.buffer.begin();
350 auto buffer_end_it_position = other.buffer_end_it - other.buffer.begin();
351
352 std::ptrdiff_t bucket_it_position = 0;
353 if (buffer_it_position != buffer_end_it_position)
354 bucket_it_position = other.bucket_it - other.buffer_it->begin();
355
356 // Move the buffer and set the buffer and bucket iterator accordingly.
357 buffer = std::move(other.buffer);
358 buffer_it = buffer.begin() + buffer_it_position;
359 buffer_end_it = buffer.begin() + buffer_end_it_position;
360
361 if (buffer_it_position != buffer_end_it_position)
362 bucket_it = buffer_it->begin() + bucket_it_position;
363 }
364
366 execution_handler_t exec_handler{};
367
369 resource_type resource; // a std::ranges::view
373 algorithm_t algorithm{};
374
384 size_t buffer_size{1};
385};
386
393template <typename resource_rng_t, std::semiregular algorithm_t, std::semiregular algorithm_result_t>
394algorithm_executor_blocking(resource_rng_t &&, algorithm_t, algorithm_result_t const &) ->
397} // namespace seqan3::detail
T begin(T... args)
A blocking algorithm executor for algorithms.
Definition: algorithm_executor_blocking.hpp:64
buffer_type buffer
The buffer storing the algorithm results in buckets.
Definition: algorithm_executor_blocking.hpp:376
algorithm_executor_blocking & operator=(algorithm_executor_blocking &&other)
Move assigns from the resource of another executor.
Definition: algorithm_executor_blocking.hpp:134
void reset_buffer()
Resets the buckets.
Definition: algorithm_executor_blocking.hpp:292
bucket_iterator_type bucket_it
The bucket iterator pointing to the next result within the current bucket.
Definition: algorithm_executor_blocking.hpp:382
fill_status fill_buffer()
Fills the buffer by storing the results of an algorithm invocation into a pre-assigned bucket.
Definition: algorithm_executor_blocking.hpp:245
resource_type resource
The underlying resource.
Definition: algorithm_executor_blocking.hpp:369
algorithm_executor_blocking(resource_rng_t &&, algorithm_t, algorithm_result_t const &) -> algorithm_executor_blocking< resource_rng_t, algorithm_t, algorithm_result_t, execution_handler_sequential >
Deduce the type from the provided arguments and set the sequential execution handler.
void move_initialise(algorithm_executor_blocking &&other, resource_difference_type old_resource_position) noexcept
Helper function to move initialise this from other.
Definition: algorithm_executor_blocking.hpp:340
std::ranges::iterator_t< resource_type > resource_iterator_type
The iterator over the underlying resource.
Definition: algorithm_executor_blocking.hpp:72
buffer_iterator_type buffer_it
The iterator pointing to the current bucket in the buffer.
Definition: algorithm_executor_blocking.hpp:378
std::optional< algorithm_result_t > next_result()
}
Definition: algorithm_executor_blocking.hpp:193
algorithm_executor_blocking()=delete
Deleted default constructor because this class manages an external resource.
std::ranges::iterator_t< buffer_type > buffer_iterator_type
The iterator type of the buffer.
Definition: algorithm_executor_blocking.hpp:87
algorithm_executor_blocking(algorithm_executor_blocking &&other) noexcept
Move constructs the resource of the other executor.
Definition: algorithm_executor_blocking.hpp:125
algorithm_executor_blocking(algorithm_executor_blocking const &)=delete
This class provides unique ownership over the managed resource and is therefor not copyable.
bool is_eof() noexcept
Checks whether the end of the input resource was reached.
Definition: algorithm_executor_blocking.hpp:212
algorithm_executor_blocking(resource_t resource, algorithm_t algorithm, algorithm_result_t const result=algorithm_result_t{}, execution_handler_t &&exec_handler=execution_handler_t{})
Constructs this executor with the given resource range.
Definition: algorithm_executor_blocking.hpp:160
size_t buffer_size
The end get pointer in the buffer.
Definition: algorithm_executor_blocking.hpp:384
execution_handler_t exec_handler
The execution policy.
Definition: algorithm_executor_blocking.hpp:366
void find_next_non_empty_bucket()
Finds the first non-empty bucket starting from the current position of the buffer iterator.
Definition: algorithm_executor_blocking.hpp:310
algorithm_executor_blocking & operator=(algorithm_executor_blocking const &)=delete
This class provides unique ownership over the managed resource and is therefor not copyable.
buffer_iterator_type buffer_end_it
The iterator pointing behind the last bucket (must not be the end of the buffer).
Definition: algorithm_executor_blocking.hpp:380
resource_iterator_type resource_it
The iterator over the resource that stores the current state of the executor.
Definition: algorithm_executor_blocking.hpp:371
resource_difference_type resource_position()
Number of times the resource_it was incremented.
Definition: algorithm_executor_blocking.hpp:236
algorithm_t algorithm
The algorithm to invoke.
Definition: algorithm_executor_blocking.hpp:373
fill_status
Return status for seqan3::detail::algorithm_executor_blocking::fill_buffer.
Definition: algorithm_executor_blocking.hpp:92
@ end_of_resource
The end of the resource was reached.
Definition: algorithm_executor_blocking.hpp:95
@ empty_buffer
The buffer is empty after calling fill_buffer.
Definition: algorithm_executor_blocking.hpp:94
@ non_empty_buffer
The buffer is not fully consumed yet and contains at least one element.
Definition: algorithm_executor_blocking.hpp:93
std::ranges::iterator_t< bucket_type > bucket_iterator_type
The iterator type of a bucket.
Definition: algorithm_executor_blocking.hpp:83
void go_to_next_result()
Moves the bucket iterator to the next available result.
Definition: algorithm_executor_blocking.hpp:330
std::views::all_t< resource_t > resource_type
The underlying resource type.
Definition: algorithm_executor_blocking.hpp:70
algorithm_executor_blocking(algorithm_executor_blocking &&other, resource_difference_type old_resource_position) noexcept
This constructor is needed to ensure initialisation order for the move construction.
Definition: algorithm_executor_blocking.hpp:223
bool is_buffer_empty() const
Whether the internal buffer is empty.
Definition: algorithm_executor_blocking.hpp:279
T empty(T... args)
T end(T... args)
Provides seqan3::detail::execution_handler_parallel.
Provides seqan3::detail::execution_handler_sequential.
T find_if(T... args)
The internal SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:29
The <ranges> header from C++20's standard library.
T resize(T... args)