mirror of https://github.com/PentHertz/srsLTE.git
added the continuation functionality to procedures via the "then()" method.
This commit is contained in:
parent
c1be118d1d
commit
8864787f59
|
@ -23,6 +23,7 @@
|
||||||
#include <list>
|
#include <list>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#ifndef SRSLTE_RESUMABLE_PROCEDURES_H
|
#ifndef SRSLTE_RESUMABLE_PROCEDURES_H
|
||||||
#define 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
|
* helper functions for overloading
|
||||||
************************************************************************************/
|
************************************************************************************/
|
||||||
namespace detail {
|
namespace detail {
|
||||||
|
// used by proc_manager to call proc finally() method only if it exists
|
||||||
template <class T>
|
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_complete(...) -> void
|
||||||
inline auto optional_stop(...) -> void
|
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
auto optional_clear(T* obj) -> decltype(obj->clear())
|
auto optional_clear(T* obj) -> decltype(obj->clear())
|
||||||
{
|
{
|
||||||
obj->clear();
|
obj->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto optional_clear(...) -> void
|
inline auto optional_clear(...) -> void
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
} // namespace detail
|
} // 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
|
* class: proc_itf_t
|
||||||
* Provides an polymorphic interface for resumable procedures. This base can then be used
|
* Provides a polymorphic interface for resumable procedures. This base can then be used
|
||||||
* by a task dispatch queue via the method "run()".
|
* by a procedure manager container via the virtual method "proc_itf_t::run()".
|
||||||
* Every procedure starts in inactive state, and finishes with success or error.
|
|
||||||
* With methods:
|
* With methods:
|
||||||
* - run() - executes a procedure, returning true if the procedure is still running
|
* - run() - executes a procedure, returning true if the procedure is still running
|
||||||
* or false, if it has completed
|
* or false, if it has completed
|
||||||
|
@ -75,7 +133,7 @@ inline auto optional_clear(...) -> void
|
||||||
* recall step() again (probably the procedure state has changed)
|
* recall step() again (probably the procedure state has changed)
|
||||||
* - error - the procedure has finished unsuccessfully
|
* - error - the procedure has finished unsuccessfully
|
||||||
* - success - the procedure has completed successfully
|
* - 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.
|
* upon procedure completion, like sending back a response.
|
||||||
* - set_proc_state() / is_#() - setter and getters for current procedure state
|
* - 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,
|
* Manages the lifetime, of a procedure T, including its alloc, launching,
|
||||||
* and reset back to initial, uninit state once the procedure has been
|
* and reset back to initial, uninit state once the procedure has been
|
||||||
* completed and the user has extracted its results.
|
* 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.
|
* 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 uses a unique_ptr<T> to allow the use of procedures that are forward declared.
|
||||||
* It provides the following methods:
|
* 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
|
* - launch() - initializes the procedure T by calling T::init(...). Handles the case
|
||||||
* of failed initialization, and forbids the initialization of procedures
|
* of failed initialization, and forbids the initialization of procedures
|
||||||
* that are already active.
|
* that are already active.
|
||||||
|
@ -110,6 +169,8 @@ struct proc_result_t;
|
||||||
template <class T>
|
template <class T>
|
||||||
class proc_t final : public proc_manager_itf_t
|
class proc_t final : public proc_manager_itf_t
|
||||||
{
|
{
|
||||||
|
using complete_callback_list_t = callback_list_t<std::function<void(bool)> >;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
explicit proc_t(Args&&... args) : proc_impl_ptr(new T(std::forward<Args>(args)...))
|
explicit proc_t(Args&&... args) : proc_impl_ptr(new T(std::forward<Args>(args)...))
|
||||||
|
@ -144,6 +205,9 @@ public:
|
||||||
if (is_running()) {
|
if (is_running()) {
|
||||||
proc_outcome_t outcome = proc_impl_ptr->trigger_event(std::forward<Event>(e));
|
proc_outcome_t outcome = proc_impl_ptr->trigger_event(std::forward<Event>(e));
|
||||||
handle_outcome(outcome);
|
handle_outcome(outcome);
|
||||||
|
if (outcome == proc_outcome_t::repeat) {
|
||||||
|
run();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,30 +240,32 @@ public:
|
||||||
|
|
||||||
proc_result_t<T> pop();
|
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:
|
protected:
|
||||||
friend proc_result_t<T>;
|
friend proc_result_t<T>;
|
||||||
bool is_running() const { return proc_state == proc_state_t::on_going; }
|
bool is_running() const { return proc_state == proc_state_t::on_going; }
|
||||||
void handle_outcome(proc_outcome_t outcome)
|
void handle_outcome(proc_outcome_t outcome)
|
||||||
{
|
{
|
||||||
switch (outcome) {
|
if (outcome == proc_outcome_t::error or outcome == proc_outcome_t::success) {
|
||||||
case proc_outcome_t::error:
|
bool success = outcome == proc_outcome_t::success;
|
||||||
proc_state = proc_state_t::error;
|
proc_state = success ? proc_state_t::success : proc_state_t::error;
|
||||||
detail::optional_stop(proc_impl_ptr.get(), false);
|
detail::optional_complete(proc_impl_ptr.get(), success);
|
||||||
break;
|
complete_callbacks.run(success);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc_state_t proc_state = proc_state_t::inactive;
|
proc_state_t proc_state = proc_state_t::inactive;
|
||||||
std::unique_ptr<T> proc_impl_ptr;
|
std::unique_ptr<T> proc_impl_ptr;
|
||||||
|
complete_callback_list_t complete_callbacks;
|
||||||
};
|
};
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -244,7 +310,7 @@ private:
|
||||||
|
|
||||||
/**************************************************************************************
|
/**************************************************************************************
|
||||||
* class: query_proc_t
|
* 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.
|
* signals that the method has finished and store a result of type OutcomeType.
|
||||||
************************************************************************************/
|
************************************************************************************/
|
||||||
template <class OutcomeType>
|
template <class OutcomeType>
|
||||||
|
|
|
@ -143,7 +143,7 @@ public:
|
||||||
}
|
}
|
||||||
return proc_outcome_t::yield;
|
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"; }
|
const char* name() const { return "custom proc"; }
|
||||||
void clear()
|
void clear()
|
||||||
{
|
{
|
||||||
|
@ -159,6 +159,34 @@ private:
|
||||||
int counter = 0;
|
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()
|
int test_local_1()
|
||||||
{
|
{
|
||||||
new_test();
|
new_test();
|
||||||
|
@ -358,6 +386,41 @@ int test_callback_5()
|
||||||
return 0;
|
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()
|
int main()
|
||||||
{
|
{
|
||||||
TESTASSERT(test_local_1() == 0);
|
TESTASSERT(test_local_1() == 0);
|
||||||
|
@ -366,6 +429,7 @@ int main()
|
||||||
TESTASSERT(test_callback_3() == 0);
|
TESTASSERT(test_callback_3() == 0);
|
||||||
TESTASSERT(test_callback_4() == 0);
|
TESTASSERT(test_callback_4() == 0);
|
||||||
TESTASSERT(test_callback_5() == 0);
|
TESTASSERT(test_callback_5() == 0);
|
||||||
|
TESTASSERT(test_complete_callback_1() == 0);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -108,7 +108,7 @@ public:
|
||||||
explicit cell_selection_proc(rrc* parent_);
|
explicit cell_selection_proc(rrc* parent_);
|
||||||
srslte::proc_outcome_t init();
|
srslte::proc_outcome_t init();
|
||||||
srslte::proc_outcome_t step();
|
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; }
|
cs_result_t get_cs_result() const { return cs_result; }
|
||||||
static const char* name() { return "Cell Selection"; }
|
static const char* name() { return "Cell Selection"; }
|
||||||
|
|
||||||
|
@ -134,7 +134,7 @@ public:
|
||||||
explicit plmn_search_proc(rrc* parent_);
|
explicit plmn_search_proc(rrc* parent_);
|
||||||
srslte::proc_outcome_t init();
|
srslte::proc_outcome_t init();
|
||||||
srslte::proc_outcome_t step();
|
srslte::proc_outcome_t step();
|
||||||
void stop(bool is_success);
|
void on_complete(bool is_success);
|
||||||
static const char* name() { return "PLMN Search"; }
|
static const char* name() { return "PLMN Search"; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -159,7 +159,7 @@ public:
|
||||||
srslte::proc_outcome_t
|
srslte::proc_outcome_t
|
||||||
init(srslte::establishment_cause_t cause_, srslte::unique_byte_buffer_t dedicated_info_nas_);
|
init(srslte::establishment_cause_t cause_, srslte::unique_byte_buffer_t dedicated_info_nas_);
|
||||||
srslte::proc_outcome_t step();
|
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);
|
srslte::proc_outcome_t trigger_event(const cell_selection_complete& e);
|
||||||
static const char* name() { return "Connection Request"; }
|
static const char* name() { return "Connection Request"; }
|
||||||
|
|
||||||
|
|
|
@ -455,7 +455,7 @@ proc_outcome_t rrc::cell_selection_proc::step()
|
||||||
return proc_outcome_t::error;
|
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
|
// Inform Connection Request Procedure
|
||||||
Info("Completed with %s. Informing proc %s\n",
|
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;
|
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
|
// on cleanup, call plmn_search_completed
|
||||||
if (is_success) {
|
if (is_success) {
|
||||||
|
@ -658,7 +658,7 @@ proc_outcome_t rrc::connection_request_proc::step()
|
||||||
return proc_outcome_t::error;
|
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) {
|
if (not is_success) {
|
||||||
log_h->warning("Could not establish connection. Deallocating dedicatedInfoNAS PDU\n");
|
log_h->warning("Could not establish connection. Deallocating dedicatedInfoNAS PDU\n");
|
||||||
|
|
|
@ -345,24 +345,20 @@ void nas::start_attach_request(srslte::proc_state_t* result, srslte::establishme
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
callbacks.defer_task([this, result]() {
|
callbacks.defer_proc(plmn_searcher);
|
||||||
if (plmn_searcher.run()) {
|
plmn_searcher.then([this, result](bool is_success) {
|
||||||
return proc_outcome_t::yield;
|
nas_log->info("Attach Request from PLMN Search %s\n", is_success ? "finished successfully" : "failed");
|
||||||
}
|
|
||||||
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");
|
|
||||||
if (result != nullptr) {
|
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
|
// start T3411
|
||||||
nas_log->debug("Starting T3411\n");
|
nas_log->debug("Starting T3411\n");
|
||||||
timers->get(t3411)->reset();
|
timers->get(t3411)->reset();
|
||||||
timers->get(t3411)->run();
|
timers->get(t3411)->run();
|
||||||
|
|
||||||
if (not p.is_success()) {
|
if (not is_success) {
|
||||||
enter_emm_deregistered();
|
enter_emm_deregistered();
|
||||||
}
|
}
|
||||||
return proc_outcome_t::success;
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
nas_log->error("PLMN selected in state %s\n", emm_state_text[state]);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
callbacks.defer_task([this, result]() {
|
callbacks.defer_proc(rrc_connector);
|
||||||
if (rrc_connector.run()) {
|
rrc_connector.then([this, result](bool is_success) {
|
||||||
return proc_outcome_t::yield;
|
if (is_success) {
|
||||||
}
|
nas_log->info("NAS attached successfully\n");
|
||||||
proc_result_t<rrc_connect_proc> proc = rrc_connector.pop();
|
|
||||||
if (proc.is_success()) {
|
|
||||||
nas_log->info("NAS attached successfully.\n");
|
|
||||||
} else {
|
} else {
|
||||||
nas_log->error("Could not attach from attach_request\n");
|
nas_log->error("Could not attach from attach_request\n");
|
||||||
}
|
}
|
||||||
if (result != nullptr) {
|
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;
|
return proc_outcome_t::success;
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue