2019-06-01 01:55:21 +03:00
|
|
|
module Vere.Worker where
|
|
|
|
|
|
|
|
import ClassyPrelude
|
|
|
|
import Control.Lens
|
|
|
|
import Data.Void
|
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
import System.Exit (ExitCode)
|
|
|
|
|
2019-06-01 01:55:21 +03:00
|
|
|
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 01:55:21 +03:00
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
--------------------------------------------------------------------------------
|
2019-06-01 01:55:21 +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 01:55:21 +03:00
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
instance FromNoun Cord where
|
|
|
|
parseNoun n = do
|
|
|
|
atom <- parseNoun n
|
|
|
|
pure $ Cord (atom ^. pill . pillBS)
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
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)
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
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)
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
data ShipId = ShipId { addr :: Ship, fake :: Bool }
|
|
|
|
deriving (Eq, Show)
|
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
--------------------------------------------------------------------------------
|
|
|
|
|
2019-06-01 01:55:21 +03:00
|
|
|
data Play
|
|
|
|
= PlayNone -- ~
|
|
|
|
| PlaySnap EventId Mug ShipId -- [@ @ @ ?]
|
2019-06-01 03:21:44 +03:00
|
|
|
deriving (Eq, Show)
|
2019-06-01 01:55:21 +03:00
|
|
|
|
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
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
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 01:55:21 +03:00
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
instance FromNoun Plea where
|
|
|
|
parseNoun = undefined
|
|
|
|
|
|
|
|
--------------------------------------------------------------------------------
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
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 01:55:21 +03:00
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
instance Exception WorkerExn
|
|
|
|
|
|
|
|
-- Utils -----------------------------------------------------------------------
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
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 01:55:21 +03:00
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
boot :: a -> IO b
|
|
|
|
boot = undefined
|
2019-06-01 01:55:21 +03:00
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
sendAndRecv :: Worker -> EventId -> Atom -> IO WorkerResp
|
2019-06-01 01:55:21 +03:00
|
|
|
sendAndRecv w eventId event =
|
|
|
|
do
|
2019-06-01 03:21:44 +03:00
|
|
|
sendAtom w $ work eventId (Jam event)
|
2019-06-01 01:55:21 +03:00
|
|
|
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))
|
2019-06-01 01:55:21 +03:00
|
|
|
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 01:55:21 +03:00
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
sendBootEvent :: Worker -> IO ()
|
|
|
|
sendBootEvent = do
|
|
|
|
undefined
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
-- 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)
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
-- 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)
|
2019-06-01 01:55:21 +03:00
|
|
|
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
|
2019-06-01 01:55:21 +03:00
|
|
|
-- -- 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]
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
-- response <- recvAtom w
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-06-01 03:21:44 +03:00
|
|
|
-- Basic Send and Receive Operations -------------------------------------------
|
2019-06-01 01:55:21 +03:00
|
|
|
|
|
|
|
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
|
2019-06-01 01:55:21 +03:00
|
|
|
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
|