diff --git a/lfs.c b/lfs.c index e5b51692..d9da8319 100644 --- a/lfs.c +++ b/lfs.c @@ -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; } diff --git a/lfs.h b/lfs.h index 5307faf3..803f38e6 100644 --- a/lfs.h +++ b/lfs.h @@ -162,12 +162,12 @@ enum lfs_open_flags { LFS_O_EXCL = 0x0200, // Fail if a file already exists LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size LFS_O_APPEND = 0x0800, // Move to end of file on every write + LFS_O_DESYNC = 0x1000, // Do not sync or recieve file updates #endif // internally used flags LFS_F_UNFLUSHED = 0x010000, // File's data does not match storage LFS_F_UNSYNCED = 0x020000, // File's metadata does not match storage - LFS_F_ERRORED = 0x040000, // An error occurred during write }; // File seek flags @@ -778,8 +778,13 @@ int lfsr_file_opencfg(lfs_t *lfs, lfsr_file_t *file, // Close a file // -// Any pending writes are written out to storage as though -// sync had been called and releases any allocated resources. +// If the file is not desynchronized, any pending writes are written out +// to storage as though sync had been called. +// +// Releases any allocated resources, even if there is an error. +// +// Readonly and desynchronized files do not touch disk and will always +// return 0. // // Returns a negative error code on failure. int lfs_file_close(lfs_t *lfs, lfs_file_t *file); @@ -787,11 +792,30 @@ int lfsr_file_close(lfs_t *lfs, lfsr_file_t *file); // Synchronize a file on storage // -// Any pending writes are written out to storage. +// Any pending writes are written out to storage and other open files. +// +// If the file was desynchronized, it is now marked as synchronized. It will +// now recieve file updates and syncs on close. +// // Returns a negative error code on failure. int lfs_file_sync(lfs_t *lfs, lfs_file_t *file); int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file); +// Mark a file as desynchronized +// +// Desynchronized files do not recieve file updates and do not sync on close. +// They effectively act as snapshots of the underlying file at that point +// in time. +// +// If an error occurs during a write operation, the file is implicitly marked +// as desynchronized. +// +// An explicit and successful call to lfsr_file_sync reverses this, marking +// the file as synchronized again. +// +// Returns a negative error code on failure. +int lfsr_file_desync(lfs_t *lfs, lfsr_file_t *file); + // Read data from file // // Takes a buffer and size indicating where to store the read data. diff --git a/tests/test_fmulti.toml b/tests/test_fmulti.toml index 1cbca13a..97cfebef 100644 --- a/tests/test_fmulti.toml +++ b/tests/test_fmulti.toml @@ -1,6 +1,7 @@ # Test multiple open file handles in different r/w configurations after = 'test_fwrite' + # Test multiple readers, this shouldn't really have any issues [cases.test_fmulti_rrrr] defines.R = 4 @@ -161,7 +162,7 @@ code = ''' } lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK; memcpy(&after[i], wbuf, CHUNK); - if (SYNC == 1) { + if (SYNC != 0) { lfsr_file_sync(&lfs, &writer) => 0; } @@ -184,7 +185,7 @@ code = ''' lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, &after, SIZE) == 0); + assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; @@ -248,7 +249,7 @@ code = ''' } lfsr_file_write(&lfs, &writer, wbuf, size) => size; memcpy(&after[off], wbuf, size); - if (SYNC == 1) { + if (SYNC != 0) { lfsr_file_sync(&lfs, &writer) => 0; } @@ -277,7 +278,7 @@ code = ''' lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, &after, SIZE) == 0); + assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; @@ -332,13 +333,13 @@ code = ''' wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK; - if (SYNC == 1) { - lfsr_file_sync(&lfs, &writers[w]) => 0; - memcpy(&after[i], wbuf, CHUNK); - } else { + if (SYNC == 0) { if (w == 0) { memcpy(&after[i], wbuf, CHUNK); } + } else { + lfsr_file_sync(&lfs, &writers[w]) => 0; + memcpy(&after[i], wbuf, CHUNK); } } } @@ -350,7 +351,7 @@ code = ''' lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, &after, SIZE) == 0); + assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; @@ -412,13 +413,13 @@ code = ''' wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &writers[w], wbuf, size) => size; - if (SYNC == 1) { - lfsr_file_sync(&lfs, &writers[w]) => 0; - memcpy(&after[off], wbuf, size); - } else { + if (SYNC == 0) { if (w == 0) { memcpy(&after[off], wbuf, size); } + } else { + lfsr_file_sync(&lfs, &writers[w]) => 0; + memcpy(&after[off], wbuf, size); } } } @@ -430,7 +431,7 @@ code = ''' lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, &after, SIZE) == 0); + assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; @@ -490,13 +491,13 @@ code = ''' wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK; - if (SYNC == 1) { - lfsr_file_sync(&lfs, &writers[w]) => 0; - memcpy(&after[i], wbuf, CHUNK); - } else { + if (SYNC == 0) { if (w == 0) { memcpy(&after[i], wbuf, CHUNK); } + } else { + lfsr_file_sync(&lfs, &writers[w]) => 0; + memcpy(&after[i], wbuf, CHUNK); } } for (lfs_size_t r = 0; r < R; r++) { @@ -520,7 +521,7 @@ code = ''' lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, &after, SIZE) == 0); + assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; @@ -587,13 +588,13 @@ code = ''' wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &writers[w], wbuf, size) => size; - if (SYNC == 1) { - lfsr_file_sync(&lfs, &writers[w]) => 0; - memcpy(&after[off], wbuf, size); - } else { + if (SYNC == 0) { if (w == 0) { memcpy(&after[off], wbuf, size); } + } else { + lfsr_file_sync(&lfs, &writers[w]) => 0; + memcpy(&after[off], wbuf, size); } } for (lfs_size_t r = 0; r < R; r++) { @@ -623,15 +624,15 @@ code = ''' lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, &after, SIZE) == 0); + assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # Test multiple rd/wrers -[cases.test_fmulti_mmmm] -defines.M = 4 +[cases.test_fmulti_rwrw] +defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync defines.SYNC = [0, 1] @@ -657,9 +658,9 @@ code = ''' for (lfs_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } - uint8_t between[M][SIZE]; - for (lfs_size_t m = 0; m < M; m++) { - memcpy(between[M], before, SIZE); + uint8_t between[RW][SIZE]; + for (lfs_size_t rw = 0; rw < RW; rw++) { + memcpy(between[rw], before, SIZE); } uint8_t after[SIZE]; memcpy(after, before, SIZE); @@ -670,56 +671,56 @@ code = ''' lfsr_file_write(&lfs, &file, before, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; - // write M rdwrs in parallel - lfsr_file_t rdwrs[M]; - for (lfs_size_t m = 0; m < M; m++) { - lfsr_file_open(&lfs, &rdwrs[m], "jello", LFS_O_RDWR) => 0; + // write RW rdwrs in parallel + lfsr_file_t rdwrs[RW]; + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_open(&lfs, &rdwrs[rw], "jello", LFS_O_RDWR) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { - for (lfs_size_t m = 0; m < M; m++) { + for (lfs_size_t rw = 0; rw < RW; rw++) { uint8_t wbuf[CHUNK]; for (lfs_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } - lfsr_file_seek(&lfs, &rdwrs[m], i, LFS_SEEK_SET) => i; - lfsr_file_write(&lfs, &rdwrs[m], wbuf, CHUNK) => CHUNK; - memcpy(&between[m][i], wbuf, CHUNK); - if (SYNC == 1) { - lfsr_file_sync(&lfs, &rdwrs[m]) => 0; - memcpy(&after[i], wbuf, CHUNK); - } else { - if (m == 0) { + lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i; + lfsr_file_write(&lfs, &rdwrs[rw], wbuf, CHUNK) => CHUNK; + memcpy(&between[rw][i], wbuf, CHUNK); + if (SYNC == 0) { + if (rw == 0) { memcpy(&after[i], wbuf, CHUNK); } + } else { + lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + memcpy(&after[i], wbuf, CHUNK); } } - for (lfs_size_t m = 0; m < M; m++) { - lfsr_file_seek(&lfs, &rdwrs[m], i, LFS_SEEK_SET) => i; + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i; uint8_t rbuf[CHUNK]; - lfsr_file_read(&lfs, &rdwrs[m], rbuf, CHUNK) => CHUNK; + lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => CHUNK; if (SYNC == 0) { - assert(memcmp(rbuf, &between[m][i], CHUNK) == 0); + assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0); } else { assert(memcmp(rbuf, &after[i], CHUNK) == 0); } } } - for (lfs_size_t m = 0; m < M; m++) { - lfsr_file_close(&lfs, &rdwrs[m]) => 0; + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_close(&lfs, &rdwrs[rw]) => 0; } // check that file was written as expected lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, &after, SIZE) == 0); + assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' -[cases.test_fmulti_mmmm_fuzz] -defines.M = 4 +[cases.test_fmulti_rwrw_fuzz] +defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync defines.SYNC = [0, 1] @@ -747,9 +748,9 @@ code = ''' for (lfs_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } - uint8_t between[M][SIZE]; - for (lfs_size_t m = 0; m < M; m++) { - memcpy(between[m], before, SIZE); + uint8_t between[RW][SIZE]; + for (lfs_size_t rw = 0; rw < RW; rw++) { + memcpy(between[rw], before, SIZE); } uint8_t after[SIZE]; memcpy(after, before, SIZE); @@ -760,59 +761,619 @@ code = ''' lfsr_file_write(&lfs, &file, before, SIZE) => SIZE; lfsr_file_close(&lfs, &file) => 0; - // write M rdwrs in parallel - lfsr_file_t rdwrs[M]; - for (lfs_size_t m = 0; m < M; m++) { - lfsr_file_open(&lfs, &rdwrs[m], "jello", LFS_O_RDWR) => 0; + // write RW rdwrs in parallel + lfsr_file_t rdwrs[RW]; + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_open(&lfs, &rdwrs[rw], "jello", LFS_O_RDWR) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { - for (lfs_size_t m = 0; m < M; m++) { + for (lfs_size_t rw = 0; rw < RW; rw++) { // choose a random offset lfs_off_t off = TEST_PRNG(&prng) % SIZE; lfs_size_t size = lfs_min32(CHUNK, SIZE - off); - lfsr_file_seek(&lfs, &rdwrs[m], off, LFS_SEEK_SET) => off; + lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; // write uint8_t wbuf[CHUNK]; for (lfs_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } - lfsr_file_write(&lfs, &rdwrs[m], wbuf, size) => size; - memcpy(&between[m][off], wbuf, size); - if (SYNC == 1) { - lfsr_file_sync(&lfs, &rdwrs[m]) => 0; - memcpy(&after[off], wbuf, size); - } else { - if (m == 0) { + lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size; + memcpy(&between[rw][off], wbuf, size); + if (SYNC == 0) { + if (rw == 0) { memcpy(&after[off], wbuf, size); } + } else { + lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + memcpy(&after[off], wbuf, size); } } - for (lfs_size_t m = 0; m < M; m++) { + for (lfs_size_t rw = 0; rw < RW; rw++) { // choose a random offset lfs_off_t off = TEST_PRNG(&prng) % SIZE; lfs_size_t size = lfs_min32(CHUNK, SIZE - off); - lfsr_file_seek(&lfs, &rdwrs[m], off, LFS_SEEK_SET) => off; + lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; // read uint8_t rbuf[CHUNK]; - lfsr_file_read(&lfs, &rdwrs[m], rbuf, CHUNK) => size; + lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size; if (SYNC == 0) { - assert(memcmp(rbuf, &between[m][off], size) == 0); + assert(memcmp(rbuf, &between[rw][off], size) == 0); } else { assert(memcmp(rbuf, &after[off], size) == 0); } } } - for (lfs_size_t m = 0; m < M; m++) { - lfsr_file_close(&lfs, &rdwrs[m]) => 0; + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_close(&lfs, &rdwrs[rw]) => 0; } // check that file was written as expected lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; - assert(memcmp(rbuf, &after, SIZE) == 0); + assert(memcmp(rbuf, after, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + + +# Desynced files make things interesting + +# Test one desynced writer, multiple readers +[cases.test_fmulti_drrr] +defines.R = 4 +# 0 => no sync, readers not updated +# 1 => sync via lfsr_file_sync +defines.SYNC = [0, 1] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +if = 'CHUNK <= SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + uint32_t prng = 42; + uint8_t before[SIZE]; + for (lfs_size_t i = 0; i < SIZE; i++) { + before[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t after[SIZE]; + memcpy(after, before, SIZE); + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, before, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // write 1 handle, read R handles in parallel + lfsr_file_t writer; + lfsr_file_t readers[R]; + lfsr_file_open(&lfs, &writer, "jello", + LFS_O_WRONLY | LFS_O_DESYNC) => 0; + for (lfs_size_t r = 0; r < R; r++) { + lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0; + } + for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK; + memcpy(&after[i], wbuf, CHUNK); + if (SYNC != 0) { + lfsr_file_sync(&lfs, &writer) => 0; + } + + for (lfs_size_t r = 0; r < R; r++) { + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK; + if (SYNC == 0) { + assert(memcmp(rbuf, &before[i], CHUNK) == 0); + } else { + assert(memcmp(rbuf, &after[i], CHUNK) == 0); + } + } + } + lfsr_file_close(&lfs, &writer) => 0; + for (lfs_size_t r = 0; r < R; r++) { + lfsr_file_close(&lfs, &readers[r]) => 0; + } + + // check that file was written as expected + lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + if (SYNC == 0) { + assert(memcmp(rbuf, before, SIZE) == 0); + } else { + assert(memcmp(rbuf, after, SIZE) == 0); + } + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fmulti_drrr_fuzz] +defines.R = 4 +# 0 => no sync, readers not updated +# 1 => sync via lfsr_file_sync +defines.SYNC = [0, 1] +defines.SEED = 'range(10)' +defines.N = 20 +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +if = 'CHUNK <= SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + uint32_t prng = 42; + uint8_t before[SIZE]; + for (lfs_size_t i = 0; i < SIZE; i++) { + before[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t after[SIZE]; + memcpy(after, before, SIZE); + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, before, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // write 1 handle, read R handles in parallel + lfsr_file_t writer; + lfsr_file_t readers[R]; + lfsr_file_open(&lfs, &writer, "jello", + LFS_O_WRONLY | LFS_O_DESYNC) => 0; + for (lfs_size_t r = 0; r < R; r++) { + lfsr_file_open(&lfs, &readers[r], "jello", LFS_O_RDONLY) => 0; + } + for (lfs_size_t i = 0; i < N; i++) { + // choose a random offset + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + lfs_size_t size = lfs_min32(CHUNK, SIZE - off); + lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off; + + // write + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < size; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &writer, wbuf, size) => size; + memcpy(&after[off], wbuf, size); + if (SYNC != 0) { + lfsr_file_sync(&lfs, &writer) => 0; + } + + for (lfs_size_t r = 0; r < R; r++) { + // choose a random offset + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + lfs_size_t size = lfs_min32(CHUNK, SIZE - off); + lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off; + + // read + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size; + if (SYNC == 0) { + assert(memcmp(rbuf, &before[off], size) == 0); + } else { + assert(memcmp(rbuf, &after[off], size) == 0); + } + } + } + lfsr_file_close(&lfs, &writer) => 0; + for (lfs_size_t r = 0; r < R; r++) { + lfsr_file_close(&lfs, &readers[r]) => 0; + } + + // check that file was written as expected + lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + if (SYNC == 0) { + assert(memcmp(rbuf, before, SIZE) == 0); + } else { + assert(memcmp(rbuf, after, SIZE) == 0); + } + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# Test one writer, multiple desynced readers +[cases.test_fmulti_wddd] +defines.R = 4 +# 0 => no sync, readers not updated +# 1 => sync via lfsr_file_sync +defines.SYNC = [0, 1] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +if = 'CHUNK <= SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + uint32_t prng = 42; + uint8_t before[SIZE]; + for (lfs_size_t i = 0; i < SIZE; i++) { + before[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t after[SIZE]; + memcpy(after, before, SIZE); + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, before, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // write 1 handle, read R handles in parallel + lfsr_file_t writer; + lfsr_file_t readers[R]; + lfsr_file_open(&lfs, &writer, "jello", LFS_O_WRONLY) => 0; + for (lfs_size_t r = 0; r < R; r++) { + lfsr_file_open(&lfs, &readers[r], "jello", + LFS_O_RDONLY | LFS_O_DESYNC) => 0; + } + for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK; + memcpy(&after[i], wbuf, CHUNK); + if (SYNC != 0) { + lfsr_file_sync(&lfs, &writer) => 0; + } + + for (lfs_size_t r = 0; r < R; r++) { + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => CHUNK; + assert(memcmp(rbuf, &before[i], CHUNK) == 0); + } + } + lfsr_file_close(&lfs, &writer) => 0; + for (lfs_size_t r = 0; r < R; r++) { + lfsr_file_close(&lfs, &readers[r]) => 0; + } + + // check that file was written as expected + lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, after, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fmulti_wddd_fuzz] +defines.R = 4 +# 0 => no sync, readers not updated +# 1 => sync via lfsr_file_sync +defines.SYNC = [0, 1] +defines.SEED = 'range(10)' +defines.N = 20 +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +if = 'CHUNK <= SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + uint32_t prng = 42; + uint8_t before[SIZE]; + for (lfs_size_t i = 0; i < SIZE; i++) { + before[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t after[SIZE]; + memcpy(after, before, SIZE); + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, before, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // write 1 handle, read R handles in parallel + lfsr_file_t writer; + lfsr_file_t readers[R]; + lfsr_file_open(&lfs, &writer, "jello", LFS_O_WRONLY) => 0; + for (lfs_size_t r = 0; r < R; r++) { + lfsr_file_open(&lfs, &readers[r], "jello", + LFS_O_RDONLY | LFS_O_DESYNC) => 0; + } + for (lfs_size_t i = 0; i < N; i++) { + // choose a random offset + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + lfs_size_t size = lfs_min32(CHUNK, SIZE - off); + lfsr_file_seek(&lfs, &writer, off, LFS_SEEK_SET) => off; + + // write + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < size; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &writer, wbuf, size) => size; + memcpy(&after[off], wbuf, size); + if (SYNC != 0) { + lfsr_file_sync(&lfs, &writer) => 0; + } + + for (lfs_size_t r = 0; r < R; r++) { + // choose a random offset + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + lfs_size_t size = lfs_min32(CHUNK, SIZE - off); + lfsr_file_seek(&lfs, &readers[r], off, LFS_SEEK_SET) => off; + + // read + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &readers[r], rbuf, CHUNK) => size; + assert(memcmp(rbuf, &before[off], size) == 0); + } + } + lfsr_file_close(&lfs, &writer) => 0; + for (lfs_size_t r = 0; r < R; r++) { + lfsr_file_close(&lfs, &readers[r]) => 0; + } + + // check that file was written as expected + lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + assert(memcmp(rbuf, after, SIZE) == 0); + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +# Test multiple desynced rd/wrers +[cases.test_fmulti_rdrd] +defines.RW = 4 +# 0 => no sync, readers not updated +# 1 => sync via lfsr_file_sync +defines.SYNC = [0, 1] +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = [32, 8, 1] +if = 'CHUNK <= SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + uint32_t prng = 42; + uint8_t before[SIZE]; + for (lfs_size_t i = 0; i < SIZE; i++) { + before[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t between[RW][SIZE]; + for (lfs_size_t rw = 0; rw < RW; rw++) { + memcpy(between[rw], before, SIZE); + } + uint8_t after[SIZE]; + memcpy(after, before, SIZE); + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, before, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // write RW rdwrs in parallel + lfsr_file_t rdwrs[RW]; + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_open(&lfs, &rdwrs[rw], "jello", + LFS_O_RDWR | LFS_O_DESYNC) => 0; + } + for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { + for (lfs_size_t rw = 0; rw < RW; rw++) { + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < CHUNK; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i; + lfsr_file_write(&lfs, &rdwrs[rw], wbuf, CHUNK) => CHUNK; + memcpy(&between[rw][i], wbuf, CHUNK); + if (SYNC == 0) { + if (rw == 0) { + memcpy(&after[i], wbuf, CHUNK); + } + } else { + lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + if (i == 0) { + memcpy(after, between[rw], SIZE); + } else { + memcpy(&after[i], wbuf, CHUNK); + } + } + } + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_seek(&lfs, &rdwrs[rw], i, LFS_SEEK_SET) => i; + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => CHUNK; + if (SYNC == 0) { + assert(memcmp(rbuf, &between[rw][i], CHUNK) == 0); + } else { + assert(memcmp(rbuf, &after[i], CHUNK) == 0); + } + } + } + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_close(&lfs, &rdwrs[rw]) => 0; + } + + // check that file was written as expected + lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + if (SYNC == 0) { + assert(memcmp(rbuf, before, SIZE) == 0); + } else { + assert(memcmp(rbuf, after, SIZE) == 0); + } + lfsr_file_close(&lfs, &file) => 0; + + lfsr_unmount(&lfs) => 0; +''' + +[cases.test_fmulti_rdrd_fuzz] +defines.RW = 4 +# 0 => no sync, readers not updated +# 1 => sync via lfsr_file_sync +defines.SYNC = [0, 1] +defines.SEED = 'range(10)' +defines.N = 20 +defines.SIZE = [ + '0', + 'CACHE_SIZE/2', + '2*CACHE_SIZE', + 'BLOCK_SIZE/2', + 'BLOCK_SIZE', + '2*BLOCK_SIZE', + '4*BLOCK_SIZE', +] +defines.CHUNK = 32 +if = 'CHUNK <= SIZE' +code = ''' + lfs_t lfs; + lfsr_format(&lfs, CFG) => 0; + lfsr_mount(&lfs, CFG) => 0; + + // create a file + uint32_t prng = 42; + uint8_t before[SIZE]; + for (lfs_size_t i = 0; i < SIZE; i++) { + before[i] = 'a' + (TEST_PRNG(&prng) % 26); + } + uint8_t between[RW][SIZE]; + for (lfs_size_t rw = 0; rw < RW; rw++) { + memcpy(between[rw], before, SIZE); + } + uint8_t after[SIZE]; + memcpy(after, before, SIZE); + + lfsr_file_t file; + lfsr_file_open(&lfs, &file, "jello", + LFS_O_WRONLY | LFS_O_CREAT | LFS_O_EXCL) => 0; + lfsr_file_write(&lfs, &file, before, SIZE) => SIZE; + lfsr_file_close(&lfs, &file) => 0; + + // write RW rdwrs in parallel + lfsr_file_t rdwrs[RW]; + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_open(&lfs, &rdwrs[rw], "jello", + LFS_O_RDWR | LFS_O_DESYNC) => 0; + } + for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { + for (lfs_size_t rw = 0; rw < RW; rw++) { + // choose a random offset + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + lfs_size_t size = lfs_min32(CHUNK, SIZE - off); + lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; + + // write + uint8_t wbuf[CHUNK]; + for (lfs_size_t j = 0; j < size; j++) { + wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); + } + lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size; + memcpy(&between[rw][off], wbuf, size); + if (SYNC == 0) { + if (rw == 0) { + memcpy(&after[off], wbuf, size); + } + } else { + lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; + if (i == 0) { + memcpy(after, between[rw], SIZE); + } else { + memcpy(&after[off], wbuf, size); + } + } + } + for (lfs_size_t rw = 0; rw < RW; rw++) { + // choose a random offset + lfs_off_t off = TEST_PRNG(&prng) % SIZE; + lfs_size_t size = lfs_min32(CHUNK, SIZE - off); + lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; + + // read + uint8_t rbuf[CHUNK]; + lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size; + if (SYNC == 0) { + assert(memcmp(rbuf, &between[rw][off], size) == 0); + } else { + assert(memcmp(rbuf, &after[off], size) == 0); + } + } + } + for (lfs_size_t rw = 0; rw < RW; rw++) { + lfsr_file_close(&lfs, &rdwrs[rw]) => 0; + } + + // check that file was written as expected + lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; + uint8_t rbuf[SIZE]; + lfsr_file_read(&lfs, &file, rbuf, SIZE) => SIZE; + if (SYNC == 0) { + assert(memcmp(rbuf, before, SIZE) == 0); + } else { + assert(memcmp(rbuf, after, SIZE) == 0); + } lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0;