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.
318 lines
11 KiB
C
318 lines
11 KiB
C
6 years ago
|
/*
|
||
|
* 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>
|
||
|
|
||
|
#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 };
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* class: proc_impl_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.
|
||
|
* 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
|
||
|
* - stop() - 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_impl_t
|
||
|
{
|
||
|
public:
|
||
|
proc_impl_t() : proc_state(proc_state_t::inactive) {}
|
||
|
virtual ~proc_impl_t() = default;
|
||
|
virtual proc_outcome_t step() = 0;
|
||
|
virtual void stop() {} // may be overloaded
|
||
|
|
||
|
bool run()
|
||
|
{
|
||
|
proc_outcome_t outcome = proc_outcome_t::repeat;
|
||
|
while (is_running() and outcome == proc_outcome_t::repeat) {
|
||
|
outcome = step();
|
||
|
if (outcome == proc_outcome_t::error) {
|
||
|
set_proc_state(proc_state_t::error);
|
||
|
} else if (outcome == proc_outcome_t::success) {
|
||
|
set_proc_state(proc_state_t::success);
|
||
|
}
|
||
|
}
|
||
|
return is_running();
|
||
|
}
|
||
|
|
||
|
void set_proc_state(proc_state_t new_state)
|
||
|
{
|
||
|
proc_state = new_state;
|
||
|
if (proc_state == proc_state_t::error or proc_state == proc_state_t::success) {
|
||
|
stop();
|
||
|
}
|
||
|
}
|
||
|
bool is_error() const { return proc_state == proc_state_t::error; }
|
||
|
bool is_success() const { return proc_state == proc_state_t::success; }
|
||
|
bool is_running() const { return proc_state == proc_state_t::on_going; }
|
||
|
bool is_complete() const { return is_success() or is_error(); }
|
||
|
bool is_active() const { return proc_state != proc_state_t::inactive; }
|
||
|
|
||
|
private:
|
||
|
proc_state_t proc_state;
|
||
|
};
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* class: proc_t<T>
|
||
|
* Handles the lifetime, of a procedure T that derives from proc_impl_t, including
|
||
|
* its alloc, initialization, and reset back to initial state once the procedure has been
|
||
|
* completed and the user has extracted its results.
|
||
|
* 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 proc_impl_t::run(). See above.
|
||
|
* - 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
|
||
|
{
|
||
|
public:
|
||
|
explicit proc_t() : proc_impl_ptr(new T()) {}
|
||
|
T* get() { return proc_impl_ptr.get(); }
|
||
|
bool is_active() const { return proc_impl_ptr->is_active(); }
|
||
|
bool is_complete() const { return proc_impl_ptr->is_complete(); }
|
||
|
T* release() { return proc_impl_ptr.release(); }
|
||
|
bool run() { return proc_impl_ptr->run(); }
|
||
|
void clear()
|
||
|
{
|
||
|
// Destructs the current object, and calls default ctor (which sets proc back to inactive and ready for another run)
|
||
|
proc_impl_ptr->~T();
|
||
|
new (proc_impl_ptr.get()) T();
|
||
|
}
|
||
|
|
||
|
template <class Event>
|
||
|
void trigger_event(Event&& e)
|
||
|
{
|
||
|
if (proc_impl_ptr->is_running()) {
|
||
|
proc_outcome_t outcome = proc_impl_ptr->trigger_event(std::forward<Event>(e));
|
||
|
if (outcome == proc_outcome_t::error) {
|
||
|
proc_impl_ptr->set_proc_state(proc_state_t::error);
|
||
|
} else if (outcome == proc_outcome_t::success) {
|
||
|
proc_impl_ptr->set_proc_state(proc_state_t::success);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
T pop()
|
||
|
{
|
||
|
if (not proc_impl_ptr->is_complete()) {
|
||
|
return T();
|
||
|
}
|
||
|
T ret(std::move(*proc_impl_ptr));
|
||
|
clear();
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
template <class... Args>
|
||
|
bool launch(Args&&... args)
|
||
|
{
|
||
|
if (is_active()) {
|
||
|
// if already active
|
||
|
return false;
|
||
|
}
|
||
|
proc_impl_ptr->set_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:
|
||
|
proc_impl_ptr->set_proc_state(proc_state_t::error); // call stop as an error
|
||
|
clear();
|
||
|
return false;
|
||
|
case proc_outcome_t::success:
|
||
|
proc_impl_ptr->set_proc_state(proc_state_t::success);
|
||
|
break;
|
||
|
case proc_outcome_t::repeat:
|
||
|
run(); // call run right away
|
||
|
break;
|
||
|
case proc_outcome_t::yield:
|
||
|
break;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
std::unique_ptr<T> proc_impl_ptr;
|
||
|
};
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* class: func_proc_t
|
||
|
* A proc_impl_t 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 proc_impl_t
|
||
|
{
|
||
|
public:
|
||
|
proc_outcome_t init(std::function<proc_outcome_t()> step_func_)
|
||
|
{
|
||
|
step_func = std::move(step_func_);
|
||
|
return proc_outcome_t::yield;
|
||
|
}
|
||
|
|
||
|
proc_outcome_t step() final { return step_func(); }
|
||
|
|
||
|
private:
|
||
|
std::function<proc_outcome_t()> step_func;
|
||
|
};
|
||
|
|
||
|
/**************************************************************************************
|
||
|
* class: func_proc_t
|
||
|
* A helper proc_impl_t whose step()/stop() 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_impl_t
|
||
|
{
|
||
|
public:
|
||
|
proc_outcome_t init() { return proc_outcome_t::yield; }
|
||
|
proc_outcome_t step() final { 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: callback_list_t
|
||
|
* Stores procedures that derive from proc_impl_t. Its run() method 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 via proc_t<T>::pop()
|
||
|
* - 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 callback_list_t
|
||
|
{
|
||
|
public:
|
||
|
typedef std::function<void(proc_impl_t*)> proc_deleter_t;
|
||
|
typedef std::unique_ptr<proc_impl_t, proc_deleter_t> callback_obj_t;
|
||
|
template <class T>
|
||
|
struct recycle_deleter_t {
|
||
|
void operator()(proc_impl_t* p)
|
||
|
{
|
||
|
if (p != nullptr) {
|
||
|
T* Tp = static_cast<T*>(p);
|
||
|
Tp->~T();
|
||
|
new (Tp) T();
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template <class T>
|
||
|
void add_proc(proc_t<T>& proc)
|
||
|
{
|
||
|
if (proc.is_complete()) {
|
||
|
return;
|
||
|
}
|
||
|
callback_obj_t ptr(proc.get(), [](proc_impl_t* p) { /* do nothing */ });
|
||
|
callbacks.push_back(std::move(ptr));
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void consume_proc(proc_t<T>&& proc)
|
||
|
{
|
||
|
if (proc.is_complete()) {
|
||
|
return;
|
||
|
}
|
||
|
callback_obj_t ptr(proc.release(), std::default_delete<proc_impl_t>());
|
||
|
callbacks.push_back(std::move(ptr));
|
||
|
}
|
||
|
|
||
|
template <class T>
|
||
|
void defer_proc(proc_t<T>& proc)
|
||
|
{
|
||
|
if (proc.is_complete()) {
|
||
|
proc.pop();
|
||
|
return;
|
||
|
}
|
||
|
callback_obj_t ptr(proc.get(), recycle_deleter_t<T>());
|
||
|
callbacks.push_back(std::move(ptr));
|
||
|
}
|
||
|
|
||
|
bool defer_task(std::function<proc_outcome_t()> step_func)
|
||
|
{
|
||
|
proc_t<func_proc_t> proc;
|
||
|
if (not proc.launch(std::move(step_func))) {
|
||
|
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.
|
||
|
callbacks.remove_if([](callback_obj_t& elem) { return not elem->run(); });
|
||
|
}
|
||
|
|
||
|
size_t size() const { return callbacks.size(); }
|
||
|
|
||
|
private:
|
||
|
std::list<callback_obj_t> callbacks;
|
||
|
};
|
||
|
|
||
|
} // namespace srslte
|
||
|
|
||
|
#endif // SRSLTE_RESUMABLE_PROCEDURES_H
|