# 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_fsync_rrrr] defines.R = 4 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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); } 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; // read R handles in parallel lfsr_file_t readers[R]; 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) { 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); } } for (lfs_size_t r = 0; r < R; r++) { lfsr_file_close(&lfs, &readers[r]) => 0; } lfsr_unmount(&lfs) => 0; ''' [cases.test_fsync_rrrr_fuzz] defines.R = 4 defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; // create a file uint32_t prng = SEED; uint8_t before[SIZE]; for (lfs_size_t i = 0; i < SIZE; i++) { before[i] = 'a' + (TEST_PRNG(&prng) % 26); } 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; // read R handles in parallel lfsr_file_t readers[R]; 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++) { 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); } } for (lfs_size_t r = 0; r < R; r++) { lfsr_file_close(&lfs, &readers[r]) => 0; } lfsr_unmount(&lfs) => 0; ''' # Test one writer, multiple readers [cases.test_fsync_wrrr] defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 (FLUSH == 1) { lfsr_file_flush(&lfs, &writer) => 0; } if (SYNC == 1) { 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; assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' [cases.test_fsync_wrrr_fuzz] defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 (FLUSH == 1) { lfsr_file_flush(&lfs, &writer) => 0; } if (SYNC == 1) { 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; assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # Test multiple writers [cases.test_fsync_wwww] defines.W = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 W handles in parallel lfsr_file_t writers[W]; for (lfs_size_t w = 0; w < W; w++) { lfsr_file_open(&lfs, &writers[w], "jello", LFS_O_WRONLY | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t w = 0; w < W; w++) { uint8_t wbuf[CHUNK]; for (lfs_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK; if (SYNC == 0) { if (w == 0) { memcpy(&after[i], wbuf, CHUNK); } } else { if (FLUSH == 1) { lfsr_file_flush(&lfs, &writers[w]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &writers[w]) => 0; } memcpy(&after[i], wbuf, CHUNK); } } } for (lfs_size_t w = 0; w < W; w++) { lfsr_file_close(&lfs, &writers[w]) => 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_fsync_wwww_fuzz] defines.W = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 W files in parallel lfsr_file_t writers[W]; for (lfs_size_t w = 0; w < W; w++) { lfsr_file_open(&lfs, &writers[w], "jello", LFS_O_WRONLY | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t w = 0; w < W; w++) { // 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, &writers[w], 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, &writers[w], wbuf, size) => size; if (SYNC == 0) { if (w == 0) { memcpy(&after[off], wbuf, size); } } else { if (FLUSH == 1) { lfsr_file_flush(&lfs, &writers[w]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &writers[w]) => 0; } memcpy(&after[off], wbuf, size); } } } for (lfs_size_t w = 0; w < W; w++) { lfsr_file_close(&lfs, &writers[w]) => 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 writers and multiple readers [cases.test_fsync_wwrr] defines.W = 4 defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 W handles, R handles in parallel lfsr_file_t writers[W]; lfsr_file_t readers[R]; for (lfs_size_t w = 0; w < W; w++) { lfsr_file_open(&lfs, &writers[w], "jello", LFS_O_WRONLY | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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) { for (lfs_size_t w = 0; w < W; w++) { uint8_t wbuf[CHUNK]; for (lfs_size_t j = 0; j < CHUNK; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } lfsr_file_write(&lfs, &writers[w], wbuf, CHUNK) => CHUNK; if (SYNC == 0) { if (w == 0) { memcpy(&after[i], wbuf, CHUNK); } } else { if (FLUSH == 1) { lfsr_file_flush(&lfs, &writers[w]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &writers[w]) => 0; } memcpy(&after[i], wbuf, CHUNK); } } 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); } } } for (lfs_size_t w = 0; w < W; w++) { lfsr_file_close(&lfs, &writers[w]) => 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_fsync_wwrr_fuzz] defines.W = 4 defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 W files in parallel lfsr_file_t writers[W]; lfsr_file_t readers[R]; for (lfs_size_t w = 0; w < W; w++) { lfsr_file_open(&lfs, &writers[w], "jello", LFS_O_WRONLY | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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) { for (lfs_size_t w = 0; w < W; w++) { // 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, &writers[w], 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, &writers[w], wbuf, size) => size; if (SYNC == 0) { if (w == 0) { memcpy(&after[off], wbuf, size); } } else { if (FLUSH == 1) { lfsr_file_flush(&lfs, &writers[w]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &writers[w]) => 0; } memcpy(&after[off], wbuf, size); } } 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); } } } for (lfs_size_t w = 0; w < W; w++) { lfsr_file_close(&lfs, &writers[w]) => 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 rd/wrers [cases.test_fsync_rwrw] defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 { if (FLUSH == 1) { lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; } 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; assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' [cases.test_fsync_rwrw_fuzz] defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 { if (FLUSH == 1) { lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; } 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; assert(memcmp(rbuf, after, SIZE) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # Test multiple rw files without fixed size [cases.test_fsync_rwrw_sparse_fuzz] defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 40 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs_size_t between_size[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; } uint8_t after[SIZE]; lfs_size_t after_size = 0; // write RW rdwrs in parallel lfsr_file_t rdwrs[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { // open files lfsr_file_open(&lfs, &rdwrs[rw], "jello", LFS_O_RDWR | LFS_O_CREAT | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 2; // writing? if (op == 0) { // choose a random offset lfs_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs_size_t size = lfs_min32(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs_max32(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off); // read lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); } // broadcast sim? if (SYNC) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } for (lfs_size_t rw = 0; rw < RW; rw++) { // close files lfsr_file_close(&lfs, &rdwrs[rw]) => 0; // broadcast sim one last time? memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } // check that file was written as expected lfsr_file_t file; lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # Test multiple rw files while also truncating/fruncating [cases.test_fsync_rwtfrwtf_sparse_fuzz] defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 40 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs_size_t between_size[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; } uint8_t after[SIZE]; lfs_size_t after_size = 0; // write RW rdwrs in parallel lfsr_file_t rdwrs[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { // open files lfsr_file_open(&lfs, &rdwrs[rw], "jello", LFS_O_RDWR | LFS_O_CREAT | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 4; // writing? if (op == 0) { // choose a random offset lfs_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs_size_t size = lfs_min32(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs_max32(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off); // read lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); // truncating? } else if (op == 2) { // choose a random new file size lfs_off_t size = TEST_PRNG(&prng) % SIZE; // truncate lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0; if (FLUSH == 1) { // (flush does nothing) lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; } // update the sim if (size > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, size - between_size[rw]); } between_size[rw] = size; // fruncating? } else if (op == 3) { // choose a random new file size lfs_off_t size = TEST_PRNG(&prng) % SIZE; // fruncate lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0; if (FLUSH == 1) { // (flush does nothing) lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } if (SYNC == 1) { lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; } // update the sim if (size > between_size[rw]) { memmove(&between[rw][size - between_size[rw]], between[rw], between_size[rw]); memset(between[rw], 0, size - between_size[rw]); } else { memmove(between[rw], &between[rw][between_size[rw] - size], size); } between_size[rw] = size; } // broadcast sim? if (SYNC) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } for (lfs_size_t rw = 0; rw < RW; rw++) { // close files lfsr_file_close(&lfs, &rdwrs[rw]) => 0; // broadcast sim one last time? memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } // check that file was written as expected lfsr_file_t file; lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, 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_fsync_drrr] defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 (FLUSH == 1) { lfsr_file_flush(&lfs, &writer) => 0; } if (SYNC == 1) { 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_fsync_drrr_fuzz] defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 (FLUSH == 1) { lfsr_file_flush(&lfs, &writer) => 0; } if (SYNC == 1) { 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_fsync_wddd] defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 (FLUSH == 1) { lfsr_file_flush(&lfs, &writer) => 0; } if (SYNC == 1) { 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_fsync_wddd_fuzz] defines.R = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 (FLUSH == 1) { lfsr_file_flush(&lfs, &writer) => 0; } if (SYNC == 1) { 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_fsync_rwdrwd] defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 { if (FLUSH == 1) { lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } if (SYNC == 1) { 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_fsync_rwdrwd_fuzz] defines.RW = 4 # 0 => no sync, readers not updated # 1 => sync via lfsr_file_sync # 2 => sync via LFS_O_SYNC defines.SYNC = [0, 1, 2] # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 20 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' 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 | ((FLUSH == 2) ? LFS_O_FLUSH : 0) | ((SYNC == 2) ? LFS_O_SYNC : 0)) => 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 { if (FLUSH == 1) { lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } if (SYNC == 1) { 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; ''' # Test multiple rwd files without fixed size [cases.test_fsync_rwdrwd_sparse_fuzz] defines.RW = 4 # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 40 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs_size_t between_size[RW]; bool between_desync[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; between_desync[rw] = false; } uint8_t after[SIZE]; lfs_size_t after_size = 0; // write RW rdwrs in parallel lfsr_file_t rdwrs[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { // open files lfsr_file_open(&lfs, &rdwrs[rw], "jello", LFS_O_RDWR | LFS_O_CREAT | ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 2; // choose a random sync state uint8_t sync = TEST_PRNG(&prng) % 3; // writing? if (op == 0) { // choose a random offset lfs_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs_size_t size = lfs_min32(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs_max32(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off); // read lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); } // desync? if (sync == 0) { lfsr_file_desync(&lfs, &rdwrs[rw]) => 0; between_desync[rw] = true; // sync? } else if (sync == 1) { lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; between_desync[rw] = false; // otherwise no change } // broadcast sim? if (sync == 1) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } } for (lfs_size_t rw = 0; rw < RW; rw++) { // close files lfsr_file_close(&lfs, &rdwrs[rw]) => 0; // broadcast sim one last time? if (!between_desync[rw]) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } // check that file was written as expected lfsr_file_t file; lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; ''' # Test multiple rwd files while also truncating/fruncating [cases.test_fsync_rwtfdrwtfd_sparse_fuzz] defines.RW = 4 # 0 => no flush # 1 => flush via lfsr_file_flush # 2 => flush via LFS_O_FLUSH defines.FLUSH = [0, 1, 2] defines.SEED = 'range(10)' defines.N = 40 defines.SIZE = [ 'CACHE_SIZE/2', '2*CACHE_SIZE', 'BLOCK_SIZE/2', 'BLOCK_SIZE', '2*BLOCK_SIZE', '4*BLOCK_SIZE', ] defines.CHUNK = '(SIZE+16-1) / 16' code = ''' lfs_t lfs; lfsr_format(&lfs, CFG) => 0; lfsr_mount(&lfs, CFG) => 0; uint32_t prng = 42; uint8_t between[RW][SIZE]; lfs_size_t between_size[RW]; bool between_desync[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { between_size[rw] = 0; between_desync[rw] = false; } uint8_t after[SIZE]; lfs_size_t after_size = 0; // write RW rdwrs in parallel lfsr_file_t rdwrs[RW]; for (lfs_size_t rw = 0; rw < RW; rw++) { // open files lfsr_file_open(&lfs, &rdwrs[rw], "jello", LFS_O_RDWR | LFS_O_CREAT | ((FLUSH == 2) ? LFS_O_FLUSH : 0)) => 0; } for (lfs_size_t i = 0; i < SIZE; i += CHUNK) { for (lfs_size_t rw = 0; rw < RW; rw++) { // choose a random operation uint8_t op = TEST_PRNG(&prng) % 4; // choose a random sync state uint8_t sync = TEST_PRNG(&prng) % 3; // writing? if (op == 0) { // choose a random offset lfs_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs_size_t size = lfs_min32(CHUNK, SIZE - off); uint8_t wbuf[CHUNK]; for (lfs_size_t j = 0; j < size; j++) { wbuf[j] = 'a' + (TEST_PRNG(&prng) % 26); } // write lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; lfsr_file_write(&lfs, &rdwrs[rw], wbuf, size) => size; if (FLUSH == 1) { lfsr_file_flush(&lfs, &rdwrs[rw]) => 0; } // update sim if (off > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, off - between_size[rw]); } memcpy(&between[rw][off], wbuf, size); between_size[rw] = lfs_max32(off + size, between_size[rw]); // reading? } else if (op == 1) { // choose a random offset lfs_off_t off = (between_size[rw] > 0) ? TEST_PRNG(&prng) % between_size[rw] : 0; lfs_size_t size = lfs_min32(CHUNK, between_size[rw] - off); // read lfsr_file_seek(&lfs, &rdwrs[rw], off, LFS_SEEK_SET) => off; uint8_t rbuf[CHUNK]; lfsr_file_read(&lfs, &rdwrs[rw], rbuf, CHUNK) => size; assert(memcmp(rbuf, &between[rw][off], size) == 0); // truncating? } else if (op == 2) { // choose a random new file size lfs_off_t size = TEST_PRNG(&prng) % SIZE; // truncate lfsr_file_truncate(&lfs, &rdwrs[rw], size) => 0; // update the sim if (size > between_size[rw]) { memset(&between[rw][between_size[rw]], 0, size - between_size[rw]); } between_size[rw] = size; // fruncating? } else if (op == 3) { // choose a random new file size lfs_off_t size = TEST_PRNG(&prng) % SIZE; // fruncate lfsr_file_fruncate(&lfs, &rdwrs[rw], size) => 0; // update the sim if (size > between_size[rw]) { memmove(&between[rw][size - between_size[rw]], between[rw], between_size[rw]); memset(between[rw], 0, size - between_size[rw]); } else { memmove(between[rw], &between[rw][between_size[rw] - size], size); } between_size[rw] = size; } // desync? if (sync == 0) { lfsr_file_desync(&lfs, &rdwrs[rw]) => 0; between_desync[rw] = true; // sync? } else if (sync == 1) { lfsr_file_sync(&lfs, &rdwrs[rw]) => 0; between_desync[rw] = false; // otherwise no change } // broadcast sim? if (sync == 1) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } } for (lfs_size_t rw = 0; rw < RW; rw++) { // close files lfsr_file_close(&lfs, &rdwrs[rw]) => 0; // broadcast sim one last time? if (!between_desync[rw]) { memcpy(after, between[rw], SIZE); after_size = between_size[rw]; for (lfs_size_t rw_ = 0; rw_ < RW; rw_++) { if (!between_desync[rw_]) { memcpy(between[rw_], between[rw], SIZE); between_size[rw_] = between_size[rw]; } } } } // check that file was written as expected lfsr_file_t file; lfsr_file_open(&lfs, &file, "jello", LFS_O_RDONLY) => 0; uint8_t rbuf[SIZE]; lfsr_file_read(&lfs, &file, rbuf, SIZE) => after_size; assert(memcmp(rbuf, after, after_size) == 0); lfsr_file_close(&lfs, &file) => 0; lfsr_unmount(&lfs) => 0; '''