packages feed

hhlo-0.11.0.0: test/Test/Runtime/Buffer.hs

{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}

module Test.Runtime.Buffer where

import Control.Concurrent (threadDelay)
import Data.IORef (newIORef, readIORef, writeIORef)
import qualified Foreign.Concurrent as Conc (addForeignPtrFinalizer)
import qualified Data.Vector.Storable as V
import System.Mem (performMajorGC)
import Test.Tasty
import Test.Tasty.HUnit

import HHLO.Runtime.Buffer
import HHLO.Runtime.PJRT.Types
import Test.Utils

tests :: TestTree
tests = testGroup "Runtime.Buffer"
    [ testCase "buffer round-trip f32" $ withPJRTCPU $ \api client -> do
        let inp = V.fromList [1.0, 2.0, 3.0, 4.0]
        buf <- toDeviceF32 api client inp [2, 2]
        out <- fromDeviceF32 api buf 4
        out @?= inp
    , testCase "buffer dimensions" $ withPJRTCPU $ \api client -> do
        let inp = V.fromList [1.0, 2.0, 3.0, 4.0, 5.0, 6.0]
        buf <- toDeviceF32 api client inp [2, 3]
        dims <- bufferDimensions api buf
        dims @?= [2, 3]
    , testCase "buffer element type f32" $ withPJRTCPU $ \api client -> do
        let inp = V.fromList [1.0, 2.0]
        buf <- toDeviceF32 api client inp [2]
        et <- bufferElementType api buf
        et @?= bufferTypeF32
    , testCase "buffer on-device size" $ withPJRTCPU $ \api client -> do
        let inp = V.fromList [1.0, 2.0, 3.0, 4.0]
        buf <- toDeviceF32 api client inp [2, 2]
        sz <- bufferOnDeviceSize api buf
        sz @?= 16  -- 4 floats * 4 bytes

    -- Regression test for post-session buffer finalizer safety.
    -- Creates a buffer inside a session bracket, returns it so it
    -- outlives the session, then forces GC.  The finalizer must not
    -- segfault even though the client has been destroyed.
    , testCase "buffer finalizer safe after CPU session closes" $ do
        ref <- newIORef False
        _ <- withPJRTCPU $ \api client -> do
            let inp = V.fromList [1.0, 2.0] :: V.Vector Float
            buf <- toDeviceF32 api client inp [2]
            let PJRTBuffer fp = buf
            Conc.addForeignPtrFinalizer fp $ writeIORef ref True
            return buf
        performMajorGC
        -- Poll until the witness finalizer fires (max ~1s).
        let wait n = do
                done <- readIORef ref
                if done
                    then return ()
                    else if n > 100
                        then assertFailure "buffer finalizer did not run"
                        else threadDelay 10000 >> wait (n + 1)
        wait 0
    ]