Skip to content

Commit f5ae4c7

Browse files
committed
blockio: refactor and document the IO module
1 parent b39874c commit f5ae4c7

File tree

11 files changed

+221
-111
lines changed

11 files changed

+221
-111
lines changed

bench/macro/lsm-tree-bench-lookups.hs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,11 +310,10 @@ totalNumEntriesSanityCheck l1 runSizes =
310310
withFS ::
311311
(FS.HasFS IO FS.HandleIO -> FS.HasBlockIO IO FS.HandleIO -> IO a)
312312
-> IO a
313-
withFS action = do
314-
let hfs = FS.ioHasFS (FS.MountPoint "_bench_lookups")
315-
exists <- FS.doesDirectoryExist hfs (FS.mkFsPath [""])
316-
unless exists $ error ("_bench_lookups directory does not exist")
317-
FS.withIOHasBlockIO hfs FS.defaultIOCtxParams $ \hbio ->
313+
withFS action =
314+
FS.withIOHasBlockIO (FS.MountPoint "_bench_lookups") FS.defaultIOCtxParams $ \hfs hbio -> do
315+
exists <- FS.doesDirectoryExist hfs (FS.mkFsPath [""])
316+
unless exists $ error ("_bench_lookups directory does not exist")
318317
action hfs hbio
319318

320319
-- | Input environment for benchmarking lookup functions.

bench/macro/lsm-tree-bench-wp8.hs

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ import Prelude hiding (lookup)
6464
import qualified System.Clock as Clock
6565
import qualified System.FS.API as FS
6666
import qualified System.FS.BlockIO.IO as FsIO
67-
import qualified System.FS.IO as FsIO
6867
import System.IO
6968
import System.Mem (performMajorGC)
7069
import qualified System.Random as Random
@@ -414,17 +413,8 @@ doSetup gopts opts = do
414413
void $ timed_ $ doSetup' gopts opts
415414

416415
doSetup' :: GlobalOpts -> SetupOpts -> IO ()
417-
doSetup' gopts opts = do
418-
let mountPoint :: FS.MountPoint
419-
mountPoint = FS.MountPoint (rootDir gopts)
420-
421-
let hasFS :: FS.HasFS IO FsIO.HandleIO
422-
hasFS = FsIO.ioHasFS mountPoint
423-
424-
hasBlockIO <- FsIO.ioHasBlockIO hasFS FsIO.defaultIOCtxParams
425-
426-
let name = LSM.toSnapshotName "bench"
427-
416+
doSetup' gopts opts =
417+
FsIO.withIOHasBlockIO mountPoint FsIO.defaultIOCtxParams $ \hasFS hasBlockIO ->
428418
LSM.withOpenSession (mkTracer gopts) hasFS hasBlockIO benchSalt (FS.mkFsPath []) $ \session -> do
429419
tbl <- LSM.newTableWith @IO @K @V @B (mkTableConfigSetup gopts opts benchTableConfig) session
430420

@@ -438,6 +428,12 @@ doSetup' gopts opts = do
438428
]
439429

440430
LSM.saveSnapshot name label tbl
431+
where
432+
mountPoint :: FS.MountPoint
433+
mountPoint = FS.MountPoint (rootDir gopts)
434+
435+
name = LSM.toSnapshotName "bench"
436+
441437

442438
-------------------------------------------------------------------------------
443439
-- dry-run
@@ -576,17 +572,8 @@ toOperations lookups inserts = (batch1, batch2)
576572
-------------------------------------------------------------------------------
577573

578574
doRun :: GlobalOpts -> RunOpts -> IO ()
579-
doRun gopts opts = do
580-
let mountPoint :: FS.MountPoint
581-
mountPoint = FS.MountPoint (rootDir gopts)
582-
583-
let hasFS :: FS.HasFS IO FsIO.HandleIO
584-
hasFS = FsIO.ioHasFS mountPoint
585-
586-
hasBlockIO <- FsIO.ioHasBlockIO hasFS FsIO.defaultIOCtxParams
587-
588-
let name = LSM.toSnapshotName "bench"
589-
575+
doRun gopts opts =
576+
FsIO.withIOHasBlockIO mountPoint FsIO.defaultIOCtxParams $ \hasFS hasBlockIO ->
590577
LSM.withOpenSession (mkTracer gopts) hasFS hasBlockIO benchSalt (FS.mkFsPath []) $ \session ->
591578
withLatencyHandle $ \h -> do
592579
-- open snapshot
@@ -628,6 +615,11 @@ doRun gopts opts = do
628615

629616
let ops = batchCount opts * batchSize opts
630617
printf "Operations per second: %7.01f ops/sec\n" (fromIntegral ops / time)
618+
where
619+
mountPoint :: FS.MountPoint
620+
mountPoint = FS.MountPoint (rootDir gopts)
621+
622+
name = LSM.toSnapshotName "bench"
631623

632624
-------------------------------------------------------------------------------
633625
-- sequential

bench/micro/Bench/Database/LSMTree.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,8 +433,7 @@ mkFiles ::
433433
mkFiles = do
434434
sysTmpDir <- getCanonicalTemporaryDirectory
435435
benchTmpDir <- createTempDirectory sysTmpDir "full"
436-
let hfs = FS.ioHasFS (FS.MountPoint benchTmpDir)
437-
hbio <- FS.ioHasBlockIO hfs FS.defaultIOCtxParams
436+
(hfs, hbio) <- FS.ioHasBlockIO (FS.MountPoint benchTmpDir) FS.defaultIOCtxParams
438437
pure (benchTmpDir, hfs, hbio)
439438

440439
cleanupFiles ::

bench/micro/Bench/Database/LSMTree/Internal/Lookup.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,8 +191,7 @@ lookupsInBatchesEnv Config {..} = do
191191
sysTmpDir <- getCanonicalTemporaryDirectory
192192
benchTmpDir <- createTempDirectory sysTmpDir "lookupsInBatchesEnv"
193193
(storedKeys, lookupKeys) <- lookupsEnv (mkStdGen 17) nentries npos nneg
194-
let hasFS = FS.ioHasFS (FS.MountPoint benchTmpDir)
195-
hasBlockIO <- FS.ioHasBlockIO hasFS (fromMaybe FS.defaultIOCtxParams ioctxps)
194+
(hasFS, hasBlockIO) <- FS.ioHasBlockIO (FS.MountPoint benchTmpDir) (fromMaybe FS.defaultIOCtxParams ioctxps)
196195
wbblobs <- WBB.new hasFS (FS.mkFsPath ["0.wbblobs"])
197196
wb <- WB.fromMap <$> traverse (traverse (WBB.addBlob hasFS wbblobs)) storedKeys
198197
let fsps = RunFsPaths (FS.mkFsPath []) (RunNumber 0)

bench/micro/Bench/Database/LSMTree/Internal/Merge.hs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -373,8 +373,7 @@ mergeEnv ::
373373
mergeEnv config = do
374374
sysTmpDir <- getCanonicalTemporaryDirectory
375375
benchTmpDir <- createTempDirectory sysTmpDir "mergeEnv"
376-
let hasFS = FS.ioHasFS (FS.MountPoint benchTmpDir)
377-
hasBlockIO <- FS.ioHasBlockIO hasFS FS.defaultIOCtxParams
376+
(hasFS, hasBlockIO) <- FS.ioHasBlockIO (FS.MountPoint benchTmpDir) FS.defaultIOCtxParams
378377
runs <- randomRuns hasFS hasBlockIO config (mkStdGen 17)
379378
pure (benchTmpDir, hasFS, hasBlockIO, runs)
380379

