O''''Reilly Network For Information About''''s Book part 25 ppsx

8 238 0
O''''Reilly Network For Information About''''s Book part 25 ppsx

Đang tải... (xem toàn văn)

Thông tin tài liệu

8.2. Continuations You've probably played video games. Think of a continuation as a save game feature. As you're playing your game, you save your current game. You can feel free to take your chances with the monster control center. If you die, you simply restore the game. Said another way, a continuation is a snapshot of a point in time. Continuations let you save the system's state (in the form of an execution stack) in one place, and then return to that state on command. Since I've already introduced Ruby's syntax, I'll first show you continuations in Ruby, where continuation syntax is clean and precise. Then, I'll show you Seaside, the most popular continuation-based server, in Smalltalk. In Ruby, a code block defines the universe for the continuation. You'll use a continuation object to hold the execution state, consisting of the execution stack. You'll later invoke a call method on the continuation object to restore the system state, replacing the current execution state, including the call stack, with the one in the continuation object. The call returns execution to the point immediately after the code block. From Ruby's perspective, you're conceptually letting your execution state jump back in time. 8.2.1. The Syntax In Ruby, you get a continuation by calling the callcc method on Kernel and passing it a code block. This block does nothing with the continuation but print its object identifier: irb(main):001:0> callcc {|continuation| puts continuation} #<Continuation:0x28c2dd8> This passive little program does more than you think it does. The argument called continuation is a powerful little gem that has the whole execution context, with variable values and the entire call stack, at the time that you called callcc. Look at it as a saved game, or a frozen moment in time. You can return to that moment in time. Specifically, Ruby will return to execute the statement immediately after the continuation block by calling the continuation. Here's a trickier continuation example: callcc do |continuation| for i in 1 10 do continuation.call if (i = = 7) puts i end puts 'This never happens.' end puts 'Good bye.' And the output: >ruby forloop.rb 1 2 3 4 5 6 Good bye. > Once again, the whole callcc statement is a point in time. When i is 7, Ruby executes continuation.call. That takes control to the point right after the continuation code block, so the last two numbers don't get printed, and the puts 'This never happens.' in fact doesn't happen. The callcc method loads the application stack in the continuation, abruptly sending execution to the line of code immediately after the continuation code block, or puts 'Good bye.'. It moves execution around a little bit like a goto. Of course, you'd not usually use continuations to break out of a for loop. Continuations take on a little more power when you pass them out of the code block, such as with a method call. 8.2.2. A More Powerful Example Keep in mind that the continuation will return the call stack and local variables in the block to the way they were when you made the continuation call. So, this program: 1 def loop 2 for i in 1 5 do 3 puts i 4 callcc {|continuation| return continuation} if i= =2 5 end # cont.call returns here 6 return nil 7 end 8 9 puts "Before loop call" 10 cont=loop( ) 11 puts "After loop call" 12 cont.call if cont 13 puts "After continuation call" gives you this result: >ruby continuation.rb Before loop call 1 2 After loop call 3 4 5 After loop call After continuation call So, we were able to exit the loop when something happened and return to the loop on command. Since continuations are so alien, let's look at this example in a little more detail. It's not too bad to read, once you know what's hap pening. Line 4 saves the game, putting it into a container. Line 12 restores the game. Let's break it down a little further, thinking like a Ruby interpreter:  Start on line 9, after the method declaration.  Execute line 9, printing the string Before loop call.  Execute line 10, calling the method called loop. Put line 10 on the call stack, so you'll remember where to return after the method call.  Enter the method loop, specified in line 1.  Do the first pass through the for loop in lines 25. i has a value of 1. You'll print 1.  Start the second pass through the for loop. i now has a value of 2. You'll print 2.  At line 4, i is 2, so make the callcc call in three steps. First, make a copy of the call stack. Second, make a copy of the instance variables (i is 2). Third, push the line after the continuation block (line 5) onto the copy of the call stack, so now the continuation's copy of the stack has (line 5, line 10). The call stack simply has (line 10).  At line 4, execute the return statement. You'll return the value of continuation to the line on the top of the call stack. The call stack has line 10, so you'll return the value of continuation to line 10. Set cont to the returned continuation. Recall the continuation has the current execution contextthe call stack has (line 5, line 10), and variable i has a value of 2.  Execute line 11, printing the screen After call loop.  Execute line 12. Calling the continuation restores the execution state. Set the value of i to 2. Go to the line number on the top of the call stack so that you'll remove it from the call stack. Now the call stack has only line 10.  Execute the rest of the for loop, for i=3, 4, and 5.  You'll return nil. The call stack has 10 on it, so you'll return to line 10, and assign cont to nil.  Execute lines 13 and 15. Skip line 14 because cont is nil. This continuation example shows you a few nice capabilities. You can take a snapshot of execution state at some point in time, like we did within the for loop. You can save that execution state in an object, as we did in the cont object. You can then return to the execution state stored in a continuation object at any point. 8.2.3. Why Would You Use Them? You might first think that continuations are the most useful when you want to break logical control structures, as in implementing a break for our for loop, or processing exceptions. For the most part, though, you want to think "suspend and resume." Continuations are marvelous in these kinds of scenarios. Cooperative multitasking lets one program voluntarily relinquish control to another application, and resume at a later date. This problem is remarkably easy to solve using continuations. A subtler use involves communication. When you've got an application that spans multiple computers with synchronous request/response communication, you often want to suspend control until the remote system responds. When you need to scale this solution, suspending control while you wait frees the system to handle other requests. The system can conveniently resume your application without disruption when the remote system responds, simply by calling a continuation. 8.3. Continuation Servers You can probably begin to see why continuations might be interesting for web servers. If you want to look at a web application as one continuous application with suspend/resume breaks in between to communicate with the user, it makes more sense. While waiting for user input in the form of an HTTP request, the web server could simply store a state, stash the continuation object away in the HTTP session, and instantly return to that frozen point in time when it's time to process another request. Notice in Figure 8-1 that I've conveniently inverted the control. Instead of thinking of a web app as a series of request/response pairs initiated by the user, I can think of a web app as a series of response/request pairs controlled by the server. My server code gets much simpler. Figure 8-1. Continuation servers invert control from client to server, simplifying the world view, and the code, of the server Your web application server is no longer composed of many different independent requests. The server can conveniently look at the world as a bunch of simple end- to-end applications. It processes individual requests by loading the state of each user when it's time to process another request, and suspending the user's application when it's time to communicate with the user again. Voilá! Your application can maintain state, and use it to seamlessly control application flow. At a lower level, the continuation server becomes a collection of web applications with states frozen at a point in time, in the form of continuations. Each user has a session. The continuation server assigns an ID to each session, and organizes the continuations per session. After each request, the continuation server takes a snapshot of the execution state with a continuation object, and associates that continuation with the session. So, a server has multiple sessions, and each session has one or more continuations representing frozen points in time, as shown in Figure 8-2. You can no longer see individual HTTP requests, because they're buried in the application flow. As they should be! Glenn Vanderburg: Continuation Servers Author of Maximum Java 1.1 Glenn Vanderburg, a consultant from Dallas, has been writing Java programs since before it was called Java, and was the author of one of the first advanced Java books. Glenn has 19 years of software development experience, encompassing a wide variety of languages, platforms, industries, and domains. What's wrong with current web development models, like the Servlet model? GV: There are two big problems. I'll start with the most obvious. When I did mainframe programming, I would build a screen of information mixed with form fields, and push it out to a 3270 terminal. The program wouldn't hear from the terminal again until the user hit Enter. Sound familiar? In the mainframe days, the program got to pause and wait on the user's submission. Web programming is actually worse, because in the interest of scaling to thousands of users (as opposed to hundreds), the program is asked to forget as much as possible between each interaction so that each submission can stand alone. The stateless nature of the web programming model forces programmers to manually manipulate, store, and retrieve the program state at every stage. Web frameworks help some, but programmers still have to consider carefully how to deal with each piece of state. One mistake and we get web applications that are (at best) very confusing to use. The other big deficiency of the web development model is that our programs are held together with strings. The navigational structure is defined by URLs we stick in links, and those URLs have to also go in configuration files to tie them to pieces of code that get invoked. User input comes to us in form fields that are named with strings. State that we store in the session is usually referenced by a key that is a string. We have all of these strongly typed programming languages and IDEs to go with them to make sure we don't make silly errors like misspelling variable names, but that all goes out the window with web apps, because the tools don't help us to validate all of our uses of . want to break logical control structures, as in implementing a break for our for loop, or processing exceptions. For the most part, though, you want to think "suspend and resume." Continuations. specified in line 1.  Do the first pass through the for loop in lines 25. i has a value of 1. You'll print 1.  Start the second pass through the for loop. i now has a value of 2. You'll. before it was called Java, and was the author of one of the first advanced Java books. Glenn has 19 years of software development experience, encompassing a wide variety of languages, platforms,

Ngày đăng: 07/07/2014, 08:20

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan