haxl-2.0.0.0: Haxl/DataSource/ConcurrentIO.hs
-- Copyright (c) 2014-present, Facebook, Inc.
-- All rights reserved.
--
-- This source code is distributed under the terms of a BSD license,
-- found in the LICENSE file.
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TypeFamilies #-}
-- |
-- A generic Haxl datasource for performing arbitrary IO concurrently.
-- Every IO operation will be performed in a separate thread.
-- You can use this with any kind of IO, but each different operation
-- requires an instance of the 'ConcurrentIO' class.
--
-- For example, to make a concurrent sleep operation:
--
-- > sleep :: Int -> GenHaxl u Int
-- > sleep n = dataFetch (Sleep n)
-- >
-- > data Sleep
-- > instance ConcurrentIO Sleep where
-- > data ConcurrentIOReq Sleep a where
-- > Sleep :: Int -> ConcurrentIOReq Sleep Int
-- >
-- > performIO (Sleep n) = threadDelay (n*1000) >> return n
-- >
-- > deriving instance Eq (ConcurrentIOReq Sleep a)
-- > deriving instance Show (ConcurrentIOReq Sleep a)
-- >
-- > instance ShowP (ConcurrentIOReq Sleep) where showp = show
-- >
-- > instance Hashable (ConcurrentIOReq Sleep a) where
-- > hashWithSalt s (Sleep n) = hashWithSalt s n
--
-- Note that you can have any number of constructors in your
-- ConcurrentIOReq GADT, so most of the boilerplate only needs to be
-- written once.
module Haxl.DataSource.ConcurrentIO
( mkConcurrentIOState
, ConcurrentIO(..)
) where
import Control.Concurrent
import Control.Exception as Exception
import Control.Monad
import qualified Data.Text as Text
import Data.Typeable
import Haxl.Core
class ConcurrentIO tag where
data ConcurrentIOReq tag a
performIO :: ConcurrentIOReq tag a -> IO a
deriving instance Typeable ConcurrentIOReq -- not needed by GHC 7.10 and later
instance (Typeable tag) => StateKey (ConcurrentIOReq tag) where
data State (ConcurrentIOReq tag) = ConcurrentIOState
getStateType _ = typeRep (Proxy :: Proxy ConcurrentIOReq)
mkConcurrentIOState :: IO (State (ConcurrentIOReq ()))
mkConcurrentIOState = return ConcurrentIOState
instance Typeable tag => DataSourceName (ConcurrentIOReq tag) where
dataSourceName _ =
Text.pack (show (typeRepTyCon (typeRep (Proxy :: Proxy tag))))
instance
(Typeable tag, ShowP (ConcurrentIOReq tag), ConcurrentIO tag)
=> DataSource u (ConcurrentIOReq tag)
where
fetch _state _flags _u = BackgroundFetch $ \bfs -> do
forM_ bfs $ \(BlockedFetch req rv) ->
mask $ \unmask ->
forkFinally (unmask (performIO req)) (putResultFromChildThread rv)