1 /* 2 * SObjectizer-5 3 */ 4 5 /*! 6 * \file 7 * \brief Repository of stop_guards. 8 * 9 * \since 10 * v.5.5.19.2 11 */ 12 13 #pragma once 14 15 #include <so_5/stop_guard.hpp> 16 17 #include <so_5/details/sync_helpers.hpp> 18 #include <so_5/details/invoke_noexcept_code.hpp> 19 20 #include <mutex> 21 #include <algorithm> 22 #include <iterator> 23 24 namespace so_5 { 25 26 namespace impl { 27 28 // 29 // stop_guard_repository_t 30 // 31 /*! 32 * \brief Repository of stop_guards. 33 * 34 * \note 35 * Performs multitheading defense by very simple way: via std::mutex object. 36 * 37 * \since 38 * v.5.5.19.2 39 */ 40 class stop_guard_repository_t 41 : public ::so_5::details::lock_holder_detector< std::mutex >::type 42 { 43 public : 44 //! Action which must be performed as result of operation on repository. 45 enum class action_t 46 { 47 //! Nothing to do. Stop operation is not started. 48 do_nothing, 49 //! Stop operation is started but can't be finished right now. 50 wait_for_completion, 51 //! Stop operation must be finished. 52 do_actual_stop 53 }; 54 stop_guard_repository_t()55 stop_guard_repository_t() 56 {} 57 stop_guard_repository_t( const stop_guard_repository_t & ) = delete; 58 stop_guard_repository_t( stop_guard_repository_t && ) = delete; 59 60 //! Setup new stop_guard. 61 /*! 62 * \note 63 * Uniqueness of stop_guard is not checked. It means that the same 64 * stop_guard can be added to repository several times. 65 * 66 * \retval stop_guard_t::setup_result_t::ok if stop is not in progress 67 * and new stop_guard has been set up successfully. 68 * \retval stop_guard_t::setup_result_t::stop_already_in_progress new 69 * stop_guard is not set because stop is already in progress. 70 */ 71 stop_guard_t::setup_result_t setup_guard(stop_guard_shptr_t guard)72 setup_guard( stop_guard_shptr_t guard ) 73 { 74 return this->lock_and_perform( [&] { 75 auto ret_value = stop_guard_t::setup_result_t::ok; 76 if( status_t::not_started != m_status ) 77 ret_value = stop_guard_t::setup_result_t::stop_already_in_progress; 78 else 79 { 80 auto it = std::lower_bound( 81 std::begin(m_guards), std::end(m_guards), guard ); 82 m_guards.insert( it, std::move(guard) ); 83 } 84 85 return ret_value; 86 } ); 87 } 88 89 //! Remove stop_guard. 90 /*! 91 * \retval action_t::do_nothing there is no stop in progress and nothing 92 * need to be done. 93 * \retval action_t::wait_for_completion there is stop in progress but 94 * there are also other stop_guards which do not complete their 95 * actions yet. 96 * \retval action_t::do_actual_stop stop operation needs to be completed. 97 */ 98 action_t remove_guard(stop_guard_shptr_t guard)99 remove_guard( stop_guard_shptr_t guard ) noexcept 100 { 101 return so_5::details::invoke_noexcept_code( [&] { 102 return this->lock_and_perform( [&] { 103 auto ret_value = action_t::do_nothing; 104 105 const auto it = std::lower_bound( 106 std::begin(m_guards), 107 std::end(m_guards), 108 guard ); 109 if( it != std::end(m_guards) && *it == guard ) 110 m_guards.erase(it); 111 112 if( status_t::started == m_status ) 113 ret_value = m_guards.empty() ? 114 action_t::do_actual_stop : 115 action_t::wait_for_completion; 116 117 return ret_value; 118 } ); 119 } ); 120 } 121 122 //! Initiate stop operation. 123 /*! 124 * \note 125 * It is safe to call this method when stop is already in progress. 126 * 127 * \retval action_t::wait_for_completion there are some stop_guards 128 * which do not complete their actions yet. 129 * \retval action_t::do_actual_stop stop operation needs to be completed. 130 */ 131 action_t initiate_stop()132 initiate_stop() noexcept 133 { 134 return so_5::details::invoke_noexcept_code( [&] { 135 auto ret_value = action_t::wait_for_completion; 136 137 // Copy of actual guards at this moment. 138 guards_container_t guards; 139 140 // The first stage: change status and take a copy 141 // of actual guards list if necessary. 142 this->lock_and_perform( [&] { 143 if( status_t::not_started == m_status ) 144 { 145 // The stop operation is not started yet. 146 m_status = status_t::start_in_progress; 147 guards = m_guards; 148 } 149 } ); 150 151 // The second stage: calling stop() for all stop_guards. 152 // If guards is empty then nothing will be called. 153 for( auto & g : guards ) 154 g->stop(); 155 156 // The third stage: check for possibitility to complete 157 // the stop operation right now. 158 // It is possible that all stop_guards will be removed 159 // on the second stage. 160 this->lock_and_perform( [&] { 161 if( m_guards.empty() ) 162 { 163 m_status = status_t::completed; 164 ret_value = action_t::do_actual_stop; 165 } 166 else 167 // The stop operation is started. We must wait 168 // while all live stop_guards finish their work. 169 m_status = status_t::started; 170 } ); 171 172 return ret_value; 173 } ); 174 } 175 176 private : 177 //! Status of the stop operation. 178 enum class status_t 179 { 180 not_started, 181 start_in_progress, 182 started, 183 completed 184 }; 185 186 //! The current status of the stop operation 187 status_t m_status = status_t::not_started; 188 189 //! Type of the stop_guards list. 190 using guards_container_t = std::vector< stop_guard_shptr_t >; 191 192 //! List of actual stop_guards. 193 guards_container_t m_guards; 194 }; 195 196 } /* namespace impl */ 197 198 } /* namespace so_5 */ 199 200