packages feed

hhlo-0.1.0.0: src/HHLO/Runtime/PJRT/Error.hs

{-# LANGUAGE ForeignFunctionInterface #-}

module HHLO.Runtime.PJRT.Error
    ( PJRTException(..)
    , checkError
    , withErrorMessage
    ) where

import Control.Exception
import Foreign.C
import Foreign.Marshal.Alloc
import Foreign.Ptr
import Foreign.Storable
import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8)
import qualified Data.ByteString as BS

import HHLO.Runtime.PJRT.FFI (c_pjrtErrorMessage, c_pjrtErrorDestroy)
import HHLO.Runtime.PJRT.Types (PJRTApi, PJRTError)

data PJRTException = PJRTException !String
    deriving (Eq, Show)

instance Exception PJRTException

-- | Check a returned PJRT error pointer. If non-null, extract the message,
-- destroy the error, and throw a 'PJRTException'.
checkError :: Ptr PJRTApi -> IO (Ptr PJRTError) -> IO ()
checkError api action = do
    err <- action
    if err == nullPtr
        then return ()
        else withErrorMessage api err >>= throwIO . PJRTException

-- | Extract the human-readable message from a PJRT error, then destroy it.
withErrorMessage :: Ptr PJRTApi -> Ptr PJRTError -> IO String
withErrorMessage api err = do
    alloca $ \msgPtrPtr -> do
        alloca $ \sizePtr -> do
            err2 <- c_pjrtErrorMessage api err msgPtrPtr sizePtr
            if err2 /= nullPtr
                then do
                    _ <- c_pjrtErrorDestroy api err2
                    _ <- c_pjrtErrorDestroy api err
                    return "PJRT error (failed to retrieve message)"
                else do
                    msgPtr <- peek msgPtrPtr
                    msgSize <- peek sizePtr
                    msgBs <- BS.packCStringLen (msgPtr, fromIntegral msgSize)
                    _ <- c_pjrtErrorDestroy api err
                    return $ T.unpack $ decodeUtf8 msgBs