From 2eca52b4a4c14f74a5269b7eb4643fca638d1fbe Mon Sep 17 00:00:00 2001 From: Cheng Shao <terrorjack@type.dance> Date: Thu, 14 Dec 2023 02:02:35 +0000 Subject: [PATCH] base: treat all FDs as "nonblocking" on wasm On posix platforms, when performing read/write on FDs, we check the nonblocking flag first. For FDs without this flag (e.g. stdout), we call fdReady() first, which in turn calls poll() to wait for I/O to be available on that FD. This is problematic for wasm32-wasi: although select()/poll() is supported via the poll_oneoff() wasi syscall, that syscall is rather heavyweight and runtime behavior differs in different wasi implementations. The issue is even worse when targeting browsers, given there's no satisfactory way to implement async I/O as a synchronous syscall, so existing JS polyfills for wasi often give up and simply return ENOSYS. Before we have a proper I/O manager that avoids poll_oneoff() for async I/O on wasm, this patch improves the status quo a lot by merely pretending all FDs are "nonblocking". Read/write on FDs will directly invoke read()/write(), which are much more reliably handled in existing wasi implementations, especially those in browsers. Fixes #23275 and the following test cases: T7773 isEOF001 openFile009 T4808 cgrun025 Approved by CLC proposal #234: https://github.com/haskell/core-libraries-committee/issues/234 --- libraries/base/cbits/inputReady.c | 4 ++++ libraries/base/changelog.md | 2 ++ libraries/base/tests/IO/all.T | 5 ++--- libraries/base/tests/all.T | 5 ++--- testsuite/tests/codeGen/should_run/all.T | 2 +- 5 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libraries/base/cbits/inputReady.c b/libraries/base/cbits/inputReady.c index 3f636c6b287..d1c1da2e4c4 100644 --- a/libraries/base/cbits/inputReady.c +++ b/libraries/base/cbits/inputReady.c @@ -153,6 +153,9 @@ compute_WaitForSingleObject_timeout(bool infinite, Time remaining) int fdReady(int fd, bool write, int64_t msecs, bool isSock) { +#if defined(wasm32_HOST_ARCH) + return 1; +#else bool infinite = msecs < 0; // if we need to track the time then record the end time in case we are @@ -477,4 +480,5 @@ fdReady(int fd, bool write, int64_t msecs, bool isSock) } } #endif +#endif // wasm32_HOST_ARCH } diff --git a/libraries/base/changelog.md b/libraries/base/changelog.md index 538a81ba9c4..2d9973a8d36 100644 --- a/libraries/base/changelog.md +++ b/libraries/base/changelog.md @@ -35,6 +35,8 @@ * Add more instances for `Compose`: `Fractional`, `RealFrac`, `Floating`, `RealFloat` ([CLC proposal #226](https://github.com/haskell/core-libraries-committee/issues/226)) + * Treat all FDs as "nonblocking" on wasm32 ([CLC proposal #234](https://github.com/haskell/core-libraries-committee/issues/234)) + ## 4.19.0.0 *October 2023* * Add `{-# WARNING in "x-partial" #-}` to `Data.List.{head,tail}`. Use `{-# OPTIONS_GHC -Wno-x-partial #-}` to disable it. diff --git a/libraries/base/tests/IO/all.T b/libraries/base/tests/IO/all.T index 946d8ea510b..5c134201451 100644 --- a/libraries/base/tests/IO/all.T +++ b/libraries/base/tests/IO/all.T @@ -63,7 +63,7 @@ test('hSetBuffering004', set_stdin('hSetBuffering004.hs'), compile_and_run, [''] test('ioeGetErrorString001', normal, compile_and_run, ['-cpp']) test('ioeGetFileName001', normal, compile_and_run, ['-cpp']) test('ioeGetHandle001', normal, compile_and_run, ['-cpp']) -test('isEOF001', [extra_run_opts('</dev/null'), when(arch('wasm32'), fragile(23275))], compile_and_run, ['']) +test('isEOF001', [extra_run_opts('</dev/null')], compile_and_run, ['']) test('misc001', [extra_run_opts('misc001.hs misc001.out')], compile_and_run, ['']) @@ -76,7 +76,7 @@ test('openFile005', js_broken(22261), compile_and_run, ['']) test('openFile006', [], compile_and_run, ['']) test('openFile007', js_broken(22261), compile_and_run, ['']) test('openFile008', [cmd_prefix('ulimit -n 1024; ')], compile_and_run, ['']) -test('openFile009', [when(arch('wasm32'), fragile(23284))], compile_and_run, ['']) +test('openFile009', [], compile_and_run, ['']) test('putStr001', normal, compile_and_run, ['']) test('readFile001', js_broken(22261), compile_and_run, ['']) @@ -155,7 +155,6 @@ test('encodingerror001', normal, compile_and_run, ['']) # Requires use of the FD interface which is not supported under WINIO test('T4808', [when(opsys('mingw32'), skip) - , when(arch('wasm32'), fragile(23284)) ,fragile_for(16909, concurrent_ways), exit_code(1)] , compile_and_run, ['']) test('T4895', normal, compile_and_run, ['']) diff --git a/libraries/base/tests/all.T b/libraries/base/tests/all.T index 1a7ebed8aaf..1f3d92c1191 100644 --- a/libraries/base/tests/all.T +++ b/libraries/base/tests/all.T @@ -175,9 +175,8 @@ test('T7457', normal, compile_and_run, ['']) test('T7773', [when(opsys('mingw32'), skip), js_broken(22261), - expect_broken_for(23272, ['ghci-opt']), # unclear - when(arch('wasm32'), - fragile(23275))], + expect_broken_for(23272, ['ghci-opt']) # unclear + ], compile_and_run, ['']) # Andreas says that T7773 will not (and should not) work on Windows diff --git a/testsuite/tests/codeGen/should_run/all.T b/testsuite/tests/codeGen/should_run/all.T index 5d4e36a3ae7..4bfa5b3be46 100644 --- a/testsuite/tests/codeGen/should_run/all.T +++ b/testsuite/tests/codeGen/should_run/all.T @@ -29,7 +29,7 @@ test('cgrun021', extra_ways(['nursery_chunks']), compile_and_run, ['']) test('cgrun022', normal, compile_and_run, ['']) test('cgrun024', normal, compile_and_run, ['']) test('cgrun025', - [ omit_ghci, extra_run_opts('cgrun025.hs < /dev/null'), exit_code(1), when(arch('wasm32'), fragile(23275))], + [ omit_ghci, extra_run_opts('cgrun025.hs < /dev/null'), exit_code(1) ], compile_and_run, ['']) test('cgrun026', normal, compile_and_run, ['']) test('cgrun027', normal, compile_and_run, ['']) -- GitLab