Files
littlefs/tests/test_fmulti.toml
T
Christopher Haster b15940461d Implemented desynchronized files
Desynchronized files are a new concept intended to capture some useful
quirks of the previous multiple-open-file behavior.

This adds:

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

Desynced files:

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

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

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

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

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

            code          stack
  before:  33324           3072
  after:   33360 (+0.1%)   3072 (+0.0%)
2024-02-03 18:14:41 -06:00

1381 lines
40 KiB
TOML

# 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 != 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;
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 != 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;
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 == 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 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 == 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 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 == 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++) {
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 == 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++) {
// 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_rwrw]
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) => 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;
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_fmulti_rwrw_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) => 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;
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;
'''
# 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;
'''