diff --git a/lib/include/srslte/common/stack_procedure.h b/lib/include/srslte/common/stack_procedure.h index b2a488f45..0e2c44513 100644 --- a/lib/include/srslte/common/stack_procedure.h +++ b/lib/include/srslte/common/stack_procedure.h @@ -23,6 +23,7 @@ #include #include #include +#include #ifndef SRSLTE_RESUMABLE_PROCEDURES_H #define SRSLTE_RESUMABLE_PROCEDURES_H @@ -36,34 +37,91 @@ enum class proc_outcome_t { repeat, yield, success, error }; * helper functions for overloading ************************************************************************************/ namespace detail { +// used by proc_manager to call proc finally() method only if it exists template -auto optional_stop(T* obj, int is_success) -> decltype(obj->stop()) +auto optional_complete(T* obj, int is_success) -> decltype(obj->on_complete(is_success)) { - obj->stop(is_success); + obj->on_complete(is_success); } - -inline auto optional_stop(...) -> void +inline auto optional_complete(...) -> void { // do nothing } - template auto optional_clear(T* obj) -> decltype(obj->clear()) { obj->clear(); } - inline auto optional_clear(...) -> void { // do nothing } } // namespace detail +/************************************************************************************** + * class: callback_list_t + ************************************************************************************/ + +template +class callback_list_t +{ +public: + using id_type = uint32_t; + using callback_t = Func; + // register new callbacks + id_type call_once(callback_t f_) + { + uint32_t idx = get_new_callback(); + func_list[idx].func = std::move(f_); + func_list[idx].call_always = false; + return idx; + } + id_type call_always(callback_t f_) + { + uint32_t idx = get_new_callback(); + func_list[idx].func = std::move(f_); + func_list[idx].call_always = true; + return idx; + } + + // call all callbacks + void run(bool is_success) + { + for (auto& f : func_list) { + if (f.active) { + f.func(is_success); + if (not f.call_always) { + f.active = false; + } + } + } + } + +private: + uint32_t get_new_callback() + { + uint32_t i = 0; + for (; i < func_list.size() and func_list[i].active; ++i) { + } + if (i == func_list.size()) { + func_list.emplace_back(); + } + func_list[i].active = true; + return i; + } + + struct call_item_t { + bool active; + std::function func; + bool call_always; + }; + std::vector func_list; +}; + /************************************************************************************** * class: proc_itf_t - * Provides an polymorphic interface for resumable procedures. This base can then be used - * by a task dispatch queue via the method "run()". - * Every procedure starts in inactive state, and finishes with success or error. + * Provides a polymorphic interface for resumable procedures. This base can then be used + * by a procedure manager container via the virtual method "proc_itf_t::run()". * With methods: * - run() - executes a procedure, returning true if the procedure is still running * or false, if it has completed @@ -75,7 +133,7 @@ inline auto optional_clear(...) -> void * recall step() again (probably the procedure state has changed) * - error - the procedure has finished unsuccessfully * - success - the procedure has completed successfully - * - stop() - called automatically when a procedure has finished. Useful for actions + * - finally() - called automatically when a procedure has finished. Useful for actions * upon procedure completion, like sending back a response. * - set_proc_state() / is_#() - setter and getters for current procedure state ************************************************************************************/ @@ -94,10 +152,11 @@ struct proc_result_t; * Manages the lifetime, of a procedure T, including its alloc, launching, * and reset back to initial, uninit state once the procedure has been * completed and the user has extracted its results. + * Every procedure starts in inactive state, and finishes with success or error. * Can only be re-launched when a procedure T becomes inactive. * It uses a unique_ptr to allow the use of procedures that are forward declared. * It provides the following methods: - * - run() - calls T::step() and update the procedure state + * - run() - calls T::step() and update the procedure state. * - launch() - initializes the procedure T by calling T::init(...). Handles the case * of failed initialization, and forbids the initialization of procedures * that are already active. @@ -110,6 +169,8 @@ struct proc_result_t; template class proc_t final : public proc_manager_itf_t { + using complete_callback_list_t = callback_list_t >; + public: template explicit proc_t(Args&&... args) : proc_impl_ptr(new T(std::forward(args)...)) @@ -144,6 +205,9 @@ public: if (is_running()) { proc_outcome_t outcome = proc_impl_ptr->trigger_event(std::forward(e)); handle_outcome(outcome); + if (outcome == proc_outcome_t::repeat) { + run(); + } } } @@ -176,30 +240,32 @@ public: proc_result_t pop(); + // on_complete interface + complete_callback_list_t::id_type then(const complete_callback_list_t::callback_t& c) + { + return complete_callbacks.call_once(c); + } + complete_callback_list_t::id_type then_always(const complete_callback_list_t::callback_t& c) + { + return complete_callbacks.call_always(c); + } + protected: friend proc_result_t; bool is_running() const { return proc_state == proc_state_t::on_going; } void handle_outcome(proc_outcome_t outcome) { - switch (outcome) { - case proc_outcome_t::error: - proc_state = proc_state_t::error; - detail::optional_stop(proc_impl_ptr.get(), false); - break; - case proc_outcome_t::success: - proc_state = proc_state_t::success; - detail::optional_stop(proc_impl_ptr.get(), true); - break; - case proc_outcome_t::repeat: - run(); - break; - default: - break; + if (outcome == proc_outcome_t::error or outcome == proc_outcome_t::success) { + bool success = outcome == proc_outcome_t::success; + proc_state = success ? proc_state_t::success : proc_state_t::error; + detail::optional_complete(proc_impl_ptr.get(), success); + complete_callbacks.run(success); } } - proc_state_t proc_state = proc_state_t::inactive; - std::unique_ptr proc_impl_ptr; + proc_state_t proc_state = proc_state_t::inactive; + std::unique_ptr proc_impl_ptr; + complete_callback_list_t complete_callbacks; }; template @@ -244,7 +310,7 @@ private: /************************************************************************************** * class: query_proc_t - * A helper proc_impl_t whose step()/stop() are no op, but has a trigger_event() that + * A helper proc_impl_t whose step()/finally() are no op, but has a trigger_event() that * signals that the method has finished and store a result of type OutcomeType. ************************************************************************************/ template diff --git a/lib/test/common/stack_procedure_test.cc b/lib/test/common/stack_procedure_test.cc index d15598424..318ccf0fe 100644 --- a/lib/test/common/stack_procedure_test.cc +++ b/lib/test/common/stack_procedure_test.cc @@ -143,7 +143,7 @@ public: } return proc_outcome_t::yield; } - void stop(bool is_success) { printf("TestObj %d stop() was called\n", obj.id); } + void on_complete(bool is_success) { printf("TestObj %d stop() was called\n", obj.id); } const char* name() const { return "custom proc"; } void clear() { @@ -159,6 +159,34 @@ private: int counter = 0; }; +class custom_proc2_t +{ +public: + proc_outcome_t init() + { + exit_val = "init"; + counter = 0; + return proc_outcome_t::yield; + } + proc_outcome_t step() + { + if (counter++ > 5) { + return proc_outcome_t::success; + } + return proc_outcome_t::yield; + } + void on_complete(bool is_success) + { + if (is_success) { + exit_val = "success"; + } else { + exit_val = "fail"; + } + } + std::string exit_val = ""; + int counter = 0; +}; + int test_local_1() { new_test(); @@ -358,6 +386,41 @@ int test_callback_5() return 0; } +int test_complete_callback_1() +{ + /* + * Description: Test if on_complete() callbacks are correctly called + */ + printf("\n--- Test %s ---\n", __func__); + srslte::proc_manager_list_t callbacks; + srslte::proc_t proc; + + std::string run_result; + auto continuation_task = [&run_result](bool is_success) { run_result = is_success ? "SUCCESS" : "FAILURE"; }; + std::string results[] = {"", "SUCCESS", "", "SUCCESS", "SUCCESS", "SUCCESS"}; + for (uint32_t i = 0; i < 6; ++i) { + run_result = ""; + if (i == 1) { + TESTASSERT(proc.then(continuation_task) == 0); + } else if (i == 3) { + TESTASSERT(proc.then_always(continuation_task) == 0); + } + + TESTASSERT(proc.launch()); + TESTASSERT(proc.get()->exit_val == "init"); + while (proc.run()) { + TESTASSERT(proc.get()->exit_val == "init"); + TESTASSERT(proc.is_active()); + } + TESTASSERT(proc.is_active() and proc.is_complete()); + srslte::proc_result_t ret = proc.pop(); + TESTASSERT(ret.is_success() and ret.proc()->exit_val == "success"); + + TESTASSERT(run_result == results[i]); + } + return 0; +} + int main() { TESTASSERT(test_local_1() == 0); @@ -366,6 +429,7 @@ int main() TESTASSERT(test_callback_3() == 0); TESTASSERT(test_callback_4() == 0); TESTASSERT(test_callback_5() == 0); + TESTASSERT(test_complete_callback_1() == 0); return 0; } diff --git a/srsue/hdr/stack/rrc/rrc_procedures.h b/srsue/hdr/stack/rrc/rrc_procedures.h index 627de5ce8..66f268842 100644 --- a/srsue/hdr/stack/rrc/rrc_procedures.h +++ b/srsue/hdr/stack/rrc/rrc_procedures.h @@ -108,7 +108,7 @@ public: explicit cell_selection_proc(rrc* parent_); srslte::proc_outcome_t init(); srslte::proc_outcome_t step(); - void stop(bool is_success); + void on_complete(bool is_success); cs_result_t get_cs_result() const { return cs_result; } static const char* name() { return "Cell Selection"; } @@ -134,7 +134,7 @@ public: explicit plmn_search_proc(rrc* parent_); srslte::proc_outcome_t init(); srslte::proc_outcome_t step(); - void stop(bool is_success); + void on_complete(bool is_success); static const char* name() { return "PLMN Search"; } private: @@ -159,7 +159,7 @@ public: srslte::proc_outcome_t init(srslte::establishment_cause_t cause_, srslte::unique_byte_buffer_t dedicated_info_nas_); srslte::proc_outcome_t step(); - void stop(bool is_success); + void on_complete(bool is_success); srslte::proc_outcome_t trigger_event(const cell_selection_complete& e); static const char* name() { return "Connection Request"; } diff --git a/srsue/src/stack/rrc/rrc_procedures.cc b/srsue/src/stack/rrc/rrc_procedures.cc index 883bb1a23..af0ab39aa 100644 --- a/srsue/src/stack/rrc/rrc_procedures.cc +++ b/srsue/src/stack/rrc/rrc_procedures.cc @@ -455,7 +455,7 @@ proc_outcome_t rrc::cell_selection_proc::step() return proc_outcome_t::error; } -void rrc::cell_selection_proc::stop(bool is_success) +void rrc::cell_selection_proc::on_complete(bool is_success) { // Inform Connection Request Procedure Info("Completed with %s. Informing proc %s\n", @@ -532,7 +532,7 @@ proc_outcome_t rrc::plmn_search_proc::step() return proc_outcome_t::repeat; } -void rrc::plmn_search_proc::stop(bool is_success) +void rrc::plmn_search_proc::on_complete(bool is_success) { // on cleanup, call plmn_search_completed if (is_success) { @@ -658,7 +658,7 @@ proc_outcome_t rrc::connection_request_proc::step() return proc_outcome_t::error; } -void rrc::connection_request_proc::stop(bool is_success) +void rrc::connection_request_proc::on_complete(bool is_success) { if (not is_success) { log_h->warning("Could not establish connection. Deallocating dedicatedInfoNAS PDU\n"); diff --git a/srsue/src/stack/upper/nas.cc b/srsue/src/stack/upper/nas.cc index f386a3d3d..d4ac62765 100644 --- a/srsue/src/stack/upper/nas.cc +++ b/srsue/src/stack/upper/nas.cc @@ -345,24 +345,20 @@ void nas::start_attach_request(srslte::proc_state_t* result, srslte::establishme } return; } - callbacks.defer_task([this, result]() { - if (plmn_searcher.run()) { - return proc_outcome_t::yield; - } - proc_result_t p = plmn_searcher.pop(); - nas_log->info("Attach Request from PLMN Search %s\n", p.is_success() ? "finished successfully" : "failed"); + callbacks.defer_proc(plmn_searcher); + plmn_searcher.then([this, result](bool is_success) { + nas_log->info("Attach Request from PLMN Search %s\n", is_success ? "finished successfully" : "failed"); if (result != nullptr) { - *result = p.is_success() ? proc_state_t::success : proc_state_t::error; + *result = is_success ? proc_state_t::success : proc_state_t::error; } // start T3411 nas_log->debug("Starting T3411\n"); timers->get(t3411)->reset(); timers->get(t3411)->run(); - if (not p.is_success()) { + if (not is_success) { enter_emm_deregistered(); } - return proc_outcome_t::success; }); } else { nas_log->error("PLMN selected in state %s\n", emm_state_text[state]); @@ -386,18 +382,15 @@ void nas::start_attach_request(srslte::proc_state_t* result, srslte::establishme } return; } - callbacks.defer_task([this, result]() { - if (rrc_connector.run()) { - return proc_outcome_t::yield; - } - proc_result_t proc = rrc_connector.pop(); - if (proc.is_success()) { - nas_log->info("NAS attached successfully.\n"); + callbacks.defer_proc(rrc_connector); + rrc_connector.then([this, result](bool is_success) { + if (is_success) { + nas_log->info("NAS attached successfully\n"); } else { nas_log->error("Could not attach from attach_request\n"); } if (result != nullptr) { - *result = proc.is_success() ? proc_state_t::success : proc_state_t::error; + *result = is_success ? proc_state_t::success : proc_state_t::error; } return proc_outcome_t::success; });