programming paradigms for concurrency

52
Programming Paradigms for Concurrency Lecture 9 Part III – Message Passing Concurrency

Upload: holly-case

Post on 02-Jan-2016

55 views

Category:

Documents


2 download

DESCRIPTION

Programming Paradigms for Concurrency. Lecture 9 Part III – Message Passing Concurrency. Classical Shared Memory Concurrency - The Downsides. shared memory typically implies physically shared memory l ocks: the “ goto statements ” of concurrency - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: Programming Paradigms for Concurrency

Programming Paradigms for Concurrency

Lecture 9

Part III – Message Passing Concurrency

Page 2: Programming Paradigms for Concurrency

Classical Shared Memory Concurrency -The Downsides

• shared memory typically implies physically shared memory

• locks: the “goto statements” of concurrency

• OS threads are resource-hungry and context-switching is expensivenumber of threads = number of available cores

≠ number of logical tasks

no natural adaptation to distributed computing

reasoning about programs is (even more) difficult

Page 3: Programming Paradigms for Concurrency

Message Passing Concurrency• no shared-memory (in its pure form)

+ some classes of concurrency errors avoided by design+ natural programming model for distributed architectures- less efficient on shared-memory architectures:

data must be copied before sending• synchronization between processes is explicit

+ reasoning about program behavior is simplified- some say: it’s harder to parallelize a sequential program using

MP• higher level of abstraction

+ decoupling between computation tasks and physical threads possible -> event-based programming

Page 4: Programming Paradigms for Concurrency

Message Passing Paradigms

Two important categories of MP paradigms:

1. Actor or agent-based paradigms- unique receivers: messages are sent directly from

one process to another

2. Channel-based paradigms– multiple receivers: messages are sent to channels

that are shared between processesWe will look at one programming language in each category.

Page 5: Programming Paradigms for Concurrency

Reading Material

• Actors in Scala. Haller and Sommers, Artima, to appear 2011 (preprint available).

• Concurrent Programming in ML. Reppy, Cambridge University Press, 1999.

• Communicating and Mobile Systems: The Pi Calculus. Milner, Cambridge University Press, 1999.

Additional material will be posted on the lecture web site.

Page 6: Programming Paradigms for Concurrency

The Actor Paradigm

Actors are the object-oriented approach to concurrency

“everything is an actor”

actor = object + logical thread

Page 7: Programming Paradigms for Concurrency

A Brief History of Actors• Hewitt, Bishop, Steiger 1973: proposal of actors• Greif 1975: operational semantics• Baker, Hewitt 1977: axiomatic semantics• Lieberman, Theriault 1981: Act-1 language• Clinger 1981: denotational semantics• Agha 1986: transition semantics• …• Armstrong et al. 1990s: Erlang language• …• Haller, Odersky 2007: actors in the Scala language

Page 8: Programming Paradigms for Concurrency

Actors in a Nutshell• actors perform local computations and communicate via MP• communication is

– asynchronous– buffered (unordered in theory but FIFO in practice)– over unique-receiver channels (mailboxes)– restricted to “known” actors

• computation is – even-driven: react to incoming messages– dynamically create other actors– send messages to other actors– dynamically change behavior

• languages supporting actor-based concurrency– Erlang– Salsa– Scala– many implementations in form of libraries

A

A

B

Page 9: Programming Paradigms for Concurrency

The Scala Language

• unifies object-oriented and functional programming concepts– mixin class composition– higher-order functions– algebraic data types + pattern matching– closures

• statically typed (type system based on System F)• interoperable with Java (compiles to the JVM)• enables embedding of rich domain-specific languages• Open-source: available from http://www.scala-lang.org

Page 10: Programming Paradigms for Concurrency

Scala Actors[Haller, Odersky, 2007]

• Scala library extension for high-level concurrent programming – part of the Scala standard library

• pair of message receive operations (receive/react)– allows trade-off between efficiency and flexibility– react enables event-based programming without inversion of

control• message handlers as first-class partial functions

– enables extension of actor behavior• wide adoption

– Lift web framework– Twitter

Page 11: Programming Paradigms for Concurrency

Scala Actors through an Example

Page 12: Programming Paradigms for Concurrency

Actor Chat

