content: {-
    Copyright (c) Meta Platforms, Inc. and affiliates.
    All rights reserved.
  
    This source code is licensed under the BSD-style license found in the
    LICENSE file in the root directory of this source tree.
  -}
  
  module Glean.Interprocess.Worklist
    ( Worker, Worklist, Range(..), withTemp, get, next, peek, doNext
    ) where
  
  import Util.FFI (invoke)
  
  import Control.Exception (bracket)
  import Data.Word (Word32)
  import Foreign.C.String
  import Foreign.C.Types
  import Foreign.Marshal.Array
  import Foreign.Ptr (Ptr)
  import Foreign.Storable (Storable)
  import System.IO (hClose)
  import System.IO.Temp (withSystemTempFile)
  
  newtype Worklist = Worklist (Ptr Worklist)
    deriving(Storable)
  
  data Range = Range
    { rangeStart :: {-# UNPACK #-} !Int
    , rangeEnd   :: {-# UNPACK #-} !Int
    }
    deriving(Eq,Ord,Show)
  
  mkRange :: Word32 -> Word32 -> Range
  mkRange start end = Range (fromIntegral start) (fromIntegral end)
  
  withTemp :: [Range] -> (FilePath -> Worklist -> IO a) -> IO a
  withTemp xs f = withSystemTempFile ".glean-worklist" $ \path h -> do
    hClose h
    withCString path $ \cpath -> do
      withArray (map (fromIntegral . rangeStart) xs) $ \starts ->
        withArray (map (fromIntegral . rangeEnd) xs) $ \ends ->
          invoke $ glean_interprocess_worklist_create
            cpath
            (fromIntegral $ length xs)
            starts
            ends
      bracket
        (invoke $ glean_interprocess_worklist_open cpath)
        glean_interprocess_worklist_close
        (f path)
  
  type Worker = Int
  
  get :: Worklist -> Worker -> IO Range
  get w i = fmap (uncurry mkRange) $
    invoke $ glean_interprocess_worklist_get w $ fromIntegral i
  
  next :: Worklist -> Worker -> IO (Range, Worker)
  next w i = do
    (start,end,victim) <-
      invoke $ glean_interprocess_worklist_next w $ fromIntegral i
    return (mkRange start end, fromIntegral victim)
  
  withWorkfile
    :: (Worklist -> Worker -> IO a)
    -> FilePath -> Worker -> IO a
  withWorkfile op workfile w = withCString workfile $ \ cpath -> bracket
    (invoke $ glean_interprocess_worklist_open cpath)
    glean_interprocess_worklist_close
    (`op` w)
  
  doNext :: FilePath -> Worker -> IO (Range, Worker)
  doNext w = withWorkfile next w
  
  peek :: FilePath -> Worker -> IO Range
  peek w = withWorkfile get w
  
  foreign import ccall unsafe glean_interprocess_worklist_create
    :: CString -> CSize -> Ptr Word32 -> Ptr Word32 -> IO CString
  
  foreign import ccall unsafe glean_interprocess_worklist_open
    :: CString -> Ptr Worklist -> IO CString
  
  foreign import ccall unsafe glean_interprocess_worklist_close
    :: Worklist -> IO ()
  
  foreign import ccall unsafe glean_interprocess_worklist_get
    :: Worklist -> CSize -> Ptr Word32 -> Ptr Word32 -> IO ()
  
  foreign import ccall unsafe glean_interprocess_worklist_next
    :: Worklist -> CSize -> Ptr Word32 -> Ptr Word32 -> Ptr CSize -> IO ()
  