Slide 37 of 74

Concurrency: with timeout

proc slow(label: Str, ms: Int): Str {
	hive.task.sleep(ms)
	echo "{label} finished anyway"
	return label
}

proc main(): void {
	if slow("c", 100) with timeout 20 is Result.Error(err) {
		echo "gave up after {err.waited}ms"
	}
	hive.task.sleep(150)      // long enough to see the work finish regardless

	both := await [slow("a", 10), slow("b", 10)] with timeout 500
	if both is Result.Ok(labels) {
		echo labels
	}
}

A wait written where it happens may be bounded: f(x) with timeout 500, or await […] with timeout 500, in milliseconds. It changes what the wait answers with — a Result<T, hive.task.TimeoutError> instead of the value — so running out of patience is a value to handle rather than a crash. A TimeoutError carries waited, the milliseconds asked for, and a message.

  • It abandons the waiting, not the work: a thread cannot be stopped from outside, so the call runs on to its end, side effects and all, and only its answer is dropped.
  • On an await-all it is one deadline for the whole barrier, which fails together.
  • A call that answers with nothing has no value for the Result to carry, so bounding one is refused, and so is bounding an async binding: the binding's wait is wherever its name is read.
  • A request to a service is the exception to the shape: it already answers with a Result, so the deadline becomes that Result's own error, with the reason "Timeout", as the services slide shows.