Implemented desynchronized files

Desynchronized files are a new concept intended to capture some useful
quirks of the previous multiple-open-file behavior.

This adds:

- LFS_O_DESYNC     - Mark a file as desync during open
- lfsr_file_desync - Mark a file as desync whenever
- lfsr_file_sync   - Mark a file as NOT desync, and sync the file

Desynced files:

1. Don't recieve updates from writes to other file handles. This makes
   desynced files act as a sort of snapshot of the file at the time it
   was marked desync.

2. Don't call lfsr_file_sync on close. Unless lfsr_file_sync is
   explicitly called, changes to desynced files are not reflected on
   disk and not broadcasted to other file handles.

A side-effect of 2., is that this gives you a quick way to abort a file
write. Marking a file as desync and then closing the file will never
error.

Additionally, if an error occurs during a write operation, the file is
implicitly marked as desync. This provides graceful write aborting in
unlikely error cases. This has actually always been a feature in
littlefs, it was just named differently and didn't have an optional
recovery mode.

Since littlefs actually has to do more work to keep files in sync, the
desync feature is quite cheap:

            code          stack
  before:  33324           3072
  after:   33360 (+0.1%)   3072 (+0.0%)
This commit is contained in:
Christopher Haster
2024-01-02 13:44:19 -06:00
parent 3e32569454
commit b15940461d
3 changed files with 699 additions and 99 deletions
+36 -21
View File
@@ -9241,6 +9241,10 @@ static inline bool lfsr_o_isappend(uint32_t flags) {
return flags & LFS_O_APPEND;
}
static inline bool lfsr_o_isdesync(uint32_t flags) {
return flags & LFS_O_DESYNC;
}
static inline bool lfsr_f_isunflushed(uint32_t flags) {
return flags & LFS_F_UNFLUSHED;
}
@@ -9249,10 +9253,6 @@ static inline bool lfsr_f_isunsynced(uint32_t flags) {
return flags & LFS_F_UNSYNCED;
}
static inline bool lfsr_f_iserrored(uint32_t flags) {
return flags & LFS_F_ERRORED;
}
// file operations
// needed in lfsr_file_opencfg
@@ -9457,7 +9457,15 @@ int lfsr_file_open(lfs_t *lfs, lfsr_file_t *file,
int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file);
int lfsr_file_close(lfs_t *lfs, lfsr_file_t *file) {
int err = lfsr_file_sync(lfs, file);
// don't call lfsr_file_sync if we're readonly or desynced
int err = 0;
if (lfsr_o_iswriteable(file->flags)
&& !lfsr_o_isdesync(file->flags)) {
err = lfsr_file_sync(lfs, file);
if (err) {
return err;
}
}
// remove from tracked mdirs
lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, (lfsr_openedmdir_t*)file);
@@ -10694,8 +10702,8 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
failed:;
// remove from tracked mdirs
lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, (lfsr_openedmdir_t*)&ftree_);
// mark as errored so lfsr_file_close doesn't write to disk
file->flags |= LFS_F_ERRORED;
// mark as desync so lfsr_file_close doesn't write to disk
file->flags |= LFS_O_DESYNC;
return err;
}
@@ -10755,17 +10763,12 @@ static int lfsr_file_flush(lfs_t *lfs, lfsr_file_t *file) {
failed:;
// remove from tracked mdirs
lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, (lfsr_openedmdir_t*)&ftree_);
// mark as errored so lfsr_file_close doesn't write to disk
file->flags |= LFS_F_ERRORED;
// mark as desync so lfsr_file_close doesn't write to disk
file->flags |= LFS_O_DESYNC;
return err;
}
int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
// it's not safe to do anything if our file errored
if (lfsr_f_iserrored(file->flags)) {
return 0;
}
// do nothing if our file has been removed
if (file->ftree.mdir.mid == -1) {
return 0;
@@ -10774,11 +10777,15 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
// do nothing if our file is readonly
if (!lfsr_o_iswriteable(file->flags)) {
LFS_ASSERT(!lfsr_f_isunsynced(file->flags));
// but do clear desync flag
file->flags &= ~LFS_O_DESYNC;
return 0;
}
// do nothing if we're already in sync
if (!lfsr_f_isunsynced(file->flags)) {
// but do clear desync flag
file->flags &= ~LFS_O_DESYNC;
return 0;
}
@@ -10839,14 +10846,16 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
}
// mark as synced
file->flags &= ~LFS_F_UNSYNCED;
file->flags &= ~LFS_F_UNSYNCED & ~LFS_O_DESYNC;
// update other file handles
for (lfsr_openedmdir_t *opened = lfs->opened[
LFS_TYPE_REG-LFS_TYPE_REG];
opened;
opened = opened->next) {
lfsr_file_t *file_ = (lfsr_file_t*)opened;
if (file_->ftree.mdir.mid == file->ftree.mdir.mid) {
if (file_->ftree.mdir.mid == file->ftree.mdir.mid
// don't update desynced file handles
&& !lfsr_o_isdesync(file_->flags)) {
file_->size = file->size;
file_->ftree.u = file->ftree.u;
file_->buffer_pos = file->buffer_pos;
@@ -10858,10 +10867,16 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
return 0;
failed:;
file->flags |= LFS_F_ERRORED;
file->flags |= LFS_O_DESYNC;
return err;
}
int lfsr_file_desync(lfs_t *lfs, lfsr_file_t *file) {
(void)lfs;
file->flags |= LFS_O_DESYNC;
return 0;
}
lfs_soff_t lfsr_file_seek(lfs_t *lfs, lfsr_file_t *file,
lfs_soff_t off, uint8_t whence) {
// TODO check for out-of-range?
@@ -10997,8 +11012,8 @@ int lfsr_file_truncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size) {
failed:;
// remove from tracked mdirs
lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, (lfsr_openedmdir_t*)&ftree_);
// mark as errored so lfsr_file_close doesn't write to disk
file->flags |= LFS_F_ERRORED;
// mark as desync so lfsr_file_close doesn't write to disk
file->flags |= LFS_O_DESYNC;
return err;
}
@@ -11112,8 +11127,8 @@ int lfsr_file_fruncate(lfs_t *lfs, lfsr_file_t *file, lfs_off_t size) {
failed:;
// remove from tracked mdirs
lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, (lfsr_openedmdir_t*)&ftree_);
// mark as errored so lfsr_file_close doesn't write to disk
file->flags |= LFS_F_ERRORED;
// mark as desync so lfsr_file_close doesn't write to disk
file->flags |= LFS_O_DESYNC;
return err;
}