packages feed

blockio-uring 0.1.0.3 → 0.2.0.0

raw patch · 7 files changed

+106/−24 lines, 7 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ System.IO.BlockIO: [ioctxIOWaitMetrics] :: IOCtxParams -> !Bool
- System.IO.BlockIO: IOCtxParams :: !Int -> !Int -> IOCtxParams
+ System.IO.BlockIO: IOCtxParams :: !Int -> !Int -> !Bool -> IOCtxParams

Files

CHANGELOG.md view
@@ -1,5 +1,31 @@ # Revision history for blockio-uring +## 0.2.0.0 -- 2026-04-30++### Breaking changes++* `IOCtxParams` has a new record field for configuring the use of IOWAIT metrics+  (see below).++### New features++* Support enabling/disabling IOWAIT metrics. See [issue+  #55](https://github.com/well-typed/blockio-uring/issues/55) and [PR+  #57](https://github.com/well-typed/blockio-uring/pull/57).++### Minor changes++None++### Bug fixes++* On systems with limited CPU and memory resources, `submitIO` could reliably+  fail with `EFAULT` error numbers because of frequent reaping and rescheduling+  of Haskell's lightweight threads. The bug is fixed by always running+  `submitIO` in a fresh bound thread. See [issue+  #58](https://github.com/well-typed/blockio-uring/issues/58) and [PR+  #60](https://github.com/well-typed/blockio-uring/pull/60).+ ## 0.1.0.3 -- 2026-03-12  * PATCH: support `ghc-9.14`. See [PR
benchmark/Bench.hs view
@@ -127,7 +127,8 @@       lastBlock = fromIntegral (size `div` 4096 - 1)       params    = IOCtxParams {                     ioctxBatchSizeLimit   = 64,-                    ioctxConcurrencyLimit = 64 * 4+                    ioctxConcurrencyLimit = 64 * 4,+                    ioctxIOWaitMetrics    = True                   }       ntasks    = 4 * ncaps       batchsz   = 32@@ -243,4 +244,3 @@         (i, rng') = uniformR (0, Set.size xs - 1) rng         !x   = Set.elemAt i xs         !xs' = Set.deleteAt i xs-
blockio-uring.cabal view
@@ -1,6 +1,6 @@ cabal-version:   3.4 name:            blockio-uring-version:         0.1.0.3+version:         0.2.0.0 synopsis:        Perform batches of asynchronous disk IO operations. description:   This library supports disk I/O operations using the Linux io_uring API. The@@ -24,7 +24,9 @@ copyright:       (c) Well-Typed LLP 2022 - 2025 category:        System build-type:      Simple-tested-with:     GHC ==9.2 || ==9.4 || ==9.6 || ==9.8 || ==9.10 || ==9.12 || ==9.14+tested-with:+  GHC ==9.2 || ==9.4 || ==9.6 || ==9.8 || ==9.10 || ==9.12 || ==9.14+ extra-doc-files:   CHANGELOG.md   README.md@@ -36,7 +38,7 @@ source-repository this   type:     git   location: https://github.com/well-typed/blockio-uring-  tag:      blockio-uring-0.1.0.3+  tag:      blockio-uring-0.2.0.0  common warnings   ghc-options:@@ -88,7 +90,7 @@     , base        <5     , containers     , primitive-    , random ^>= 1.3+    , random      ^>=1.3     , time     , unix        ^>=2.8.7.0     , vector
src/System/IO/BlockIO.hs view
@@ -29,7 +29,7 @@ import Control.Monad import Control.Monad.Primitive import Control.Concurrent (forkOn, myThreadId, threadCapability,-                           getNumCapabilities)+                           getNumCapabilities, runInBoundThread) import Control.Concurrent.MVar import Control.Concurrent.QSemN import Control.Concurrent.Chan@@ -122,7 +122,16 @@     -- If a use of 'submitIO' would lead to this limit being exceeded, then the     -- call to 'submitIO' will block until enough I/O batches have been     -- processed.-    ioctxConcurrencyLimit :: !Int+    ioctxConcurrencyLimit :: !Int,++    -- | Enable or disable IOWAIT metrics+    --+    -- If @io_uring_set_iowait(3)@ is available, then it will be called with+    -- this value to either enable ('True') or disable ('False') IOWAIT+    -- metrics. This is innocuous in terms of performance but changes how CPU+    -- idle time is reported. @io_uring_set_iowait(3)@ is available for+    -- liburing versions >= 2.10 and Linux kernels versions >= 6.15.+    ioctxIOWaitMetrics :: !Bool   }   deriving stock Show @@ -132,7 +141,8 @@ defaultIOCtxParams =   IOCtxParams {     ioctxBatchSizeLimit   = 64,-    ioctxConcurrencyLimit = 64 * 3+    ioctxConcurrencyLimit = 64 * 3,+    ioctxIOWaitMetrics    = True   }  validateIOCtxParams :: IOCtxParams -> Maybe String@@ -179,11 +189,13 @@ initIOCapCtx :: IOCtxParams -> CapNo -> IO IOCapCtx initIOCapCtx IOCtxParams {                ioctxBatchSizeLimit,-               ioctxConcurrencyLimit+               ioctxConcurrencyLimit,+               ioctxIOWaitMetrics              } capno = do     mask_ $ do       ioctxQSemN         <- newQSemN ioctxConcurrencyLimit       uring              <- URing.setupURing (URing.URingParams ioctxBatchSizeLimit ioctxConcurrencyLimit)+      URing.setIOWait uring ioctxIOWaitMetrics       ioctxURing         <- newMVar (Just uring)       ioctxChanIOBatch   <- newChan       ioctxChanIOBatchIx <- newChan@@ -284,16 +296,24 @@ --   at least the target number in flight at once. -- submitIO :: IOCtx -> V.Vector (IOOp RealWorld) -> IO (VU.Vector IOResult)-submitIO (IOCtx capctxs) !ioops = do-    -- Find out which capability the thread is currently running on and use-    -- that one. It does _not matter_ for correctness if the thread is migrated-    -- while the I/O is submitted or when waiting for completion. Migration-    -- happens sufficiently infrequently that it should not be a performance-    -- problem.-    tid <- myThreadId-    (capno, _) <- threadCapability tid-    let !capctx = capctxs V.! (capno `mod` V.length capctxs)-    submitCapIO capctx ioops+submitIO (IOCtx capctxs) !ioops =+    -- Requests have to be submitted from within a bound thread, or we might get+    -- @EFAULT@ errors. See issue #58.+    --+    -- TODO <https://github.com/well-typed/blockio-uring/issues/61>: ideally,+    -- for performance, it would be better to run a smaller section of the+    -- 'submitIO' code inside 'runInBoundThread'. However, it is not 100% clear+    -- what the critical code section is that /has/ to run in a bound thread .+    -- So, for now we pick the safe option of running the entirety of 'submitIO'+    -- in a bound thread.+    runInBoundThread $ do+      -- Find out which capability the thread is currently running on and use+      -- that one. It does _not matter_ for correctness that the thread submits+      -- IO to that same thread's capability's ring, but it is more performant.+      tid <- myThreadId+      (capno, _) <- threadCapability tid+      let !capctx = capctxs V.! (capno `mod` V.length capctxs)+      submitCapIO capctx ioops  submitCapIO :: IOCapCtx -> V.Vector (IOOp RealWorld) -> IO (VU.Vector IOResult) submitCapIO ioctx@IOCapCtx {ioctxBatchSizeLimit'} !ioops
src/System/IO/BlockIO/URing.hs view
@@ -7,6 +7,7 @@     setupURing,     closeURing,     withURing,+    setIOWait,     IOOpId(..),     prepareRead,     prepareWrite,@@ -94,6 +95,10 @@ withURing params =     bracket (setupURing params) closeURing +setIOWait :: URing -> Bool -> IO ()+setIOWait URing {uringptr} b =+    callIfSupported_ "setIOWAIT" $+      FFI.io_uring_set_iowait uringptr (if b then 1 else 0)  -- -- Submitting I/O@@ -231,6 +236,19 @@                    (Errno (-res))                    Nothing Nothing +callIfSupported_ :: String -> IO CInt -> IO ()+callIfSupported_ label action = do+  res <- action+  if Errno (-res) == eOPNOTSUPP+    then pure ()+    else+      when (res < 0) $+        throwIO $+          errnoToIOError+          label+          (Errno (-res))+          Nothing Nothing+ throwErrResIfNull :: String -> IOErrorType -> String -> IO (Ptr a) -> IO (Ptr a) throwErrResIfNull location ioErrorType description action = do     res <- action@@ -243,4 +261,3 @@                  Nothing Nothing)               description       else return res-
src/System/IO/BlockIO/URingFFI.hsc view
@@ -125,3 +125,20 @@ foreign import capi unsafe "liburing.h io_uring_cqe_seen"   io_uring_cqe_seen :: Ptr URing -> Ptr URingCQE -> IO () ++--+-- Calling @io_uring_set_iowait(3)@ if available+--++-- | Available only on liburing >= 2.10 and kernel >= 6.15.+--+-- See @io_uring_set_iowait(3)@ man page.+#ifdef IORING_FEAT_NO_IOWAIT+#if IORING_FEAT_NO_IOWAIT+foreign import capi unsafe "liburing.h io_uring_set_iowait"+  io_uring_set_iowait :: Ptr URing -> CBool -> IO CInt+#endif+#else+io_uring_set_iowait :: Ptr URing -> CBool -> IO CInt+io_uring_set_iowait _ _ = pure #{const EOPNOTSUPP}+#endif
test/test.hs view
@@ -98,9 +98,9 @@     concurrencyLimitBoundsExcl batchSizeLimit =  (batchSizeLimit, 2^(16::Int))  instance Arbitrary IOCtxParams where-  arbitrary = IOCtxParams <$> genLimit <*> genLimit-  shrink (IOCtxParams a b) =-      [ IOCtxParams a' b' | (a', b') <- liftShrink2 shrinkLimit shrinkLimit (a, b) ]+  arbitrary = IOCtxParams <$> genLimit <*> genLimit <*> arbitrary+  shrink (IOCtxParams a b i) =+      [ IOCtxParams a' b' i' | (a', (b', i')) <- liftShrink2 shrinkLimit (liftShrink2 shrinkLimit shrink) (a, (b, i)) ]  genLimit :: Gen Int genLimit = frequency [