CPL 2016, week 6 - Asynchronous execution€¦ · CPL 2016, week 6 Asynchronousexecution...

30
CPL 2016, week 6 Asynchronous execution Oleg Batrashev Institute of Computer Science, Tartu, Estonia March 14, 2016

Transcript of CPL 2016, week 6 - Asynchronous execution€¦ · CPL 2016, week 6 Asynchronousexecution...

CPL 2016, week 6Asynchronous execution

Oleg Batrashev

Institute of Computer Science, Tartu, Estonia

March 14, 2016

Overview

Studied so far:1. Inter-thread visibility: JMM2. Inter-thread synchronization: locks and monitors3. Thread management: executors, tasks, cancelation4. Inter-thread communication: confinements, queues, back

pressure5. Inter-thread collaboration: actors, inboxes, state diagrams

Today:I Asynchronous execution: callbacks, Pyramid of Doom, Java

8 promises.Next weeks:

I Performance considerations: asynchronous IO, Java 8streams.

Asynchronous execution 118/145 Why asynchronous -

Outline

Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries

Asynchronous execution 119/145 Why asynchronous -

Synchronous vs asynchronousSynchronous execution – algorithm executes sequentially, one stepafter another in single thread

res1 = doSomething1 ()res2 = doSomething2(res1)res3 = doSomething3(res1)print (res2+res3)

I doSomething3 executes after doSomething2I even with optimizations, think of the execution this way

Asynchronous execution – parts of the algorithm run separately,possibly in different threads

I execute doSomething2 and doSomething3 in separate threadsand wait for their results

I solvable with Java Futures

I execute them in separate threads but release this thread untilres2 and res3 are available, then execute printing

I not solvable with Java Futures

Asynchronous execution 120/145 Why asynchronous -

Problems of synchronous execution

I parallelism in the algorithm is not utilized (solved with Javafutures)

I current thread is not released even if nothing to do, consider

res1 = doRequest1 ()res2 = doRequest2(res1)print (res2)

I if the thread is waiting for the result of request 1, it hasnothing else to do

I the thread is not released, it may not be used elsewhereI e.g. making 10000 network requests or waiting for data from

10000 web clients requires the same number of threads

I solution: release the thread between doRequest1() anddoRequest2()

I how to do that?

Asynchronous execution 121/145 Why asynchronous -

Authentication example: the algorithmNeed for an algorithm with several steps that run over a network.Authentication with the salt and hash digest:1. Client provides his name2. Server checks if the name is ok and returns the salt

I the salt is just any random string

3. Client combines the salt and his password into single string, itthen computes the hash for it. The hash is sent to the server.

I hash is a sequence of bytes

4. Server compares received hash with the locally computedhash. If they are equal, then the password was correct. Theserver sends resulting response to the client.

I password is in the server database

5. Client knows whether the authentication was successfulAdvantage: the password is never sent over the network.

Asynchronous execution 122/145 Why asynchronous -

Authentication example: synchronous code// send user nameconn.sendMessage(new AuthComm.LoginRequest("Oleg"));// receive params or result responseMessage <Type > m1 = conn.readMessage ();if (m1.type==Type.RES_RESULT) {

ResultResponse m = (ResultResponse) m1;System.out.println("Success "+m);return;

}{ // send password digest

ParamsResponse m = (ParamsResponse) m1;MessageDigest digestAlgo = MessageDigest.getInstance("SHA -1");String password = "mypassword";byte[] digest = digestAlgo.digest ((m.salt+password ). getBytes ());conn.sendMessage(new AuthComm.AuthRequest(digest ));

}// receive result responseMessage <Type > m2 = conn.readMessage ();{

ResultResponse m = (ResultResponse) m2;System.out.println("Success "+m);return;

}

Asynchronous execution 123/145 Callback hell -

Outline

Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries

Asynchronous execution 124/145 Callback hell -

Callback solutionI give the method another one which should be executed when

the operations completes

doRequest1(callback1 );

void callback1(res1) {doRequest2(res1 , callback2 );

}

void callback2(res2) {print (res2);

}

I put into inline callbacks results in Pyramid of Doom (not Javacode!)

doRequest1(void callback1(res1) {doRequest2(res1 , void callback2(res2) {

print (res2);});

});

Asynchronous execution 125/145 Callback hell -

Authentication example: callbacks

I response with the salt to the login request

public interface ParamsCallback {void onSalt(String salt);

}public void login(String username ,

ParamsCallback paramsCallback ,ResultCallback resultCallback)

I response with result to the auth request

public interface ResultCallback {void onSuccess ();void onFailure(String error );

}public void authenticate(byte[] digest ,

ResultCallback callback)

I login may result in failure through the result callback: if theusername is invalid

Asynchronous execution 126/145 Callback hell -

