SlideShare a Scribd company logo
atomically {
delete your actors
}
John A. De Goes — @jdegoes
Wiem Zine Elabidine — @wiemzin
The Bank Heist The Hero Become a Hero!
The Bank Heist
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
The Bank
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
#1
#1
12
def transfer(from: Account, to: Account,
amount: Amount): Unit = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
13
def transfer(from: Account, to: Account,
amount: Amount): Unit = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
14
def transfer(from: Account, to: Account,
amount: Amount): Unit = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
15
def transfer(from: Account, to: Account,
amount: Amount): Unit = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
Double-Spend Exploits
$1 M
$1 M
$1 M $2 M
def transfer(from: Account, to: Account,
amount: Amount) = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
def transfer(from: Account, to: Account,
amount: Amount) = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
Thread 1 Thread 2
Race Condition Exploit
def transfer(from: Account, to: Account,
amount: Amount) = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
def transfer(from: Account, to: Account,
amount: Amount) = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
Thread 1 Thread 2
Race Condition Exploit
Atomically { Delete Your Actors }
20
class Account {
var balance : Amount
var accountID : AccountID
var name : String
val opened : Instant
val status : AccountStatus
val tpe : AccountType
}
balance 200,000
accountID 2816157231
... ...
Main Memory
balance 200,000
accountID 2816157231
... ...
Core 1 Cache
balance 200,000
accountID 2816157231
... ...
Core 2 Cache
def transfer(from: Account, to: Account,
amount: Amount) = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
def transfer(from: Account, to: Account,
amount: Amount) = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
Thread 1 Thread 2
Stale Cache Exploit
Atomically { Delete Your Actors }
from.balance -= amount
4: getfield #2
7: invokevirtual #3
10: aload_2
11: invokevirtual #3
14: isub
15: invokestatic #4
18: dup_x1
19: putfield #2
def transfer(from: Account, to: Account,
amount: Amount) = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
def transfer(from: Account, to: Account,
amount: Amount) = {
if (from.balance >= amount) {
from.balance -= amount
to.balance += amount
} else {
Thread.sleep(100)
transfer(from, to, amount)
}
}
Thread 1 Thread 2
Nonatomic Instruction Exploit
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
abstract class Account {
trait State {
@volatile var balance: BigDecimal = _
}
val state: State
def modify[A](f: State => A): A = this.synchronized {
val result = f(state)
this.notifyAll()
result
}
def await(): Unit = this.synchronized { this.wait() }
}
def transfer(from: Account, to: Account,
amount: Amount): Unit = {
var loop = true
while (loop) {
from.modify { state =>
if (state.balance >= amount.value) {
state.balance -= amount.value
to.modify(state => state.balance += amount.value)
loop = false
}
else from.await()
}
}
transfer(from, to, amount) transfer(to, from, amount)
Thread 1 Thread 2
Deadlock Exploit
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
class Account extends Actor {
var balance = Amount.zero
var todos = List.empty[(ActorRef, Message)]
def receive = {
case Deposit(amount) =>
balance = balance + amount.value
sender ! Success(balance)
todos.foreach { case (s, m) => self ! m }
todos = Nil
case v @ Withdraw(amount) =>
if (balance >= amount.value) {
balance = balance - amount.value
sender ! Success(balance)
} else todos = (sender, v) :: todos
case Balance => sender ! Success(balance)
}
}
def transfer(from: ActorRef, to: ActorRef,
amount: Amount)(implicit garbage: Timeout): Unit =
(from ? Withdraw(amount)).flatMap { _ =>
to ? Deposit(amount)
}
Atomically { Delete Your Actors }
for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$70
$99
for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$70
$6,000,000 $99
for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$70
$6,000,000 $99
$70
~$6m
for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$10 $99
$70
-$60
for {
john <- johnAccount ? Balance
wiem <- wiemAccount ? Balance
} yield
if (wiem.value > john.value)
transfer(wiem, cafe, amount)
else transfer(john, cafe, amount)
$99
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
Atomically { Delete Your Actors }
46
def transfer(from : TRef[Account],
to : TRef[Account],
amount : Amount): UIO[Unit] =
atomically {
for {
balance <- from.get
_ <- check(balance >= amount)
_ <- from.update(_ - amount)
_ <- to.update(_ + amount)
} yield ()
}
47
def transfer(from : TRef[Account],
to : TRef[Account],
amount : Amount): UIO[Unit] =
atomically {
for {
balance <- from.get
_ <- check(balance >= amount)
_ <- from.update(_ - amount)
_ <- to.update(_ + amount)
} yield ()
}
48
def transfer(from : TRef[Account],
to : TRef[Account],
amount : Amount): UIO[Unit] =
atomically {
for {
balance <- from.get
_ <- check(balance >= amount)
_ <- from.update(_ - amount)
_ <- to.update(_ + amount)
} yield ()
}
49
def transfer(from : TRef[Account],
to : TRef[Account],
amount : Amount): UIO[Unit] =
atomically {
for {
balance <- from.get
_ <- check(balance >= amount)
_ <- from.update(_ - amount)
_ <- to.update(_ + amount)
} yield ()
}
50
def transfer(from : TRef[Account],
to : TRef[Account],
amount : Amount): UIO[Unit] =
atomically {
for {
balance <- from.get
_ <- check(balance >= amount)
_ <- from.update(_ - amount)
_ <- to.update(_ + amount)
} yield ()
}
51
def transfer(from : TRef[Account],
to : TRef[Account],
amount : Amount): UIO[Unit] =
atomically {
for {
balance <- from.get
_ <- check(balance >= amount)
_ <- from.update(_ - amount)
_ <- to.update(_ + amount)
} yield ()
}
Atomically { Delete Your Actors }
The Hero
STM: Software Transactional Memory
Provides the ability to atomically commit a series of
reads and writes to transactional memory when a set
of conditions is satisfied.
STM
STM
...Op 2Op 1 Op n
Final
State
Initial
State
commit
failure - rollback
retry - rollback
complete complete
STM
STM[E, A]
A transaction,
which models
reads & writes,
and can fail, retry,
or succeed.
TRef[A]
A transactional
reference, which
is read & written
inside STM
transactions.
STM[E, A]
Succeed with a
value of type A
Fail with an
error of type E
STM
STM
STM[E, A]
A transaction,
which models
reads & writes,
and can fail, retry,
or succeed.
TRef[A]
A transactional
reference, which
is read & written
inside STM
transactions.
TRef[A]
An immutable value
of type A
STM
STM
STM
STM
STM
Composable
trait TRef[A] {
val get: STM[Nothing, A]
def set(newValue: A): STM[Nothing, Unit]
def update(f: A => A): STM[Nothing, A]
def modify[B](f: A => (B, A)): STM[Nothing, B]
}
object TRef {
def make[A](a: A): STM[Nothing, TRef[A]]
}
TRef
trait TRef[A] {
val get: STM[Nothing, A]
def set(newValue: A): STM[Nothing, Unit]
def update(f: A => A): STM[Nothing, A]
def modify[B](f: A => (B, A)): STM[Nothing, B]
}
object TRef {
def make[A](a: A): STM[Nothing, TRef[A]]
}
TRef
trait TRef[A] {
val get: STM[Nothing, A]
def set(newValue: A): STM[Nothing, Unit]
def update(f: A => A): STM[Nothing, A]
def modify[B](f: A => (B, A)): STM[Nothing, B]
}
object TRef {
def make[A](a: A): STM[Nothing, TRef[A]]
}
TRef
trait TRef[A] {
val get: STM[Nothing, A]
def set(newValue: A): STM[Nothing, Unit]
def update(f: A => A): STM[Nothing, A]
def modify[B](f: A => (B, A)): STM[Nothing, B]
}
object TRef {
def make[A](a: A): STM[Nothing, TRef[A]]
}
TRef
trait TRef[A] {
val get: STM[Nothing, A]
def set(newValue: A): STM[Nothing, Unit]
def update(f: A => A): STM[Nothing, A]
def modify[B](f: A => (B, A)): STM[Nothing, B]
}
object TRef {
def make[A](a: A): STM[Nothing, TRef[A]]
}
TRef
trait STM[+E, +A] {
def commit: IO[E, A] = STM.atomically { this }
}
object STM {
def atomically[E, A](stm: STM[E, A]): IO[E, A]
}
STM - Commit
val hello: STM[Nothing, String] =
STM.succeed("Welcome to Scalar")
STM - Succeed
val sumBalances: STM[Nothing, Int] =
balance1.get.flatMap(a =>
balance2.get.map(b => a + b))
STM - map & flatMap
val sumBalances: STM[Nothing, Int] =
for {
a <- balance1.get
b <- balance2.get
} yield a + b
STM - map & flatMap
STM - Fail
def debit(sender: TRef[Amount], amount: Amount):
STM[String, Amount] =
for {
balance <- sender.update(_ - amount)
_ <- if (balance < 0) STM.fail("Insufficient funds")
else STM.succeed(())
} yield balance
def debitSuccess(sender: TRef[Amount],
amount: Amount): STM[Nothing, Boolean] =
debit(sender, amount).fold(_ => false, _ => true)
STM - Fold
def debitWithBalance(sender: TRef[Amount],
amount: Amount): STM[Nothing, Amount] =
debit(sender, amount)
.foldM(
_ => sender.get,
_ => sender.get)
STM - FoldM
def awaitTicket(tref: TRef[Option[Ticket]]): STM[Nothing, Ticket] =
for {
option <- tref.get
ticket <- option match {
case None => STM.retry
case Some(ticket) => STM.succeed(ticket)
}
} yield ticket
STM - Retry
def tripTickets: STM[Nothing, (Ticket, Ticket)] =
awaitTicket(toWarsaw) zip awaitTicket(toHome)
STM - Zip
def bid(price : Amount,
org : TRef[TravelCompany]): STM[Nothing, Ticket] =
for {
v <- org.get
_ <- STM.check(v.availTix.exists(_.price <= price))
ticket = findCheapest(v, price)
_ <- org.update(_.removeTix(ticket))
} yield ticket
STM - Check
STM - Filter
def bid(price : Amount,
org : TRef[TravelCompany]): STM[Nothing, Ticket] =
for {
v <- org.get.filter(_.availTix.exists(_.price <= price))
ticket = findCheapest(v, price)
_ <- org.update(_.removeTix(ticket))
} yield ticket
val trainOrAirplane: STM[Nothing, Ticket] =
bid(25.00, Railway) orElse bid(50.00, Airline)
STM - Choice
def checkIn(passengers: TRef[List[Person]]): STM[Nothing, Person] =
for {
head <- passengers.get.collect { case head :: tail => head }
_ <- passengers.update(_.drop(1))
} yield head
STM - Collect
STM
STM
STM
STM
Composable Easy to Reason About
def transfer(from : TRef[Account],
to : TRef[Account],
amount : Amount): UIO[Unit] =
atomically {
for {
balance <- from.get
_ <- check(balance >= amount)
_ <- from.update(_ - amount)
_ <- to.update(_ + amount)
} yield ()
}
Become a Hero
Semaphore
Thread #2 holds
1 permit
Thread #1 holds
2 permits
Thread #3 waits
for 4 permits
Semaphore
6 permits
Semaphore
7 contributors
11 months+
301 lines of code
Semaphore
type Semaphore = TRef[Int]
Semaphore
def makeSemaphore(n: Int): UIO[Semaphore] =
TRef.make(n).commit
Semaphore
def acquire(semaphore: Semaphore, n: Int): UIO[Unit] =
(for {
value <- semaphore.get
_ <- STM.check(value >= n)
_ <- semaphore.set(value - n)
} yield ()).commit
Semaphore
def acquire(semaphore: Semaphore, n: Int): UIO[Unit] =
(for {
value <- semaphore.get
_ <- STM.check(value >= n)
_ <- semaphore.set(value - n)
} yield ()).commit
Semaphore
def acquire(semaphore: Semaphore, n: Int): UIO[Unit] =
(for {
value <- semaphore.get
_ <- STM.check(value >= n)
_ <- semaphore.set(value - n)
} yield ()).commit
Semaphore
def acquire(semaphore: Semaphore, n: Int): UIO[Unit] =
(for {
value <- semaphore.get
_ <- STM.check(value >= n)
_ <- semaphore.set(value - n)
} yield ()).commit
Semaphore
def release(semaphore: Semaphore, n: Int): UIO[Unit] =
semaphore.update(_ + n).commit
Semaphore
1 author (you!)
10 minutes
8 lines of code
Your Semaphore using ZIO!
Promise
Unset Set
Wait Wait Continue Continue
Promise
7 contributors
11 months+
274 lines of code
Promise
type Promise[A] = TRef[Option[A]]
Promise
def makePromise[A]: UIO[Promise[A]] =
TRef.make(None).commit
Promise
def complete[A](promise: Promise[A], v: A): UIO[Boolean] =
(for {
value <- promise.get
change <- value match {
case Some(_) => STM.succeed(false)
case None => promise.set(Some(v)) *>
STM.succeed(true)
}
} yield change).commit
Promise
def complete[A](promise: Promise[A], v: A): UIO[Boolean] =
(for {
value <- promise.get
change <- value match {
case Some(_) => STM.succeed(false)
case None => promise.set(Some(v)) *>
STM.succeed(true)
}
} yield change).commit
Promise
def complete[A](promise: Promise[A], v: A): UIO[Boolean] =
(for {
value <- promise.get
change <- value match {
case Some(_) => STM.succeed(false)
case None => promise.set(Some(v)) *>
STM.succeed(true)
}
} yield change).commit
Promise
def complete[A](promise: Promise[A], v: A): UIO[Boolean] =
(for {
value <- promise.get
change <- value match {
case Some(_) => STM.succeed(false)
case None => promise.set(Some(v)) *>
STM.succeed(true)
}
} yield change).commit
Promise
def await[A](promise: Promise[A]): UIO[A] =
promise.get.collect { case Some(a) => a }.commit
Promise
1 author (you!)
10 minutes
8 lines of code
Your Promise using ZIO!
Queue
Empty Queue
Capacity: 6
Full Queue
Capacity: 6
Offer
(Continue)
Take
(Wait)
Offer
(Wait)
Take
(Continue)
Queue
4 contributors
9 months+
487 lines of code
Queue
case class Queue[A](
capacity : Int,
tref : TRef[ScalaQueue[A]])
Queue
def makeQueue[A](capacity: Int): UIO[Queue[A]] =
TRef.make(ScalaQueue.empty[A]).commit
.map(Queue(capacity, _))
Queue
def offer[A](queue: Queue[A], a: A): UIO[Unit] =
(for {
q <- queue.tref.get
_ <- STM.check(q.length < queue.capacity)
_ <- queue.tref.update(_ enqueue a)
} yield ()).commit
Queue
def offer[A](queue: Queue[A], a: A): UIO[Unit] =
(for {
q <- queue.tref.get
_ <- STM.check(q.length < queue.capacity)
_ <- queue.tref.update(_ enqueue a)
} yield ()).commit
Queue
def offer[A](queue: Queue[A], a: A): UIO[Unit] =
(for {
q <- queue.tref.get
_ <- STM.check(q.length < queue.capacity)
_ <- queue.tref.update(_ enqueue a)
} yield ()).commit
Queue
def offer[A](queue: Queue[A], a: A): UIO[Unit] =
(for {
q <- queue.tref.get
_ <- STM.check(q.length < queue.capacity)
_ <- queue.tref.update(_ enqueue a)
} yield ()).commit
Queue
def take[A](queue: Queue[A]): UIO[A] =
(for {
q <- queue.tref.get
a <- q.dequeueOption match {
case Some((a, as)) =>
queue.tref.set(as) *> STM.succeed(a)
case _ => STM.retry
}
} yield a).commit
Queue
def take[A](queue: Queue[A]): UIO[A] =
(for {
q <- queue.tref.get
a <- q.dequeueOption match {
case Some((a, as)) =>
queue.tref.set(as) *> STM.succeed(a)
case _ => STM.retry
}
} yield a).commit
Queue
def take[A](queue: Queue[A]): UIO[A] =
(for {
q <- queue.tref.get
a <- q.dequeueOption match {
case Some((a, as)) =>
queue.tref.set(as) *> STM.succeed(a)
case _ => STM.retry
}
} yield a).commit
Queue
def take[A](queue: Queue[A]): UIO[A] =
(for {
q <- queue.tref.get
a <- q.dequeueOption match {
case Some((a, as)) =>
queue.tref.set(as) *> STM.succeed(a)
case _ => STM.retry
}
} yield a).commit
Queue
1 author (you)
14 minutes
13 lines of code
Your Queue using ZIO!
Atomically { Delete Your Actors }
Wrap Up
THANK YOU!
LEARN MORE
github.com/scalaz/scalaz-zio
gitter.im/scalaz/scalaz-zio
FOLLOW US
@jdegoes @wiemzin

More Related Content

PDF
The Death of Final Tagless
PDF
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
PDF
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
PDF
Scalaz 8 vs Akka Actors
PPTX
Scala - where objects and functions meet
PDF
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
PDF
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
PDF
One Monad to Rule Them All
The Death of Final Tagless
Blazing Fast, Pure Effects without Monads — LambdaConf 2018
ZIO Schedule: Conquering Flakiness & Recurrence with Pure Functional Programming
Scalaz 8 vs Akka Actors
Scala - where objects and functions meet
Side by Side - Scala and Java Adaptations of Martin Fowler’s Javascript Refac...
The Easy-Peasy-Lemon-Squeezy, Statically-Typed, Purely Functional Programming...
One Monad to Rule Them All

What's hot (20)

PDF
Refactoring Functional Type Classes
PDF
Hammurabi
PDF
Principled Error Handling with FP
PDF
Testing in the World of Functional Programming
PDF
PDF
Berlin meetup
PDF
Functor, Apply, Applicative And Monad
PPTX
ZIO: Powerful and Principled Functional Programming in Scala
PDF
Advanced Tagless Final - Saying Farewell to Free
PDF
Pure Future
PDF
Scalaz 8: A Whole New Game
PPTX
Java 7, 8 & 9 - Moving the language forward
PDF
Post-Free: Life After Free Monads
PDF
"Немного о функциональном программирование в JavaScript" Алексей Коваленко
PDF
Zio from Home
PPTX
Flying Futures at the same sky can make the sun rise at midnight
PDF
Monad Transformers In The Wild
PDF
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
PDF
Fiber supervision in ZIO
PDF
7 Habits For a More Functional Swift
Refactoring Functional Type Classes
Hammurabi
Principled Error Handling with FP
Testing in the World of Functional Programming
Berlin meetup
Functor, Apply, Applicative And Monad
ZIO: Powerful and Principled Functional Programming in Scala
Advanced Tagless Final - Saying Farewell to Free
Pure Future
Scalaz 8: A Whole New Game
Java 7, 8 & 9 - Moving the language forward
Post-Free: Life After Free Monads
"Немного о функциональном программирование в JavaScript" Алексей Коваленко
Zio from Home
Flying Futures at the same sky can make the sun rise at midnight
Monad Transformers In The Wild
Quark: A Purely-Functional Scala DSL for Data Processing & Analytics
Fiber supervision in ZIO
7 Habits For a More Functional Swift
Ad

Similar to Atomically { Delete Your Actors } (20)

PDF
Monadologie
PDF
Dip into Coroutines - KTUG Munich 202303
PPTX
Python Homework Help
KEY
Clojure workshop
PDF
Functional Programming with Groovy
PDF
Clojure functions examples
PDF
Implement the following sorting algorithms Bubble Sort Insertion S.pdf
PDF
ES6 patterns in the wild
PDF
b. (10 pts) Implement the rotate left method for AVL trees.c. (10 .pdf
PDF
Functional programming techniques in real-world microservices
PDF
Mutation testing: Too good to be true? (4Developers)
PPT
SDC - Einführung in Scala
KEY
ddd+scala
PDF
Swift, via "swift-2048"
PPTX
Python Tidbits
PDF
Mutation testing: Too good to be true? (Devoxx)
PDF
A Playful Introduction to Rx
PPTX
Python Homework Help
PDF
High-Performance Haskell
PPTX
Concurrent Application Development using Scala
Monadologie
Dip into Coroutines - KTUG Munich 202303
Python Homework Help
Clojure workshop
Functional Programming with Groovy
Clojure functions examples
Implement the following sorting algorithms Bubble Sort Insertion S.pdf
ES6 patterns in the wild
b. (10 pts) Implement the rotate left method for AVL trees.c. (10 .pdf
Functional programming techniques in real-world microservices
Mutation testing: Too good to be true? (4Developers)
SDC - Einführung in Scala
ddd+scala
Swift, via "swift-2048"
Python Tidbits
Mutation testing: Too good to be true? (Devoxx)
A Playful Introduction to Rx
Python Homework Help
High-Performance Haskell
Concurrent Application Development using Scala
Ad

More from John De Goes (20)

PDF
Error Management: Future vs ZIO
PDF
Scalaz Stream: Rebirth
PDF
Scalaz Stream: Rebirth
PDF
ZIO Queue
PDF
Orthogonal Functional Architecture
PDF
The Design of the Scalaz 8 Effect System
PDF
Streams for (Co)Free!
PDF
MTL Versus Free
PDF
Halogen: Past, Present, and Future
PDF
All Aboard The Scala-to-PureScript Express!
PDF
Getting Started with PureScript
PPTX
SlamData - How MongoDB Is Powering a Revolution in Visual Analytics
PDF
The Next Great Functional Programming Language
PPTX
The Dark Side of NoSQL
PDF
First-Class Patterns
PDF
Quirrel & R for Dummies
PDF
In-Database Predictive Analytics
PDF
Analytics Maturity Model
PDF
Rise of the scientific database
PDF
Fun with automata
Error Management: Future vs ZIO
Scalaz Stream: Rebirth
Scalaz Stream: Rebirth
ZIO Queue
Orthogonal Functional Architecture
The Design of the Scalaz 8 Effect System
Streams for (Co)Free!
MTL Versus Free
Halogen: Past, Present, and Future
All Aboard The Scala-to-PureScript Express!
Getting Started with PureScript
SlamData - How MongoDB Is Powering a Revolution in Visual Analytics
The Next Great Functional Programming Language
The Dark Side of NoSQL
First-Class Patterns
Quirrel & R for Dummies
In-Database Predictive Analytics
Analytics Maturity Model
Rise of the scientific database
Fun with automata

Recently uploaded (20)

PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PDF
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
PDF
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
PPTX
A Presentation on Artificial Intelligence
PDF
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
PPTX
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Machine learning based COVID-19 study performance prediction
PPT
“AI and Expert System Decision Support & Business Intelligence Systems”
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Modernizing your data center with Dell and AMD
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Electronic commerce courselecture one. Pdf
PDF
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
PPTX
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
PDF
Chapter 3 Spatial Domain Image Processing.pdf
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
The Rise and Fall of 3GPP – Time for a Sabbatical?
PPTX
Cloud computing and distributed systems.
PDF
Empathic Computing: Creating Shared Understanding
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Build a system with the filesystem maintained by OSTree @ COSCUP 2025
How UI/UX Design Impacts User Retention in Mobile Apps.pdf
A Presentation on Artificial Intelligence
Shreyas Phanse Resume: Experienced Backend Engineer | Java • Spring Boot • Ka...
VMware vSphere Foundation How to Sell Presentation-Ver1.4-2-14-2024.pptx
Mobile App Security Testing_ A Comprehensive Guide.pdf
Machine learning based COVID-19 study performance prediction
“AI and Expert System Decision Support & Business Intelligence Systems”
Digital-Transformation-Roadmap-for-Companies.pptx
Modernizing your data center with Dell and AMD
Reach Out and Touch Someone: Haptics and Empathic Computing
Electronic commerce courselecture one. Pdf
Bridging biosciences and deep learning for revolutionary discoveries: a compr...
Detection-First SIEM: Rule Types, Dashboards, and Threat-Informed Strategy
Chapter 3 Spatial Domain Image Processing.pdf
The AUB Centre for AI in Media Proposal.docx
The Rise and Fall of 3GPP – Time for a Sabbatical?
Cloud computing and distributed systems.
Empathic Computing: Creating Shared Understanding

Atomically { Delete Your Actors }

  • 1. atomically { delete your actors } John A. De Goes — @jdegoes Wiem Zine Elabidine — @wiemzin
  • 2. The Bank Heist The Hero Become a Hero!
  • 10. #1
  • 11. #1
  • 12. 12 def transfer(from: Account, to: Account, amount: Amount): Unit = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } }
  • 13. 13 def transfer(from: Account, to: Account, amount: Amount): Unit = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } }
  • 14. 14 def transfer(from: Account, to: Account, amount: Amount): Unit = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } }
  • 15. 15 def transfer(from: Account, to: Account, amount: Amount): Unit = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } }
  • 17. def transfer(from: Account, to: Account, amount: Amount) = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } } def transfer(from: Account, to: Account, amount: Amount) = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } } Thread 1 Thread 2 Race Condition Exploit
  • 18. def transfer(from: Account, to: Account, amount: Amount) = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } } def transfer(from: Account, to: Account, amount: Amount) = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } } Thread 1 Thread 2 Race Condition Exploit
  • 20. 20 class Account { var balance : Amount var accountID : AccountID var name : String val opened : Instant val status : AccountStatus val tpe : AccountType }
  • 21. balance 200,000 accountID 2816157231 ... ... Main Memory balance 200,000 accountID 2816157231 ... ... Core 1 Cache balance 200,000 accountID 2816157231 ... ... Core 2 Cache
  • 22. def transfer(from: Account, to: Account, amount: Amount) = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } } def transfer(from: Account, to: Account, amount: Amount) = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } } Thread 1 Thread 2 Stale Cache Exploit
  • 24. from.balance -= amount 4: getfield #2 7: invokevirtual #3 10: aload_2 11: invokevirtual #3 14: isub 15: invokestatic #4 18: dup_x1 19: putfield #2
  • 25. def transfer(from: Account, to: Account, amount: Amount) = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } } def transfer(from: Account, to: Account, amount: Amount) = { if (from.balance >= amount) { from.balance -= amount to.balance += amount } else { Thread.sleep(100) transfer(from, to, amount) } } Thread 1 Thread 2 Nonatomic Instruction Exploit
  • 29. abstract class Account { trait State { @volatile var balance: BigDecimal = _ } val state: State def modify[A](f: State => A): A = this.synchronized { val result = f(state) this.notifyAll() result } def await(): Unit = this.synchronized { this.wait() } }
  • 30. def transfer(from: Account, to: Account, amount: Amount): Unit = { var loop = true while (loop) { from.modify { state => if (state.balance >= amount.value) { state.balance -= amount.value to.modify(state => state.balance += amount.value) loop = false } else from.await() } }
  • 31. transfer(from, to, amount) transfer(to, from, amount) Thread 1 Thread 2 Deadlock Exploit
  • 35. class Account extends Actor { var balance = Amount.zero var todos = List.empty[(ActorRef, Message)] def receive = { case Deposit(amount) => balance = balance + amount.value sender ! Success(balance) todos.foreach { case (s, m) => self ! m } todos = Nil case v @ Withdraw(amount) => if (balance >= amount.value) { balance = balance - amount.value sender ! Success(balance) } else todos = (sender, v) :: todos case Balance => sender ! Success(balance) } }
  • 36. def transfer(from: ActorRef, to: ActorRef, amount: Amount)(implicit garbage: Timeout): Unit = (from ? Withdraw(amount)).flatMap { _ => to ? Deposit(amount) }
  • 38. for { john <- johnAccount ? Balance wiem <- wiemAccount ? Balance } yield if (wiem.value > john.value) transfer(wiem, cafe, amount) else transfer(john, cafe, amount) $70 $99
  • 39. for { john <- johnAccount ? Balance wiem <- wiemAccount ? Balance } yield if (wiem.value > john.value) transfer(wiem, cafe, amount) else transfer(john, cafe, amount) $70 $6,000,000 $99
  • 40. for { john <- johnAccount ? Balance wiem <- wiemAccount ? Balance } yield if (wiem.value > john.value) transfer(wiem, cafe, amount) else transfer(john, cafe, amount) $70 $6,000,000 $99
  • 41. $70 ~$6m for { john <- johnAccount ? Balance wiem <- wiemAccount ? Balance } yield if (wiem.value > john.value) transfer(wiem, cafe, amount) else transfer(john, cafe, amount) $10 $99
  • 42. $70 -$60 for { john <- johnAccount ? Balance wiem <- wiemAccount ? Balance } yield if (wiem.value > john.value) transfer(wiem, cafe, amount) else transfer(john, cafe, amount) $99
  • 46. 46 def transfer(from : TRef[Account], to : TRef[Account], amount : Amount): UIO[Unit] = atomically { for { balance <- from.get _ <- check(balance >= amount) _ <- from.update(_ - amount) _ <- to.update(_ + amount) } yield () }
  • 47. 47 def transfer(from : TRef[Account], to : TRef[Account], amount : Amount): UIO[Unit] = atomically { for { balance <- from.get _ <- check(balance >= amount) _ <- from.update(_ - amount) _ <- to.update(_ + amount) } yield () }
  • 48. 48 def transfer(from : TRef[Account], to : TRef[Account], amount : Amount): UIO[Unit] = atomically { for { balance <- from.get _ <- check(balance >= amount) _ <- from.update(_ - amount) _ <- to.update(_ + amount) } yield () }
  • 49. 49 def transfer(from : TRef[Account], to : TRef[Account], amount : Amount): UIO[Unit] = atomically { for { balance <- from.get _ <- check(balance >= amount) _ <- from.update(_ - amount) _ <- to.update(_ + amount) } yield () }
  • 50. 50 def transfer(from : TRef[Account], to : TRef[Account], amount : Amount): UIO[Unit] = atomically { for { balance <- from.get _ <- check(balance >= amount) _ <- from.update(_ - amount) _ <- to.update(_ + amount) } yield () }
  • 51. 51 def transfer(from : TRef[Account], to : TRef[Account], amount : Amount): UIO[Unit] = atomically { for { balance <- from.get _ <- check(balance >= amount) _ <- from.update(_ - amount) _ <- to.update(_ + amount) } yield () }
  • 54. STM: Software Transactional Memory Provides the ability to atomically commit a series of reads and writes to transactional memory when a set of conditions is satisfied. STM
  • 55. STM ...Op 2Op 1 Op n Final State Initial State commit failure - rollback retry - rollback complete complete
  • 56. STM STM[E, A] A transaction, which models reads & writes, and can fail, retry, or succeed. TRef[A] A transactional reference, which is read & written inside STM transactions.
  • 57. STM[E, A] Succeed with a value of type A Fail with an error of type E STM
  • 58. STM STM[E, A] A transaction, which models reads & writes, and can fail, retry, or succeed. TRef[A] A transactional reference, which is read & written inside STM transactions.
  • 61. trait TRef[A] { val get: STM[Nothing, A] def set(newValue: A): STM[Nothing, Unit] def update(f: A => A): STM[Nothing, A] def modify[B](f: A => (B, A)): STM[Nothing, B] } object TRef { def make[A](a: A): STM[Nothing, TRef[A]] } TRef
  • 62. trait TRef[A] { val get: STM[Nothing, A] def set(newValue: A): STM[Nothing, Unit] def update(f: A => A): STM[Nothing, A] def modify[B](f: A => (B, A)): STM[Nothing, B] } object TRef { def make[A](a: A): STM[Nothing, TRef[A]] } TRef
  • 63. trait TRef[A] { val get: STM[Nothing, A] def set(newValue: A): STM[Nothing, Unit] def update(f: A => A): STM[Nothing, A] def modify[B](f: A => (B, A)): STM[Nothing, B] } object TRef { def make[A](a: A): STM[Nothing, TRef[A]] } TRef
  • 64. trait TRef[A] { val get: STM[Nothing, A] def set(newValue: A): STM[Nothing, Unit] def update(f: A => A): STM[Nothing, A] def modify[B](f: A => (B, A)): STM[Nothing, B] } object TRef { def make[A](a: A): STM[Nothing, TRef[A]] } TRef
  • 65. trait TRef[A] { val get: STM[Nothing, A] def set(newValue: A): STM[Nothing, Unit] def update(f: A => A): STM[Nothing, A] def modify[B](f: A => (B, A)): STM[Nothing, B] } object TRef { def make[A](a: A): STM[Nothing, TRef[A]] } TRef
  • 66. trait STM[+E, +A] { def commit: IO[E, A] = STM.atomically { this } } object STM { def atomically[E, A](stm: STM[E, A]): IO[E, A] } STM - Commit
  • 67. val hello: STM[Nothing, String] = STM.succeed("Welcome to Scalar") STM - Succeed
  • 68. val sumBalances: STM[Nothing, Int] = balance1.get.flatMap(a => balance2.get.map(b => a + b)) STM - map & flatMap
  • 69. val sumBalances: STM[Nothing, Int] = for { a <- balance1.get b <- balance2.get } yield a + b STM - map & flatMap
  • 70. STM - Fail def debit(sender: TRef[Amount], amount: Amount): STM[String, Amount] = for { balance <- sender.update(_ - amount) _ <- if (balance < 0) STM.fail("Insufficient funds") else STM.succeed(()) } yield balance
  • 71. def debitSuccess(sender: TRef[Amount], amount: Amount): STM[Nothing, Boolean] = debit(sender, amount).fold(_ => false, _ => true) STM - Fold
  • 72. def debitWithBalance(sender: TRef[Amount], amount: Amount): STM[Nothing, Amount] = debit(sender, amount) .foldM( _ => sender.get, _ => sender.get) STM - FoldM
  • 73. def awaitTicket(tref: TRef[Option[Ticket]]): STM[Nothing, Ticket] = for { option <- tref.get ticket <- option match { case None => STM.retry case Some(ticket) => STM.succeed(ticket) } } yield ticket STM - Retry
  • 74. def tripTickets: STM[Nothing, (Ticket, Ticket)] = awaitTicket(toWarsaw) zip awaitTicket(toHome) STM - Zip
  • 75. def bid(price : Amount, org : TRef[TravelCompany]): STM[Nothing, Ticket] = for { v <- org.get _ <- STM.check(v.availTix.exists(_.price <= price)) ticket = findCheapest(v, price) _ <- org.update(_.removeTix(ticket)) } yield ticket STM - Check
  • 76. STM - Filter def bid(price : Amount, org : TRef[TravelCompany]): STM[Nothing, Ticket] = for { v <- org.get.filter(_.availTix.exists(_.price <= price)) ticket = findCheapest(v, price) _ <- org.update(_.removeTix(ticket)) } yield ticket
  • 77. val trainOrAirplane: STM[Nothing, Ticket] = bid(25.00, Railway) orElse bid(50.00, Airline) STM - Choice
  • 78. def checkIn(passengers: TRef[List[Person]]): STM[Nothing, Person] = for { head <- passengers.get.collect { case head :: tail => head } _ <- passengers.update(_.drop(1)) } yield head STM - Collect
  • 79. STM STM STM STM Composable Easy to Reason About def transfer(from : TRef[Account], to : TRef[Account], amount : Amount): UIO[Unit] = atomically { for { balance <- from.get _ <- check(balance >= amount) _ <- from.update(_ - amount) _ <- to.update(_ + amount) } yield () }
  • 81. Semaphore Thread #2 holds 1 permit Thread #1 holds 2 permits Thread #3 waits for 4 permits Semaphore 6 permits
  • 84. Semaphore def makeSemaphore(n: Int): UIO[Semaphore] = TRef.make(n).commit
  • 85. Semaphore def acquire(semaphore: Semaphore, n: Int): UIO[Unit] = (for { value <- semaphore.get _ <- STM.check(value >= n) _ <- semaphore.set(value - n) } yield ()).commit
  • 86. Semaphore def acquire(semaphore: Semaphore, n: Int): UIO[Unit] = (for { value <- semaphore.get _ <- STM.check(value >= n) _ <- semaphore.set(value - n) } yield ()).commit
  • 87. Semaphore def acquire(semaphore: Semaphore, n: Int): UIO[Unit] = (for { value <- semaphore.get _ <- STM.check(value >= n) _ <- semaphore.set(value - n) } yield ()).commit
  • 88. Semaphore def acquire(semaphore: Semaphore, n: Int): UIO[Unit] = (for { value <- semaphore.get _ <- STM.check(value >= n) _ <- semaphore.set(value - n) } yield ()).commit
  • 89. Semaphore def release(semaphore: Semaphore, n: Int): UIO[Unit] = semaphore.update(_ + n).commit
  • 90. Semaphore 1 author (you!) 10 minutes 8 lines of code Your Semaphore using ZIO!
  • 91. Promise Unset Set Wait Wait Continue Continue
  • 93. Promise type Promise[A] = TRef[Option[A]]
  • 95. Promise def complete[A](promise: Promise[A], v: A): UIO[Boolean] = (for { value <- promise.get change <- value match { case Some(_) => STM.succeed(false) case None => promise.set(Some(v)) *> STM.succeed(true) } } yield change).commit
  • 96. Promise def complete[A](promise: Promise[A], v: A): UIO[Boolean] = (for { value <- promise.get change <- value match { case Some(_) => STM.succeed(false) case None => promise.set(Some(v)) *> STM.succeed(true) } } yield change).commit
  • 97. Promise def complete[A](promise: Promise[A], v: A): UIO[Boolean] = (for { value <- promise.get change <- value match { case Some(_) => STM.succeed(false) case None => promise.set(Some(v)) *> STM.succeed(true) } } yield change).commit
  • 98. Promise def complete[A](promise: Promise[A], v: A): UIO[Boolean] = (for { value <- promise.get change <- value match { case Some(_) => STM.succeed(false) case None => promise.set(Some(v)) *> STM.succeed(true) } } yield change).commit
  • 99. Promise def await[A](promise: Promise[A]): UIO[A] = promise.get.collect { case Some(a) => a }.commit
  • 100. Promise 1 author (you!) 10 minutes 8 lines of code Your Promise using ZIO!
  • 101. Queue Empty Queue Capacity: 6 Full Queue Capacity: 6 Offer (Continue) Take (Wait) Offer (Wait) Take (Continue)
  • 103. Queue case class Queue[A]( capacity : Int, tref : TRef[ScalaQueue[A]])
  • 104. Queue def makeQueue[A](capacity: Int): UIO[Queue[A]] = TRef.make(ScalaQueue.empty[A]).commit .map(Queue(capacity, _))
  • 105. Queue def offer[A](queue: Queue[A], a: A): UIO[Unit] = (for { q <- queue.tref.get _ <- STM.check(q.length < queue.capacity) _ <- queue.tref.update(_ enqueue a) } yield ()).commit
  • 106. Queue def offer[A](queue: Queue[A], a: A): UIO[Unit] = (for { q <- queue.tref.get _ <- STM.check(q.length < queue.capacity) _ <- queue.tref.update(_ enqueue a) } yield ()).commit
  • 107. Queue def offer[A](queue: Queue[A], a: A): UIO[Unit] = (for { q <- queue.tref.get _ <- STM.check(q.length < queue.capacity) _ <- queue.tref.update(_ enqueue a) } yield ()).commit
  • 108. Queue def offer[A](queue: Queue[A], a: A): UIO[Unit] = (for { q <- queue.tref.get _ <- STM.check(q.length < queue.capacity) _ <- queue.tref.update(_ enqueue a) } yield ()).commit
  • 109. Queue def take[A](queue: Queue[A]): UIO[A] = (for { q <- queue.tref.get a <- q.dequeueOption match { case Some((a, as)) => queue.tref.set(as) *> STM.succeed(a) case _ => STM.retry } } yield a).commit
  • 110. Queue def take[A](queue: Queue[A]): UIO[A] = (for { q <- queue.tref.get a <- q.dequeueOption match { case Some((a, as)) => queue.tref.set(as) *> STM.succeed(a) case _ => STM.retry } } yield a).commit
  • 111. Queue def take[A](queue: Queue[A]): UIO[A] = (for { q <- queue.tref.get a <- q.dequeueOption match { case Some((a, as)) => queue.tref.set(as) *> STM.succeed(a) case _ => STM.retry } } yield a).commit
  • 112. Queue def take[A](queue: Queue[A]): UIO[A] = (for { q <- queue.tref.get a <- q.dequeueOption match { case Some((a, as)) => queue.tref.set(as) *> STM.succeed(a) case _ => STM.retry } } yield a).commit
  • 113. Queue 1 author (you) 14 minutes 13 lines of code Your Queue using ZIO!