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

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

  • Upload
    others

  • View
    15

  • Download
    0

Embed Size (px)

Citation preview

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