MONIX TASK LAZY, ASYNC & AWESOME. Alexandru Nedelcu. Software / alexn.org

Similar documents
Compositional I/Ο Streams in Scala. Rúnar Bjarnason, Verizon QCon London, March 2016

Combining Concurrency Abstractions

CS Programming Languages: Scala

First-Class Synchronization Barriers. Franklyn Turbak Wellesley College

Executive Summary. It is important for a Java Programmer to understand the power and limitations of concurrent programming in Java using threads.

September 15th, Finagle + Java. A love story (

POSIX Threads: a first step toward parallel programming. George Bosilca

CS 6112 (Fall 2011) Foundations of Concurrency

Programming Paradigms for Concurrency Lecture 10 The Actor Paradigm

Scala Actors. Scalable Multithreading on the JVM. Philipp Haller. Ph.D. candidate Programming Methods Lab EPFL, Lausanne, Switzerland

Chair of Software Engineering. Java and C# in depth. Carlo A. Furia, Marco Piccioni, Bertrand Meyer. Java: concurrency

Lazy Evaluation in Scala

Concurrency: Past and Present

Threads and Parallelism in Java

CSL373: Lecture 5 Deadlocks (no process runnable) + Scheduling (> 1 process runnable)

Simplifying Asynchronous Code with

Written Presentation: JoCaml, a Language for Concurrent Distributed and Mobile Programming

Practical Concurrency. Copyright 2007 SpringSource. Copying, publishing or distributing without express written permission is prohibited.

Chapter Machine instruction level 2. High-level language statement level 3. Unit level 4. Program level

CS 351 Design of Large Programs Threads and Concurrency

Problems with Concurrency

Multicore programming in Haskell. Simon Marlow Microsoft Research

Synchronization Spinlocks - Semaphores

Processor speed. Concurrency Structure and Interpretation of Computer Programs. Multiple processors. Processor speed. Mike Phillips <mpp>

Microprofile Fault Tolerance. Emily Jiang 1.0,

Essential Scala. Six Core Concepts for Learning Scala Noel underscore

Threading and Synchronization. Fahd Albinali

Multithreading and Interactive Programs

Overview. Elements of Programming Languages. Evaluation order. Evaluation order

Continuations and Continuation-Passing Style

CS 179: GPU Programming LECTURE 5: GPU COMPUTE ARCHITECTURE FOR THE LAST TIME

David Walker. Thanks to Kathleen Fisher and recursively to Simon Peyton Jones for much of the content of these slides.

Actors in the Small. Making Actors more Useful. Bill La

ECE 462 Object-Oriented Programming using C++ and Java. Scheduling and Critical Section

Multiple Inheritance. Computer object can be viewed as

The Dining Philosophers Problem CMSC 330: Organization of Programming Languages

Stateful Objects. can I withdraw 100 CHF? may vary over the course of the lifetime of the account.

CMSC 330: Organization of Programming Languages. The Dining Philosophers Problem

Synchronizing the Asynchronous

an overview Alceste Scalas Programming languages reading group

Reminder from last time

Concurrent Programming using Threads

CSE 332: Locks and Deadlocks. Richard Anderson, Steve Seitz Winter 2014

CMSC 433 Programming Language Technologies and Paradigms. Composing Objects

Problems with Concurrency. February 19, 2014

COS 326 David Walker. Thanks to Kathleen Fisher and recursively to Simon Peyton Jones for much of the content of these slides.

The fringe of a binary tree are the values in left-to-right order. For example, the fringe of the following tree:

G52CON: Concepts of Concurrency

Variables. Substitution

CSE332: Data Abstractions Lecture 23: Programming with Locks and Critical Sections. Tyler Robison Summer 2010

AC: COMPOSABLE ASYNCHRONOUS IO FOR NATIVE LANGUAGES. Tim Harris, Martín Abadi, Rebecca Isaacs & Ross McIlroy

Chapter 5 Asynchronous Concurrent Execution

CMSC 330: Organization of Programming Languages. Threads Classic Concurrency Problems

Concurrency: Deadlock and Starvation. Chapter 6

Simplifying Asynchronous Code with

Futures. Prof. Clarkson Fall Today s music: It's Gonna be Me by *NSYNC

Last Class: Synchronization

Process Management And Synchronization

Programming in Parallel COMP755

Lecture 1 Introduction to Parallel Programming

Chapter 13 Topics. Introduction. Introduction

Concept of a process

CPSC/ECE 3220 Fall 2017 Exam Give the definition (note: not the roles) for an operating system as stated in the textbook. (2 pts.

Implementing Mutual Exclusion. Sarah Diesburg Operating Systems CS 3430

The story so far. Elements of Programming Languages. While-programs. Mutable vs. immutable

CS 31: Intro to Systems Misc. Threading. Kevin Webb Swarthmore College December 6, 2018

CSCD 330 Network Programming

Lecture 21: The Many Hats of Scala: OOP 10:00 AM, Mar 14, 2018

Learning from Bad Examples. CSCI 5828: Foundations of Software Engineering Lecture 25 11/18/2014

Synchronization for Concurrent Tasks

Implicit threading, Speculation, and Mutation in Manticore

Scala. Fernando Medeiros Tomás Paim

n Closed book n You are allowed 5 cheat pages n Practice problems available in Course Materials n Check grades in Rainbow grades

COSE212: Programming Languages. Lecture 4 Recursive and Higher-Order Programming

Imperative languages

G52CON: Concepts of Concurrency

The learning objectives of this chapter are the followings. At the end of this chapter, you shall

Erlang. Functional Concurrent Distributed Soft real-time OTP (fault-tolerance, hot code update ) Open. Check the source code of generic behaviours

G Programming Languages - Fall 2012

Recursion. What is Recursion? Simple Example. Repeatedly Reduce the Problem Into Smaller Problems to Solve the Big Problem

STUDENT NAME: STUDENT ID: Problem 1 Problem 2 Problem 3 Problem 4 Problem 5 Total

CS377P Programming for Performance Multicore Performance Synchronization

CS 475. Process = Address space + one thread of control Concurrent program = multiple threads of control

<Insert Picture Here> Get on the Grid. JVM Language Summit Getting Started Guide. Cameron Purdy, VP of Development, Oracle

A Non-Blocking Concurrent Queue Algorithm

Operating Systems (2INC0) 2017/18

What is a thread anyway?

Programs as Values Pure Functional Database Access in Scala

Lecture 8: September 30

Composable Concurrency in Perl 6. Jonathan Worthington

Erlang. Functional Concurrent Distributed Soft real-time OTP (fault-tolerance, hot code update ) Open. Check the source code of generic behaviours

Fault tolerance made easy A head-start to resilient software design

Concurrency. Lecture 14: Concurrency & exceptions. Why concurrent subprograms? Processes and threads. Design Issues for Concurrency.

Overview. CMSC 330: Organization of Programming Languages. Concurrency. Multiprocessors. Processes vs. Threads. Computation Abstractions

COMP 322: Fundamentals of Parallel Programming

What the optimizer does to your code

Hystrix Python Documentation

Concurrent and Multicore Haskell

Lecture 17: Sharing Objects in Java

Mastering Advanced Scala

Transcription:

LAZY, ASYNC & AWESOME Alexandru Nedelcu Software Developer @ eloquentix.com @alexelcu / alexn.org

WHAT IS MONIX? Scala / Scala.js library For composing asynchronous programs Exposes Observable & Task Typelevel (see typelevel.org) 2.3.0 See: monix.io

WHAT S WRONG WITH THE FUTURE[A]?

DELAYING THE FUTURE[A] Side-effects handling Throughput Error handling

MONIX TASK A type that: Describes lazy or (possibly) async computations Is effective at controlling side-effects Is effective at dealing with concurrency

MONIX TASK A type that: Describes lazy or (possibly) async computations Is effective at controlling side-effects Is effective at dealing with concurrency

MONIX TASK A type that: Describes lazy or (possibly) async computations Is effective at controlling side-effects Is effective at dealing with concurrency

MONIX TASK Inspired by: Scalaz Task (scalaz.concurrent.task) Haskel s IO Alternatives: FS2 Task cats.effect.io

EVALUATION

EVALUATION IN SCALA Eager A Lazy () => A

EVALUATION IN SCALA Eager Lazy Synchronous A () => A Asynchronous (A => Unit) => Unit () => (A => Unit) => Unit

EVALUATION IN SCALA Eager Lazy Synchronous A () => A Function0[A] Asynchronous (A => Unit) => Unit () => (A => Unit) => Unit Future[A] Task[A]

TASK FUTURE import monix.execution.scheduler import Scheduler.Implicits.global import monix.eval.task val task = Task { 1 + 1 } // Later... task.runasync { case Success(value) => println(v) case Failure(ex) => println(ex.getmessage) } import scala.concurrent.executioncontext import ExecutionContext.Implicits.global import scala.concurrent.future val future = Future { 1 + 1 } // Later... future.oncomplete { case Success(value) => println(v) case Failure(ex) => println(ex.getmessage) }

MONIX TASK S BEHAVIOR allows fine-grained control over the evaluation model doesn t trigger any effects until runasync doesn t necessarily execute on another logical thread allows for cancelling of a running computation

EVALUATION!// Strict evaluation Task.now { println("effect"); "immediate" }!// Lazy / memoized evaluation Task.evalOnce { println("effect"); "memoized" }!// Equivalent to a function Task.eval { println("effect"); "always" }!// Builds a factory of tasks ;-) Task.defer(Task.now { println("effect") })!// Guarantees asynchronous execution Task.fork(Task.eval("Hello!"))

MEMOIZATION (1/4) import monix.execution.atomic.atomic val task1 = { } val effect = Atomic(0) Task.evalOnce(effect.incrementAndGet()) val task2 = { } val effect = Atomic(0) Task.eval(effect.incrementAndGet()).memoize

MEMOIZATION (2/4) task.memoize vs task.runasync

MEMOIZATION (3/4) val effect = Atomic(0) val source = Task.eval { val current = effect.incrementandget() if (current!>= 3) current else throw new RuntimeException("dummy") } source.memoizeonsuccess

MEMOIZATION (4/4) memoizeonsuccess cannot be done with Future Task can do it because Task is a function ;-)

TAIL RECURSIVE LOOPS (1/4) @tailrec def fib(cycles: Int, a: BigInt, b: BigInt): BigInt = if (cycles > 0) fib(cycles-1, b, a + b) else b

TAIL RECURSIVE LOOPS (2/4) def fib(cycles: Int, a: BigInt, b: BigInt): Task[BigInt] = if (cycles > 0) Task.defer(fib(cycles-1, b, a+b)) else Task.now(b)

TAIL RECURSIVE LOOPS (3/4) def fib(cycles: Int, a: BigInt, b: BigInt): Task[BigInt] = Task.eval(cycles > 0).flatMap { } case true!=> fib(cycles-1, b, a+b) case false!=> Task.now(b) FlatMap, like all of Task s operators, is stack-safe ;-)

