-- |
module Process
  ( Id,
    spawn,
    sleep,
    kill,
  )
where

import Basics
import qualified Control.Concurrent
import qualified Control.Concurrent.Async as Async
import Internal.Shortcut
import qualified Platform.Internal as Internal
import qualified Result
import Task (Task)
import qualified Prelude

-- | A light-weight process that runs concurrently. You can use @spawn@ to get
-- a bunch of different tasks running in different processes. The Elm Haskell
-- will interleave their progress. So if a task is taking too long, we will
-- pause it at an @andThen@ and switch over to other stuff.
--
-- __Note:__ We make a distinction between concurrency which means interleaving
-- different sequences and parallelism which means running different sequences
-- at the exact same time. For example, a time-sharing system is definitely
-- concurrent, but not necessarily parallel.
newtype Id = Id (Async.Async ())

-- | Run a task in its own light-weight process. In the following example,
-- @task1@ and @task2@ will be interleaved. If @task1@ makes a long HTTP
-- request or is just taking a long time, we can hop over to @task2@ and do
-- some work there.
--
-- > spawn task1
-- >     |> Task.andThen (\_ -> spawn task2)
--
-- __Note:__ This creates a relatively restricted kind of Process because it
-- cannot receive any messages. More flexibility for user-defined processes
-- will come in a later release!
spawn :: Task x a -> Task y Id
spawn :: Task x a -> Task y Id
spawn (Internal.Task LogHandler -> IO (Result x a)
f) =
  (LogHandler -> IO (Result y Id)) -> Task y Id
forall x a. (LogHandler -> IO (Result x a)) -> Task x a
Internal.Task
    ( \LogHandler
handler ->
        LogHandler -> IO (Result x a)
f LogHandler
handler
          IO (Result x a)
-> (IO (Result x a) -> IO (Async (Result x a)))
-> IO (Async (Result x a))
forall a b. a -> (a -> b) -> b
|> IO (Result x a) -> IO (Async (Result x a))
forall a. IO a -> IO (Async a)
Async.async
          IO (Async (Result x a))
-> (IO (Async (Result x a)) -> IO (Result y Id))
-> IO (Result y Id)
forall a b. a -> (a -> b) -> b
|> (Async (Result x a) -> Result y Id)
-> IO (Async (Result x a)) -> IO (Result y Id)
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map (Id -> Result y Id
forall error value. value -> Result error value
Result.Ok (Id -> Result y Id) -> (Async () -> Id) -> Async () -> Result y Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
<< Async () -> Id
Id (Async () -> Result y Id)
-> (Async (Result x a) -> Async ())
-> Async (Result x a)
-> Result y Id
forall b c a. (b -> c) -> (a -> b) -> a -> c
<< (Result x a -> ()) -> Async (Result x a) -> Async ()
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map (\Result x a
_ -> ()))
    )

-- | Block progress on the current process for the given number of
-- milliseconds. The JavaScript equivalent of this is @setTimeout@ which lets
-- you delay work until later.
sleep :: Float -> Task x ()
sleep :: Float -> Task x ()
sleep Float
delay = (LogHandler -> IO (Result x ())) -> Task x ()
forall x a. (LogHandler -> IO (Result x a)) -> Task x a
Internal.Task (\LogHandler
_ -> (() -> Result x ()) -> IO () -> IO (Result x ())
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map () -> Result x ()
forall error value. value -> Result error value
Result.Ok (Int -> IO ()
Control.Concurrent.threadDelay (Float -> Int
forall a b. (RealFrac a, Integral b) => a -> b
Prelude.floor (Float
delay Float -> Float -> Float
forall number. Num number => number -> number -> number
* Float
1000))))

-- | Sometimes you @spawn@ a process, but later decide it would be a waste to
-- have it keep running and doing stuff. The @kill@ function will force a
-- process to bail on whatever task it is running. So if there is an HTTP
-- request in flight, it will also abort the request.
kill :: Id -> Task x ()
kill :: Id -> Task x ()
kill (Id Async ()
async) = (LogHandler -> IO (Result x ())) -> Task x ()
forall x a. (LogHandler -> IO (Result x a)) -> Task x a
Internal.Task (\LogHandler
_ -> (() -> Result x ()) -> IO () -> IO (Result x ())
forall (m :: * -> *) a value.
Functor m =>
(a -> value) -> m a -> m value
map () -> Result x ()
forall error value. value -> Result error value
Result.Ok (Async () -> IO ()
forall a. Async a -> IO ()
Async.cancel Async ()
async))