/* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #ifndef STREAM_HH_ #define STREAM_HH_ #include "future.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_ */