ChatRoom

session

private state

ChatClient

ChatClient

ChatClient

ChatClient

User subscribe to a chat room to receive chat messages.The chat room maintains a session of subscribers.

Page 13: Programming Paradigms for Concurrency

Defining Actors

import scala.actors.Actor

class ChatRoom extends Actor {def act() {

// the actor’s behavior}

}

Actors are regular Scala objects that extend the Actor trait.

Page 14: Programming Paradigms for Concurrency

Creating and Starting Actors

object main extends Application {val chatRoom = new ChatRoomchatRoom.start() // calls chatRoom.act

}

Page 15: Programming Paradigms for Concurrency

Communication in Actor Chat

ChatRoom

session

private state

All communication is via message passing.

Subscribeuser: User

Unsubscribeuser: User

UserPostuser: Userpost: Post

ChatClient

private state

Page 16: Programming Paradigms for Concurrency

Messages

case class User(name: String)case class Post(msg: String)

abstract class Msgcase class Subscribe(user: User) extends Msgcase class Unsubscribe(user: User) extends Msg

case class UserPost(user: User, post: Post) extends Msg

Any Scala object can serve as a messageGood practice: use immutable case classes

Page 17: Programming Paradigms for Concurrency

Defining the act Methodclass ChatRoom extends Actor {

def act() { while (true) {

receive { case Subscribe(user) =>

// handle subscription message case Unsubscribe(user) =>

// handle unsubscribe request case UserPost(user, post) =>

// handle a post from a user}

}}

}• Actor reacts to incoming message via the receive method of the Actor trait

• receive takes a partial function f as argument

Defines a closure f

Page 18: Programming Paradigms for Concurrency

Defining the act Methodclass ChatRoom extends Actor {

def act() { while (true) {

receive { case Subscribe(user) =>

// handle subscription message case Unsubscribe(user) =>

// handle unsubscribe request case UserPost(user, post) =>

// handle a post from a user}

}}

}• f maps incoming messages to the corresponding action performed by the actor

• f is matched against the messages in the actor’s mailbox

Defines a closure f

Page 19: Programming Paradigms for Concurrency

Defining the act Methodclass ChatRoom extends Actor {

def act() { while (true) {

receive { case Subscribe(user) =>

// handle subscription message case Unsubscribe(user) =>

// handle unsubscribe request case UserPost(user, post) =>

// handle a post from a user}

}}

}• the first message on which f is defined is received• unmatched messages remain in the mailbox• if no message matches, receive blocks until a matching message is received

Defines a closure f

Page 20: Programming Paradigms for Concurrency

Inplace Actor Definitionsval chatRoom = actor { while (true) {

receive { case Subscribe(user) =>

// handle subscription message case Unsubscribe(user) =>

// handle unsubscribe request case UserPost(user, post) =>

// handle a post from a user}

}}

closure defines act method of chatRoom

Page 21: Programming Paradigms for Concurrency

Handling Subscriptionsvar session = Map.empty[Actor, Actor]while (true) { receive { case Subscribe(user) => val sessionHandler = actor {

while (true) { self.receive { case Post(msg) => // forward message to

subscribers } }

} session = session + (subsriber -> sessionHandler)

// handle UserPost and Unsubscribe message }}

Page 22: Programming Paradigms for Concurrency

Sending Messages

val chatRoom = new ChatRoomchatRoom ! Subscribe(User("Bob"))

Method ! asynchronously sends a message to the recipient.

Page 23: Programming Paradigms for Concurrency

Sending Messages

val chatRoom = new ChatRoomchatRoom !? Subscribe(User("Bob")) match { case response: String => println(response)}

Method !? asynchronously sends a message and then blocks until a reply message has been received from the recipient.

Page 24: Programming Paradigms for Concurrency

Handling Subscriptionsvar session = Map.empty[Actor, Actor]while (true) { receive {

case Subscribe(user) => val subscriber = sender val sessionHandler = actor {

while (true) { self.receive {

case Post(msg) => subscriber ! Post(msg) } }

} session = session + (subscriber -> sessionHandler) reply(“subscribed: “ + user.name) // handle UserPost and Unsubscribe message }}

address of the sender of matched message

sends message to sender

Page 25: Programming Paradigms for Concurrency

Message Timeouts with receiveWithinvar session = Map.empty[Actor, Actor]while (true) { receive {

case Subscribe(user) => val (subscriber, room) = (sender, self) val sessionHandler = actor {

while (true) { self.receiveWithin (1800 * 1000) {

case Post(msg) => for (key <- session.keys; if key != subscriber)

session(key) ! Post(msg) case TIMEOUT =>

room ! Unsubscribe(user) case ‘die => self.exit()

} }

} ... }}

Page 26: Programming Paradigms for Concurrency

Processing Remaining Messagesvar session = Map.empty[Actor, Actor]while (true) { receive { // handle Subscribe message

case Unsubscribe(user) => session(sender) ! ‘die session = session – sender

case UserPost(user, msg) => session(sender) ! msg

}}

Page 27: Programming Paradigms for Concurrency

Remote Actors

Page 28: Programming Paradigms for Concurrency

Remote Actors

Remote actors enable transparent communication between Scala actors over networks

import scala.actors.Actor._import scala.actors.remote.RemoteActor._

class ChatRoom(port: Int) extends Actor {def act() {

alive(port) register(‘chatRoom, self) // ...

}}

attach this actor to given port

register given symbol with given actor

Page 29: Programming Paradigms for Concurrency

Remote Actors

Remote actors enable transparent communication between Scala actors over networks

import scala.actors.Actor._import scala.actors.remote.RemoteActor._

class ChatClient(chatURL: String, chatPort: Int)extends Actor {

def act() { val node = Node(chatURL, chatPort) val chatRoom = select(node, ‘chatRoom) chatRoom ! Subscribe(User(“Bob”)) // ...

}}

obtain local interface to

remote actor

Page 30: Programming Paradigms for Concurrency

Event-Based Programming

Page 31: Programming Paradigms for Concurrency

Event-Based Programming

receive binds an actor to a dedicated JVM thread

if receive blocks, so does the dedicated thread of the blocking actor

each actor (blocking or nonblocking) needs its own thread scales badly with the number of actors, since JVM threads

are resource hungry

Scala actors provide an alternative to receive, which enables event-based programming.

Page 32: Programming Paradigms for Concurrency

receive vs. react

react behaves like receive but with a different waiting strategy.

if react blocks, the actor is suspended and its dedicated thread released for processing events of other actors

event-based programming with react scales to large numbers of actors

Page 33: Programming Paradigms for Concurrency

Event-Based Programming with react

class ChatRoom extends Actor {def act() { while (true) {

react { case Subscribe(user) =>

// handle subscription message case Unsubscribe(user) =>

// handle unsubscribe request case UserPost(user, post) =>

// handle a post from a user}

}}

}

never returns!

Page 34: Programming Paradigms for Concurrency

Event-Based Programming with react

class ChatRoom extends Actor {def act(): Unit = { react {

case Subscribe(user) => // handle subscription message act()case Unsubscribe(user) => // handle unsubscribe request act()case UserPost(user, post) => // handle a post from a user act()

}}

}

closure must encompass full continuation of the actor

Page 35: Programming Paradigms for Concurrency

Or Simpler...class ChatRoom extends Actor {

def act() { loop {

react { case Subscribe(user) =>

// handle subscription message case Unsubscribe(user) =>

// handle unsubscribe request case UserPost(user, post) =>

// handle a post from a user}

}}

}

special combinators for composing react blocks

behaves likewhile (true) { receive { ... }}

Page 36: Programming Paradigms for Concurrency

Lightweight Execution Environment

Actors (many)

Worker threads (few)Task queue

Page 37: Programming Paradigms for Concurrency

Creating Actors

T1

T2

T3

actor { // body}

closure => T3

Events generate tasks

Page 38: Programming Paradigms for Concurrency

Suspend in Event Mode

def react(f: PartialFunction[Any, Unit]): Nothing = { mailbox.dequeueFirst(f.isDefinedAt) match { case None => continuation = f; suspended = true case Some(msg) => ... } throw new SuspendActorException}

...react{ case Msg(x) => // handle msg}

Task TiException

1) unwinds stack of actor/worker thread

2) finishes current task

Page 39: Programming Paradigms for Concurrency

Resume in Event Mode

Ti+1

{ case Msg(x) => // handle msg}

w.t. executes Ti

Actor a suspended with

Ti+2Task Ti:...a ! Msg(42)...Task Ti+2: .apply(Msg(42))

Page 40: Programming Paradigms for Concurrency

Advanced Example

Page 41: Programming Paradigms for Concurrency

Code Hot Swapping

• payload of a message can be arbitrary scala object

• actors can send new behavior to other actors• enables dynamic reconfiguration of actor

behavior

Page 42: Programming Paradigms for Concurrency

Hot Swapping Servercase class HotSwap(code: PartialFunction[Any, Unit])

class Server extends Actor { def act = loop { react { genericBase orElse actorBase } }

private def actorBase: PartialFunction[Any, Unit] =  hotswap getOrElse body 

private var hotswap: Option[PartialFunction[Any, Unit]] = None

private val genericBase: PartialFunction[Any, Unit] = { case HotSwap(code) => hotswap = code   }    

def body: PartialFunction[Any, Unit] = { ... } }

Page 43: Programming Paradigms for Concurrency

Hot Swapping Servercase class HotSwap(code: PartialFunction[Any, Unit])

class Server extends Actor { ... private val genericBase: PartialFunction[Any, Unit] = { case HotSwap(code) => hotswap = Some(code)   }    }

case object Ping...val server = new Serverserver.startserver ! HotSwap({case Ping => println(“Ping”)})server ! Ping

Hot swapping the server:

Page 44: Programming Paradigms for Concurrency

Exception Handling and Monitoring

Page 45: Programming Paradigms for Concurrency

Actors and Exceptions

• act method of an actor may throw exceptions• unhandled exceptions do not seep out of the

throwing actor • but unhandled exceptions terminate the

throwing actor other actors might wait for messages from

actors that died silently may cause deadlocks that are hard to debug

Page 46: Programming Paradigms for Concurrency

Simple Exception Handlingobject A extends Actor { def act() { react { case 'hello => throw new Exception("Error!") } }

override def exceptionHandler = { case e: Exception => println(e.getMessage()) }}

scala> A.start()scala> A ! 'helloError!

Page 47: Programming Paradigms for Concurrency

Monitoring Actors

Actor library provides special support for monitoring the life cycle of (a group of) actors:

• exit(reason): terminates this actor• link(otherActor): links this actor with otherActor

In case of termination/uncaught exceptions, exit is called implictely.Calls to exit are propagated to all linked actors.

Enables• error propagation• delegated error handling• fault tolerance

Page 48: Programming Paradigms for Concurrency

Error Propagationobject Master extends Actor { def act() { Slave ! 'doWork receive { case 'done => throw new Exception("Master crashed") } }}

object Slave extends Actor { def act() { link(Master) while (true) { receive { case 'doWork => println("Done") reply('done) } } }}

Slave terminates, if Master crashes or terminates

Page 49: Programming Paradigms for Concurrency

Delegated Error Handlingval a = actor { receive { case 'start => val somethingBadHappened = ... // some error condition if (somethingBadHappened) throw new Exception("Error!") println("Nothing bad happened") }}val b = actor { self.trapExit = true link(a) a ! 'start receive { case Exit(from, reason) if from == a => println("Actor 'a' terminated because of " + reason) }}

calls to a’s exit method are converted to Exit messages in b’s mailbox

Page 50: Programming Paradigms for Concurrency

More Features of Scala Actors

• Futures• Pluggable schedulers• Unique references: race-free imperative actors• ...

Page 51: Programming Paradigms for Concurrency

Further Reading

For more details about Scala actors see• Actors in Scala. Haller and Sommers, Artima,

to appear 2011 (preprint available)• Tutorial on Scala web page• Scala actors: Unifying thread-based and event-

based programming. Haller and Odersky, Theoretical Computer Science, 2008

• Dissertation of Philipp Haller, EPFL, Switzerland, 2010

Page 52: Programming Paradigms for Concurrency

Outlook

• next week – Channel-based message passing concurrency– Concurrent ML: first-class synchronous events

• after Christmas– formal semantics of MP programs: process calculi– formal reasoning about MP programs