Top Banner
Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Wednesday, December 12, 12
26

Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Jun 15, 2020

Download

Documents

dariahiddleston
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Combining Concurrency Abstractions

Philipp HallerTypesafe, Switzerland

Wednesday, December 12, 12

Page 2: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Combining Concurrency Abstractions

Philipp HallerTypesafe, Switzerland

Correctly and Efficiently

Wednesday, December 12, 12

Page 3: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

The Problem

• Tendency to combine several concurrency abstractions in a single project

• Actors, futures, threads, latches, ...

• Source of hard-to-diagnose concurrency bugs

• Non-blocking vs. blocking

• Threads vs. thread pools

• Closures and state

Wednesday, December 12, 12

Page 4: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Actors + X

Wednesday, December 12, 12

Page 5: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Actors, State & Futuresimport akka.actor.Actorimport scala.concurrent.future

class MyActor extends Actor { // implicit ExecutionContext of context import context.dispatcher

var state = 0

def receive = { case Request(x) => future { handleRequest(x, state) } case ChangeState(newState) => state = newState }}

Wednesday, December 12, 12

Page 6: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Actors, State & Futuresimport akka.actor.Actorimport scala.concurrent.future

class MyActor extends Actor { // implicit ExecutionContext of context import context.dispatcher

var state = 0

def receive = { case Request(x) => future { handleRequest(x, state) } case ChangeState(newState) => state = newState }}

racy!!

Wednesday, December 12, 12

Page 7: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Actors, State & Futuresimport akka.actor.Actorimport scala.concurrent.future

class MyActor extends Actor { // implicit ExecutionContext of context import context.dispatcher

var state = 0

def receive = { case Request(x) => future { handleRequest(x, state) } case ChangeState(newState) => state = newState }}

racy!!

not safely published!

Wednesday, December 12, 12

Page 8: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Safely Publishing Stateimport akka.actor.Actorimport scala.concurrent.future

class MyActor extends Actor { // implicit ExecutionContext of context import context.dispatcher

var state = 0

def receive = { case Request(x) => val currentState = state future { handleRequest(x, currentState) } case ChangeState(newState) => state = newState }}

Wednesday, December 12, 12

Page 9: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Actors, Futures & Senders

import akka.actor.Actorimport scala.concurrent.future

class MyActor extends Actor { // implicit ExecutionContext of context import context.dispatcher

def receive = { case Request(x) => future { val res = handleRequest(x) sender ! Response(res) } }}

Wednesday, December 12, 12

Page 10: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Actors, Futures & Senders

import akka.actor.Actorimport scala.concurrent.future

class MyActor extends Actor { // implicit ExecutionContext of context import context.dispatcher

def receive = { case Request(x) => future { val res = handleRequest(x) sender ! Response(res) } }} not constant!!

Wednesday, December 12, 12

Page 11: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

The Pipe Patternimport akka.actor.Actorimport akka.pattern.pipeimport scala.concurrent.futureclass MyActor extends Actor { // implicit ExecutionContext of context import context.dispatcher

def receive = { case Request(x) => future { val res = handleRequest(x) Response(res) } pipeTo sender }}

Wednesday, December 12, 12

Page 12: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

The Pipe Patternimport akka.actor.Actorimport akka.pattern.pipeimport scala.concurrent.futureclass MyActor extends Actor { // implicit ExecutionContext of context import context.dispatcher

def receive = { case Request(x) => future { val res = handleRequest(x) Response(res) } pipeTo sender }}

obtain sender once and store it

Wednesday, December 12, 12

Page 13: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Actors + Threads

• How to exchange messages between an actor and a regular (JVM) thread?

Wednesday, December 12, 12

Page 14: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Actors + Threads

• ask pattern (? operator):

• akka.actor.ActorDSL.Inbox (Akka 2.1)

implicit val i = ActorDSL.inbox()someActor ! someMsg // replies will go to `i`

val reply = i.receive()val transformedReply = i.select(5.seconds) { case x: Int => 2 * x}

val fut = actor ? msg

• How to exchange messages between an actor and a regular (JVM) thread?

Wednesday, December 12, 12

Page 15: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

A MapActor (not remote)

import akka.actor.Actor

class MapActor[K, V] extends Actor { var state = Map[K, V]()

def receive = { case Put(k, v) => state += (k -> v) sender ! AckPut case Get(k) => sender ! state.get(k) }}

Wednesday, December 12, 12

Page 16: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

A MapActor (not remote)

import akka.actor.Actor

class MapActor[K, V] extends Actor { var state = Map[K, V]()

def receive = { case Put(k, v) => state += (k -> v) sender ! AckPut case Get(k) => sender ! state.get(k) }}

just use a ParTrieMap! :-)

Wednesday, December 12, 12

Page 17: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Miscellaneous

• Thread locals

• Scope: thread, not actor or future callback chain

• Shared-memory actors (same JVM)

• Prefer sharing immutable data

• Mutable data: Java Memory Model (@volatile etc.)

Wednesday, December 12, 12

Page 18: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Combining Async and Blocking APIs

Wednesday, December 12, 12

Page 19: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Blocking APIs

• java.lang.Object.wait

• java.io.Reader.read etc.

• java.util.concurrent: Future.get, CountDownLatch.await, BlockingQueue.put/take

• Scala 2.10 (SIP-14): Await.{result, ready}

• ...

Wednesday, December 12, 12

Page 20: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Blocking Futures

import scala.concurrent._import java.util.concurrent.{Future => JFuture}import ExecutionContext.Implicits.global

object Main extends App {

val futs: List[JFuture[String]] = // list of 4’000 Java futures

val transformed = for (fut <- futs) yield future { fut.get(10, TimeUnit.SECONDS).toUpperCase }}

Wednesday, December 12, 12

Page 21: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Managed Blocking

import scala.concurrent._import java.util.concurrent.{Future => JFuture}import ExecutionContext.Implicits.global

object Main extends App {

val futs: List[JFuture[String]] = // list of 4’000 Java futures

val transformed = for (fut <- futs) yield future { blocking { fut.get(10, TimeUnit.SECONDS).toUpperCase } }}

Wednesday, December 12, 12

Page 22: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Fully Async

import scala.concurrent._

import ExecutionContext.Implicits.global

object Main extends App {

val futs: List[Future[String]] = // list of 4’000 Scala futures

val transformed = for (fut <- futs) yield fut.map(_.toUpperCase) }

Wednesday, December 12, 12

Page 23: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Preventing Misuse

Wednesday, December 12, 12

Page 24: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Requiring Managed Blocking

trait Awaitable[+T] { def result(atMost: Duration) (implicit permit: CanAwait): T}package concurrent { @implicitNotFound("Use the `Await` object") sealed trait CanAwait

private[concurrent] object AwaitPermission extends CanAwait

object Await { def result[T](awaitable: Awaitable[T], ...): T = blocking(awaitable.result(atMost)(AwaitPermission)) }}

Wednesday, December 12, 12

Page 25: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Your Turn!

• What do you find hard/confusing when combining concurrency abstractions?

• What practices do you follow/recommend to avoid concurrency hazards?

Wednesday, December 12, 12

Page 26: Combining Concurrency Abstractions - EPFLlampphaller/doc/Combining_Concurrency.pdf · Combining Concurrency Abstractions Philipp Haller Typesafe, Switzerland Correctly and Efficiently

Thanks! Questions?Philipp Haller

Typesafe, Switzerland

Wednesday, December 12, 12