task (ex_stdlib v0.2.0)

View Source

Conveniences for spawning and awaiting tasks.

Tasks are processes meant to execute one particular action throughout their lifetime, often with little or no communication with other processes. The most common use case for tasks is to convert sequential code into concurrent code by computing a value asynchronously:

   Task = task:async(fun() -> do_some_work() end),
   Res = do_some_other_work(),
   Res + task:await(Task).

Tasks spawned with async/1 can be awaited on by their caller process (and only their caller) as shown in the example above. They are implemented by spawning a process that sends a message to the caller once the given computation is performed.

Compared to plain processes, started with spawn/1, tasks include monitoring metadata and logging in case of errors.

Besides async/1 and await/2, tasks can also be started as part of a supervision tree and dynamically spawned on remote nodes.

async and await

One of the common uses of tasks is to convert sequential code into concurrent code with async/1 while keeping its semantics. When invoked, a new process will be created, linked and monitored by the caller. Once the task action finishes, a message will be sent to the caller with the result.

await/2 is used to read the message sent by the task.

There are two important things to consider when using async:

1. If you are using async tasks, you **must await** a reply as they are *always* sent. If you are not expecting a reply, consider using start_link/1 as detailed below.

2. Async tasks link the caller and the spawned process. This means that, if the caller crashes, the task will crash too and vice-versa. This is on purpose: if the process meant to receive the result no longer exists, there is no purpose in completing the computation.

Tasks are processes

Tasks are processes and so data will need to be completely copied to them. Consider extracting only the necessary data before creating the task, or move the data loading altogether to the task.

Summary

Functions

Starts a task that must be awaited on.

Starts a task that must be awaited on with the given module, function, and arguments.

Awaits a task reply and returns the result.

Returns a task that is already completed with the given result.

Shuts down the task with the given reason.

Starts a task with the given function.

Starts a task with the given module, function, and arguments.

Starts a task as part of a supervision tree with the given function.

Starts a task as part of a supervision tree with the given module, function, and arguments.

Yields to a task for a given time interval.

Types

task/0

-opaque task()

task_fun/0

-type task_fun() :: fun(() -> term()).

Functions

async(Fun)

-spec async(task_fun()) -> task().

Starts a task that must be awaited on.

This function spawns a new process linked to the caller that will execute the given function. A task record is returned containing the information needed to await the result.

The spawned process will send a message to the caller once the computation is finished.

This function should be used when you want to run a computation concurrently and retrieve its result. The caller process must call await/1 or await/2 to retrieve the result.

Example

   Task = task:async(fun() -> 1 + 1 end),
   2 = task:await(Task).

async(Module, Function, Args)

-spec async(module(), atom(), [term()]) -> task().

Starts a task that must be awaited on with the given module, function, and arguments.

Similar to async/1 except the function to be started is specified by the given Module, Function, and Args. The Module, Function, and its arity are stored as a tuple in the mfa field for reflection purposes.

await(Task)

-spec await(task()) -> term().

Awaits a task reply and returns the result.

This function waits for the task to complete and returns its result. If the task fails, this function will raise an error with the same reason as the task.

A timeout, in milliseconds or :infinity, can be given with a default value of 5000. If the timeout is exceeded, await/2 will exit.

This function can only be called by the process that started the task.

await(Task, Timeout)

-spec await(task(), timeout()) -> term().

completed(Result)

-spec completed(term()) -> task().

Returns a task that is already completed with the given result.

This function creates a task that immediately has a result available. It can be awaited or yielded like any other task.

This is useful when you want to create a task that represents a computation that has already been completed, often for consistency in APIs that work with both synchronous and asynchronous operations.

shutdown(Task)

-spec shutdown(task()) -> {ok, term()} | {exit, term()} | nil.

Shuts down the task with the given reason.

If the task is not linked, it will be killed immediately. If the task is linked and trapping exits, it will be asked to terminate with the given reason. If the task does not terminate within the timeout, it will be killed.

Returns {ok, Result} if the task was already completed, {exit, Reason} if the task exited, or nil if the task was killed.

shutdown(Task, Timeout)

-spec shutdown(task(), timeout()) -> {ok, term()} | {exit, term()} | nil.

start(Fun)

-spec start(task_fun()) -> {ok, pid()}.

Starts a task with the given function.

This function spawns a new process that is not linked to the caller. The function should be a zero-arity anonymous function.

This should only be used when the task is used for side-effects (like I/O) and you have no interest in its results nor if it completes successfully.

start(Module, Function, Args)

-spec start(module(), atom(), [term()]) -> {ok, pid()}.

Starts a task with the given module, function, and arguments.

This function spawns a new process that is not linked to the caller.

This should only be used when the task is used for side-effects (like I/O) and you have no interest in its results nor if it completes successfully.

start_link(Fun)

-spec start_link(task_fun()) -> {ok, pid()}.

Starts a task as part of a supervision tree with the given function.

This function spawns a new process as part of a supervision tree. The function should be a zero-arity anonymous function.

This is used to start a statically supervised task under a supervision tree. Unlike async/1, this function does not return a task record and the result cannot be awaited. It's meant for fire-and-forget operations.

start_link(Module, Function, Args)

-spec start_link(module(), atom(), [term()]) -> {ok, pid()}.

Starts a task as part of a supervision tree with the given module, function, and arguments.

This is used to start a statically supervised task under a supervision tree. Unlike async/3, this function does not return a task record and the result cannot be awaited. It's meant for fire-and-forget operations.

yield(Task)

-spec yield(task()) -> {ok, term()} | {exit, term()} | nil.

Yields to a task for a given time interval.

This function checks if a task has completed within the given timeout. If the task completes, it returns {ok, Result}. If the timeout expires, it returns nil. If the task fails, it returns {exit, Reason}.

Unlike await/2, this function does not raise an error if the task fails. Instead, it returns the error reason wrapped in an exit tuple.

This function is useful when you want to periodically check on a task or when you want to handle task failures gracefully.

yield(Task, Timeout)

-spec yield(task(), timeout()) -> {ok, term()} | {exit, term()} | nil.