| 806 | typename Control = typename detail::result_of_t<Iterate()>::ValueType, |
| 807 | typename BreakValueType = typename Control::value_type> |
| 808 | Future<BreakValueType> Loop(Iterate iterate) { |
| 809 | struct Callback { |
| 810 | bool CheckForTermination(const Result<Control>& control_res) { |
| 811 | if (!control_res.ok()) { |
| 812 | break_fut.MarkFinished(control_res.status()); |
| 813 | return true; |
| 814 | } |
| 815 | if (control_res->has_value()) { |
| 816 | break_fut.MarkFinished(**control_res); |
| 817 | return true; |
| 818 | } |
| 819 | return false; |
| 820 | } |
| 821 | |
| 822 | void operator()(const Result<Control>& maybe_control) && { |
| 823 | if (CheckForTermination(maybe_control)) return; |
| 824 | |
| 825 | auto control_fut = iterate(); |
| 826 | while (true) { |
| 827 | if (control_fut.TryAddCallback([this]() { return *this; })) { |
| 828 | // Adding a callback succeeded; control_fut was not finished |
| 829 | // and we must wait to CheckForTermination. |
| 830 | return; |
| 831 | } |
| 832 | // Adding a callback failed; control_fut was finished and we |
| 833 | // can CheckForTermination immediately. This also avoids recursion and potential |
| 834 | // stack overflow. |
| 835 | if (CheckForTermination(control_fut.result())) return; |
| 836 | |
| 837 | control_fut = iterate(); |
| 838 | } |
| 839 | } |
| 840 | |
| 841 | Iterate iterate; |
| 842 | |
| 843 | // If the future returned by control_fut is never completed then we will be hanging on |
| 844 | // to break_fut forever even if the listener has given up listening on it. Instead we |
| 845 | // rely on the fact that a producer (the caller of Future<>::Make) is always |
| 846 | // responsible for completing the futures they create. |
| 847 | // TODO: Could avoid this kind of situation with "future abandonment" similar to mesos |
| 848 | Future<BreakValueType> break_fut; |
| 849 | }; |
| 850 | |
| 851 | auto break_fut = Future<BreakValueType>::Make(); |
| 852 | auto control_fut = iterate(); |
| 853 | control_fut.AddCallback(Callback{std::move(iterate), break_fut}); |
| 854 | |
| 855 | return break_fut; |
| 856 | } |
| 857 | |
| 858 | inline Future<> ToFuture(Status status) { |
| 859 | return Future<>::MakeFinished(std::move(status)); |