1616// under the License.
1717
1818#pragma once
19+
20+ #include < cassert>
21+ #include < deque>
1922#include < queue>
2023
2124#include " arrow/util/functional.h"
2225#include " arrow/util/future.h"
2326#include " arrow/util/iterator.h"
2427#include " arrow/util/logging.h"
28+ #include " arrow/util/mutex.h"
2529#include " arrow/util/optional.h"
2630#include " arrow/util/queue.h"
2731#include " arrow/util/thread_pool.h"
@@ -36,6 +40,11 @@ Future<T> AsyncGeneratorEnd() {
3640 return Future<T>::MakeFinished (IterationTraits<T>::End ());
3741}
3842
43+ template <typename T>
44+ bool IsGeneratorEnd (const T& value) {
45+ return value == IterationTraits<T>::End ();
46+ }
47+
3948// / Iterates through a generator of futures, visiting the result of each one and
4049// / returning a future that completes when all have been visited
4150template <typename T>
@@ -336,6 +345,103 @@ class ReadaheadGenerator {
336345 std::queue<Future<T>> readahead_queue_;
337346};
338347
348+ // / \brief A generator where the producer pushes items on a queue.
349+ // /
350+ // / No back-pressure is applied, so this generator is mostly useful when
351+ // / producing the values is neither CPU- nor memory-expensive (e.g. fetching
352+ // / filesystem metadata).
353+ // /
354+ // / This generator is not async-reentrant.
355+ template <typename T>
356+ class PushGenerator {
357+ struct State {
358+ util::Mutex mutex;
359+ std::deque<Result<T>> result_q;
360+ util::optional<Future<T>> consumer_fut;
361+ bool finished = false ;
362+ };
363+
364+ public:
365+ // / Producer API for PushGenerator
366+ class Producer {
367+ public:
368+ explicit Producer (std::shared_ptr<State> state) : state_(std::move(state)) {}
369+
370+ // / Push a value on the queue
371+ void Push (Result<T> result) {
372+ auto lock = state_->mutex .Lock ();
373+ if (state_->finished ) {
374+ // Closed early
375+ return ;
376+ }
377+ if (state_->consumer_fut .has_value ()) {
378+ auto fut = std::move (state_->consumer_fut .value ());
379+ state_->consumer_fut .reset ();
380+ lock.Unlock (); // unlock before potentially invoking a callback
381+ fut.MarkFinished (std::move (result));
382+ return ;
383+ }
384+ state_->result_q .push_back (std::move (result));
385+ }
386+
387+ // / \brief Tell the consumer we have finished producing
388+ // /
389+ // / It is allowed to call this and later call Push() again ("early close").
390+ // / In this case, calls to Push() after the queue is closed are silently
391+ // / ignored. This can help implementing non-trivial cancellation cases.
392+ void Close () {
393+ auto lock = state_->mutex .Lock ();
394+ if (state_->finished ) {
395+ // Already closed
396+ return ;
397+ }
398+ state_->finished = true ;
399+ if (state_->consumer_fut .has_value ()) {
400+ auto fut = std::move (state_->consumer_fut .value ());
401+ state_->consumer_fut .reset ();
402+ lock.Unlock (); // unlock before potentially invoking a callback
403+ fut.MarkFinished (IterationTraits<T>::End ());
404+ }
405+ }
406+
407+ bool is_closed () const {
408+ auto lock = state_->mutex .Lock ();
409+ return state_->finished ;
410+ }
411+
412+ private:
413+ const std::shared_ptr<State> state_;
414+ };
415+
416+ PushGenerator () : state_(std::make_shared<State>()) {}
417+
418+ // / Read an item from the queue
419+ Future<T> operator ()() {
420+ auto lock = state_->mutex .Lock ();
421+ assert (!state_->consumer_fut .has_value ()); // Non-reentrant
422+ if (!state_->result_q .empty ()) {
423+ auto fut = Future<T>::MakeFinished (std::move (state_->result_q .front ()));
424+ state_->result_q .pop_front ();
425+ return fut;
426+ }
427+ if (state_->finished ) {
428+ return AsyncGeneratorEnd<T>();
429+ }
430+ auto fut = Future<T>::Make ();
431+ state_->consumer_fut = fut;
432+ return fut;
433+ }
434+
435+ // / \brief Return producer-side interface
436+ // /
437+ // / The returned object must be used by the producer to push values on the queue.
438+ // / Only a single Producer object should be instantiated.
439+ Producer producer () { return Producer{state_}; }
440+
441+ private:
442+ const std::shared_ptr<State> state_;
443+ };
444+
339445// / \brief Creates a generator that pulls reentrantly from a source
340446// / This generator will pull reentrantly from a source, ensuring that max_readahead
341447// / requests are active at any given time.
0 commit comments