added the continuation functionality to procedures via the "then()" method.

This commit is contained in:
Francisco Paisana 2019-10-01 19:31:47 +01:00 committed by Andre Puschmann
parent c1be118d1d
commit 8864787f59
5 changed files with 175 additions and 52 deletions

View File

@ -23,6 +23,7 @@
#include <list>
#include <memory>
#include <mutex>
#include <vector>
#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 <class T>
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 <class T>
auto optional_clear(T* obj) -> decltype(obj->clear())
{
obj->clear();
}
inline auto optional_clear(...) -> void
{
// do nothing
}
} // namespace detail
/**************************************************************************************
* class: callback_list_t
************************************************************************************/
template <typename Func>
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<void(bool)> func;
bool call_always;
};
std::vector<call_item_t> 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<T> 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 T>
class proc_t final : public proc_manager_itf_t
{
using complete_callback_list_t = callback_list_t<std::function<void(bool)> >;
public:
template <typename... Args>
explicit proc_t(Args&&... args) : proc_impl_ptr(new T(std::forward<Args>(args)...))
@ -144,6 +205,9 @@ public:
if (is_running()) {
proc_outcome_t outcome = proc_impl_ptr->trigger_event(std::forward<Event>(e));
handle_outcome(outcome);
if (outcome == proc_outcome_t::repeat) {
run();
}
}
}
@ -176,30 +240,32 @@ public:
proc_result_t<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<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<T> proc_impl_ptr;
proc_state_t proc_state = proc_state_t::inactive;
std::unique_ptr<T> proc_impl_ptr;
complete_callback_list_t complete_callbacks;
};
template <typename T>
@ -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 <class OutcomeType>

View File

@ -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<custom_proc2_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<custom_proc2_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;
}

View File

@ -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"; }

View File

@ -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");

View File

@ -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<plmn_search_proc> 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<rrc_connect_proc> 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;
});