Authentication example: Pyramid of DoomI Java callbacks are 2 level, because they are in interfaces

// send user nameauthProxy.login("Oleg", new ParamsCallback () {

public void onSalt(String salt) {// send password digestString password = "mypassword";byte[] digest = algo.digest ((salt+password ). getBytes ());authProxy.authenticate(digest , new ResultCallback () {

public void onSuccess () {System.out.println("Login was successful");

}public void onFailure(String error) {

System.out.println("Login failed: "+error);}

});}

}, null);

I not pleasant to read, not reusableI last null is the callback where error handling for login should

happen, omitted here

Asynchronous execution 127/145 Callback hell -

Named callbacksI explicit classes: reusable code, but more difficult to follow

public void run() { // send user nameauthProxy.login("Oleg", new ParamsCallbackImpl (), null);

}class ParamsCallbackImpl implements ParamsCallback {

public void onSalt(String salt) {// send password digestString password = "mypassword";byte[] digest = algo.digest ((salt + password ). getBytes ());authProxy.authenticate(digest , new ResultCallbackImpl ());

}}class ResultCallbackImpl implements ResultCallback {

public void onSuccess () {System.out.println("Login was successful");

}public void onFailure(String error) {

System.out.println("Login failed: " + error);}

}

I imagine if callbacks are split between different files

Asynchronous execution 128/145 Callback hell -

Callback summary

I asynchronous execution requires code that will be executedlater – callbacks

I inline callbacks result in the Pyramid of Doom, which isunpleasant to read

I named callbacks split sequential logic into separate methods,which is even more difficult to follow

I Java callback as an object with method – adds 2 levels to thePyramid of Doom

Java 8 solutions:I Java 8 promises (CompletableFuture) solve the problem with

unreadable and hard to follow codeI Java 8 lambdas solve the problem of 2-level callbacks

Asynchronous execution 129/145 Lambdas in Java 8 -

Outline

Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries

Asynchronous execution 130/145 Lambdas in Java 8 -

Java 8 lambdasI Syntactic sugar for “inline” methods:

(arg1 ,arg2 ,arg3) -> { code ;..; return res; }

I It is possible to write:Runnable r = ()->System.out.println("hello");Callable <String > c1 = ()->"hello";Callable <String > c2 = ()-> {

System.out.println("in c2");return "hello";

};r.run (); c1.call (); c2.call ();

I However, the following is invalid, because need an interface:Object o = ()->System.out.println("hello");

“The target type of this expression must be a functionalinterface”

I Java makes methods out of interface implementationsI functional interface[1, sec 1.3] must contain single method

declaration

Asynchronous execution 131/145 Lambdas in Java 8 -

Java 8 method references

Java 8 provides nice way to reference methods as functionalinterfaces:

I obj::methodNameI this::methodName

I ClassName::staticMethodName

java.util.function interfaces:

Function <Integer ,Integer > m1 = Math::abs;BiFunction <Random ,Integer ,Integer > m2 = Random :: nextInt;BiConsumer <PrintStream ,char[]> m3 = PrintStream :: print;

Asynchronous execution 132/145 Lambdas in Java 8 -

Lambdas in Pyramid of Doom

// send user nameauthProxy.login("Oleg", salt -> { // Java8 lambda

// send password digestString password = "mypassword";byte[] digest = algo.digest ((salt+password ). getBytes ());authProxy.authenticate(digest , new ResultCallback () {

public void onSuccess () {System.out.println("Login was successful");

}public void onFailure(String error) {

System.out.println("Login failed: "+error);}

});}, null);

I problem with success/failure result, interface should containsingle method

I failure handling at each call may pollute the codeI Java 8 provides error forwarding for promises

Asynchronous execution 133/145 Promises in Java 8 -

Outline

Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries

Asynchronous execution 134/145 Promises in Java 8 -

Problems of Java Future

I Java Future is the result of task executionI it provides the result once execution is completedI must submit a task before you can have its futureI what if want to return the future for the task executed third in

the sequence of async steps

I Java Future does not providefut.runThisCallback(callback) methods

I i.e. can only wait with fut.get() blocking the thread

Java 8 CompletableFuture (promise):I may be completed at any time by promise.complete(res)

I only first result is preserved and valid

I have a bunch of promise.thenApply(callback) methodsI all registered callbacks are executed when promise is completed

Asynchronous execution 135/145 Promises in Java 8 -

Java 8 CompletableFuturejava.util.concurrent.CompletableFuture<Result>

I like Java Future completed once but no specific task requiredI use complete(), completeExceptionally(), cancel()

I like java Future may be waited with get() or get(t,unit)I unlike Java Futures “continuations” may be added to it

