async

Asynchronous programming library for MoonBit

moon add moonbitlang/async@0.20.5
Download zip
Version
0.20.5
License
Apache-2.0
Last updated
5 days ago
Downloads
423K
README

#Asynchronous programming library for MoonBit

This library provides basic asynchronous IO functionality for MoonBit, as well as useful asynchronous programming facilities. Currently, this library only supports native/LLVM backends on Linux/MacOS.

WARNING: this library is current experimental, API is subjected to future change.

#Features

#Structured concurrency and error propagation

moonbitlang/async features structured concurrency. In moonbitlang/async, every asynchronous task must be spawned in a task group. Task groups can be created with the with_task_group function

async fn[X] with_task_group(async (TaskGroup) -> X raise) -> X raise

When with_task_group returns, it is guaranteed that all tasks spawned in the group already terminate, leaving no room for orphan task and resource leak.

If any child task in a task group fail with error, all other tasks in the group will be cancelled. So there will be no silently ignored error.

For more behavior detail and useful API, consult the API document.

#Task cancellation

In moonbitlang/async, all asynchronous operations are by default cancellable. So no need to worry about accidentally creating uncancellable task.

In moonbitlang/async, when a task is cancelled, it will receive an error at where it suspended. The cancelled task can then perform cleanup logic using try .. catch. Since most asynchronous operations may throw other error anyway, correct error handling automatically gives correct cancellation handling, so most of the time correct cancellation handling just come for free in moonbitlang/async.

Currently, it is not allowed to perform other asynchronous operation after a task is cancelled. Those operations will be cancelled immediately if current task is already cancelled. Spawn a task in some parent context if asynchronous cleanup is necessary.

#Caveats

Currently, moonbitlang/async features a single-threaded, cooperative multitasking model. With this single-threaded model, code without suspension point can always be considered atomic. So no need for expensive lock and less bug. However, this model also come with some caveats:

  • task scheduling can only happen when current task suspend itself, by performing some asynchronous IO operation or manually calling @async.pause. If you perform heavy computation loop without pausing from time to time, the whole program will be blocked until the loop terminates, and other task will not get executed before that. Similarly, performing blocking IO operation not provided by moonbitlang/async may block the whole program as well

  • in the same way, task cancellation can only happen when a task is in suspended state (blocked by IO operation or manually pause'ed)

  • although internally moonbitlang/async may use OS threads to perform some IO job, user code can only utilize one hardware processor

#
AlreadyTerminated

pub type! AlreadyTerminated

#
Task

type Task[X]

Task[X] represents a running task with result type X, it can be used to wait and retrieve the result value of the task.

#
Task::cancel

fn[X] Task::cancel(self : Task[X]) -> Unit

Cancel a task. Subsequent attempt to wait for the task will receive error. Note that if the task is not spawned with allow_failure=true, the whole task group will fail too.

#
Task::wait

fn[X] Task::wait(self : Task[X]) -> X raise Error

Wait for a task and retrieve its result value. If the task fails, wait will also fail with the same error.

If the current task is cancelled, wait return immediately with error.

#
TaskGroup

type TaskGroup[X]

A TaskGroup can be used to spawn children tasks that run in parallel. Task groups implements structured concurrency: a task group will only return after all its children task terminates.

Task groups also handles error propagation: by default, if any child task raises error, the whole task group will also raise that error, and all other remaining child tasks will be cancelled.

The type parameter X in TaskGroup[X] is the result type of the group, see with_task_group for more detail.

#
TaskGroup::return_immediately

fn[X] TaskGroup::return_immediately(self : TaskGroup[X], value : X) -> Unit raise Error

Force a task group to terminate immediately with the given result value. All child tasks in the group, including potentially the current one, will be cancelled.

#
TaskGroup::spawn

fn[G, X] TaskGroup::spawn(self : TaskGroup[G], f : () -> X raise Error, no_wait~ : Bool = .., allow_failure~ : Bool = ..) -> Task[X] raise Error

Spawn a child task in a task group, compute a result asynchronously. A task handle will be returned, the result value of the task can be waited and retrieved using .wait(), or cancelled using .cancel().

Unless no_wait (false by default) is true, the whole task group will only exit after this child task terminates.

Unless allow_failure (false by default) is true, Ithe whole task group will also fail if the spawned task fails, other tasks in the group will be cancelled in this case.

If the task group is already cancelled or has been terminated, spawn will fail with error and the child task will not be spawned.

It is undefined whether the child task will start running immediately before spawn returns.

#
TaskGroup::spawn_bg

fn[X] TaskGroup::spawn_bg(self : TaskGroup[X], f : () -> Unit raise Error, no_wait~ : Bool = .., allow_failure~ : Bool = ..) -> Unit raise Error

Spawn a child task in a task group, and run it asynchronously in the background.

Unless no_wait (false by default) is true, the whole task group will only exit after this child task terminates.

Unless allow_failure (false by default) is true, Ithe whole task group will also fail if the spawned task fails, other tasks in the group will be cancelled in this case.

If the task group is already cancelled or has been terminated, spawn_bg will fail with error and the child task will not be spawned.

It is undefined whether the child task will start running immediately before spawn_bg returns.

#
pause

fn pause() -> Unit raise Error

Pause current task and give other tasks chance to execute. When performing long running pure computation (i.e. no IO involved), pause can be used to avoid starving other tasks.

#
protect_from_cancel

fn protect_from_cancel(f : () -> Unit raise Error) -> Unit raise Error

protect_from_cancel(f) executes f and protect f from cancellation. If current task is cancelled while running protect_from_cancel(f), f will be protected from the cancellation and still run to finish, and the cancellation will be delayed until f returns. Things waiting for current task, such as the task group, will also wait until f finish.

This function should be use with extra care and only when absolutely necessary, because it will break other abstraction such as with_timeout. A common scenario is avoiding corrupted state due to partial write to file etc.

#
sleep

fn sleep(duration : Int) -> Unit raise Error

sleep will wait for the given time (in milliseconds) before returning. Other task can still run while current task is sleeping. If current task is cancelled, sleep will return early with an error.

#
with_event_loop

fn with_event_loop(f : (TaskGroup[Unit]) -> Unit raise Error) -> Unit raise Error

Create a fresh event loop and run a async program inside the loop. A new task group will be created for convenience, that is, with_event_loop(f) will run with_task_group(f) using the event loop.

There can only one event loop running for every program, calling with_event_loop inside another event loop is invalid, and will result in immediate failure.

#
with_task_group

fn[X] with_task_group(f : (TaskGroup[X]) -> X raise Error) -> X raise Error

with_task_group(f) creates a new task group and run f with the new group. f itself will be run in a child task of the new group. with_task_group exits after all the whole group terminates, which means all child tasks in the group have terminated, including f.

If all children task terminate successfully, with_task_group will return the result of f.

#
with_timeout

fn with_timeout(time : Int, f : () -> Unit raise Error) -> Unit raise Error

with_timeout(timeout, f) run the async function f. If f return or fail before timeout, with_timeout return immediately with the same result. If f is still running after timeout milliseconds, with_timeout will also return immediately, and f will be cancelled.

Powered by MoonBit

Site sourceReport issuePackagesBuild queueSkillsStatistics

© 2026 mooncakes.io