Lines changed: 154 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,174 @@
1+
-- | Implementations using the real file system.
2+
--
3+
-- The implementation of the 'HasBlockIO' interface provided in this module is
4+
-- platform-dependent. Most importantly, on Linux, the implementation of
5+
-- 'submitIO' is backed by @blockio-uring@: a library for asynchronous I/O. On
6+
-- Windows and MacOS, the implementation of 'submitIO' only supports serial I/O.
17
module System.FS.BlockIO.IO (
8+
-- * Implementation details #impl#
9+
-- $impl
10+
11+
-- * Initialisation
212
ioHasBlockIO
313
, withIOHasBlockIO
14+
-- ** Parameters
415
, IOI.IOCtxParams (..)
516
, IOI.defaultIOCtxParams
17+
-- ** Unsafe
18+
, unsafeFromHasFS
19+
, withUnsafeFromHasFS
620
) where
721

822
import Control.Exception (bracket)
9-
import System.FS.API (HasFS)
23+
import System.FS.API (HasFS, MountPoint)
1024
import System.FS.BlockIO.API (HasBlockIO (..))
1125
import qualified System.FS.BlockIO.Internal as I
1226
import qualified System.FS.BlockIO.IO.Internal as IOI
13-
import System.FS.IO (HandleIO)
27+
import System.FS.IO (HandleIO, ioHasFS)
1428

15-
-- | Platform-dependent IO instantiation of 'HasBlockIO'.
16-
ioHasBlockIO ::
29+
{- $impl
30+
31+
Though the 'HasBlockIO' interface tries to capture uniform behaviour, each
32+
function in this implementation for the real file system can have subtly
33+
different effects depending on the underlying patform. For example, some
34+
features are not provided by some operating systems, and in some cases the
35+
features behave subtly differently for different operating systems. For this
36+
reason, we include below some documentation about the effects of calling the
37+
interface functions on different platforms.
38+
39+
Note: if the @serialblockio@ Cabal flag is enabled, then the Linux implementation
40+
uses a mocked context and serial I/O for 'close' and 'submitIO', just like the
41+
MacOS and Windows implementations do.
42+
43+
[IO context]: When an instance of the 'HasBlockIO' interface for Linux
44+
systems is initialised, an @io_uring@ context is created using the
45+
@blockio-uring@ package and stored in the 'HasBlockIO' closure. For uniform
46+
behaviour, each other platform creates and stores a mocked IO context that
47+
has the same open/closed behaviour as an @io_uring@ context. In summary,
48+
each platform creates:
49+
50+
* Linux: an @io_uring@ context provided by the @blockio-uring@ package
51+
* MacOS: a mocked context using an @MVar@
52+
* Windows: a mocked conext using an @MVar@
53+
54+
['close']:
55+
56+
* Linux: close the @io_uring@ context through the @blockio-uring@ package
57+
* MacOS: close the mocked context
58+
* Windows: close the mocked context
59+
60+
['submitIO']: Submit a batch of I/O operations using:
61+
62+
* Linux: the @submitIO@ function from the @blockio-uring@ package
63+
* MacOS: serial I/O using a 'HasFS'
64+
* Windows: serial I/O using a 'HasFS'
65+
66+
['hSetNoCache']:
67+
68+
* Linux: set the @O_DIRECT@ flag
69+
* MacOS: set the @F_NOCACHE@ flag
70+
* Windows: no-op
71+
72+
['hAdvise']:
73+
74+
* Linux: perform @posix_fadvise(2)@
75+
* MacOS: no-op
76+
* Windows: no-op
77+
78+
['hAllocate']:
79+
80+
* Linux: perform @posix_fallocate(2)@
81+
* MacOS: no-op
82+
* Windows: no-op
83+
84+
['tryLockFile']: This uses different locking methods depending on the OS.
85+
86+
* Linux: Open file descriptor (OFD)
87+
* MacOS: @flock@
88+
* Windows: @LockFileEx@
89+
90+
['hSynchronise']:
91+
92+
* Linux: perform @fsync(2)@
93+
* MacOS: perform @fsync(2)@
94+
* Windows: perform @flushFileBuffers@
95+
96+
['synchroniseDirectory']:
97+
98+
* Linux: perform @fsync(2)@
99+
* MacOS: perform @fsync(2)@
100+
* Windows: no-op
101+
102+
['createHardLink']:
103+
104+
* Linux: perform @link@
105+
* MacOS: perform @link@
106+
* Windows: perform @CreateHardLinkW@
107+
-}
108+
109+
-- | An implementation of the 'HasBlockIO' interface using the real file system.
110+
--
111+
-- Make sure to use 'close' the resulting 'HasBlockIO' when it is no longer
112+
-- used. 'withUnsafeFromHasFS' does this automatically.
113+
--
114+
-- === Unsafe
115+
--
116+
-- You will probably want to use 'ioHasBlockIO' or 'withIOHasBlockIO' instead.
117+
--
118+
-- Only a 'HasFS' for the real file system, like 'ioHasFS', should be passed to
119+
-- 'unsafeFromHasFS'. Technically, one could pass a 'HasFS' for a simulated file
120+
-- system, but then the resulting 'HasBlockIO' would contain a mix of simulated
121+
-- and real functions, which is probably not what you want.
122+
unsafeFromHasFS ::
17123
HasFS IO HandleIO
18124
-> IOI.IOCtxParams
19125
-> IO (HasBlockIO IO HandleIO)
20-
ioHasBlockIO = I.ioHasBlockIO
126+
unsafeFromHasFS = I.ioHasBlockIO
21127

