-
Notifications
You must be signed in to change notification settings - Fork 3k
KVStore: Add init bit flags for read/write and creation mode #12759
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
Conversation
@pea-pod, thank you for your changes. |
I read the description and I would like to get an additional information appended to it which would explain why all these changes are done/ what is the problem you're trying to solve. |
Thank you for the contribution. This is API extension, without any suggested use cases. |
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.
Thanks for the enum/bitset stuff. Gave me something to think about. I like the idea of having the platform helper, but there's a lot to bikeshed about there.
From API point of view, we could probably pin down what the KVStore::InitModeFlags
looks like to the user - let's say it's a C++ "bitmask type" (https://en.cppreference.com/w/cpp/named_req/BitmaskType). Maybe first pass doesn't use the helper yet, if that takes time to figure out. Exactly what it looks like doesn't matter much to the user - they're just using |
and passing it to functions. It mainly matters in the implementation - how much hassle it is to test the bits.
* ExclusiveCreation. | ||
* | ||
*/ | ||
MBED_SCOPED_ENUM_FLAGS(InitModeFlags) |
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.
Could this be a member of KVStore
? There's precedent for that in create_flags
.
If it is placed inside KVStore
, you might be able to get away with being unscoped, eg values being KVStore::Read
rather than KVStore::InitModeFlags::Read
.
You could probably drop the Flags
at least to get to KVStore::InitMode::Read
. Comparing with https://en.cppreference.com/w/cpp/named_req/BitmaskType, "flags" is usually not explicit.
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 would prefer to keep the enum external to the class. It is possible that this enum class could be useful in other contexts.
{ | ||
Read = (1 << 0), //!< Enable read access from the KVStore | ||
Write = (1 << 1), //!< Enable write access to the KVStore | ||
ReadWrite = ((1 << 0) | (1 << 1)), //!< Enable read and write access to the KVSTore. This is the default. |
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.
You can just say Read | Write
in this context I think.
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.
Done.
|
||
bool KVStore::_has_flags_any(InitModeFlags flags) const | ||
{ | ||
return !(((_flags & flags)) == InitModeFlags::NoFlags); |
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.
Convoluted? Why not
(_flags & flags) != InitModeFlags::NoFlags
or, as per the comment above below
(_flags & flags) != InitModeFlags()
No need to use a named constant for "no flags".
Do you even need the special helper function, as this is no more complex than what you have to do for a single bit? If you do have it, maybe the single bit test should use it - _has_flags_any(InitModeFlags::Write)
...
return false; | ||
} | ||
// Only allow blank Append, etc. if no writing | ||
if (((flags & InitModeFlags::Write) == InitModeFlags::Write) && (wo_flags == 0)) { |
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 sort of verbosity grates, particularly the repetition. I feel it should be fine to say if (flags & X)
. But it isn't :(
Digging into it, I see there is some confusion about "bitmask types". See issue 3092 at https://cplusplus.github.io/LWG/lwg-unresolved.html
And previously there have been notes about stuff assuming what I was thinking, eg: https://cplusplus.github.io/LWG/issue2659
In the latter they settled on if ((flags & options::X) != options::none)
, which is mildly better IMO, if only because it's not repetitive. As the 3092 issue points out though, that could be shortened to if ((flags & options::X) != options())
, which I quite like, visualising the empty bitset.
Still, would have been nice to get the contextual conversion to bool - I see one maniac on the web trying to go fully type safe, distinguishing the "bitmask" type from the "bit value" type, so you get contextual conversion to bool on the result of &
and aren't allowed to accidentally do ==
instead of &
: https://dalzhim.github.io/2017/08/11/Improving-the-enum-class-bitmask/
I find that actually quite tempting. BSD license...
@VeijoPesonen: I can provide more detail on use cases in the PR body for record keeping. I do have non-brief use cases below, but I would not put them in as is in the PR itself. @SeppoTakalo: I am so sorry for the lack of brevity in the following, but here it is. First, I have existing code in a project I was working on (in a different context). This project was based on os version 2. The code simply saved off a struct to NV memory. Fine, except for the wear-leveling, the lack of upgrading, etc. The newer firmware (that I am writing) would also need to handle both fresh hardware, and hardware with data already written to the NVM. As I looked into the TDBStore source, I realized this was both a duplication of efforts, both my check and the TDBStore checks did roughly the same thing. However, when the TDBStore found used memory (but not a TDBStore), it tried to wipe it and go from there. I thought about copying the code out and doing that portion of the check myself but then I came upon these issues:
In any case, that's where I started. It was initially a bool and had this signature: Finally, for the use cases, besides the one I just defined (which I will have to use some form of, whether or not this improvement is accepted), here are each of the flags. Access modeRead only
Write only
Write only but with read keys Comment: on either of the "onlys", if LTO is enabled, and if the code is done properly (meaning my changes are done properly), the linker could even remove the unused code (i.e. if _flags is guaranteed to never have the write flag) which may not be super secure, but more helpful than not, I believe. Creation modeI basically took these from Python, but with bitflags to make sure there weren't magic strings lurking about to pounce. Plus, vscode knows how to read doc strings. The modes are meant to be virtually identical to their use in Python's Append Truncate - Not yet implemented CreateNewOnly Not yet implemented ExclusiveCreation Regarding the complexity, there are three spots to look at, in my opinion. Some of this may be obviated by events if the scoped enum goes in separately, but here are my thoughts in any case: enum class Making this an enum class forces it to a single word and allows it to have namespace access in ways that are all but impossible for a normal enum or its ugly cousin, Code complexity
One last argument here: this is the only class of I/O functionality that I am aware of where when it fails to open/read/etc. it doesn't allow the application developer options on how to deal with it. For networking, you get numerous error codes, state machines, and the like. For file I/O, you get not only failure, but why it failed. If it succeeds, it doesn't do so at the expense of your current data, unless you want it to. In the case of KVStore, only some of the failures are addressed by the class itself. Beyond that, you have to deal with the underlying device, or add functionality externally. And in case this is an issue: I do believe it an apt comparison to fopen, if for no other reason than both obviate the need to deal with the underlying block device. I quite like having to only deal with one level if possible, and I think this would be an appropriate place to do it in. @kjbracey-arm: I will try to work through the suggestions soon. However, if it is not too much to ask, I would like to have more clarity on whether this PR has a chance to make it in. And to all: I'm sorry I let loose the Word Cannon. |
This PR cannot be merged due to conflicts. Please rebase to resolve them. |
880c444
to
f5b8344
Compare
Thanks @pea-pod for providing more details for this addition. @ARMmbed/mbed-os-storage would you be able to review this new feature? |
ahem Would someone review this? |
This PR cannot be merged due to conflicts. Please rebase to resolve them. |
Hi @pea-pod most of our Finnish colleagues are currently on vacation (they tend to take all of July off). We will try and get someone to review again once they are back. |
Thank you, @adbridge. I was hoping this would make it in before the Great Restructuring of 2020, but it seems like that was not in the cards. I understand (at least a little). I used to work for a certain company with an office in Oulu, so I'm familiar with the summer vacations habits of the average Suomalainen. I am still interested in a review. I am holding off on rebasing until the review, at least for the features themselves. If that is not quite good enough, then I will work on it. I would rather not work too much at this if the work itself will not be useful. |
@ARMmbed/mbed-os-core I assume you will take over this one to help reviewing? |
If I say that this is an update for the cmake feature branch, would someone review it, even though it is not? Asking for a friend. |
Thanks for the PR and the effort you put into it. Your original use case was for exclusive creation. I'd rather that you simplify the code and only provide support for that use case. This would remove the other modes which are not implemented (Truncate, CreateNewOnly) and the R/W access modes which I am not convinced add benefits for the common use. You can keep the flags concept (as opposed to a boolean) as it will give more flexibilty to add the other flags if and when needed. |
@pea-pod I'll close this as it has not been updated for more than a month. Please reopen once updated (changes requested above). |
Fair enough. Every week I say to myself, "I'll get to it this week." Every week, it gets to Friday and Saturday and I haven't gotten to it. I will try to move on it soon, but I'll update it before I reopen it. Thanks! |
Summary of changes
Add read, write, and creation mode flags to the initialization method for each KVStore instance using a default parameter. The addition should not cause existing code to fail to compile. These flags use an
enum class
new to C++11.Add documentation to each class implementation regarding new failure values returned in each case if the R/W mode does not match the flag used when init() was called.
Add a platform level macro in
platform/mbed_enum_flags.h
namedMBED_SCOPED_ENUM_FLAGS
which provides bitwise operator overloads (including assignment) to scoped enum types.Add tests to check if the flag is set, the KVStore instances do not create if a valid instance does not already exist.
Move KVStore concrete protected method
_is_valid_flags
to KVStore.cpp and added helper functions for flags.Note: some of the creation modes (truncate, etc.) are not yet implemented. This will be followed by another PR to add these methods, assuming this addition is satisfactory. For now, the unimplemented modes do not fail silently, but return an error.
Impact of changes
New creation methods are allowed if desired. The device can avoid overwriting existing data if the data is valid, but not a KVStore instance. See the
InitModeFlags.h
file for more details.Migration actions required
None.
Documentation
Added documentation to each class which now implements the new feature.
Added documentation to the new macro and the new InitModeFlags enum class.
Pull request type
Test results
Reviewers