Implemented file sync broadcasting
Now, when files are synced, they broadcast their disk changes to any other
opened file handles. In effect, all open files match disk after a sync
call to any opened file handle pointing to that file.
This was a much requested feature, as the previous behavior (multiple
opened file handles maintain independent snapshots) is pretty different
from other filesystems. It's also quite difficult to implement outside
of the filesystem, since you need to track all opened files, requiring
either unbounded RAM or a known upper limit.
---
A bit unrelated, but this commit also changes bshrub estimate
calculation to include all opened file handles. This adds some annoying
complexity, but is necessary to prevent sporadic ERANGE errors when
the same file is opened multiple times.
The current implementation just refetches on-disk metadata. This adds
some maybe unnecessary metadata lookups, but simplifies things by
avoiding the tracking of on-disk sprout/shrub size, which risks falling
out of date. Keep in mind we only recalculate the estimate every
~inline_size/2 bytes written.
Just like lfsr_mdir_estimate, this scales O(n^2) with the number of
opened files (this are basically the same function... hmmm... can they
be deduplicated?). This is unlikely to be a problem for littlefs's use
case, but just something to be aware of.
Code changes:
code stack
before: 32920 3032
after: 33192 (+0.8%) 3048 (+0.5%)
This commit is contained in:
@@ -0,0 +1,819 @@
|
||||
# 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
|
||||
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);
|
||||
}
|
||||
|
||||
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_fmulti_rrrr_fuzz]
|
||||
defines.R = 4
|
||||
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 = 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_fmulti_wrrr]
|
||||
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) => 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 == 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_fmulti_wrrr_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) => 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 == 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_fmulti_wwww]
|
||||
defines.W = 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 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) => 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 == 1) {
|
||||
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
||||
memcpy(&after[i], wbuf, CHUNK);
|
||||
} else {
|
||||
if (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_fmulti_wwww_fuzz]
|
||||
defines.W = 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 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) => 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 == 1) {
|
||||
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
||||
memcpy(&after[off], wbuf, size);
|
||||
} else {
|
||||
if (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_fmulti_wwrr]
|
||||
defines.W = 4
|
||||
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 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) => 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 == 1) {
|
||||
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
||||
memcpy(&after[i], wbuf, CHUNK);
|
||||
} else {
|
||||
if (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_fmulti_wwrr_fuzz]
|
||||
defines.W = 4
|
||||
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
|
||||
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 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) => 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 == 1) {
|
||||
lfsr_file_sync(&lfs, &writers[w]) => 0;
|
||||
memcpy(&after[off], wbuf, size);
|
||||
} else {
|
||||
if (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_fmulti_mmmm]
|
||||
defines.M = 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[M][SIZE];
|
||||
for (lfs_size_t m = 0; m < M; m++) {
|
||||
memcpy(between[M], 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 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;
|
||||
}
|
||||
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
||||
for (lfs_size_t m = 0; m < M; m++) {
|
||||
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) {
|
||||
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;
|
||||
uint8_t rbuf[CHUNK];
|
||||
lfsr_file_read(&lfs, &rdwrs[m], rbuf, CHUNK) => CHUNK;
|
||||
if (SYNC == 0) {
|
||||
assert(memcmp(rbuf, &between[m][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;
|
||||
}
|
||||
|
||||
// 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_mmmm_fuzz]
|
||||
defines.M = 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[M][SIZE];
|
||||
for (lfs_size_t m = 0; m < M; m++) {
|
||||
memcpy(between[m], 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 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;
|
||||
}
|
||||
for (lfs_size_t i = 0; i < SIZE; i += CHUNK) {
|
||||
for (lfs_size_t m = 0; m < M; m++) {
|
||||
// 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;
|
||||
|
||||
// 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) {
|
||||
memcpy(&after[off], wbuf, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (lfs_size_t m = 0; m < M; m++) {
|
||||
// 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;
|
||||
|
||||
// read
|
||||
uint8_t rbuf[CHUNK];
|
||||
lfsr_file_read(&lfs, &rdwrs[m], rbuf, CHUNK) => size;
|
||||
if (SYNC == 0) {
|
||||
assert(memcmp(rbuf, &between[m][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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
'''
|
||||
Reference in New Issue
Block a user