mirror of https://github.com/pvnis/srsRAN_4G.git
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
429 lines
14 KiB
C++
429 lines
14 KiB
C++
/*
|
|
* Copyright 2013-2019 Software Radio Systems Limited
|
|
*
|
|
* This file is part of srsLTE.
|
|
*
|
|
* srsLTE is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as
|
|
* published by the Free Software Foundation, either version 3 of
|
|
* the License, or (at your option) any later version.
|
|
*
|
|
* srsLTE is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* A copy of the GNU Affero General Public License can be found in
|
|
* the LICENSE file in the top-level directory of this distribution
|
|
* and at http://www.gnu.org/licenses/.
|
|
*
|
|
*/
|
|
|
|
#include <functional>
|
|
#include <list>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <vector>
|
|
|
|
#ifndef SRSLTE_RESUMABLE_PROCEDURES_H
|
|
#define SRSLTE_RESUMABLE_PROCEDURES_H
|
|
|
|
namespace srslte {
|
|
|
|
enum class proc_state_t { on_going, success, error, inactive };
|
|
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_complete(T* obj, int is_success) -> decltype(obj->on_complete(is_success))
|
|
{
|
|
obj->on_complete(is_success);
|
|
}
|
|
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 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
|
|
* - step() - method overriden by child class that will be called by run(). step()
|
|
* executes a procedure "action" based on its current internal state,
|
|
* and return a proc_outcome_t variable with possible values:
|
|
* - yield - the procedure performed the action but hasn't completed yet.
|
|
* - repeat - the same as yield, but explicitly asking that run() should
|
|
* recall step() again (probably the procedure state has changed)
|
|
* - error - the procedure has finished unsuccessfully
|
|
* - success - the procedure has completed successfully
|
|
* - 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
|
|
************************************************************************************/
|
|
class proc_manager_itf_t
|
|
{
|
|
public:
|
|
virtual bool run() = 0;
|
|
virtual ~proc_manager_itf_t() = default;
|
|
};
|
|
|
|
template <typename T>
|
|
struct proc_result_t;
|
|
|
|
/**************************************************************************************
|
|
* class: proc_manager_t<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.
|
|
* - 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.
|
|
* - pop() - extracts the result of the procedure if it has finished, and sets
|
|
* proc_t<T> back to inactive
|
|
* - trigger_event(Event) - used for handling external events. The procedure T will
|
|
* have to define a method "trigger_event(Event)" as well,
|
|
* specifying how each event type should be handled.
|
|
************************************************************************************/
|
|
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)...))
|
|
{
|
|
}
|
|
|
|
bool run() override
|
|
{
|
|
proc_outcome_t outcome = proc_outcome_t::repeat;
|
|
while (is_running() and outcome == proc_outcome_t::repeat) {
|
|
outcome = proc_impl_ptr->step();
|
|
handle_outcome(outcome);
|
|
}
|
|
return is_running();
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
// resets procedure and sets proc_t back to inactive
|
|
detail::optional_clear(proc_impl_ptr.get());
|
|
proc_state = proc_state_t::inactive;
|
|
}
|
|
|
|
const T* get() const { return proc_impl_ptr.get(); }
|
|
bool is_active() const { return proc_state != proc_state_t::inactive; }
|
|
bool is_complete() const { return proc_state == proc_state_t::success or proc_state == proc_state_t::error; }
|
|
T* release() { return proc_impl_ptr.release(); }
|
|
|
|
template <class Event>
|
|
void trigger_event(Event&& e)
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
|
|
template <class... Args>
|
|
bool launch(Args&&... args)
|
|
{
|
|
if (is_active()) {
|
|
// if already active
|
|
return false;
|
|
}
|
|
proc_state = proc_state_t::on_going;
|
|
proc_outcome_t init_ret = proc_impl_ptr->init(std::forward<Args>(args)...);
|
|
switch (init_ret) {
|
|
case proc_outcome_t::error:
|
|
handle_outcome(init_ret);
|
|
clear();
|
|
return false;
|
|
case proc_outcome_t::success:
|
|
handle_outcome(init_ret);
|
|
// does not reset, and returns true
|
|
break;
|
|
case proc_outcome_t::repeat:
|
|
run(); // call run right away
|
|
break;
|
|
case proc_outcome_t::yield:
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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)
|
|
{
|
|
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;
|
|
complete_callback_list_t complete_callbacks;
|
|
};
|
|
|
|
template <typename T>
|
|
struct proc_result_t {
|
|
explicit proc_result_t(proc_t<T>* parent_) : parent(parent_) {}
|
|
~proc_result_t()
|
|
{
|
|
if (parent->is_complete()) {
|
|
parent->clear();
|
|
}
|
|
}
|
|
const T* proc() const { return parent->is_complete() ? parent->proc_impl_ptr.get() : nullptr; }
|
|
bool is_success() const { return parent->proc_state == proc_state_t::success; }
|
|
bool is_error() const { return parent->proc_state == proc_state_t::error; }
|
|
bool is_complete() const { return parent->is_complete(); }
|
|
|
|
private:
|
|
proc_t<T>* parent;
|
|
};
|
|
|
|
template <typename T>
|
|
proc_result_t<T> proc_t<T>::pop()
|
|
{
|
|
return proc_result_t<T>{this};
|
|
}
|
|
|
|
/**************************************************************************************
|
|
* class: func_proc_t
|
|
* A proc used to store lambda functions and other function pointers as a step()
|
|
* method, avoiding this way, always having to create a new class per procedure.
|
|
************************************************************************************/
|
|
class func_proc_t
|
|
{
|
|
public:
|
|
explicit func_proc_t(std::function<proc_outcome_t()> step_func_) : step_func(std::move(step_func_)) {}
|
|
proc_outcome_t init() { return proc_outcome_t::yield; }
|
|
proc_outcome_t step() { return step_func(); }
|
|
|
|
private:
|
|
std::function<proc_outcome_t()> step_func;
|
|
};
|
|
|
|
/**************************************************************************************
|
|
* class: query_proc_t
|
|
* 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>
|
|
class query_proc_t
|
|
{
|
|
public:
|
|
proc_outcome_t init() { return proc_outcome_t::yield; }
|
|
proc_outcome_t step() { return proc_outcome_t::yield; }
|
|
|
|
proc_outcome_t trigger_event(const OutcomeType& outcome_)
|
|
{
|
|
outcome = outcome_;
|
|
return proc_outcome_t::success;
|
|
}
|
|
|
|
const OutcomeType& result() const { return outcome; }
|
|
|
|
private:
|
|
OutcomeType outcome;
|
|
};
|
|
|
|
/**************************************************************************************
|
|
* class: proc_manager_list_t
|
|
* Stores procedure managers and, when run() is called, calls sequentially all
|
|
* the stored procedures run() method, and removes the procedures if they have
|
|
* completed.
|
|
* There are different ways to add a procedure to the list:
|
|
* - add_proc(...) - adds a proc_t<T>, and once the procedure has completed, takes it
|
|
* out of the container without resetting it back to its initial state
|
|
* or deleting. This is useful, if the user wants to extract the
|
|
* procedure result after it has been taken off the manager list.
|
|
* "proc" variable has to outlive its completion
|
|
* - consume_proc(...) - receives a proc_t<T> as a rvalue, and calls the proc_t<T>
|
|
* destructor once the procedure has ended. Useful, for procedures
|
|
* for which the user is not interested in the result, or reusing
|
|
* - defer_proc(...) - same as add_proc(...), but once the procedure has finished, it
|
|
* automatically sets the procedure back to its initial state.
|
|
* Useful if the user is not interested in handling the result
|
|
* - defer_task(...) - same as consume_proc(...) but takes a function pointer that
|
|
* specifies a proc_impl_t step() function
|
|
************************************************************************************/
|
|
class proc_manager_list_t
|
|
{
|
|
using proc_deleter_t = std::function<void(proc_manager_itf_t*)>;
|
|
using proc_obj_t = std::unique_ptr<proc_manager_itf_t, proc_deleter_t>;
|
|
|
|
template <typename T>
|
|
struct recycle_deleter_t {
|
|
void operator()(proc_manager_itf_t* p)
|
|
{
|
|
if (p != nullptr) {
|
|
T* Tp = static_cast<T*>(p);
|
|
Tp->clear();
|
|
// just resets back to inactive, and does not dealloc
|
|
}
|
|
}
|
|
};
|
|
|
|
public:
|
|
template <typename T>
|
|
void add_proc(proc_t<T>& proc)
|
|
{
|
|
if (proc.is_complete()) {
|
|
return;
|
|
}
|
|
proc_obj_t ptr(&proc, [](proc_manager_itf_t* p) { /* do nothing */ });
|
|
proc_list.push_back(std::move(ptr));
|
|
}
|
|
|
|
template <class T>
|
|
void consume_proc(proc_t<T>&& proc)
|
|
{
|
|
if (proc.is_complete()) {
|
|
return;
|
|
}
|
|
proc_obj_t ptr(new proc_t<T>(std::move(proc)), std::default_delete<proc_manager_itf_t>());
|
|
proc_list.push_back(std::move(ptr));
|
|
}
|
|
|
|
template <typename T>
|
|
void defer_proc(proc_t<T>& proc)
|
|
{
|
|
if (proc.is_complete()) {
|
|
proc.clear();
|
|
return;
|
|
}
|
|
proc_obj_t ptr(&proc, recycle_deleter_t<proc_t<T> >());
|
|
proc_list.push_back(std::move(ptr));
|
|
}
|
|
|
|
bool defer_task(std::function<proc_outcome_t()> step_func)
|
|
{
|
|
proc_t<func_proc_t> proc(std::move(step_func));
|
|
if (not proc.launch()) {
|
|
return false;
|
|
}
|
|
consume_proc(std::move(proc));
|
|
return true;
|
|
}
|
|
|
|
void run()
|
|
{
|
|
// Calls run for all callbacks. Remove the ones that have finished. The proc dtor is called.
|
|
proc_list.remove_if([](proc_obj_t& elem) { return not elem->run(); });
|
|
}
|
|
|
|
size_t size() const { return proc_list.size(); }
|
|
|
|
private:
|
|
std::list<proc_obj_t> proc_list;
|
|
};
|
|
|
|
} // namespace srslte
|
|
|
|
#endif // SRSLTE_RESUMABLE_PROCEDURES_H
|