SoCal Workshop Feb 2, 2008 Outline Tasks: Language Support for The problem Event-Driven Programming Example: Threads vs. Events The TaskJava language Related work Jeffrey Fischer, Rupak Majumdar, and Todd Millstein Applications UCLA Computer Science Dept. The Problem The Problem Current solutions for interleaved computation Current solutions for interleaved computation suffer a number of drawbacks suffer a number of drawbacks Multi-threaded servers Event-driven servers Introduce concurrency Must manually translate to continuation-passing style Performance issues when using large number of threads Difficult to follow control flow May lead to bugs! Not suitable for some contexts (embedded systems, some OS kernels, business processes) Cannot easily take advantage of language features such as inheritance and exceptions Our solution: TaskJava Code layout: threads vs. events High performance concurrency while preserving Multithreaded version program structure. Extension to Java foo() { X = blockingCall(buf); Programming model: Tasks “look like” threads, but if (x==OK) {...} run like events else {...} } Blocks in Compiler performs modular CPS translation operating system Jeff Fischer, UCLA 1
SoCal Workshop Feb 2, 2008 Code layout: threads vs. events Code layout: threads vs. events Multithreaded version Event-driven version Multithreaded version Event-driven version foo() { foo() { nonblockCall(buf, nonblockCall(buf, foo2); foo2); foo() { foo() { } } X = X = blockingCall(buf); blockingCall(buf); Initiate call Scheduler runs other events Scheduler runs other events and return if (x==OK) {...} Continuation if (x==OK) {...} else {...} else {...} } } foo2(x) { foo2(x) { if (x==OK) {...} if (x==OK) {...} else {...} else {...} } } Scheduler runs continuation Bugs in event-driven code Bugs in event-driven code nonblockCall(buf, cnt) { nonblockCall(buf, cnt) { nonblockCall(buf, cnt) { nonblockCall(buf, cnt) { foo() { foo() { ... ... ... ... nonblockCall(buf, nonblockCall(buf, reg(e 1 , e 2 , nb2); reg(e 1 , e 2 , nb2); reg(e 1 , e 2 , nb2); reg(e 1 , e 2 , nb2); foo2); foo2); } } } } } } Register events Scheduler runs other events Scheduler runs other events and callback with nb2(event) { nb2(event) { foo2(result) { nb2(event) { nb2(event) { foo2(result) { scheduler nb2(event) { nb2(event) { if (result==OK){ if (event==e 1 ) { if (event==e 1 ) { if (event==e 1 ) { if (result==OK){ if (event==e 1 ) { if (event==e 1 ) { if (event==e 1 ) { ... ... ... ... ... ... ... ... } else { cnt(OK); cnt(OK); cnt(OK); } else { cnt(OK); cnt(OK); cnt(OK); } } ... } } ... } } } else cnt(NOT_OK); else cnt(NOT_OK); else cnt(NOT_OK); } else cnt(NOT_OK); else cnt(NOT_OK); else cnt(NOT_OK); } } } } } } } } Bugs in event-driven code Code layout: TaskJava Source code Compiled code nonblockCall(buf, cnt) { nonblockCall(buf, cnt) { foo() { ... ... nonblockCall(buf, reg(e 1 , e 2 , nb2); reg(e 1 , e 2 , nb2); foo2); foo() { } } } blockingCall(buf, foo2); Lost continuation async foo() { } bug if nb2 forgets Scheduler runs other events X = to call foo2 blockingCall(buf); nb2(event) { foo2(result) { nb2(event) { nb2(event) { Scheduler runs other events if (x==OK) {...} if (result==OK){ if (event==e 1 ) { if (event==e 1 ) { if (event==e 1 ) { else {...} ... ... ... ... } } else { cnt(OK); cnt(OK); cnt(OK); foo2(x) { } if (x==OK) {...} ... } } } else cnt(NOT_OK); else cnt(NOT_OK); else cnt(NOT_OK); else {...} Lost exception bug if } } } } } nb2 forgets to pass error to foo2 Jeff Fischer, UCLA 2
SoCal Workshop Feb 2, 2008 TaskJava features TaskJava features Asychronous Class WriteTask implements Task { Class WriteTask implements Task { method call The Task void run() { ... void run() { ... interface looks do { write(buffer); do { write(buffer); like Java’s } while (buffer.hasRemaining()); ... } while (buffer.hasRemaining()); ... Thread interface } } Method async void write(CharBuffer buffer) async void write(CharBuffer buffer) annotation throws IOException { throws IOException { Event e = wait (channel, Event.WRITE, Event e = wait (channel, Event.WRITE, Event.ERROR); Event.ERROR); switch (e.type()) { switch (e.type()) { case Event.WRITE: ch.write(buffer); break; case Event.WRITE: ch.write(buffer); break; case Event.ERROR: throw new IOException(); case Event.ERROR: throw new IOException(); } } } } } } TaskJava features Translating TaskJava to Java Class WriteTask implements Task { Bodies of Task run and async methods split void run() { ... into continuation passing style do { write(buffer); } while (buffer.hasRemaining()); ... Explicit callbacks introduced by compiler Yield until one } of the events async void write(CharBuffer buffer) occurs throws IOException { wait (Set) → Event e = wait (channel, Event.WRITE, <Scheduler>.register(Set, new cb(...)) Event.ERROR); switch (e.type()) { case Event.WRITE: ch.write(buffer); break; Scheduler class provided as option to case Event.ERROR: throw new IOException(); compiler } Errors signaled } } by throwing exceptions Formalizing TaskJava Translating TaskJava: the tricky bits Local variables Defined semantic rules and type system Move to heap if used across async calls Nested asynchronous calls Prove key properties: Introduce temporary variables Type soundness No lost continuations or lost exceptions Loops Flatten and use switch statement to simulate goto’s Translation to Java Exceptions Separate callbacks for error control flow Jeff Fischer, UCLA 3
SoCal Workshop Feb 2, 2008 Formalizing TaskJava Formalizing TaskJava e → e’ e → e’ Program Program Current task’s Current task’s Evaluator Evaluator expr expr E[Wait (es )] E[Wait(es)] E[ ε ] E[ ε ] Scheduler state Scheduler state S → S’ S → S’ Non-deterministic Non-deterministic {(E 1 [], es 1 ), {(E 1 [], es 1 ), Scheduler (E 2 [], es 2 ),…} Scheduler (E 2 [], es 2 ),…} Formalizing TaskJava Formalizing TaskJava e → e’ e → e’ Program Program Current task’s Current task’s Evaluator Evaluator expr expr E[Wait (es )] E[Wait (es )] E[ ε ] E[ ε ] Scheduler state Scheduler state S → S’ S → S’ Non-deterministic Non-deterministic {(E 1 [], es 1 ), {(E 1 [], es 1 ), Scheduler (E 2 [], es 2 ),…} Scheduler (E 2 [], es 2 ),…} Formalizing TaskJava So, what’s new? Type system tracks blocking methods e → e’ Program Current task’s Compiler translates only blocking methods Evaluator expr Safe Coexists with existing libraries E[Wait (es )] E[ ε ] Compiler generates scheduler-independent Scheduler state S → S’ Non-deterministic code {(E 1 [], es 1 ), Scheduler (E 2 [], es 2 ),…} Case study: web server with pure-event and thread-pooled schedulers Jeff Fischer, UCLA 4
SoCal Workshop Feb 2, 2008 Related work Related work: Cooperative Multitasking User-level threads through stack Cooperative multitasking manipulation Functional programming languages Many implementations E.g. [Engelschall 00], [von Behren, et. al. 03] Other language approaches Does not work for most VM-based languages Scheduler is fixed Related work: Functional programming Related work: Functional programming languages languages Scheme: avoid inversion of control issues in web “Continuations from generalized stack programming inspection” [Pettyjohn, et al. 05] First-class continuations [Graunke 01], [Queinnec 03] Whole program CPS transformations [Matthews, et. al. 04] Implements Scheme continuations on .NET VM Concurrent ML [Reppy 91] Uses exception handlers and stack copying User-level pre-emptive threads and first class events Built on top of continuations Related work: other language approaches Applications of TaskJava TAME: C++ Library for event-driven programming TaskJava in embedded environments [Krohn and Kohler 06] Implements a localized CPS-transform via templates Emphasizes flexibility over safety W eb applications MAWL [Ball and Aktins 99] DSL for Web applications Scala actor library Programming provides continuation as a closure Type system ensures that “async” call does not return values Jeff Fischer, UCLA 5
Recommend
More recommend