{-# LANGUAGE NoImplicitPrelude #-} module GHC.Internal.Wasm.Prim.Conc ( threadDelay ) where import GHC.Internal.Base import GHC.Internal.IO.Unsafe import GHC.Internal.Stable {- Note [threadDelay on wasm] ~~~~~~~~~~~~~~~~~~~~~~~~~~ When you compile Haskell to wasm32-wasi and use wasmtime to run it, threadDelay works, because the poll_oneoff syscall is properly implemented, so the awaitEvent() logic in the single-threaded RTS works. However, given constraints of the browser environment, wasi implementations in pure JavaScript tend to not implement poll_oneoff at all, so we can't pretend the old posix style polling works at all. Since we can do JSFFI async imports now, it's super easy to implement threadDelay using setTimeout() in JavaScript, and an implementation has indeed been added in GHC.Internal.Wasm.Prim.Conc.Internal. But how do we make the user-facing GHC.Internal.Conc.IO.threadDelay switch to it in browser environments and fall back to the stg_delay# implementation otherwise? A first step is the isJSFFIUsed magic boolean which fetches a C boolean global variable. If JSFFI is used, then JSFFI.o would be included by wasm-ld, and the ctor there will handle RTS initialization as well as setting that boolean to true. This all takes place before actual Haskell evaluation, so you can use isJSFFIUsed in Haskell to observe whether the current project makes use of JSFFI (aka targets a JavaScript host like browsers) at all. However, merely referring to the right threadDelay implementation based on the isJSFFIUsed guard is not enough! wasm-ld will transitively include the version that uses JSFFI in the final module as well as a bunch of other JSFFI related stuff, so the resulting wasm module will now unconditionally contain non-wasi imports :( This violates our principle of "don't pay for JavaScript when you don't use it". So we need a bit of dependency injection here. We use a global stable pointer variable in C to point to the actual JSFFI-based threadDelay function closure. When JSFFI is not used, it defaults to NULL, but it's okay since it'll not be used at runtime. When JSFFI is used, the ctor in JSFFI.o will inject the right closure's stable pointer into that variable, which will be dereferenced here the first time threadDelay is called. This way, we can ensure the JSFFI based threadDelay is used in browsers, while not contaminating standalone wasm32-wasi modules with JSFFI stuff. And yes, it's safe to free the stable pointer here, once the function closure has been fetched. This allows the CAF to be garbage collected when user code no longer uses threadDelay. -} {-# OPAQUE threadDelay #-} threadDelay :: Int -> IO () threadDelay = unsafeDupablePerformIO $ do f <- deRefStablePtr rts_threadDelay_sp freeStablePtr rts_threadDelay_sp pure f foreign import ccall unsafe "rts_threadDelay_sp" rts_threadDelay_sp :: StablePtr (Int -> IO ())