TAIL RECURSIVE LOOPS (4/4) // Mutual Tail Recursion, ftw!!! def odd(n: Int): Task[Boolean] = Task.evalAlways(n == 0).flatMap { case true => Task.now(false) case false => even(n - 1) } def even(n: Int): Task[Boolean] = Task.evalAlways(n == 0).flatMap { case true => Task.now(true) case false => odd(n - 1) } even(1000000)

NO BOOBY TRAPS

NO BOOBY TRAPS (1/2) def signal[a](f:!=> A): Task[A] = Task.async { (_, callback)!=> callback.onsuccess(f) Cancelable.empty }!// Look Ma, no Stack Overflows def loop(n: Int, acc: Int): Task[Int] = Task.now(n).flatMap { x!=> } if (x!<= 0) Task.now(acc) else loop(x-1, acc+x)

NO BOOBY TRAPS (2/2)!// Look Ma, no Stack Overflows def all[a](list: List[Task[A]]): Task[List[A]] = { val initial = Task.now(List.empty[A]) } list.foldleft(initial) { (acc, e)!=> Task.mapBoth(e, acc)(_!:: _) }

SCHEDULER

SCHEDULER (1/4) package monix.execution trait Cancelable { def cancel(): Unit } trait Scheduler extends ExecutionContext { def scheduleonce(initialdelay: Long, unit: TimeUnit, r: Runnable): Cancelable def currenttimemillis(): Long def executionmodel: ExecutionModel def schedulewithfixeddelay(...): Cancelable def scheduleatfixedrate(...): Cancelable }

