exec_fsm.ipp 3.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. //
  2. // Copyright (c) 2025 Marcelo Zimbres Silva (mzimbres@gmail.com),
  3. // Ruben Perez Hidalgo (rubenperez038 at gmail dot com)
  4. //
  5. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  6. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  7. //
  8. #ifndef BOOST_REDIS_EXEC_FSM_IPP
  9. #define BOOST_REDIS_EXEC_FSM_IPP
  10. #include <boost/redis/detail/coroutine.hpp>
  11. #include <boost/redis/detail/exec_fsm.hpp>
  12. #include <boost/redis/request.hpp>
  13. #include <boost/asio/error.hpp>
  14. #include <boost/assert.hpp>
  15. namespace boost::redis::detail {
  16. inline bool is_partial_or_terminal_cancel(asio::cancellation_type_t type)
  17. {
  18. return !!(type & (asio::cancellation_type_t::partial | asio::cancellation_type_t::terminal));
  19. }
  20. inline bool is_total_cancel(asio::cancellation_type_t type)
  21. {
  22. return !!(type & asio::cancellation_type_t::total);
  23. }
  24. exec_action exec_fsm::resume(bool connection_is_open, asio::cancellation_type_t cancel_state)
  25. {
  26. switch (resume_point_) {
  27. BOOST_REDIS_CORO_INITIAL
  28. // Check whether the user wants to wait for the connection to
  29. // be established.
  30. if (elem_->get_request().get_config().cancel_if_not_connected && !connection_is_open) {
  31. BOOST_REDIS_YIELD(resume_point_, 1, exec_action_type::immediate)
  32. elem_.reset(); // Deallocate memory before finalizing
  33. return system::error_code(error::not_connected);
  34. }
  35. // No more immediate errors. Set up the supported cancellation types.
  36. // This is required to get partial and total cancellations.
  37. // This is a potentially allocating operation, so do it as late as we can.
  38. BOOST_REDIS_YIELD(resume_point_, 2, exec_action_type::setup_cancellation)
  39. // Add the request to the multiplexer
  40. mpx_->add(elem_);
  41. // Notify the writer task that there is work to do. If the task is not
  42. // listening (e.g. it's already writing or the connection is not healthy),
  43. // this is a no-op. Since this is sync, no cancellation can happen here.
  44. BOOST_REDIS_YIELD(resume_point_, 3, exec_action_type::notify_writer)
  45. while (true) {
  46. // Wait until we get notified. This will return once the request completes,
  47. // or upon any kind of cancellation
  48. BOOST_REDIS_YIELD(resume_point_, 4, exec_action_type::wait_for_response)
  49. // If the request has completed (with error or not), we're done
  50. if (elem_->is_done()) {
  51. exec_action act{elem_->get_error(), elem_->get_read_size()};
  52. elem_.reset(); // Deallocate memory before finalizing
  53. return act;
  54. }
  55. // Total cancellation can only be handled if the request hasn't been sent yet.
  56. // Partial and terminal cancellation can always be served
  57. if (
  58. (is_total_cancel(cancel_state) && elem_->is_waiting()) ||
  59. is_partial_or_terminal_cancel(cancel_state)) {
  60. mpx_->cancel(elem_);
  61. elem_.reset(); // Deallocate memory before finalizing
  62. return exec_action{asio::error::operation_aborted};
  63. }
  64. }
  65. }
  66. // We should never get here
  67. BOOST_ASSERT(false);
  68. return exec_action{system::error_code()};
  69. }
  70. } // namespace boost::redis::detail
  71. #endif