packages feed

futhask-base-0.1.0.0: src/Futhask/Object.hs

{-# LANGUAGE RankNTypes, ExistentialQuantification, FlexibleInstances, UndecidableInstances, TypeFamilyDependencies, MultiParamTypeClasses, ScopedTypeVariables, FlexibleContexts, FunctionalDependencies, ConstraintKinds, QuantifiedConstraints, TypeOperators #-}
{-|
Defines various classes for futhark objects and some utility functions to manage them.
-}

module Futhask.Object where
import Data.Kind
import Data.Word
import Data.Int
import Data.ByteString as B
import Foreign.C
import Foreign.C.String
import Foreign.Ptr
import Foreign.Storable as S
import Foreign.ForeignPtr
import Foreign.Marshal.Array as A
import Foreign.Marshal.Alloc
import Control.Monad.Base
import Control.Monad.Trans
import Control.Concurrent
import Control.Concurrent.MVar
import System.Mem

import Futhask.Array 
import Futhask.Array.Rank 
import Futhask.Context
import Futhask.Monad

-- * Wrapping Pointers and Managing Memory
-- | The basic functions for wrapping and memory management
class (Context (ObjectContext fo)) => FutharkObject fo where
    type RawObject fo = raw | raw -> fo -- injectivity broke for some reason
    type ObjectContext fo
    wrapRawObject :: ReferenceCounter -> ForeignPtr (RawObject fo) -> fo c
    objectCounter :: fo c -> ReferenceCounter
    objectPointer :: fo c -> ForeignPtr (RawObject fo)
    freeRawObject :: Ptr (RawContext (ObjectContext fo)) -> Ptr (RawObject fo) -> IO Int

-- **Wrapping
-- | Use the raw pointer and return the result    
withRawObject :: (FutharkObject fo) => fo c -> (Ptr (RawObject fo) -> IO a) -> IO a
withRawObject fo = withForeignPtr (objectPointer fo)

-- | Wrap raw pointer with a reference counter and context
wrapFO :: (FutharkObject fo) => ObjectContext fo -> Ptr (RawObject fo) -> IO (fo c) 
wrapFO context ptr = do
    refCounter <- newRefCounter
    pointer <- newForeignPtrWithRef contextRefcounter ptr (withForeignPtr contextPtr (\cptr -> freeRawObject cptr ptr)) 
    return $! wrapRawObject refCounter pointer
    where (contextRefcounter, contextPtr) = unwrapRawContext context


-- **Manual Memory Management

-- | Increments reference counter, useful for concurrent use.
addReferenceFO :: (MonadIO m, FutharkObject fo) => fo c -> FutT (ObjectContext fo) c m ()
addReferenceFO fo = lift . liftIO $ increment (objectCounter fo)

-- | Decrements reference counter, if the counter is zero the object will be finalized, and can no longer be used.
finalizeFO :: (MonadIO m, FutharkObject fo) => fo c -> FutT (ObjectContext fo) c m ()
finalizeFO fo = lift . liftIO $
    let refCounter = objectCounter fo
     in isZero refCounter >>= \b -> if b 
        then finalizeForeignPtr (objectPointer fo) 
        else decrement refCounter




-- *Value composition and decomposition
class (FutharkObject fo) => FutharkComposite fo where
    type Unpacked fo :: Type -> Type
    pack :: (Monad m) => Unpacked fo c -> FutT (ObjectContext fo) c m (fo c)
    unpack :: (Monad m) => fo c -> FutT (ObjectContext fo) c m (Unpacked fo c)

-- *Projection

-- | Takes a projection function from the @Raw@ interface and applies it.
extractWith 
    :: (FutharkObject fo, Storable a) 
    => (Ptr (RawContext (ObjectContext fo)) -> Ptr a -> Ptr (RawObject fo) -> IO Int)
    -> ObjectContext fo -> fo c -> IO a
extractWith f c o = alloca (\ptr -> withRawObject o (\ro -> inContextWithError c (\rc -> f rc ptr ro)) >> S.peek ptr)

-- | Same as `extractWith` but also wraps the extracted object. Only works on futhark objects.
extractFOWith 
    :: (FutharkObject fo1, FutharkObject fo2, ObjectContext fo2 ~ ObjectContext fo1) 
    => (Ptr (RawContext (ObjectContext fo1)) -> Ptr (Ptr (RawObject fo2)) -> Ptr (RawObject fo1) -> IO Int)
    -> ObjectContext fo1 -> fo1 c -> IO (fo2 c)    
extractFOWith f c o = extractWith f c o >>= wrapFO c



-- *Array functions
class (FutharkObject arr) => FutIndexable arr where
    type FutRank arr

-- | Basic array functions for primitive arrays
class (FutharkObject fo, FutIndexable fo, Context ctx, ObjectContext fo ~ ctx, Dim rank, FutRank fo ~ rank, Storable elem, PrimElem fo ~ elem) 
    => FutharkPrimArray' fo ctx rank elem | fo -> ctx rank elem, ctx rank elem -> fo where
    type PrimElem fo
    shapeFPA  :: Ptr (RawContext ctx) -> Ptr (RawObject fo) -> IO (Ptr Int64) 
    valuesFPA :: Ptr (RawContext ctx) -> Ptr (RawObject fo) -> Ptr elem -> IO Int
    newFPA    :: Ptr (RawContext ctx) -> Ptr elem -> DimFun rank (Ptr (RawObject fo))
    indexFPA  :: Ptr (RawContext ctx) -> Ptr elem -> Ptr (RawObject fo) -> DimFun rank Int

class (FutharkObject fo, FutharkPrimArray' fo (ObjectContext fo) (FutRank fo) (PrimElem fo)) => FutharkPrimArray fo
instance (FutharkObject fo, FutharkPrimArray' fo (ObjectContext fo) (FutRank fo) (PrimElem fo)) => FutharkPrimArray fo

-- | Basic array functions for opaque arrays
class (FutharkObject fo, FutIndexable fo, Context ctx, ObjectContext fo ~ ctx, Dim rank, FutRank fo ~ rank, FutharkObject elem, ObjectContext elem ~ ctx, OpaqueElem fo ~ elem) 
    => FutharkOpaqueArray' fo ctx rank elem | fo -> ctx rank elem, ctx rank elem -> fo where
    type OpaqueElem fo :: Type -> Type
    shapeFOA  :: Ptr (RawContext ctx) -> Ptr (RawObject fo) -> IO (Ptr Int64) 
    newFOA    :: Ptr (RawContext ctx) -> Ptr (RawObject elem) -> DimFun rank (Ptr (RawObject fo))
    indexFOA  :: Ptr (RawContext ctx) -> Ptr (Ptr (RawObject elem)) -> Ptr (RawObject fo) -> DimFun rank Int
    setFOA    :: Ptr (RawContext ctx) -> Ptr (RawObject fo) -> ptr (RawObject elem) -> DimFun rank Int

class FutharkOpaqueArray' fo (ObjectContext fo) (FutRank fo) (OpaqueElem fo) => FutharkOpaqueArray fo
instance FutharkOpaqueArray' fo (ObjectContext fo) (FutRank fo) (OpaqueElem fo) => FutharkOpaqueArray fo




-- * Transfer between Contexts

-- | Allows transfer of objects between contexts    
class (FutharkObject fo1, FutharkObject fo2) => Transferable fo1 fo2 where
    transferUp   :: (Monad m) => fo2 c2 -> FutT (ObjectContext fo1) c1 (FutT (ObjectContext fo2) c2 m) (fo1 c1)
    transferDown :: (Monad m) => fo1 c1 -> FutT (ObjectContext fo1) c1 (FutT (ObjectContext fo2) c2 m) (fo2 c2)

instance (FutharkOpaque fo) => Transferable fo fo where
    transferUp fo = unsafeLiftFromIO $ \ctx -> alloca $ \sp -> alloca $ \pp -> withRawObject fo $ \op2 -> do
        S.poke pp nullPtr
        inContextWithError ctx $ \cp -> storeOpaque cp op2 pp sp
        sync ctx
        p <- S.peek pp
        op <- inContext ctx $ \cp -> restoreOpaque cp p
        free p
        if op == nullPtr
            then error "Restoration error in transfer"
            else wrapFO ctx op        
    transferDown fo = unsafeLiftFromIO $ \ctx -> alloca $ \sp -> alloca $ \pp -> withRawObject fo $ \op2 -> do
        S.poke pp nullPtr
        inContextWithError ctx $ \cp -> storeOpaque cp op2 pp sp
        sync ctx
        p <- S.peek pp
        op <- inContext ctx $ \cp -> restoreOpaque cp p
        free p
        if op == nullPtr
            then error "Restoration error in transfer"
            else wrapFO ctx op        

-- *Reading and Writing to Files

-- | Serialization of opaques
class (FutharkObject fo) => FutharkOpaque fo where
    storeOpaque   :: Ptr (RawContext (ObjectContext fo)) -> Ptr (RawObject fo) -> Ptr (Ptr CChar) -> Ptr CSize -> IO Int
    restoreOpaque :: Ptr (RawContext (ObjectContext fo)) -> Ptr CChar -> IO (Ptr (RawObject fo))

-- | encoding and decoding objects
class (Monad m) => Serializable m a where
    encode :: a -> m ByteString
    decode :: ByteString -> m (Maybe a)

-- | write object to file
store fn a = encode a >>= lift . B.writeFile fn

-- | read object from file
load fn = lift (B.readFile fn) >>= decode 

instance (FutharkOpaque fo, FutharkObject fo, ObjectContext fo ~ ctx, Monad m)
    => Serializable (FutT ctx c m) (fo c) where 
    encode fo = unsafeLiftFromIO $ \ctx -> alloca $ \sp -> alloca $ \pp -> withRawObject fo $ \op -> do
        S.poke pp nullPtr
        inContextWithError ctx $ \cp -> storeOpaque cp op pp sp
        sync ctx
        p <- S.peek pp
        s <- S.peek sp
        bs <- packCStringLen (p, fromIntegral s)
        free p
        pure bs
    decode bs = unsafeLiftFromIO $ \ctx -> useAsCString bs $ \cs -> do
        op <- inContext ctx $ \cp -> restoreOpaque cp cs
        if op == nullPtr
            then pure Nothing
            else fmap Just $ wrapFO ctx op

-- * Conversion 

-- | Convert values to and from Futhark
class FutharkObject fo => Convertible fo where
    type Native fo
    toFuthark :: Monad m => Native fo -> FutT (ObjectContext fo) c m (fo c)
    fromFuthark :: Monad m => fo c -> FutT (ObjectContext fo) c m (Native fo)