std.sync.Future
type pub Future[T]A proxy value to resolve into the result of some asynchronous operation.
The value of a Future is set by its corresponding Promise.
A Future[T] is resolved into its T using one of the following methods:
Future.getFuture.try_getFuture.get_until
Static methods
new
Show source codeHide source code
fn pub static new -> (uni Future[uni T], uni Promise[uni T]) {
let fut: FutureState[uni T] = FutureState(
waiter: NO_WAITER as Pointer[UInt8],
locked: UNLOCKED as UInt8,
status: Status.Connected,
value: Option.None,
)
# The `Future` and `Promise` need shared access of the underlying data. This
# technically violates Inko's single-ownership rules, so to allow that we
# cast the state reference to an address, then cast that back where
# necessary.
#
# This is of course highly unsafe, but it's how this particular sausage is
# made.
let fut = fut as UInt64
(recover Future(fut), recover Promise(fut))
}fn pub static new -> (uni Future[uni T], uni Promise[uni T])Returns a new Future along with its corresponding Promise.
The Future and Promise are returned as unique references, allowing them
to be moved between processes.
Examples
import std.sync (Future)
let (future, promise) = Future.new
promise.set(42)
future.get # => 42
Instance methods
get
Show source codeHide source code
fn pub move get -> uni T {
loop {
let fut = lock
match fut.value := Option.None {
case Some(val) -> {
fut.unlock
# Ensure the shared state isn't dropped.
_INKO.moved(fut)
return val
}
case _ -> {
fut.waiter = _INKO.process
# This atomically changes the process status, unlocks the future lock
# and yields back to the scheduler.
inko_process_wait_for_value(
_INKO.process,
mut fut.locked,
LOCKED as UInt8,
UNLOCKED as UInt8,
)
# Ensure the shared state isn't dropped.
_INKO.moved(fut)
}
}
}
}fn pub move get -> uni TReturns the value of the Future, blocking the calling process until a
value is available.
This method consumes the Future, ensuring a value can only be resolved
once.
Deadlocks
If a Promise is dropped before a call to Future.get or while the
Future waits for a value to be written, the calling process of
Future.get will deadlock. This method makes no attempt at detecting such
cases as doing so is notoriously difficult.
To avoid a deadlock, make sure to always write a value to a Promise
before discarding it, or use Future.get_until to wait using a deadline.
Examples
import std.sync (Future)
let (future, promise) = Future.new
promise.set(42)
future.get # => 42
get_until
Show source codeHide source code
fn pub move get_until[D: ToInstant](deadline: ref D) -> Option[uni T] {
let nanos = deadline.to_instant.to_int as UInt64
loop {
let fut = lock
match fut.value := Option.None {
case Some(val) -> {
fut.unlock
# Ensure the shared state isn't dropped.
_INKO.moved(fut)
return Option.Some(val)
}
case _ -> {
fut.waiter = _INKO.process
# This atomically changes the process status, unlocks the future lock
# and yields back to the scheduler.
let timed_out = inko_process_wait_for_value_until(
_INKO.state,
_INKO.process,
mut fut.locked,
LOCKED as UInt8,
UNLOCKED as UInt8,
nanos,
)
# Ensure the shared state isn't dropped.
_INKO.moved(fut)
if timed_out { break }
}
}
}
# It's possible for a write to happen _just_ after we time out. We don't
# want to silently discard the value in that case. In addition, it's
# possible for a value to be written after returning from this method, which
# would result in the value also being lost.
#
# To prevent this from happening we disconnect the future immediately and
# perform a final check to see if a value is present. This ensures that
# beyond this point any values written using `Promise.set` are returned to
# the caller, instead of just being dropped.
let fut = lock
match fut.status {
case Connected -> fut.status = Status.NoFuture
case _ -> {}
}
let val = fut.value := Option.None
fut.unlock
# Ensure the shared state isn't dropped.
_INKO.moved(fut)
val
}fn pub move get_until[D: ToInstant](deadline: ref D) -> Option[uni T]Returns the value of the future, blocking the calling process until a value is available or the given deadline is exceeded.
If a value is resolved within the deadline, an Option.Some containing the
value is returned. If the timeout expired, an Option.None is returned.
In both cases self is consumed. This is because trying to wait for a
result is inherently racy, and may result in unexpected results. For
example, if a value were to be written using Promise.set just after we
return from this method, we wouldn't observe it unless the operation is
retried. If we don't do so, the value would be dropped.
However, it's more often than not clear how often the operation should be
retried, as the time waited might not necessarily be the same or longer as
the time it takes before Promise.set is called.
Always consuming self instead forces the caller to create a new Promise
and Future pair if a retry is desired, and ensures that if
Promise.set is called after returning from this method the value passed
to Promise.set is returned to its caller.
Deadlocks
Unlike Future.get, this method can't deadlock a calling process forever
due to the use of a deadline. However, if the Promise is dropped before or
during a call to Future.get_until, the calling process will be suspended
until the deadline expires.
Examples
import std.sync (Future)
import std.time (Duration)
let (future, promise) = Future.new
promise.set(42)
future.get_until(Duration.from_secs(1)) # => Option.Some(42)
try_get
Show source codeHide source code
fn pub move try_get -> Result[uni T, Future[T]] {
let fut = lock
let val = fut.value := Option.None
fut.unlock
# Ensure the shared state isn't dropped.
_INKO.moved(fut)
match val {
case Some(v) -> Result.Ok(v)
case _ -> Result.Error(self)
}
}fn pub move try_get -> Result[uni T, Future[T]]Returns the value of the future if one is present, without blocking the calling process.
If a value is present, a Result.Ok is returned containing the value. If no
value is present, a Result.Error is returned containing a new Future to
use for resolving the value.
Deadlocks
This method never deadlocks.
Examples
import std.sync (Future)
let (future, promise) = Future.new
promise.set(42)
future.try_get # => Result.Ok(42)
Implemented traits
Drop
impl Drop for Future