stdio- A simple and high performance IO toolkit for Haskell

Copyright(c) Dong Han 2017-2018
Safe HaskellNone




This module provide IO manager which bridge libuv's async interface with ghc's light weight thread.

The main procedures for doing event IO is:

  • Allocate a slot number using allocSlot.
  • Prepare you IO buffer and write them to uv loop with pokeBufferTable(both read and write).
  • Block your thread with a MVar, using getBlockMVar to get it.
  • Read the result with getResult, for read it's the read bytes number, for write it will be zero. Use throwIfError to guard error situations.
  • Return the slot back uv manager with freeSlot.

Usually slots are cache in the IO device so that you don't have to allocate new one before each IO operation. Check System.IO.Socket.TCP as an example.



data UVManager Source #

Eq UVManager Source # 
Instance details

Defined in Std.IO.UV.Manager

Show UVManager Source # 
Instance details

Defined in Std.IO.UV.Manager

getUVManager :: IO UVManager Source #

Get UVManager runing on the same capability.

getBlockMVar :: UVManager -> UVSlot -> IO (MVar Int) Source #

Get MVar from blocking table with given slot.

pokeBufferTable :: UVManager -> UVSlot -> Ptr Word8 -> Int -> IO () Source #

Poke a prepared buffer and size into loop data under given slot.

NOTE, this action is not protected with 'withUVManager_ for effcient reason, you should merge this action with other uv action and put them together inside a 'withUVManager_ or 'withUVManager\''. for example:

   withUVManager_ uvm $ do
       pokeBufferTable uvm slot buf len
       uvReadStart handle

withUVManager :: HasCallStack => UVManager -> (Ptr UVLoop -> IO a) -> IO a Source #

Lock an uv mananger, so that we can safely mutate its uv_loop's state.

libuv is not thread safe, use this function to perform any action which will mutate uv_loop's state.

withUVManager_ :: HasCallStack => UVManager -> IO a -> IO a Source #

Lock an uv mananger, so that we can safely mutate its uv_loop's state.

Some action did not request uv_loop pointer explicitly, but will mutate uv_loop underhood, for example: uv_read_start. These actions have to be protected by locking the uv_loop.

In fact most of the libuv's functions are not thread safe, so watch out!

getUVSlot :: HasCallStack => UVManager -> IO UVSlotUnSafe -> IO UVSlot Source #

Run a libuv FFI to get a UVSlotUnSafe (which may exceed block table size), resize the block table in that case, so that the returned slot always has an accompanying MVar in block table.

Always use this function to turn an UVSlotUnsafe into UVSlot, so that the block table size synchronize with libuv side's slot table.

request based async function helper

withUVRequest :: HasCallStack => UVManager -> (Ptr UVLoop -> IO UVSlotUnSafe) -> IO Int Source #

Exception safe uv request helper

This helper will run a libuv's async function, which will return a libuv side's slot, then we will accommodate a MVar in block table and wait on that MVar, until the async function finished or an exception is received, in later case we will call cancelUVReq to cancel the on-going async function with best efforts,

withUVRequest_ :: HasCallStack => UVManager -> (Ptr UVLoop -> IO UVSlotUnSafe) -> IO () Source #

Same with withUVRequest but disgard the result.

withUVRequest' Source #


:: HasCallStack 
=> UVManager 
-> (Ptr UVLoop -> IO UVSlotUnSafe) 
-> (Int -> IO b)

convert function

-> IO b 

Same with withUVRequest but apply an convert function to result.

The convert function have all access to the returned value including negative ones, it's convert funtions's responsiblity to throw an exception if appropriate.

withUVRequestEx :: HasCallStack => UVManager -> (Ptr UVLoop -> IO UVSlotUnSafe) -> (Int -> IO ()) -> IO Int Source #

Same with withUVRequest, but will also run an extra cleanup function if async exception hit this thread but the async action is already successfully performed, e.g. release result memory.

uv_stream abstraction

initUVStream :: HasCallStack => (Ptr UVLoop -> Ptr UVHandle -> IO ()) -> UVManager -> Resource UVStream Source #

Safely lock an uv manager and perform uv_handle initialization.

Initialization an UV stream usually take two step:

  • allocate an uv_stream struct with proper size
  • lock a particular uv_loop from a uv manager, and perform custom initialization, such as uv_tcp_init.

And this is what initUVStream do, all you need to do is to provide the manager you want to hook the handle onto(usually the one on the same capability, i.e. the one obtained by getUVManager), and provide a custom initialization function.

data UVStream Source #

A haskell data type wrap an uv_stream_t inside

UVStream DO NOT provide thread safety! Use UVStream concurrently in multiple threads will lead to undefined behavior.


Show UVStream Source # 
Instance details

Defined in Std.IO.UV.Manager

Output UVStream Source # 
Instance details

Defined in Std.IO.UV.Manager


writeOutput :: UVStream -> Ptr Word8 -> Int -> IO () Source #

Input UVStream Source # 
Instance details

Defined in Std.IO.UV.Manager


readInput :: UVStream -> Ptr Word8 -> Int -> IO Int Source #

concurrent helpers

forkBa :: IO () -> IO ThreadId Source #

Fork a new GHC thread with active load-balancing.

Using libuv based IO solution has a disadvantage that file handlers are bound to certain uv_loop, thus certain uv mananger/capability. Worker threads that migrate to other capability will lead contention since various APIs here is protected by manager's lock, this makes GHC's work-stealing strategy unsuitable for certain workload, such as a webserver. we solve this problem with simple round-robin load-balancing: forkBa will automatically distribute new threads to all capabilities in round-robin manner. Thus its name forkBa(lance).