/* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef STREAM_HH_ #define STREAM_HH_ #include #include // A stream/subscription pair is similar to a promise/future pair, // but apply to a sequence of values instead of a single value. // // A stream<> is the producer side. It may call produce() as long // as the future<> returned from the previous invocation is ready. // To signify no more data is available, call close(). // // A subscription<> is the consumer side. It is created by a call // to stream::listen(), which also registers the data processing // callback. It may register for end-of-stream notifications by // chaining the when_done() future, which also delivers error // events (as exceptions). // // The consumer can pause generation of new data by returning // a non-ready future; when the future becomes ready, the producer // will resume processing. template class stream; template class subscription; template class stream { subscription* _sub = nullptr; promise<> _done; promise<> _ready; public: using next_fn = std::function (T...)>; stream() = default; stream(const stream&) = delete; stream(stream&&) = delete; ~stream(); void operator=(const stream&) = delete; void operator=(stream&&) = delete; // Returns a subscription that reads value from this // stream. Each data element will be processed by the // @next function, which returns a future<> to indicate // when it is ready to process more data. subscription listen(next_fn next); // Becomes ready when the listener is ready to accept // values. Call only once, when beginning to produce // values. future<> started(); // Produce a value. Call only after started(), and after // a previous produce() is ready. future<> produce(T... data); // End the stream. Call only after started(), and after // a previous produce() is ready. No functions may be called // after this. void close(); // Signal an error. Call only after started(), and after // a previous produce() is ready. No functions may be called // after this. template void set_exception(E ex); private: void pause(future<> can_continue); void start(); friend class subscription; }; template class subscription { using next_fn = typename stream::next_fn; stream* _stream; next_fn _next; private: explicit subscription(stream* s, next_fn next); public: subscription(subscription&& x); ~subscription(); // Becomes ready when the stream is empty, or when an error // happens (in that case, an exception is held). future<> done(); friend class stream; }; template inline stream::~stream() { if (_sub) { _sub->_stream = nullptr; } } template inline subscription stream::listen(next_fn next) { return subscription(this, std::move(next)); } template inline future<> stream::started() { return _ready.get_future(); } template inline future<> stream::produce(T... data) { try { return _sub->_next(std::move(data)...); } catch (...) { _done.set_exception(std::current_exception()); // FIXME: tell the producer to stop producing abort(); } } template inline void stream::close() { _done.set_value(); } template template inline void stream::set_exception(E ex) { _sub->_done.set_exception(ex); } template inline subscription::subscription(stream* s, next_fn next) : _stream(s), _next(std::move(next)) { assert(!_stream->_sub); _stream->_sub = this; _stream->_ready.set_value(); } template inline subscription::~subscription() { if (_stream) { _stream->_sub = nullptr; } } template inline subscription::subscription(subscription&& x) : _stream(x._stream), _next(std::move(x._next)) { x._stream = nullptr; if (_stream) { _stream->_sub = this; } } template inline future<> subscription::done() { return _stream->_done.get_future(); } #endif /* STREAM_HH_ */