-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Loop validated cache #79
base: master
Are you sure you want to change the base?
Conversation
Updated integer types to uint32, reordered the loop increment to better handle the first loop (includes setting id to uint32(-1) ) Tested on hardware locally.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good. A few small things to discuss/document.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Objections to LoopId
, but looking good otherwise.
class LoopId | ||
{ | ||
public: | ||
/** | ||
* @brief Returns the current loop id | ||
* | ||
* @return uint32_t | ||
*/ | ||
static uint32_t get(){ | ||
return loop_id_; | ||
} | ||
|
||
/** | ||
* @brief Override the current loop id | ||
* | ||
* @param id_des | ||
*/ | ||
static void set(uint32_t id_des){ | ||
loop_id_ = id_des; | ||
} | ||
|
||
/** | ||
* @brief increment and return the loop id, note | ||
* @note there can only be ONE loop using this type of cache at once to avoid | ||
* prematurely invalidating caches, MAKE SURE THIS IS WHAT YOU WANT | ||
* | ||
* @return uint32_t | ||
*/ | ||
static uint32_t increment(){ | ||
return (++loop_id_); | ||
} | ||
|
||
protected: | ||
/** | ||
* @brief Loop id shared between all instances acts a "global" loop id | ||
* | ||
*/ | ||
static uint32_t loop_id_; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a mutable singleton with some possible issues.
First, philosophically, why use a singleton here? It goes against the ISO guidelines, and the resulting behavior could instead be maintained in a local scope. E.g., a loop ID is maintained in the control loop and all ValdatedLoopCache
s are constructed with a reference/pointer to that ID (and can use the same invalidation logic).
That said, since this is a pretty high-level, integral thing, then a singleton could be fine. But:
- this singleton could suffer from the static initialization order fiasco if
LoopId
were to be referenced in another static variable initialization; loop_id_
can be arbitrarily modified by children ofLoopId
;LoopId
can be multiply constructed (which could be confusing).
You could alleviate some of these issues by making LoopId
a Meyer's Singleton. I think something like this would work.
class LoopId {
public:
static LoopId& get() {
static LoopId loop_id;
return loop_id;
}
static void set(uint32_t id) {
auto loop_id = LoopId::get();
loop_id = id;
}
static void increment() {
auto loop_id = LoopId::get();
loop_id++;
}
LoopId(const LoopId&) = delete;
LoopId& operator=(const LoopId&) = delete;
private:
LoopId() {}
~LoopId() {}
};
These changes were requested in a PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, besides why loop_cache.cpp exists.
@@ -0,0 +1,13 @@ | |||
/** |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the purpose of this file?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just placeholder making sure the new version is good. Could move implementation into it if that is something we want, but otherwise I will delete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm always of the opinion that the implementation should live in the source file when possible.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since this is not templated, it's an easy move, so I will.
@ethanmusser Thoughts about this implementation? Took some of your advice, I think it should be ok now, and maybe even threadsafe with the addition of atomic. |
{ | ||
data_ = data; | ||
valid_ = true; | ||
last_loop_id_ = loop_id_.get(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
last_loop_id_ = loop_id_.get(); | |
last_loop_id_ = LoopId::get(); |
|
||
protected: | ||
uint32_t last_loop_id_ = -1; // loop id from the last set event, used to invalidate | ||
LoopId loop_id_; // Loop id, tied to control loop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LoopId loop_id_; // Loop id, tied to control loop |
Adds a Loop-tied cache class and integrates into the control loop.
Key changes:
ValidatedCache
member functions for inheritanceLoopId
class, that houses static member we use to invalidateValidatedLoopCache
class instancesuint32_t
, which is large enough to avoid repeats offering unique loop idsValidatedLoopCache
class which auto invalidates when the control loop updatesMJBotsControlLoop
incrementsLoopId
before every call ofUpdate()
Design decisions / possible issues:
uint32_t last_loop_id_
andLoopId loop_id_
members in the cache increases the memory overhead, especially for caching small data types, could be smaller if we useuint8_t
but without the same uniqueness properties (at 1kHz,uint8_t
would repeat 4 times a second)operator=
overload to make using caches easier, but it does hide the validation from the user and could lead to mistakesThis was tested locally on hardware using print statements in a simple control loop.