SCHEDULER (2/4) import monix.execution.cancelablefuture import monix.execution.scheduler.implicits.global val f: CancelableFuture[String] = Task("hello").runAsync(global)

SCHEDULER (3/4) import monix.execution.scheduler val effect = Atomic(0) val io = Scheduler.io("my-io") Task(effect.incrementAndGet()).executeOn(io).asyncBoundary.map(_ + 1)

SCHEDULER (4/4) import monix.execution.scheduler val effect = Atomic(0) val io = Scheduler.io("my-io") val computation = Scheduler.computation() Task(effect.incrementAndGet()).executeOn(io).asyncBoundary(computation).map(_ + 1)

EXECUTION MODEL

EXECUTION MODEL in batches, by default (fair, reasonable performance) always asynchronous (fair, like Scala s Future) preferably synchronous (unfair, like Scalaz s Task)

EXECUTION MODEL: BATCHED import monix.execution._ import ExecutionModel.BatchedExecution implicit val scheduler = Scheduler.computation( parallelism = 4, executionmodel=batchedexecution(batchsize=1024) )

EXECUTION MODEL: ALWAYS ASYNC import monix.execution._ import ExecutionModel.AlwaysAsyncExecution implicit val scheduler = Scheduler.computation( parallelism=4, executionmodel=alwaysasyncexecution )