22-
withIOHasBlockIO ::
128+
-- | Perform an action using a 'HasBlockIO' instance that is only open for the
129+
-- duration of the action.
130+
--
131+
-- The 'HasBlockIO' is initialised using 'unsafeFromHasFS'.
132+
--
133+
-- === Unsafe
134+
--
135+
-- You will probably want to use 'ioHasBlockIO' or 'withIOHasBlockIO' instead.
136+
--
137+
-- Only a 'HasFS' for the real file system, like 'ioHasFS', should be passed to
138+
-- 'withUnsafeFromHasFS'. Technically, one could pass a 'HasFS' for a simulated
139+
-- file system, but then the resulting 'HasBlockIO' would contain a mix of
140+
-- simulated and real functions, which is probably not what you want.
141+
withUnsafeFromHasFS ::
23142
HasFS IO HandleIO
24143
-> IOI.IOCtxParams
25144
-> (HasBlockIO IO HandleIO -> IO a)
26145
-> IO a
27-
withIOHasBlockIO hfs params action =
28-
bracket (ioHasBlockIO hfs params) (\HasBlockIO{close} -> close) action
146+
withUnsafeFromHasFS hfs params =
147+
bracket (unsafeFromHasFS hfs params) (\HasBlockIO{close} -> close)
148+
149+
-- | An implementation of the 'HasBlockIO' interface using the real file system.
150+
--
151+
-- Make sure to use 'close' the resulting 'HasBlockIO' when it is no longer
152+
-- used. 'withIOHasBlockIO' does this automatically.
153+
--
154+
-- The 'HasFS' interface is instantiated using 'ioHasFS'.
155+
ioHasBlockIO ::
156+
MountPoint
157+
-> IOI.IOCtxParams
158+
-> IO (HasFS IO HandleIO, HasBlockIO IO HandleIO)
159+
ioHasBlockIO mount params = do
160+
let hfs = ioHasFS mount
161+
hbio <- unsafeFromHasFS hfs params
162+
pure (hfs, hbio)
163+
164+
-- | Perform an action using a 'HasFS' and a 'HasBlockIO' instance. The latter
165+
-- is only open for the duration of the action.
166+
--
167+
-- The 'HasFS' and 'HasBlockIO' interfaces are initialised using 'ioHasBlockIO'.
168+
withIOHasBlockIO ::
169+
MountPoint
170+
-> IOI.IOCtxParams
171+
-> (HasFS IO HandleIO -> HasBlockIO IO HandleIO -> IO a)
172+
-> IO a
173+
withIOHasBlockIO mount params action =
174+
bracket (ioHasBlockIO mount params) (\(_, HasBlockIO{close}) -> close) (uncurry action)

0 commit comments

Comments
 (0)