promise.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. //
  2. // experimental/promise.hpp
  3. // ~~~~~~~~~~~~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2021-2022 Klemens D. Morgenstern
  6. // (klemens dot morgenstern at gmx dot net)
  7. //
  8. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  9. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  10. //
  11. #ifndef BOOST_ASIO_EXPERIMENTAL_PROMISE_HPP
  12. #define BOOST_ASIO_EXPERIMENTAL_PROMISE_HPP
  13. #if defined(_MSC_VER) && (_MSC_VER >= 1200)
  14. # pragma once
  15. #endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
  16. #include <boost/asio/detail/config.hpp>
  17. #include <boost/asio/detail/type_traits.hpp>
  18. #include <boost/asio/any_io_executor.hpp>
  19. #include <boost/asio/associated_cancellation_slot.hpp>
  20. #include <boost/asio/bind_executor.hpp>
  21. #include <boost/asio/cancellation_signal.hpp>
  22. #include <boost/asio/experimental/detail/completion_handler_erasure.hpp>
  23. #include <boost/asio/experimental/impl/promise.hpp>
  24. #include <boost/asio/post.hpp>
  25. #include <algorithm>
  26. #include <variant>
  27. #include <boost/asio/detail/push_options.hpp>
  28. namespace boost {
  29. namespace asio {
  30. namespace experimental {
  31. template <typename Executor = any_io_executor>
  32. struct use_promise_t {};
  33. constexpr use_promise_t<> use_promise;
  34. template <typename T>
  35. struct is_promise : std::false_type {};
  36. template <typename ... Ts>
  37. struct is_promise<promise<Ts...>> : std::true_type {};
  38. template <typename T>
  39. constexpr bool is_promise_v = is_promise<T>::value;
  40. template <typename T>
  41. concept is_promise_c = is_promise_v<std::remove_reference_t<T>>;
  42. template <typename ... Ts>
  43. struct promise_value_type
  44. {
  45. using type = std::tuple<Ts...>;
  46. };
  47. template <typename T>
  48. struct promise_value_type<T>
  49. {
  50. using type = T;
  51. };
  52. template <>
  53. struct promise_value_type<>
  54. {
  55. using type = std::monostate;
  56. };
  57. #if defined(GENERATING_DOCUMENTATION)
  58. /// The primary template is not defined.
  59. template<typename Signature = void(), typename Executor = any_io_executor>
  60. struct promise
  61. {
  62. };
  63. #endif // defined(GENERATING_DOCUMENTATION)
  64. template <typename ... Ts, typename Executor>
  65. struct promise<void(Ts...), Executor>
  66. {
  67. using value_type = typename promise_value_type<Ts...>::type;
  68. using tuple_type = std::tuple<Ts...>;
  69. using executor_type = Executor;
  70. executor_type get_executor() const
  71. {
  72. if (impl_)
  73. return impl_->executor;
  74. else
  75. return {};
  76. }
  77. void cancel(cancellation_type level = cancellation_type::all)
  78. {
  79. if (impl_ && !impl_->done)
  80. {
  81. boost::asio::dispatch(impl_->executor,
  82. [level, impl = impl_]{impl->cancel.emit(level);});
  83. }
  84. }
  85. bool complete() const noexcept
  86. {
  87. return impl_ && impl_->done;
  88. }
  89. template <typename CompletionToken>
  90. auto async_wait(CompletionToken&& token)
  91. {
  92. assert(impl_);
  93. return async_initiate<CompletionToken, void(Ts...)>(
  94. initiate_async_wait{impl_}, token);
  95. }
  96. promise() = delete;
  97. promise(const promise& ) = delete;
  98. promise(promise&& ) noexcept = default;
  99. ~promise() { cancel(); }
  100. template <execution::executor Executor1, is_promise_c ... Ps>
  101. static auto race(Executor1 exec, Ps ... ps)
  102. -> promise<void(std::variant<typename Ps::value_type...>), Executor1>
  103. {
  104. using var_t = std::variant<typename Ps::value_type...>;
  105. using pi = detail::promise_impl<void(var_t), Executor1>;
  106. struct impl_t : pi
  107. {
  108. impl_t(Executor1 exec, Ps&& ... ps)
  109. : pi(std::move(exec)),
  110. tup(std::move(ps)...)
  111. {
  112. this->slot.template emplace<cancel_handler>(this);
  113. }
  114. struct cancel_handler
  115. {
  116. impl_t* self;
  117. cancel_handler(impl_t* self)
  118. : self(self)
  119. {
  120. }
  121. void operator()(cancellation_type ct)
  122. {
  123. [ct, s=self]<std::size_t... Idx>(std::index_sequence<Idx...>)
  124. {
  125. (std::get<Idx>(s->tup).cancel(ct), ... );
  126. }(std::make_index_sequence<sizeof...(Ps)>{});
  127. }
  128. };
  129. std::tuple<std::remove_reference_t<Ps>...> tup;
  130. cancellation_slot slot{this->cancel.slot()};
  131. };
  132. auto impl = std::allocate_shared<impl_t>(
  133. get_associated_allocator(exec), exec, std::move(ps)...);
  134. impl->executor = exec;
  135. [impl, exec]<std::size_t... Idx>(std::index_sequence<Idx...>)
  136. {
  137. auto step =
  138. [&]<std::size_t I>(std::integral_constant<std::size_t, I>)
  139. {
  140. return [impl]<typename... Args > (Args&& ... args)
  141. {
  142. if (impl->done)
  143. return;
  144. impl->result = var_t(std::in_place_index<I>,
  145. std::forward<Args>(args)...);
  146. impl->done = true;
  147. if (auto f = std::exchange(impl->completion, nullptr); !!f)
  148. std::apply(std::move(f), std::move(*impl->result));
  149. auto cancel =
  150. [&]<std::size_t Id>(std::integral_constant<std::size_t, Id>)
  151. {
  152. if constexpr (I != Id)
  153. get<I>(impl->tup).cancel();
  154. };
  155. (cancel(std::integral_constant<std::size_t, Idx>{}), ...);
  156. };
  157. };
  158. (
  159. std::get<Idx>(impl->tup).async_wait(
  160. bind_executor(exec,
  161. step(std::integral_constant<std::size_t, Idx>{}))),
  162. ...
  163. );
  164. }(std::make_index_sequence<sizeof...(Ps)>{});
  165. return {impl};
  166. }
  167. template <execution::executor Executor1, is_promise_c ... Ps>
  168. static auto all(Executor1 exec, Ps ... ps)
  169. -> promise<void(typename Ps::value_type...), Executor1>
  170. {
  171. using pi = detail::promise_impl<
  172. void(typename Ps::value_type...), Executor1>;
  173. struct impl_t : pi
  174. {
  175. impl_t(Executor1 exec, Ps&& ... ps)
  176. : pi(std::move(exec)),
  177. tup(std::move(ps)...)
  178. {
  179. this->slot.template emplace<cancel_handler>(this);
  180. }
  181. struct cancel_handler
  182. {
  183. impl_t* self;
  184. cancel_handler(impl_t* self)
  185. : self(self)
  186. {
  187. }
  188. void operator()(cancellation_type level)
  189. {
  190. [level, s=self]<std::size_t... Idx>(std::index_sequence<Idx...>)
  191. {
  192. (std::get<Idx>(s->tup).cancel(level), ... );
  193. }(std::make_index_sequence<sizeof...(Ps)>{});
  194. }
  195. };
  196. std::tuple<std::remove_reference_t<Ps>...> tup;
  197. std::tuple<std::optional<typename Ps::value_type>...> partial_result;
  198. cancellation_slot slot{this->cancel.slot()};
  199. };
  200. auto impl = std::allocate_shared<impl_t>(
  201. get_associated_allocator(exec), exec, std::move(ps)...);
  202. impl->executor = exec;
  203. [impl, exec]<std::size_t... Idx>(std::index_sequence<Idx...>)
  204. {
  205. auto step =
  206. [&]<std::size_t I>(std::integral_constant<std::size_t, I>)
  207. {
  208. return [impl]<typename... Args>(Args&& ... args)
  209. {
  210. std::get<I>(impl->partial_result).emplace(
  211. std::forward<Args>(args)...);
  212. if ((std::get<Idx>(impl->partial_result) && ...)) // we're done.
  213. {
  214. impl->result = {*std::get<Idx>(impl->partial_result)...};
  215. impl->done = true;
  216. if (auto f = std::exchange(impl->completion, nullptr); !!f)
  217. std::apply(std::move(f), std::move(*impl->result));
  218. }
  219. };
  220. };
  221. (
  222. std::get<Idx>(impl->tup).async_wait(
  223. bind_executor(exec,
  224. step(std::integral_constant<std::size_t, Idx>{}))),
  225. ...
  226. );
  227. }(std::make_index_sequence<sizeof...(Ps)>{});
  228. return {impl};
  229. }
  230. template <is_promise_c ... Ps>
  231. static auto race(Ps ... ps)
  232. {
  233. auto exec = get<0>(std::tie(ps...)).get_executor();
  234. return race(std::move(exec), std::move(ps)...);
  235. }
  236. template <is_promise_c ... Ps>
  237. static auto all(Ps ... ps)
  238. {
  239. auto exec = get<0>(std::tie(ps...)).get_executor();
  240. return all(std::move(exec), std::move(ps)...);
  241. }
  242. template <execution::executor Executor1, typename Range>
  243. #if !defined(GENERATING_DOCUMENTATION)
  244. requires requires (Range r)
  245. {
  246. {*std::begin(r)} -> is_promise_c;
  247. {*std:: end(r)} -> is_promise_c;
  248. }
  249. #endif // !defined(GENERATING_DOCUMENTATION)
  250. static auto race(Executor1 exec, Range range)
  251. {
  252. using var_t = typename std::decay_t<
  253. decltype(*std::begin(range))>::value_type;
  254. using signature_type = std::conditional_t<
  255. std::is_same_v<var_t, std::monostate>,
  256. void(std::size_t),
  257. void(std::size_t, var_t)>;
  258. using pi = detail::promise_impl<signature_type, Executor1>;
  259. using promise_t = promise<signature_type, Executor1>;
  260. struct impl_t : pi
  261. {
  262. impl_t(Executor1 exec, Range&& range)
  263. : pi(std::move(exec)),
  264. range(std::move(range))
  265. {
  266. this->slot.template emplace<cancel_handler>(this);
  267. }
  268. struct cancel_handler
  269. {
  270. impl_t* self;
  271. cancel_handler(impl_t* self)
  272. : self(self)
  273. {
  274. }
  275. void operator()(boost::asio::cancellation_type ct)
  276. {
  277. for (auto& r : self->range)
  278. r.cancel(ct);
  279. }
  280. };
  281. Range range;
  282. cancellation_slot slot{this->cancel.slot()};
  283. };
  284. const auto size = std::distance(std::begin(range), std::end(range));
  285. auto impl = std::allocate_shared<impl_t>(
  286. get_associated_allocator(exec), exec, std::move(range));
  287. impl->executor = exec;
  288. if (size == 0u)
  289. {
  290. if constexpr (std::is_same_v<var_t, std::monostate>)
  291. impl->result = {-1};
  292. else
  293. impl->result = {-1, var_t{}};
  294. impl->done = true;
  295. if (auto f = std::exchange(impl->completion, nullptr); !!f)
  296. {
  297. boost::asio::post(exec,
  298. [impl, f = std::move(f)]() mutable
  299. {
  300. std::apply(std::move(f), std::move(*impl->result));
  301. });
  302. }
  303. return promise_t{impl};
  304. }
  305. auto idx = 0u;
  306. for (auto& val : impl->range)
  307. {
  308. val.async_wait(
  309. bind_executor(exec,
  310. [idx, impl]<typename... Args>(Args&&... args)
  311. {
  312. if (impl->done)
  313. return;
  314. if constexpr (std::is_same_v<var_t, std::monostate>)
  315. impl->result = idx;
  316. else
  317. impl->result = std::make_tuple(idx,
  318. var_t(std::forward<Args>(args)...));
  319. impl->done = true;
  320. if (auto f = std::exchange(impl->completion, nullptr); !!f)
  321. std::apply(std::move(f), std::move(*impl->result));
  322. auto jdx = 0u;
  323. for (auto &tc : impl->range)
  324. if (jdx++ != idx)
  325. tc.cancel();
  326. }));
  327. idx++;
  328. }
  329. return promise_t{impl};
  330. }
  331. template <execution::executor Executor1, typename Range>
  332. #if !defined(GENERATING_DOCUMENTATION)
  333. requires requires (Range r)
  334. {
  335. {*std::begin(r)} -> is_promise_c;
  336. {*std:: end(r)} -> is_promise_c;
  337. }
  338. #endif // !defined(GENERATING_DOCUMENTATION)
  339. static auto all(Executor1 exec, Range range)
  340. -> promise<
  341. void(
  342. std::vector<
  343. typename std::decay_t<
  344. decltype(*std::begin(range))
  345. >::value_type
  346. >
  347. ), Executor1>
  348. {
  349. using var_t = typename std::decay_t<
  350. decltype(*std::begin(range))>::value_type;
  351. using pi = detail::promise_impl<void(std::vector<var_t>), Executor1>;
  352. struct impl_t : pi
  353. {
  354. impl_t(Executor1 exec, Range&& range)
  355. : pi(std::move(exec)),
  356. range(std::move(range))
  357. {
  358. this->slot.template emplace<cancel_handler>(this);
  359. }
  360. struct cancel_handler
  361. {
  362. impl_t* self;
  363. cancel_handler(impl_t* self)
  364. : self(self)
  365. {
  366. }
  367. void operator()(cancellation_type ct)
  368. {
  369. for (auto& r : self->range)
  370. r.cancel(ct);
  371. }
  372. };
  373. Range range;
  374. std::vector<std::optional<var_t>> partial_result;
  375. cancellation_slot slot{this->cancel.slot()};
  376. };
  377. const auto size = std::distance(std::begin(range), std::end(range));
  378. auto impl = std::allocate_shared<impl_t>(
  379. get_associated_allocator(exec), exec, std::move(range));
  380. impl->executor = exec;
  381. impl->partial_result.resize(size);
  382. if (size == 0u)
  383. {
  384. impl->result.emplace();
  385. impl->done = true;
  386. if (auto f = std::exchange(impl->completion, nullptr); !!f)
  387. boost::asio::post(exec, [impl, f = std::move(f)]() mutable
  388. {
  389. std::apply(std::move(f), std::move(*impl->result));
  390. });
  391. return {impl};
  392. }
  393. auto idx = 0u;
  394. for (auto& val : impl->range) {
  395. val.async_wait(bind_executor(
  396. exec,
  397. [idx, impl]<typename... Args>(Args&&... args) {
  398. impl->partial_result[idx].emplace(std::forward<Args>(args)...);
  399. if (std::all_of(impl->partial_result.begin(),
  400. impl->partial_result.end(),
  401. [](auto &opt) {return opt.has_value();}))
  402. {
  403. impl->result.emplace();
  404. get<0>(*impl->result).reserve(impl->partial_result.size());
  405. for (auto& p : impl->partial_result)
  406. get<0>(*impl->result).push_back(std::move(*p));
  407. impl->done = true;
  408. if (auto f = std::exchange(impl->completion, nullptr); !!f)
  409. std::apply(std::move(f), std::move(*impl->result));
  410. }
  411. }));
  412. idx++;
  413. }
  414. return {impl};
  415. }
  416. template <typename Range>
  417. #if !defined(GENERATING_DOCUMENTATION)
  418. requires requires (Range r)
  419. {
  420. {*std::begin(r)} -> is_promise_c;
  421. {*std:: end(r)} -> is_promise_c;
  422. }
  423. #endif // !defined(GENERATING_DOCUMENTATION)
  424. static auto race(Range range)
  425. {
  426. if (std::begin(range) == std::end(range))
  427. throw std::logic_error(
  428. "Can't use race on an empty range with deduced executor");
  429. else
  430. return race(std::begin(range)->get_executor(), std::move(range));
  431. }
  432. template <typename Range>
  433. #if !defined(GENERATING_DOCUMENTATION)
  434. requires requires (Range&& r)
  435. {
  436. {*std::begin(r)} -> is_promise_c;
  437. {*std:: end(r)} -> is_promise_c;
  438. }
  439. #endif // !defined(GENERATING_DOCUMENTATION)
  440. static auto all(Range range)
  441. {
  442. if (std::begin(range) == std::end(range))
  443. throw std::logic_error(
  444. "Can't use all on an empty range with deduced executor");
  445. else
  446. return all(std::begin(range)->get_executor(), std::move(range));
  447. }
  448. private:
  449. #if !defined(GENERATING_DOCUMENTATION)
  450. template <typename, typename> friend struct promise;
  451. friend struct detail::promise_handler<void(Ts...)>;
  452. #endif // !defined(GENERATING_DOCUMENTATION)
  453. std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> impl_;
  454. promise(std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> impl)
  455. : impl_(impl)
  456. {
  457. }
  458. struct initiate_async_wait
  459. {
  460. std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> self_;
  461. template <typename WaitHandler>
  462. void operator()(WaitHandler&& handler) const
  463. {
  464. const auto exec = get_associated_executor(handler, self_->executor);
  465. auto cancel = get_associated_cancellation_slot(handler);
  466. if (self_->done)
  467. {
  468. boost::asio::post(exec,
  469. [self = self_, h = std::forward<WaitHandler>(handler)]() mutable
  470. {
  471. std::apply(std::forward<WaitHandler>(h),
  472. std::move(*self->result));
  473. });
  474. }
  475. else
  476. {
  477. if (cancel.is_connected())
  478. {
  479. struct cancel_handler
  480. {
  481. std::weak_ptr<detail::promise_impl<void(Ts...), Executor>> self;
  482. cancel_handler(
  483. std::weak_ptr<detail::promise_impl<void(Ts...), Executor>> self)
  484. : self(std::move(self))
  485. {
  486. }
  487. void operator()(cancellation_type level) const
  488. {
  489. if (auto p = self.lock(); p != nullptr)
  490. p->cancel.emit(level);
  491. }
  492. };
  493. cancel.template emplace<cancel_handler>(self_);
  494. }
  495. self_->completion = {exec, std::forward<WaitHandler>(handler)};
  496. }
  497. }
  498. };
  499. };
  500. } // namespace experimental
  501. #if !defined(GENERATING_DOCUMENTATION)
  502. template <typename Executor, typename R, typename... Args>
  503. struct async_result<experimental::use_promise_t<Executor>, R(Args...)>
  504. {
  505. using handler_type = experimental::detail::promise_handler<
  506. void(typename decay<Args>::type...), Executor>;
  507. template <typename Initiation, typename... InitArgs>
  508. static auto initiate(Initiation initiation,
  509. experimental::use_promise_t<Executor>, InitArgs... args)
  510. -> typename handler_type::promise_type
  511. {
  512. handler_type ht{get_associated_executor(initiation)};
  513. std::move(initiation)(ht, std::move(args)...);
  514. return ht.make_promise();
  515. }
  516. };
  517. #endif // !defined(GENERATING_DOCUMENTATION)
  518. } // namespace asio
  519. } // namespace boost
  520. #include <boost/asio/detail/pop_options.hpp>
  521. #endif // BOOST_ASIO_EXPERIMENTAL_PROMISE_HPP