-
Notifications
You must be signed in to change notification settings - Fork 931
Assert that the file isnt open in lfs_file_opencfg #443
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
Assert that the file isnt open in lfs_file_opencfg #443
Conversation
|
I think I'd prefer the operation failed by returning an error, rather than doing an assert |
I think that checking every time for something that is really a user error is not needed, but I will accept this if more people prefer checks. If your program hangs in littlefs I expect that you will compile littlefs in debug and then you will see the problem. In a different matter, I really need to find a way to check if the struct was initialized, because right now I access uninitialized memory :( |
647bfd1 to
3701b2a
Compare
|
I definitely prefer the return code. I think asserts are heavily overused in lfs. there are many cases where my application would benefit from handling return codes rather than the asserting. It is a user api. Errors should be propagated to the user. library code should only assert in fatal cases, not misuse. Also FWIW, it is not necessarily a user error to open an already open file. this is a perfectly valid case. For instance, we have a logging module that logs to the filesystem. We also have a remote protocol that can perform filesystem operations. It is a valid scenario to attempt to remotely read a file that may or may not be open by the logging framework. (this is exactly what brought me to this issue). As of right now, the remote read reports the file has zero bytes, which is not correct. An error reporting it is already open would be very useful. |
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.
Besides a check whether a file is already opened also a check whether a dir is already opened is suggested.
Error reporting rather than an Assert sounds better to me. For our multi-threading system the same files can be attempted to be opened. An internal check whether opening is possible can give added value.
lfs.c
Outdated
| ".buffer=%p, .attrs=%p, .attr_count=%"PRIu32"})", | ||
| (void*)lfs, (void*)file, path, flags, | ||
| (void*)cfg, cfg->buffer, (void*)cfg->attrs, cfg->attr_count); | ||
| LFS_ASSERT(is_node_not_in_list(lfs->mlist, (struct lfs_mlist*)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.
This check can also be added to the lfs_dir_open function:
LFS_ASSERT(is_node_not_in_list(lfs->mlist, (struct lfs_mlist*)dir));
This requires the static function to be declared earlier. Other static functions are declared at the top of the 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.
added, thanks for the feedback!
3701b2a to
a36f0a9
Compare
|
Guys, I am not sure why you should return error on such cases. Iterating over all open files in each open call seems costly, so I think we can stick to an assert. You can open the same file twice, just not with the same struct... |
Oh in our testing, we were unable to open and read a file twice with different structures.
Do you expect, in this scenario, the hdl2 read to return the bytes written by hdl1 ? Maybe our test case was buggy, but we concluded that we could not read a file that was already open. If this is not the case, then thats great. |
|
Also, isnt it still checking every open file in the assert? How is asserting any different than returning in terms of complexity ? |
|
I will check the test case you mentioned, thanks! And assert is better because it is not complied in, unless on debug |
|
Hi @NoahGorny, thanks for creating a PR! Sorry to leave this hanging for so long. This is a good addition. I wonder if it would also be good to replace LFS_F_OPENED (#237) so we have the same check for open/close files that doesn't get tripped up by uninitialized memory. Thoughts? My opinion is this check should assert. For a code size reason, not a usability reason. Asserts can be removed during compilation, return codes can not. And for microcontroller-scale programs, you really don't want to pay for code that should never run. Also the current behavior for these types of user checks is to assert, if we want to change this behavior it should be done via a different issue or PR. |
|
|
||
| // deorphan if we haven't yet, needed at most once after poweron | ||
| if ((flags & 3) != LFS_O_RDONLY) { | ||
| if ((flags & LFS_O_RDWR) != LFS_O_RDONLY) { |
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.
👍
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 changed in all places now 😄
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.
Ah I misread this. Well, I don't really have an opinion on this change. It is a curious mask.
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 think this is better than a magic num
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.
Can't argue with that
lfs.c
Outdated
| } | ||
|
|
||
| static inline bool is_node_not_in_list(struct lfs_mlist *head, | ||
| struct lfs_mlist *node) { |
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.
style nit: Two indents when inside parens (I realize I don't have this documented anywhere...)
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
lfs.c
Outdated
| superblock->attr_max = lfs_tole32(superblock->attr_max); | ||
| } | ||
|
|
||
| static inline bool is_node_not_in_list(struct lfs_mlist *head, |
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.
nit: Prefix with lfs_. Thoughts on lfs_mlist_isopen for consistency with the other functions?
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, changed to lfs_mlist_isused
If LFS_F_OPENED (#237) is replaced, it might also be a good addition to add the isOpened API mentioned in #442. But then based on lfs_is_node_not_in_list instead of LFS_F_OPENED |
a36f0a9 to
eff46f1
Compare
I agree, and added commits that deprecate LFS_F_OPENED. It required me to sometimes add files to the mlist when we did not do so before, so you might wanna take a look at 0250015 and 0ca3012 to tell me whether my solution is okay or not. Thanks alot for your feedback btw @geky |
I think @geky should voice his opinion here. I think that currently with the assert not fully catching all cases (opened with different struct) it should not be public API yet... maybe after we catch/handle all such cases |
@judgeh It indeed fails with my tests, I created a test (on a separate PR) that fails on this (ad995ce) |
Ah, this looks like the same issue being discussed in #483. Thumbing up #483 would help know who's ran into this. Also thanks for the test case! TLDR: Currently, littlefs is transactional in the sense that each open file handle gets its own snapshot. Each file handle can be read/written independently (and independently from disk). Something that came up in #483, we could go through all open files and throw out other changes on sync/close, it would just take more code size. More info in #483. |
lfs.c
Outdated
| return false; | ||
| } | ||
|
|
||
| static inline void lfs_mlist_add(lfs_t *lfs, struct lfs_mlist *mlist) { |
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.
If we add this, lfs_mlist_remove would be more valuable:
https://github.com/littlefs-project/littlefs/blob/4c9146ea539f72749d6cc3ea076372a81b12cb11/lfs.c#L2553-L2559
Also a nit: I think lfs_mlist_append captures the data structure operation better.
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 both!
lfs.h
Outdated
| LFS_F_ERRED = 0x080000, // An error occured during write | ||
| LFS_F_INLINE = 0x100000, // Currently inlined in directory entry | ||
| LFS_F_OPENED = 0x200000, // File has been opened | ||
| LFS_F_OPENED = 0x200000, // DEPRECATED: File has been opened |
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 these are marked as "internally used flags", I think it's safe to remove without deprecation.
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.
removed the change entirely
lfs.c
Outdated
| superblock->attr_max = lfs_tole32(superblock->attr_max); | ||
| } | ||
|
|
||
| static inline bool lfs_mlist_isused(struct lfs_mlist *head, |
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.
nit: I think this would be better as lfs_mlist_isopen, it is directly related to lfs_file_open and lfs_dir_open.
I can change this on merge if it's a bother.
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.
its okay, changed it
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.
It looks like this is still lfs_mlist_isused, or am I missing a commit?
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.
sorry, updated it now 😄
lfs.c
Outdated
| .cache = lfs->rcache, | ||
| }; | ||
| // So we could use lfs_read on the file | ||
| lfs_mlist_add(lfs, (struct lfs_mlist *)&orig); |
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.
Ah, would not add to the mlist here. The original LFS_F_OPENED flag wasn't strictly necessary.
This internal file is only created for code reuse, adding to the mlist may cause problems if wear-leveling needs to scan open files during lfs_file_write.
lfs.c
Outdated
| // get id, add to list of mdirs to catch update changes | ||
| file->type = LFS_TYPE_REG; | ||
| lfs_mlist_add(lfs, (struct lfs_mlist *)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.
I wouldn't move this either, the lfs_mdir_t is not initialized until the lfs_dir_find, and I'm not sure having an uninitialized mdir on in mlist is safe.
Unfortunately the mlist is a bit sensitive because all wear-leveling/allocation logic depend on it. It would be better if we prefer changing the LFS_F_OPENED behavior slightly to avoid changing the mlist logic, since LFS_F_OPENED asserts are just protection against already invalid API usage.
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.
its honestly better to leave alone the LFS_F_OPENED flag honestly. It causes those problems with the assert and the isused check can be utilized only when need (in file_opencfg and dir_open) and the rest of the API can stay with the flag
I'm still not convinced on #442. The behavior of an Also thanks for the changes! |
Almost missed this, unfortunately no, the test script currently only treats asserts as failures. The test script should also be documented better... |
eff46f1 to
95a81da
Compare
95a81da to
37ef506
Compare
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 looks great, thanks for this! Will merge into the next release.
Assert that the file isnt open in lfs_file_opencfg
|
I didn't notice until after merging that this reverted the I think I ran into some of the issues you did with the change, but one thing that helped was the new separate API-layer, I could move the check there and it solved some of complex internal situations, as well as reduced the calls to |
Closes #429
Also fixed a magic number in
opencfg