Skip to content

Commit 2f31cf1

Browse files
authored
wasip3: Fix updating offset during read/write (#791)
This commit refactors how `offset` is updated during the internal read/write operations on streams. This isn't applicable for TCP but matters for files, for example. There's not a great way to test the paths that were previously missing the update but the code is structured differently now to make it much harder to forget to do an update. The test modified here is a light test which doesn't reproduce the original issue but at least ensures that nonblocking paths update the offset as well. > **Note**: this is temporarily built on #788
1 parent fc3bc46 commit 2f31cf1

3 files changed

Lines changed: 39 additions & 30 deletions

File tree

libc-bottom-half/sources/file.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,10 @@ static off_t file_seek(void *data, off_t offset, int whence) {
273273
errno = EINVAL;
274274
return -1;
275275
}
276-
file->offset = result;
277-
file_close_streams(data);
276+
if (result != file->offset) {
277+
file_close_streams(data);
278+
file->offset = result;
279+
}
278280
return file->offset;
279281
}
280282

libc-bottom-half/sources/file_utils.c

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -291,8 +291,11 @@ static size_t wasip3_read_complete_internally(wasip3_io_state_t *state,
291291
}
292292
#endif
293293

294-
ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer,
295-
size_t length) {
294+
/// Internal implementation of `__wasilibc_write` except that this doesn't
295+
/// update `write->offset`. That's done in the wrapper around this.
296+
static ssize_t __wasilibc_write_without_offset_update(wasi_write_t *write,
297+
const void *buffer,
298+
size_t length) {
296299
#if defined(__wasip2__)
297300
assert(write->output.__handle != 0);
298301
while (true) {
@@ -319,11 +322,6 @@ ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer,
319322
if (!ok)
320323
return wasip2_handle_write_error(error);
321324
}
322-
323-
// Update the offset if this stream is tracking that.
324-
if (write->offset)
325-
*write->offset += count;
326-
327325
return count;
328326
}
329327

@@ -403,8 +401,6 @@ ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer,
403401
state->flags |= WASIP3_IO_DONE;
404402
if (amount < 0)
405403
return -1;
406-
if (write->offset)
407-
*write->offset += amount;
408404
if (amount == 0 && done)
409405
return write->eof(write->eof_data);
410406
return amount;
@@ -436,11 +432,8 @@ ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer,
436432
status = filesystem_stream_u8_cancel_write(state->stream);
437433
}
438434
size_t amount = wasip3_io_update_code(state, status);
439-
if (amount > 0) {
440-
if (write->offset)
441-
*write->offset += amount;
435+
if (amount > 0)
442436
return amount;
443-
}
444437
if (state->flags & WASIP3_IO_DONE)
445438
return write->eof(write->eof_data);
446439
}
@@ -492,11 +485,8 @@ ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer,
492485
// If the I/O is blocked, then that's ok. The data is all owned by `state`
493486
// meaning that we've effectively just faked a write of `length` bytes. Here
494487
// it's reported as having written everything.
495-
if (status == WASIP3_WAITABLE_STATUS_BLOCKED) {
496-
if (write->offset)
497-
*write->offset += length;
488+
if (status == WASIP3_WAITABLE_STATUS_BLOCKED)
498489
return length;
499-
}
500490

501491
// If that write actually succeeded, however, then deal with it here. That
502492
// means we have to deallocate the internal buffer and then handle the result
@@ -511,7 +501,20 @@ ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer,
511501
#endif
512502
}
513503

514-
ssize_t __wasilibc_read(wasi_read_t *read, void *buffer, size_t length) {
504+
ssize_t __wasilibc_write(wasi_write_t *write, const void *buffer,
505+
size_t length) {
506+
ssize_t result =
507+
__wasilibc_write_without_offset_update(write, buffer, length);
508+
if (result > 0 && write->offset)
509+
*write->offset += result;
510+
return result;
511+
}
512+
513+
/// Internal helper for `__wasilibc_read` that dosen't update `read->offset`,
514+
/// like the write helper above.
515+
static ssize_t __wasilibc_read_without_offset_update(wasi_read_t *read,
516+
void *buffer,
517+
size_t length) {
515518
#if defined(__wasip2__)
516519
while (true) {
517520
wasip2_list_u8_t result;
@@ -532,8 +535,6 @@ ssize_t __wasilibc_read(wasi_read_t *read, void *buffer, size_t length) {
532535
size_t len = result.len;
533536
memcpy(buffer, result.ptr, len);
534537
wasip2_list_u8_free(&result);
535-
if (read->offset)
536-
*read->offset += len;
537538
return len;
538539
}
539540

@@ -631,8 +632,6 @@ ssize_t __wasilibc_read(wasi_read_t *read, void *buffer, size_t length) {
631632
state->flags |= WASIP3_IO_DONE;
632633
if (amount < 0)
633634
return -1;
634-
if (read->offset)
635-
*read->offset += amount;
636635
if (amount == 0 && done)
637636
return read->eof(read->eof_data);
638637
return amount;
@@ -656,11 +655,8 @@ ssize_t __wasilibc_read(wasi_read_t *read, void *buffer, size_t length) {
656655
status = filesystem_stream_u8_cancel_read(state->stream);
657656
}
658657
size_t amount = wasip3_io_update_code(state, status);
659-
if (amount > 0) {
660-
if (read->offset)
661-
*read->offset += amount;
658+
if (amount > 0)
662659
return amount;
663-
}
664660
if (state->flags & WASIP3_IO_DONE)
665661
return read->eof(read->eof_data);
666662
}
@@ -691,6 +687,13 @@ ssize_t __wasilibc_read(wasi_read_t *read, void *buffer, size_t length) {
691687
#endif
692688
}
693689

690+
ssize_t __wasilibc_read(wasi_read_t *read, void *buffer, size_t length) {
691+
ssize_t result = __wasilibc_read_without_offset_update(read, buffer, length);
692+
if (result > 0 && read->offset)
693+
*read->offset += result;
694+
return result;
695+
}
696+
694697
#ifndef __wasip2__
695698
static void wasip3_poll_read_ready(void *data, poll_state_t *state,
696699
wasip3_event_t *event) {

test/src/fs-nonblocking.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ int main() {
2929

3030
pollfd.events = POLLWRNORM;
3131
for (int i = 0; size < max && i < 100; i++) {
32-
while (size < max && (rc = write(fd, "hello", 5)) > 0)
32+
while (size < max && (rc = write(fd, "hello", 5)) > 0) {
3333
size += rc;
34+
TEST(lseek(fd, 0, SEEK_CUR) == size);
35+
}
3436
TEST(poll(&pollfd, 1, -1) != -1);
3537
}
3638

@@ -46,8 +48,10 @@ int main() {
4648
pollfd.events = POLLRDNORM;
4749
int remaining = size;
4850
while (remaining) {
49-
while ((rc = read(fd, buf, sizeof(buf))) > 0)
51+
while ((rc = read(fd, buf, sizeof(buf))) > 0) {
5052
remaining -= rc;
53+
TEST(lseek(fd, 0, SEEK_CUR) == (size - remaining));
54+
}
5155
TEST(poll(&pollfd, 1, -1) != -1);
5256
}
5357

0 commit comments

Comments
 (0)