EXECUTION MODEL: PREFER SYNCHRONOUS import monix.execution._ import ExecutionModel.SynchronousExecution implicit val scheduler = Scheduler.computation( parallelism=4, executionmodel=synchronousexecution )

REAL ASYNCHRONY

REAL ASYNCHRONY (A => Unit) => Unit

REAL ASYNCHRONY Future[A] => A

REAL ASYNCHRONY Future[A] => A Always a platform specific hack, just say no to hacks!

REAL ASYNCHRONY def eval[a](d: FiniteDuration, f:!=> A): Task[A] = Task.create { (scheduler, callback)!=>!// Execution } val cancelable = scheduler.scheduleonce(d) { try callback.onsuccess(f) catch { case NonFatal(e)!=> callback.onerror(e) } }!// For early termination cancelable

REAL ASYNCHRONY def fromfuture[a](future: Future[A]): Task[A] = Task.create { (scheduler, callback) => implicit val ec = scheduler // Waiting... future.oncomplete { case Success(v) => callback.onsuccess(v) case Failure(ex) => callback.onerror(ex) } // Futures can't be canceled Cancelable.empty }

REAL ASYNCHRONY // From Future... val task = Task.defer( Task.fromFuture(Future { "effect" })) // And back again... val future = task.runasync // If we want the result... Await.result(future, 10.seconds)

REAL ASYNCHRONY ( ) // From Future... val task = Task.defer( Task.fromFuture(Future { "effect" })) // And back again... val future = task.runasync // If we want the result... Await.result(future, 10.seconds)

FUTURE INTEROP

FUTURE INTEROP (1/3) val effect = Atomic(0) def increment() (implicit ec: ExecutionContext): Future[Int] = Future(effect.incrementAndGet())

FUTURE INTEROP (2/3) val effect = Atomic(0) def increment() (implicit ec: ExecutionContext): Future[Int] = Future(effect.incrementAndGet()) def incrementtask() (implicit ec: ExecutionContext): Task[Int] = Task.deferFuture(increment())

FUTURE INTEROP (3/3) val effect = Atomic(0) def increment() (implicit ec: ExecutionContext): Future[Int] = Future(effect.incrementAndGet())!// Look Ma, no implicit ExecutionContext val task = Task.deferFutureAction { implicit ec!=> increment() }

CANCELABLES BECAUSE WE SHOULDN T LEAK

CANCELABLES package monix.eval sealed abstract class Task[+A] { def runasync(implicit s: Scheduler): CancelableFuture[A] def runasync(cb: Callback[A]) (implicit s: Scheduler): Cancelable def runoncomplete(f: Try[A] => Unit) (implicit s: Scheduler): Cancelable??? }

CANCELABLES // In monix.execution... trait CancelableFuture[+A] extends Future[A] with Cancelable val result: CancelableFuture[String] = Task.evalOnce { "result" }.delayexecution(10.seconds).runasync // If we change our mind... result.cancel()

CANCELABLES def delayed[a](timespan: FiniteDuration)(f: => A) = Task.create[A] { (scheduler, callback) => // Register a task in the thread-pool val cancelable = scheduler.scheduleonce( timespan.length, timespan.unit, new Runnable { def run(): Unit = callback(try(f)) }) cancelable }

CANCELABLES: SAFE FALLBACKS (1/2) def choosefirstof[a,b](fa: Task[A], fb: Task[B]): Task[Either[(A, CancelableFuture[B]), (CancelableFuture[A], B)]]

CANCELABLES: SAFE FALLBACKS (2/2) val source: Task[Int] =??? val other: Task[Int] =??? val fallback: Task[Int] = other.delayexecution(5.seconds) Task.chooseFirstOf(source, fallback).map { case Left((a, futureb)) => futureb.cancel() a case Right((futureA, b)) => futurea.cancel() b }

CANCELABLES: BETTER FUTURE.SEQUENCE val result: Task[Seq[Int]] = Task.zipList(Seq(task1, task2, task3, task4)) On error it does not wait and cancels the unfinished ;-)

CANCELABLES: BETTER FUTURE.FIRSTCOMPLETEDOF val result: Task[Int] = Task.chooseFirstOfList(Seq(task1, task2, task3)) Cancels the unfinished ;-)

CANCELABLES: LOOPS def fib(cycles: Int): Task[BigInt] = { def loop(cycles: Int, a: BigInt, b: BigInt): Task[BigInt] = Task.eval(cycles > 0).flatMap { case true!=> loop(cycles-1, b, a+b) case false!=> Task.now(b) } } loop(cycles, 0, 1).executeWithOptions(_.enableAutoCancelableRunLoops)

CANCELABLES cancel is a concurrent action (with the Task s execution) NOT cancelable by default (e.g. bring your own booze)

ERROR HANDLING "If a tree falls in a forest and no one is around to hear it, does it make a sound?"

ERROR HANDLING (1/4) task.onerrorhandlewith { case _: TimeoutException => fallbacktask case ex => Task.raiseError(ex) }

MONIX TASK ERROR HANDLING (2/4) task.onerrorrestart(maxretries = 20) task.onerrorrestartif { case _: TimeoutException => true case _ => false }

ERROR HANDLING (3/4) def retrybackoff[a](source: Task[A]) (maxretries: Int, delay: FiniteDuration): Task[A] = { } source.onerrorhandlewith { ex!=> } if (maxretries > 0) retrybackoff(source)(maxretries - 1, delay * 2).delayExecution(delay) else Task.raiseError(ex)

ERROR HANDLING (4/4) task.timeout(10.seconds) task.timeoutto(10.seconds, Task.raiseError(new TimeoutException()))

PRIMITIVES

ASYNCHRONOUS SEMAPHORE import monix.eval.tasksemaphore val semaphore = TaskSemaphore(maxParallelism = 16) val request = Task("response").delayExecution(1.second) semaphore.greenlight(request)

CIRCUIT BREAKER val circuitbreaker = TaskCircuitBreaker( maxfailures = 5, resettimeout = 10.seconds )!//!!... val problematic = Task { val nr = util.random.nextint() if (nr % 2!== 0) nr else throw new RuntimeException("dummy") } val task = circuitbreaker.protect(problematic)

MVAR Behaves like a BlockingQueue(size = 1) import monix.eval.mvar val state = MVar.empty[Int]!// Blocks until MVar is empty! val producer: Task[Unit] = state.put(1)!// Blocks until a value is available val consumer: Task[Int] = state.take

MVAR - USE CASES synchronized mutable vars asynchronous locks simple Producer/Consumer channels

QUESTIONS? monix.io @monix @monix alexn.org @alexelcu @alexandru