Skip to content

Latest commit

 

History

History
95 lines (82 loc) · 2.98 KB

14_async_computations.adoc

File metadata and controls

95 lines (82 loc) · 2.98 KB

Async Computations

Starting asynchronous computations and providing the result via a CompletableFuture is provided via the JDK methods CompletableFuture#runAsync and CompletableFuture#suppyAsync.

These methods have a few drawbacks. The first one is that in Xtend it is good practice to place the callback function as the last parameter in a parameter list to allow for more elegant and readable syntax, placing the lambda behind the closing parentheses. The JDK methods, however, have overloaded versions placing a executor for operation executor as last parameter.

The other drawback is that these methods need a further concept to allow cancellation of an operation from the caller side, e.g. when the user cancels an operation. This can e.g. be achieved via an additional java.util.concurrent.atomic.AtomicBoolean which is passed to the operation. This is unfortunate, since the CompletableFuture already knows the concept of cancellation.

This library provides the class de.fhg.fokus.xtensions.concurrent.AsyncCompute introducing the methods asyncRun and asyncSupply. These methods allow asynchronous computations like the JDK methods, but with a shuffled parameter list and passing the created CompletableFuture into the operation to be computed asynchronously.

Example using JDK classes:

import static java.util.concurrent.CompletableFuture.*
import java.util.concurrent.Executors
// ...
val ex = Executors.newCachedThreadPool
val isCancelled = new AtomicBoolean(false)
runAsync([
	if(isCancelled.get) {
		println("Oh no, I've been cancelled")
	} else {
		println("I'm fine")
	}
], ex)
isCancelled.set(true)

Same example using AsyncCompute:

import static extension de.fhg.fokus.xtensions.concurrent.AsyncCompute.*
import java.util.concurrent.Executors
// ...
val pool = Executors.newCachedThreadPool
val fut = pool.asyncRun [
	if(cancelled) {
		println("Oh no, I've been cancelled")
	} else {
		println("I'm fine")
	}
]
fut.cancel(false)

The asyncRun and asyncSupply methods have variants defining a timeout. If the provided actions do not complete in the defined timeout, the returned future will be completed with a TimeoutException. The action should then check for completion of the future passed into it, instead of for cancellation.

Example:

asyncSupply(10, TimeUnit.MILLISECONDS) [
	// some poor integration approximation
	val fx = [double x| x*x + 10 - (2*x)]
	var sum = 0.0d;
	for(i : 0..100_000) {

		// every now and then, check if we timed out
		if(i % 100 == 0) {
			if(done) {
				return 0.0d;
			}
		}
		// also using poor double accumulation
		sum += fx.apply(i as double)
	}
	sum
].whenComplete[result, error |
	if(error !== null) {
		println("Whoops, timeout")
	} else {
		println("result = " + result)
	}
]
Tip

Related JavaDocs: