Commit 972cd1f0 authored by Martin Marinov's avatar Martin Marinov

Simplify Progress::Context and Progress::Node and adapt for use in...

Simplify Progress::Context and Progress::Node and adapt for use in asynchronous tracking and abort requests.
parent b0ad4d15
......@@ -10,39 +10,49 @@
namespace Progress {
/// Context implementation
void Context::begin_track(Node* _node, const TrackFunction _trck_fnct)
{
if (_node == nullptr || _trck_fnct == nullptr)
return;
actv_node_ = _node;
_node->enter(nullptr);
trck_fnct_ = _trck_fnct;
}
void Context::end_track()
{
if (!tracking())
return;
actv_node_->exit();
actv_node_ = nullptr;
trck_fnct_ = nullptr;
}
Context::Context()
: abrt_stte_(AST_NONE), phny_root_("phony"),
root_node_(&phny_root_), actv_node_(&phny_root_)
{}
void Context::begin_abort()
void Context::abort()
{
abrt_ = true;
abrt_stte_ = AST_PROCESSING;
BASE_THROW_ERROR(PROGRESS_ABORTED);
}
void Context::resume_abort()
{
if (abrt_)
if (abrt_stte_ == AST_PROCESSING)
BASE_THROW_ERROR(PROGRESS_ABORTED);
}
void Context::end_abort()
{
abrt_ = false;
abrt_stte_ = AST_NONE;
}
void Context::enter_node(Node* _node)
{ // NOTE: this function is written carefully to allow asynchronous inspection
// of the node graph from a tracking thread. It assumes the tracking thread
// does not use the node parent.
_node->tick_nmbr_ = 0;
if (phony())
root_node_ = _node;
else
_node->prnt_ = actv_node_;
actv_node_ = _node;
}
void Context::exit_node()
{ // NOTE: this function is written carefully to allow asynchronous inspection
// of the node graph from a tracking thread. It assumes the tracking thread
// does not use the node parent.
if (phony())
return; // TODO: this condition is a bug, so we need a DEB_error() here?
auto* const prnt = actv_node_->prnt_;
actv_node_->prnt_ = nullptr;
actv_node_ = prnt == nullptr ? &phny_root_ : prnt;
}
thread_local Context cntx;
......@@ -51,9 +61,9 @@ thread_local Context cntx;
Node::~Node()
{}
void Node::on_tick_number_max_exceeded()
TickNumber Node::tick_number_max() const
{
tick_nmbr_max_ *= 2;
return tick_nmbr_;
}
} //namespace Progress
......
......@@ -12,55 +12,41 @@ typedef unsigned long long TickNumber;
//! Represent an operational node in the progress graph
class Node
{
public:
typedef Node* (*MakeNodeFunction)(Node* _next);
static inline Node* make_child_list() { return nullptr; }
template <typename... ArgT>
static inline Node* make_child_list(MakeNodeFunction _make_node_fnct,
const ArgT&... _args)
{
return (*_make_node_fnct)(make_child_list(_args...));
}
public:
const char* const name;
Node(const char* const _name,
Node* _next = nullptr, Node* _chld = nullptr)
: name(_name), next_(_next), chld_(_chld), prnt_(nullptr), tick_nmbr_(0),
tick_nmbr_max_(1)
: name(_name), next_(_next), chld_(_chld), prnt_(nullptr), tick_nmbr_(0)
{}
virtual ~Node();
const Node* next() const { return next_; };
const Node* child() const { return chld_; };
const Node* parent() const { return prnt_; };
TickNumber tick_number() const { return tick_nmbr_; }
TickNumber tick_number_max() const { return tick_nmbr_max_; }
virtual TickNumber tick_number_max() const;
void tick()
{
++tick_nmbr_;
if (tick_nmbr_ > tick_nmbr_max_)
on_tick_number_max_exceeded();
}
void tick() { ++tick_nmbr_; }
protected:
Node* next_; //!< next node on the same level, last if nullptr
Node* chld_; //!< first child node, leaf if nullptr
Node* prnt_; //!< parent node, set temporarily
protected:
TickNumber tick_nmbr_;
TickNumber tick_nmbr_max_;
protected:
virtual void on_tick_number_max_exceeded();
void enter(Node* _prnt)
{
prnt_ = _prnt;
tick_nmbr_ = 0;
}
Node* exit()
{
Node* prnt = prnt_;
prnt_ = nullptr;
return prnt;
}
TickNumber tick_nmbr_; // number of ticks in the node
friend class Context;
};
......@@ -68,25 +54,31 @@ protected:
class Context
{
public:
//! Progress tracking function type
typedef void (*TrackFunction)(Node* _node,
const char* const _flnm, const char* const _fnct);
//! Default constructor
Context();
/*!
Get if the progress is "faking" tracking, i.e., using a phony root node.
Phony tracking is faster than checking if a root has been set.
*/
bool phony() const { return actv_node_ == &phny_root_; }
public:
/*!
Get if the Context is inactive, that is, the progress is being aborted, or
the progress is not being tracked.
Get the currently active node, only makes sense to use that if not phony().
*/
bool inactive() const { return abrt_ || actv_node_ == nullptr; }
const Node* active_node() const { return actv_node_; }
void begin_track(Node* _node, const TrackFunction _trck_fnct);
bool tracking() const { return actv_node_ != nullptr; }
void end_track();
/*!
Get the root node, only makes sense to use that if not phony().
*/
const Node* root_node() const { return root_node_; }
/*!
Throws PROGRESS_ABORTED to abort the Progress of the current operation.
Request progress abort, intended to be called asynchronously from a "tracking"
thread.
The abort will be triggered on the next \ref tick() in the context thread.
*/
void begin_abort();
void request_abort() { abrt_stte_ = AST_REQUEST; }
/*!
Resume an interrupted abort if the exception could be eaten unintentionally,
......@@ -101,72 +93,40 @@ public:
*/
void end_abort();
Node* active_node() const { return actv_node_; }
void tick(const char* const _flnm, const char* const _fnct)
void tick(const char* const /*_fnct*/)
{
if (inactive())
return;
actv_node_->tick();
(*trck_fnct_)(actv_node_, _flnm, _fnct);
if (abrt_stte_ == AST_REQUEST)
abort();
}
void enter_node(Node* _node)
{
if (!tracking())
return; // exit if not tracking
_node->enter(actv_node_);
actv_node_ = _node;
}
void enter_node(Node* _node);
void exit_node();
void exit_node()
private:
enum AbortStateType
{
if (tracking())
actv_node_ = actv_node_->exit();
}
AST_NONE,
AST_REQUEST,
AST_PROCESSING
};
private:
bool abrt_ = false; // aborting flag
Node* actv_node_ = nullptr; // active node
TrackFunction trck_fnct_ = nullptr; //!< Progress current tracking function
AbortStateType abrt_stte_; //!< Abort state
Node phny_root_; //!< "phony" root
Node* root_node_; //!< Root node
Node* actv_node_; //!< Active node
private:
//! Throws PROGRESS_ABORTED to abort the current operation.
void abort();
};
//! the current thread Progress context
extern thread_local Context cntx;
struct ActiveScope
{
ActiveScope(Node* _node) { cntx.enter_node(_node); }
~ActiveScope() { cntx.exit_node(); }
};
typedef Node* (*MakeNodeFunction)(Node* _next);
inline Node* make_children_list() { return nullptr; }
template <typename... ArgT>
inline Node* make_children_list(MakeNodeFunction _make_node_fnct,
const ArgT&... _args)
{
return (*_make_node_fnct)(make_children_list(_args...));
}
} //namespace Progress
#define PROGRESS_NODE_CHILDREN(...) Progress::make_children_list(##__VA_ARGS__)
#define PROGRESS_DEFINE_NODE(OPRT, ...) \
thread_local Progress::Node* Progress::PROGRESS_NODE_NAME(OPRT) = nullptr; \
Progress::Node* Progress::PROGRESS_MAKE_NODE_NAME(OPRT) \
(Progress::Node* _next) \
{ \
static thread_local Node node(#OPRT, _next, \
PROGRESS_NODE_CHILDREN(##__VA_ARGS__));\
return OPRT##_node = &node; \
}
#define PROGRESS_NODE_NAME(OPRT) OPRT##_node
#define PROGRESS_MAKE_NODE_NAME(OPRT) make_##OPRT##_node
......@@ -177,19 +137,33 @@ inline Node* make_children_list(MakeNodeFunction _make_node_fnct,
} //namespace Progress {
#define PROGRESS_DEFINE_NODE_CUSTOM(TYPE, OPRT, ...) \
thread_local Progress::Node* Progress::PROGRESS_NODE_NAME(OPRT) = nullptr; \
Progress::Node* Progress::PROGRESS_MAKE_NODE_NAME(OPRT) \
(Progress::Node* _next) \
{ \
static thread_local TYPE node(#OPRT, _next, \
Node::make_child_list(##__VA_ARGS__));\
return OPRT##_node = &node; \
}
#define PROGRESS_DEFINE_NODE(OPRT, ...) \
PROGRESS_DEFINE_NODE_CUSTOM(Node, OPRT, ##__VA_ARGS__)
#if defined(_MSC_VER)
#define __PROGRESS_FUNCTION__ __FUNCTION__ // works in VC well
#else
#define __PROGRESS_FUNCTION__ __PRETTY_FUNCTION__ // needed for gcc & xcode
#endif// _MSC_VER
#define PROGRESS_TICK { Progress::cntx.tick(__FILE__, __PROGRESS_FUNCTION__); }
#define PROGRESS_TICK { Progress::cntx.tick(__PROGRESS_FUNCTION__); }
#define PROGRESS_RESUME_ABORT { Progress::cntx.resume_abort(); }
#define PROGRESS_END_ABORT { Progress::cntx.end_abort(); }
#else
#define PROGRESS_DECLARE_NODE(OPRT)
#define PROGRESS_DEFINE_NODE_CUSTOM(TYPE, OPRT, ...)
#define PROGRESS_DEFINE_NODE(OPRT, ...)
#define PROGRESS_TICK
#define PROGRESS_RESUME_ABORT
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment