Added LFS_O_SYNC, for implicit syncs during file writes

The motivation for this comes from the observation that many users call
sync on every file write. Much more than I expected. I think one reason
is in embedded systems it's common to just write structs to disk, either
the whole file or to a log.

O_SYNC exists in POSIX/Lunix/etc, so it makes sense to provide in
littlefs. In theory it's just one extra function call, and may even save
in total application cost (though we don't measure this) by reducing the
number of function calls at the application-level.

---

Unfortunately in-practice turned out to be quite a bit different than
in-theory... The main culprit being the improved guarantees around error
atomicity...

The ideal guarantee is that if there is an error during a write, the
entire write operation is reverted. Combining this with O_SYNC means we
need to hold a copy of the origin file state all thwe way through our
sync call. This got a bit messy...

The annoying part isn't even the functionality! Our system of tracking
btree/bshrub snapshots is quite robust! The problems were entirely with:

1. Figuring out how the heck to avoid clobbering the old file buffer
   state.

2. Figuring out how the internal APIs should work while passing around a
   bunch of staging state.

For 1., fortunately, thanks to bypassing writes, and some careful
pointer manipulation, we can void buffer clobbing. And for 2. just some
internal API work was needed. Internally all syncs end up in
lfsr_ftree_sync, though this feels a bit clumsy since the functionality
is not really ftree related...

Unfortunately, all of this added up to quite a bit more code cost than
I had hoped. In theory, adding some sort of LFS_CERAMIC/LFS_GLASS modes
that relax error atomicity for code size could help with most of this?
But it needs some thought:

            code          stack
  before:  33324           3072
  after:   33544 (+0.7%)   3072 (+0.0%)
This commit is contained in:
Christopher Haster
2024-01-04 22:56:25 -06:00
parent b5e264bec4
commit ae2644eb88
3 changed files with 248 additions and 130 deletions
+156 -87
View File
@@ -9241,6 +9241,10 @@ static inline bool lfsr_o_isappend(uint32_t flags) {
return flags & LFS_O_APPEND;
}
static inline bool lfsr_o_issync(uint32_t flags) {
return flags & LFS_O_SYNC;
}
static inline bool lfsr_o_isdesync(uint32_t flags) {
return flags & LFS_O_DESYNC;
}
@@ -10454,6 +10458,74 @@ static int lfsr_ftree_flush(lfs_t *lfs, lfsr_ftree_t *ftree,
return 0;
}
static int lfsr_ftree_sync(lfs_t *lfs, lfsr_ftree_t *ftree, bool unflushed,
lfs_off_t buffer_pos, const uint8_t *buffer, lfs_size_t buffer_size) {
// note because of small-file caching and our current write
// strategy, we never actually end up with only a direct data
// or bptr
//
// this is convenient because bptrs are a bit annoying to commit
LFS_ASSERT(!lfsr_ftree_isbsprout(ftree));
LFS_ASSERT(!lfsr_ftree_isbleaf(ftree));
// small files should start as zero, const prop should optimize this out
LFS_ASSERT(!unflushed || buffer_pos == 0);
// small files/ftree should be exclusive here
LFS_ASSERT(!unflushed || lfsr_ftree_size(ftree) == 0);
// small files must be inlined entirely in our buffer
LFS_ASSERT(!unflushed
|| (buffer_size <= lfs->cfg->cache_size
&& buffer_size <= lfs->cfg->inline_size
&& buffer_size <= lfs->cfg->fragment_size));
// commit our file's metadata
uint8_t buf[LFSR_BTREE_DSIZE];
int err = lfsr_mdir_commit(lfs, &ftree->mdir, LFSR_ATTRS(
(unflushed && buffer_size == 0)
? LFSR_ATTR(ftree->mdir.mid,
WIDE(RM(STRUCT)), 0,
NULL())
: (unflushed)
? LFSR_ATTR(ftree->mdir.mid,
WIDE(DATA), 0,
BUF(buffer, buffer_size))
: (lfsr_ftree_isbshrub(ftree))
? LFSR_ATTR(ftree->mdir.mid,
WIDE(SHRUBTRUNK), 0,
SHRUBTRUNK(&ftree->u.bshrub))
: LFSR_ATTR(ftree->mdir.mid,
WIDE(BTREE), 0,
FROMBTREE(&ftree->u.btree, buf))));
if (err) {
return err;
}
// update other file handles
for (lfsr_openedmdir_t *opened = lfs->opened[
LFS_TYPE_REG-LFS_TYPE_REG];
opened;
opened = opened->next) {
lfsr_file_t *file_ = (lfsr_file_t*)opened;
if (file_->ftree.mdir.mid == ftree->mdir.mid
// don't double update
&& &file_->ftree != ftree
// don't update desynced file handles
&& !lfsr_o_isdesync(file_->flags)) {
if (unflushed) {
file_->size = buffer_size;
} else {
file_->size = lfsr_ftree_size(ftree);
}
file_->ftree.u = ftree->u;
file_->buffer_pos = buffer_pos;
LFS_ASSERT(buffer_size <= lfs->cfg->cache_size);
memcpy(file_->buffer, buffer, buffer_size);
file_->buffer_size = buffer_size;
}
}
return 0;
}
// our high-level file operations
// needed in lfsr_file_read
@@ -10567,9 +10639,9 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
}
// copy state so we can recover from errors
lfs_off_t pos_ = file->pos;
bool unflushed_ = lfsr_f_isunflushed(file->flags);
lfs_off_t buffer_pos_ = file->buffer_pos;
const uint8_t *buffer_ = file->buffer;
lfs_size_t buffer_size_ = file->buffer_size;
lfsr_ftree_t ftree_ = file->ftree;
// add to tracked mdirs
@@ -10580,24 +10652,25 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
lfs_alloc_ckpoint(lfs);
// update pos if we are appending
lfs_off_t pos__ = file->pos;
if (lfsr_o_isappend(file->flags)) {
pos_ = file->size;
pos__ = file->size;
}
// if we're a small file, we may need to append zeros
if (pos_ > file->size
&& pos_ <= lfs->cfg->cache_size
&& pos_ <= lfs->cfg->inline_size
&& pos_ <= lfs->cfg->fragment_size) {
if (pos__ > file->size
&& pos__ <= lfs->cfg->cache_size
&& pos__ <= lfs->cfg->inline_size
&& pos__ <= lfs->cfg->fragment_size) {
LFS_ASSERT(unflushed_);
LFS_ASSERT(file->size == buffer_size_);
memset(&file->buffer[buffer_size_],
0,
pos_ - buffer_size_);
buffer_size_ = pos_;
pos__ - buffer_size_);
buffer_size_ = pos__;
}
const uint8_t *buffer_ = buffer;
const uint8_t *buffer__ = buffer;
while (size > 0) {
// bypass buffer?
//
@@ -10607,7 +10680,7 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
//
if (!unflushed_ && size >= lfs->cfg->cache_size) {
err = lfsr_ftree_flush(lfs, &ftree_,
pos_, buffer_, size);
pos__, buffer__, size);
if (err) {
goto failed;
}
@@ -10616,14 +10689,12 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
//
// note we need to clear the buffer anyways to avoid any
// out-of-date data
memcpy(file->buffer,
&buffer_[size - lfs->cfg->cache_size],
lfs->cfg->cache_size);
buffer_pos_ = pos_ + size - lfs->cfg->cache_size;
buffer_pos_ = pos__ + size - lfs->cfg->cache_size;
buffer_ = &buffer__[size - lfs->cfg->cache_size];
buffer_size_ = lfs->cfg->cache_size;
pos_ += size;
buffer_ += size;
pos__ += size;
buffer__ += size;
size -= size;
continue;
}
@@ -10639,57 +10710,101 @@ lfs_ssize_t lfsr_file_write(lfs_t *lfs, lfsr_file_t *file,
// buffer once, and flush at most twice.
//
if (!unflushed_
|| (pos_ >= buffer_pos_
&& pos_ <= buffer_pos_ + buffer_size_
&& pos_ < buffer_pos_ + lfs->cfg->cache_size)) {
|| (pos__ >= buffer_pos_
&& pos__ <= buffer_pos_ + buffer_size_
&& pos__ < buffer_pos_ + lfs->cfg->cache_size)) {
// unused buffer? we can move it where we need it
lfs_size_t d;
if (!unflushed_) {
buffer_pos_ = pos_;
buffer_size_ = 0;
d = lfs_min32(
size,
lfs->cfg->cache_size);
buffer_pos_ = pos__;
buffer_ = buffer__;
buffer_size_ = d;
} else {
d = lfs_min32(
size,
lfs->cfg->cache_size - (pos__ - buffer_pos_));
LFS_ASSERT(file->buffer == buffer_);
memcpy(&file->buffer[pos__ - buffer_pos_], buffer__, d);
buffer_size_ = lfs_max32(
buffer_size_,
pos__+d - buffer_pos_);
}
lfs_size_t d = lfs_min32(
size,
lfs->cfg->cache_size - (pos_ - buffer_pos_));
memcpy(&file->buffer[pos_ - buffer_pos_], buffer_, d);
buffer_size_ = lfs_max32(
buffer_size_,
pos_+d - buffer_pos_);
unflushed_ = true;
pos_ += d;
buffer_ += d;
pos__ += d;
buffer__ += d;
size -= d;
continue;
}
// flush our buffer so the above can't fail
err = lfsr_ftree_flush(lfs, &ftree_,
buffer_pos_, file->buffer, buffer_size_);
buffer_pos_, buffer_, buffer_size_);
if (err) {
goto failed;
}
unflushed_ = false;
}
// sync if requested
lfs_off_t size__ = lfs_max32(file->size, pos__);
bool unsynced_ = true;
if (lfsr_o_issync(file->flags)) {
// syncing requires a flush for non-small files
if (unflushed_ && !(
size__ <= lfs->cfg->cache_size
&& size__ <= lfs->cfg->inline_size
&& size__ <= lfs->cfg->fragment_size)) {
// flush
err = lfsr_ftree_flush(lfs, &ftree_,
buffer_pos_, buffer_, buffer_size_);
if (err) {
goto failed;
}
unflushed_ = false;
}
// sync
err = lfsr_ftree_sync(lfs, &ftree_, unflushed_,
buffer_pos_, buffer_, buffer_size_);
if (err) {
goto failed;
}
// mark as in-sync
unsynced_ = false;
file->flags &= ~LFS_O_DESYNC;
}
// remove from tracked mdirs
lfsr_mdir_removeopened(lfs, LFS_TYPE_REG, (lfsr_openedmdir_t*)&ftree_);
// mark as unflushed and unsynced, update file, and return amount written
lfs_size_t written;
if (lfsr_o_isappend(file->flags)) {
written = pos_ - file->size;
written = pos__ - file->size;
} else {
written = pos_ - file->pos;
written = pos__ - file->pos;
}
if (unsynced_) {
file->flags |= LFS_F_UNSYNCED;
} else {
file->flags &= ~LFS_F_UNSYNCED;
}
file->flags |= LFS_F_UNSYNCED;
if (unflushed_) {
file->flags |= LFS_F_UNFLUSHED;
} else {
file->flags &= ~LFS_F_UNFLUSHED;
}
file->pos = pos_;
file->size = lfs_max32(file->size, pos_);
file->pos = pos__;
file->size = size__;
file->buffer_pos = buffer_pos_;
if (buffer_ != file->buffer) {
LFS_ASSERT(buffer_size_ <= lfs->cfg->cache_size);
memcpy(file->buffer, buffer_, buffer_size_);
}
file->buffer_size = buffer_size_;
file->ftree.u = ftree_.u;
return written;
@@ -10799,66 +10914,20 @@ int lfsr_file_sync(lfs_t *lfs, lfsr_file_t *file) {
// checkpoint the allocator again
lfs_alloc_ckpoint(lfs);
// note because of small-file caching and our current write
// strategy, we never actually end up with only a direct data
// or bptr
//
// this is convenient because bpts are a bit annoying to commit
LFS_ASSERT(!lfsr_ftree_isbsprout(&file->ftree));
LFS_ASSERT(!lfsr_ftree_isbleaf(&file->ftree));
// if this invariant breaks something has gone horribly wrong
LFS_ASSERT(file->size == lfs_max32(
file->buffer_pos + file->buffer_size,
lfsr_ftree_size(&file->ftree)));
// small files must be inlined entirely in our buffer
LFS_ASSERT(!(file->size <= lfs->cfg->cache_size
&& file->size <= lfs->cfg->inline_size
&& file->size <= lfs->cfg->fragment_size)
|| file->size == file->buffer_size);
// commit our file's metadata
uint8_t buf[LFSR_BTREE_DSIZE];
err = lfsr_mdir_commit(lfs, &file->ftree.mdir, LFSR_ATTRS(
(file->size == 0)
? LFSR_ATTR(file->ftree.mdir.mid,
WIDE(RM(STRUCT)), 0,
NULL())
: (file->size <= lfs->cfg->cache_size
&& file->size <= lfs->cfg->inline_size
&& file->size <= lfs->cfg->fragment_size)
? LFSR_ATTR(file->ftree.mdir.mid,
WIDE(DATA), 0,
BUF(file->buffer, file->size))
: (lfsr_ftree_isbshrub(&file->ftree))
? LFSR_ATTR(file->ftree.mdir.mid,
WIDE(SHRUBTRUNK), 0,
SHRUBTRUNK(&file->ftree.u.bshrub))
: LFSR_ATTR(file->ftree.mdir.mid,
WIDE(BTREE), 0,
FROMBTREE(&file->ftree.u.btree, buf))));
err = lfsr_ftree_sync(lfs, &file->ftree, lfsr_f_isunflushed(file->flags),
file->buffer_pos, file->buffer, file->buffer_size);
if (err) {
goto failed;
}
// mark as synced
file->flags &= ~LFS_F_UNSYNCED & ~LFS_O_DESYNC;
// update other file handles
for (lfsr_openedmdir_t *opened = lfs->opened[
LFS_TYPE_REG-LFS_TYPE_REG];
opened;
opened = opened->next) {
lfsr_file_t *file_ = (lfsr_file_t*)opened;
if (file_->ftree.mdir.mid == file->ftree.mdir.mid
// don't update desynced file handles
&& !lfsr_o_isdesync(file_->flags)) {
file_->size = file->size;
file_->ftree.u = file->ftree.u;
file_->buffer_pos = file->buffer_pos;
memcpy(file_->buffer, file->buffer, file->buffer_size);
file_->buffer_size = file->buffer_size;
}
}
// mark as in-sync
file->flags &= ~LFS_O_SYNC & ~LFS_O_DESYNC;
return 0;
failed:;
+2 -1
View File
@@ -162,7 +162,8 @@ enum lfs_open_flags {
LFS_O_EXCL = 0x0200, // Fail if a file already exists
LFS_O_TRUNC = 0x0400, // Truncate the existing file to zero size
LFS_O_APPEND = 0x0800, // Move to end of file on every write
LFS_O_DESYNC = 0x1000, // Do not sync or recieve file updates
LFS_O_SYNC = 0x1000, // Sync metadata on every write
LFS_O_DESYNC = 0x2000, // Do not sync or recieve file updates
#endif
// internally used flags
+90 -42
View File
@@ -116,7 +116,8 @@ code = '''
defines.R = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SIZE = [
'0',
'CACHE_SIZE/2',
@@ -151,7 +152,8 @@ code = '''
// 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;
lfsr_file_open(&lfs, &writer, "jello",
LFS_O_WRONLY | ((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;
}
@@ -162,7 +164,7 @@ code = '''
}
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
memcpy(&after[i], wbuf, CHUNK);
if (SYNC != 0) {
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writer) => 0;
}
@@ -195,7 +197,8 @@ code = '''
defines.R = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SEED = 'range(10)'
defines.N = 20
defines.SIZE = [
@@ -232,7 +235,8 @@ code = '''
// 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;
lfsr_file_open(&lfs, &writer, "jello",
LFS_O_WRONLY | ((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;
}
@@ -249,7 +253,7 @@ code = '''
}
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
memcpy(&after[off], wbuf, size);
if (SYNC != 0) {
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writer) => 0;
}
@@ -289,7 +293,8 @@ code = '''
defines.W = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SIZE = [
'0',
'CACHE_SIZE/2',
@@ -324,7 +329,8 @@ code = '''
// 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;
lfsr_file_open(&lfs, &writers[w], "jello",
LFS_O_WRONLY | ((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++) {
@@ -338,7 +344,9 @@ code = '''
memcpy(&after[i], wbuf, CHUNK);
}
} else {
lfsr_file_sync(&lfs, &writers[w]) => 0;
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writers[w]) => 0;
}
memcpy(&after[i], wbuf, CHUNK);
}
}
@@ -361,7 +369,8 @@ code = '''
defines.W = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SEED = 'range(10)'
defines.N = 20
defines.SIZE = [
@@ -398,7 +407,8 @@ code = '''
// 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;
lfsr_file_open(&lfs, &writers[w], "jello",
LFS_O_WRONLY | ((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++) {
@@ -418,7 +428,9 @@ code = '''
memcpy(&after[off], wbuf, size);
}
} else {
lfsr_file_sync(&lfs, &writers[w]) => 0;
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writers[w]) => 0;
}
memcpy(&after[off], wbuf, size);
}
}
@@ -443,7 +455,8 @@ defines.W = 4
defines.R = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SIZE = [
'0',
'CACHE_SIZE/2',
@@ -479,7 +492,8 @@ code = '''
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;
lfsr_file_open(&lfs, &writers[w], "jello",
LFS_O_WRONLY | ((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;
@@ -496,7 +510,9 @@ code = '''
memcpy(&after[i], wbuf, CHUNK);
}
} else {
lfsr_file_sync(&lfs, &writers[w]) => 0;
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writers[w]) => 0;
}
memcpy(&after[i], wbuf, CHUNK);
}
}
@@ -532,7 +548,8 @@ defines.W = 4
defines.R = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SEED = 'range(10)'
defines.N = 20
defines.SIZE = [
@@ -570,7 +587,8 @@ code = '''
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;
lfsr_file_open(&lfs, &writers[w], "jello",
LFS_O_WRONLY | ((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;
@@ -593,7 +611,9 @@ code = '''
memcpy(&after[off], wbuf, size);
}
} else {
lfsr_file_sync(&lfs, &writers[w]) => 0;
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writers[w]) => 0;
}
memcpy(&after[off], wbuf, size);
}
}
@@ -635,7 +655,8 @@ code = '''
defines.RW = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SIZE = [
'0',
'CACHE_SIZE/2',
@@ -674,7 +695,8 @@ code = '''
// 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;
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
LFS_O_RDWR | ((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++) {
@@ -690,7 +712,9 @@ code = '''
memcpy(&after[i], wbuf, CHUNK);
}
} else {
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
if (SYNC == 1) {
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
}
memcpy(&after[i], wbuf, CHUNK);
}
}
@@ -723,7 +747,8 @@ code = '''
defines.RW = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SEED = 'range(10)'
defines.N = 20
defines.SIZE = [
@@ -764,7 +789,8 @@ code = '''
// 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;
lfsr_file_open(&lfs, &rdwrs[rw], "jello",
LFS_O_RDWR | ((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++) {
@@ -785,7 +811,9 @@ code = '''
memcpy(&after[off], wbuf, size);
}
} else {
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
if (SYNC == 1) {
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
}
memcpy(&after[off], wbuf, size);
}
}
@@ -827,7 +855,8 @@ code = '''
defines.R = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SIZE = [
'0',
'CACHE_SIZE/2',
@@ -863,7 +892,9 @@ code = '''
lfsr_file_t writer;
lfsr_file_t readers[R];
lfsr_file_open(&lfs, &writer, "jello",
LFS_O_WRONLY | LFS_O_DESYNC) => 0;
LFS_O_WRONLY
| LFS_O_DESYNC
| ((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;
}
@@ -874,7 +905,7 @@ code = '''
}
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
memcpy(&after[i], wbuf, CHUNK);
if (SYNC != 0) {
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writer) => 0;
}
@@ -911,7 +942,8 @@ code = '''
defines.R = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SEED = 'range(10)'
defines.N = 20
defines.SIZE = [
@@ -949,7 +981,9 @@ code = '''
lfsr_file_t writer;
lfsr_file_t readers[R];
lfsr_file_open(&lfs, &writer, "jello",
LFS_O_WRONLY | LFS_O_DESYNC) => 0;
LFS_O_WRONLY
| LFS_O_DESYNC
| ((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;
}
@@ -966,7 +1000,7 @@ code = '''
}
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
memcpy(&after[off], wbuf, size);
if (SYNC != 0) {
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writer) => 0;
}
@@ -1010,7 +1044,8 @@ code = '''
defines.R = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SIZE = [
'0',
'CACHE_SIZE/2',
@@ -1045,7 +1080,8 @@ code = '''
// 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;
lfsr_file_open(&lfs, &writer, "jello",
LFS_O_WRONLY | ((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;
@@ -1057,7 +1093,7 @@ code = '''
}
lfsr_file_write(&lfs, &writer, wbuf, CHUNK) => CHUNK;
memcpy(&after[i], wbuf, CHUNK);
if (SYNC != 0) {
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writer) => 0;
}
@@ -1086,7 +1122,8 @@ code = '''
defines.R = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SEED = 'range(10)'
defines.N = 20
defines.SIZE = [
@@ -1123,7 +1160,8 @@ code = '''
// 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;
lfsr_file_open(&lfs, &writer, "jello",
LFS_O_WRONLY | ((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;
@@ -1141,7 +1179,7 @@ code = '''
}
lfsr_file_write(&lfs, &writer, wbuf, size) => size;
memcpy(&after[off], wbuf, size);
if (SYNC != 0) {
if (SYNC == 1) {
lfsr_file_sync(&lfs, &writer) => 0;
}
@@ -1177,7 +1215,8 @@ code = '''
defines.RW = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SIZE = [
'0',
'CACHE_SIZE/2',
@@ -1217,7 +1256,9 @@ code = '''
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;
LFS_O_RDWR
| LFS_O_DESYNC
| ((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++) {
@@ -1233,7 +1274,9 @@ code = '''
memcpy(&after[i], wbuf, CHUNK);
}
} else {
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
if (SYNC == 1) {
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
}
if (i == 0) {
memcpy(after, between[rw], SIZE);
} else {
@@ -1274,7 +1317,8 @@ code = '''
defines.RW = 4
# 0 => no sync, readers not updated
# 1 => sync via lfsr_file_sync
defines.SYNC = [0, 1]
# 2 => sync via LFS_O_SYNC
defines.SYNC = [0, 1, 2]
defines.SEED = 'range(10)'
defines.N = 20
defines.SIZE = [
@@ -1316,7 +1360,9 @@ code = '''
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;
LFS_O_RDWR
| LFS_O_DESYNC
| ((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++) {
@@ -1337,7 +1383,9 @@ code = '''
memcpy(&after[off], wbuf, size);
}
} else {
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
if (SYNC == 1) {
lfsr_file_sync(&lfs, &rdwrs[rw]) => 0;
}
if (i == 0) {
memcpy(after, between[rw], SIZE);
} else {