CF <Void > thenRun(Runnable action)CF <Void > thenAccept(Consumer <? super T> action)CF <U> thenApply(Function <? super T,? extends U> fn)CF <U> thenCompose(

Function <? super T,? extends CompletionStage <U>> fn)CF <U> handle(BiFunction <? super T,Throwable ,? extends U> fn)CF <T> whenComplete(

BiConsumer <? super T,? super Throwable > action)

I the callbacks are executed after the promise is completed

I create the promiseI completedFuture(), runAsync(), supplyAsync()

Asynchronous execution 136/145 Promises in Java 8 -

Apply vs composeAfter a promise has completed run a function with the result:

I apply callback returns (? extends U)I compose callback returns (? extends CompletionStage<U>)I both take value of type T from the source promise

Imagine we have CompletableFuture<String> is a username we getasynchronously (e.g. receive from network)Want to check the validity of a username:1. Boolean checkLocally(String username) – only validates that

characters are ok, may be done synchronously2. CF<Boolean> checkRemotely(String username) – validates

that username exists in a database, the result comesasynchronously

Need to use apply in the first case and compose in the second:1. unamePromise.thenApply(this::checkLocally)2. unamePromise.thenCompose(this::checkRemotely)

Otherwise, compose returns CF<CF<Boolean>‌> – need to flatten

Asynchronous execution 137/145 Promises in Java 8 -

Handle vs whenComplete

I whenComplete() returns the old futureI i.e. it invokes the callback, but does not enhance the callback

chain

I handle() does return the new futureI this may be used to run actions when async action defined in

the handle method completes

Asynchronous execution 138/145 Promises in Java 8 -

Authentication example: promise chainingI use thenCompose and handle to chain promisesI error in the chain is propagated to the last promise omitting all

subsequent callbacks in the chain

// send user nameCompletableFuture <String > saltPromise = authProxy.login("Oleg");CompletableFuture <Void > resultPromise =

saltPromise.thenCompose(salt -> {// send password digestString password = "mypassword";byte[] digest = algo.digest ((salt+password ). getBytes ());return authProxy.authenticate(digest );

});// handle all errors or final successresultPromise.handle ((v, err) -> {

if (err==null)System.out.println("Login was successful");

elseSystem.out.println("Login failed: "+err);

return null;});

Asynchronous execution 139/145 Promises in Java 8 -

Promise chaining with separate methodsI easier to see the overall process

public void doAuth () throws IOException {authProxy.connect("localhost", 5000);CompletableFuture.completedFuture("Oleg")

.thenCompose(authProxy ::login)

.thenCompose(this:: sendHashFromSaltAndPassword)

.handle(this:: printResult );}CompletableFuture <Void > sendHashFromSaltAndPassword(String salt) {

// send password digestString password = "mypassword";byte[] digest = digestAlgo.digest ((salt+password ). getBytes ());return authProxy.authenticate(digest );

}Void printResult(Void v, Throwable err) {

if (err==null)System.out.println("Login was successful");

elseSystem.out.println("Login failed: "+err);

return null;}

Asynchronous execution 140/145 Promises within actors -

Outline

Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries

Asynchronous execution 141/145 Promises within actors -

Promise executors

Almost all methods provide 3 flavors, e.g. for run:

CF<Void > thenRun(Runnable action );CF<Void > thenRunAsync(Runnable action );CF<Void > thenRunAsync(Runnable action , Executor executor );

I action may be executed in the thread that has completed thepromise

I action is executed by the ForkJoinPool.commonPool()I action is executed by the provided executor

We must think about the consequences of synchronization or thelack of it!

Asynchronous execution 142/145 Promises within actors -

Promises within actors

I actor is inherently asynchronousI its logic is split between message handling code pieces

I often it is more convenient to program sequence of actions,like with authentication

I use promise chains inside an actor

Pay attention:I callbacks must be executed in the actor thread,I current state is not automatically checked when the callback is

executed,I consider promise (chain) cancellation

I if done from the actor thread, the rest of the promise chainwon’t be executed

Asynchronous execution 143/145 Promise libraries -

Outline

Asynchronous executionWhy asynchronousCallback hellLambdas in Java 8Promises in Java 8Promises within actorsPromise libraries

Asynchronous execution 144/145 Promise libraries -

Libraries

I Akka futures –http://doc.akka.io/docs/akka/2.4.2/java/futures.html

I RxJava – https://github.com/ReactiveX/RxJava/wikiI ReactiveX – http://reactivex.io/intro.html

Asynchronous execution 145/145 Promise libraries -

Summary

I asynchronous execution is necessary to utilize more resourcesand release threads when not needed

I callbacks allow to “continue” execution, but it results inI Pyramid of Doom or very scattered callback code

I Java futures help with resource utilization but not threadrelease

I Java 8 completable future (promise) solves the problemI allow to add callbacks through the then*() methods

I callbacks are called in other threads, which is especially criticalfor actors