shrub/pkg/hair/lib/Vere/Worker.hs

272 lines
6.5 KiB
Haskell
Raw Normal View History

module Vere.Worker where
import ClassyPrelude
import Control.Lens
import Data.Void
2019-06-01 03:21:44 +03:00
import System.Exit (ExitCode)
import Data.Noun
import Data.Noun.Atom
import Data.Noun.Jam
import Data.Noun.Poet
import Data.Noun.Pill
import Vere.Pier.Types
import System.Process
data Worker = Worker
{ sendHandle :: Handle
, recvHandle :: Handle
, process :: ProcessHandle
-- , getInput :: STM (Writ ())
-- , onComputed :: Writ [Effect] -> STM ()
-- , onExit :: Worker -> IO ()
-- , task :: Async ()
}
newtype Cord = Cord ByteString
2019-06-01 03:21:44 +03:00
deriving newtype (Eq, Ord, Show)
2019-06-01 03:21:44 +03:00
--------------------------------------------------------------------------------
instance ToNoun Cord where
2019-06-01 03:21:44 +03:00
toNoun (Cord bs) = Atom (bs ^. from (pill . pillBS))
2019-06-01 03:21:44 +03:00
instance FromNoun Cord where
parseNoun n = do
atom <- parseNoun n
pure $ Cord (atom ^. pill . pillBS)
--------------------------------------------------------------------------------
start :: IO Worker
start = do
-- Think about how to handle process exit
-- Tear down subprocess on exit? (terminiteProcess)
(Just stdin, Just stdout, _, ph) <-
createProcess (proc "urbit-worker" []){ std_in = CreatePipe,
std_out = CreatePipe }
pure (Worker stdin stdout ph)
2019-06-01 03:21:44 +03:00
kill :: Worker -> IO ExitCode
kill w = do
terminateProcess (process w)
waitForProcess (process w)
work :: Word64 -> Jam -> Atom
work id (Jam a) = jam $ toNoun (Cord "work", id, a)
data Job = Job Void
deriving (Eq, Show)
data Tank = Tank Void
deriving (Eq, Show)
type EventId = Word64
2019-06-01 03:21:44 +03:00
newtype Ship = Ship Word64 -- @p
deriving newtype (Eq, Show, FromNoun, ToNoun)
data ShipId = ShipId { addr :: Ship, fake :: Bool }
deriving (Eq, Show)
2019-06-01 03:21:44 +03:00
--------------------------------------------------------------------------------
data Play
= PlayNone -- ~
| PlaySnap EventId Mug ShipId -- [@ @ @ ?]
2019-06-01 03:21:44 +03:00
deriving (Eq, Show)
2019-06-01 03:21:44 +03:00
instance ToNoun Play where
toNoun = \case PlayNone -> Atom 0
PlaySnap e m (ShipId a f) -> toNoun (e, m, a, f)
instance FromNoun Play where
parseNoun = undefined
--------------------------------------------------------------------------------
data Plea
= Play Play
| Work EventId Mug Job
| Done EventId Mug [Ovum]
| Stdr EventId Cord
| Slog EventId Word32 Tank
2019-06-01 03:21:44 +03:00
deriving (Eq, Show)
2019-06-01 03:21:44 +03:00
instance FromNoun Plea where
parseNoun = undefined
--------------------------------------------------------------------------------
type CompletedEventId = Word64
type NextEventId = Word64
type LogState = Maybe EventId
type WorkerState = (EventId, Mug)
2019-06-01 03:21:44 +03:00
type ReplacementEv = (EventId, Mug, Job)
type WorkResult = (EventId, Mug, [Ovum])
type WorkerResp = (Either ReplacementEv WorkResult)
-- Exceptions ------------------------------------------------------------------
data WorkerExn
= BadComputeId EventId WorkResult
| BadReplacementId EventId ReplacementEv
| UnexpectedPlay EventId Play
| BadPleaAtom Atom
| BadPleaNoun Noun
deriving (Show)
2019-06-01 03:21:44 +03:00
instance Exception WorkerExn
-- Utils -----------------------------------------------------------------------
printTank :: Word32 -> Tank -> IO ()
printTank pri t = print "tank"
2019-06-01 03:21:44 +03:00
guardExn :: Exception e => Bool -> e -> IO ()
guardExn ok = unless ok . throwIO
fromJustExn :: Exception e => Maybe a -> e -> IO a
fromJustExn Nothing exn = throwIO exn
fromJustExn (Just x) exn = pure x
--------------------------------------------------------------------------------
2019-06-01 03:21:44 +03:00
boot :: a -> IO b
boot = undefined
2019-06-01 03:21:44 +03:00
sendAndRecv :: Worker -> EventId -> Atom -> IO WorkerResp
sendAndRecv w eventId event =
do
2019-06-01 03:21:44 +03:00
sendAtom w $ work eventId (Jam event)
loop
where
2019-06-01 03:21:44 +03:00
produce :: WorkResult -> IO WorkerResp
produce (i, m, o) = do
guardExn (i /= eventId) (BadComputeId eventId (i, m, o))
pure $ Right (i, m, o)
replace :: ReplacementEv -> IO WorkerResp
replace (i, m, j) = do
guardExn (i /= eventId) (BadReplacementId eventId (i, m, j))
pure (Left (i, m, j))
2019-06-01 03:21:44 +03:00
loop :: IO WorkerResp
loop = recvPlea w >>= \case
Play p -> throwIO (UnexpectedPlay eventId p)
Done i m o -> produce (i, m, o)
Work i m j -> replace (i, m, j)
Stdr _ cord -> print cord >> loop
Slog _ pri t -> printTank pri t >> loop
2019-06-01 03:21:44 +03:00
sendBootEvent :: Worker -> IO ()
sendBootEvent = do
undefined
-- the ship is booted, but it is behind. shove events to the worker until it is
-- caught up.
replay :: Worker -> WorkerState -> EventId
-> (EventId -> Word64 -> IO (Vector (EventId, Atom)))
-> IO ()
replay w (wid, wmug) lastCommitedId getEvents = do
2019-06-01 03:21:44 +03:00
when (wid == 1) (sendBootEvent w)
-- todo: we want to stream these in chunks
events <- getEvents wid (1 + lastCommitedId - wid)
for_ events $ \(eventId, event) -> do
(Right (i, mug, ovum)) <- sendAndRecv w eventId event
undefined
-- todo: these actually have to happen concurrently
2019-06-01 03:21:44 +03:00
playWorkerState :: Play -> WorkerState
playWorkerState = \case
PlayNone -> (1, Mug 0)
PlaySnap e m _ -> (e, m)
-- computeThread :: Worker -> IO ()
-- computeThread w = start
-- where
-- start = do
-- Just (Play p) <- recvPlea w
2019-06-01 03:21:44 +03:00
-- let (eventId, mug) = playWorkerState p
-- -- fuck it, we'll do it liv_o
-- boot :: WorkerState -> -> IO ()
-- boot w = do
-- writ <- atomically $ (getInput w)
-- sendAtom w (work (eventId writ) (event writ))
-- The flow here is that we start the worker and then we receive a play event
-- with the current worker state:
--
-- <- [%play ...]
--
-- Base on this, the main flow is
--
-- [%work ] ->
-- <- [%slog]
-- <- [%slog]
-- <- [%slog]
-- <- [%work crash=tang]
-- [%work ] -> (replacement)
-- <- [%slog]
-- <- [%done]
2019-06-01 03:21:44 +03:00
-- [%work eventId mat]
-- response <- recvAtom w
2019-06-01 03:21:44 +03:00
-- Basic Send and Receive Operations -------------------------------------------
sendAtom :: Worker -> Atom -> IO ()
sendAtom w a = hPut (sendHandle w) (unpackAtom a)
atomBytes :: Iso' Atom ByteString
atomBytes = pill . pillBS
packAtom = view (from atomBytes)
unpackAtom :: Atom -> ByteString
unpackAtom = view atomBytes
recvLen :: Worker -> IO Word64
recvLen = undefined
recvBytes :: Worker -> Word64 -> IO ByteString
recvBytes = undefined
recvAtom :: Worker -> IO (Atom)
recvAtom w = do
len <- recvLen w
bs <- recvBytes w len
pure (packAtom bs)
2019-06-01 03:21:44 +03:00
recvPlea :: Worker -> IO Plea
recvPlea w = do
a <- recvAtom w
2019-06-01 03:21:44 +03:00
n <- fromJustExn (cue a) (BadPleaAtom a)
p <- fromJustExn (fromNoun n) (BadPleaNoun n)
pure p