doc improvements: enter! and 'more'

svn: r8348
This commit is contained in:
Matthew Flatt 2008-01-16 14:09:59 +00:00
parent f210fc2ea4
commit 63c5d8a7cb
5 changed files with 138 additions and 66 deletions

View File

@ -45,7 +45,7 @@
(check-latest mod)) (check-latest mod))
(define ((enter-load/use-compiled orig re?) path name) (define ((enter-load/use-compiled orig re?) path name)
(printf " [~aloading ~a]\n" (if re? "re-" "") path) (fprintf (current-error-port) " [~aloading ~a]\n" (if re? "re-" "") path)
(if name (if name
;; Module load: ;; Module load:
(let ([code (get-module-code path (let ([code (get-module-code path

View File

@ -12,21 +12,21 @@
@(define quick @other-manual['(lib "quick.scrbl" "scribblings/quick")]) @(define quick @other-manual['(lib "quick.scrbl" "scribblings/quick")])
@(define guide @other-manual['(lib "guide.scrbl" "scribblings/guide")]) @(define guide @other-manual['(lib "guide.scrbl" "scribblings/guide")])
@(define break-eval (make-base-eval)) @(define more-eval (make-base-eval))
@interaction-eval[#:eval break-eval @interaction-eval[#:eval more-eval
(define (show-reload) (define (show-load re?)
(printf " [re-loading serve.ss]\n"))] (fprintf (current-error-port) " [~aloading serve.ss]\n" (if re? "re-" "")))]
@interaction-eval[#:eval break-eval @interaction-eval[#:eval more-eval
(define (serve n) void)] (define (serve n) void)]
@interaction-eval[#:eval break-eval @interaction-eval[#:eval more-eval
(define (show-break) (define (show-break)
(fprintf (current-error-port) "^Cuser break"))] (fprintf (current-error-port) "^Cuser break"))]
@interaction-eval[#:eval break-eval @interaction-eval[#:eval more-eval
(define (show-fail n) (define (show-fail n)
(error 'tcp-listen (error 'tcp-listen
"listen on ~a failed (address already in use)" "listen on ~a failed (address already in use)"
n))] n))]
@interaction-eval[#:eval break-eval (require xml net/url)] @interaction-eval[#:eval more-eval (require xml net/url)]
@(define (whole-prog which [last? #f]) @(define (whole-prog which [last? #f])
(let ([file (format "step~a.txt" which)]) (let ([file (format "step~a.txt" which)])
@ -47,9 +47,12 @@ processes, which is the subject of this tutorial.
Specifically, we show how to build a secure, multi-threaded, Specifically, we show how to build a secure, multi-threaded,
servlet-extensible, continuation-based web server. We use much more of servlet-extensible, continuation-based web server. We use much more of
the language than in @|quick|, and beware that the last couple of the language than in @|quick|, and we expect you to click on syntax or
sections present material that is normally considered difficult. So if function names that you don't recognize (which will take you to the
you're still new to Scheme, you may want to skip to @|guide|. relevant documentation). Beware that the last couple of sections
present material that is normally considered difficult, so if you're
still new to Scheme and have relatively little programming experience,
you may want to skip to @|guide|.
To get into the spirit of this tutorial, we suggest that you set To get into the spirit of this tutorial, we suggest that you set
DrScheme aside for a moment, and switch to raw @exec{mzscheme} in a DrScheme aside for a moment, and switch to raw @exec{mzscheme} in a
@ -109,7 +112,8 @@ scheme
Back in @exec{mzscheme}, try loading the file and running @scheme[go]: Back in @exec{mzscheme}, try loading the file and running @scheme[go]:
@interaction[ @interaction[
(eval:alts (enter! "serve.ss") (printf " [loading serve.ss]\n")) #:eval more-eval
(eval:alts (enter! "serve.ss") (show-load #f))
(eval:alts (go) 'yep-it-works) (eval:alts (go) 'yep-it-works)
] ]
@ -185,8 +189,8 @@ Copy the above three definitions---@scheme[serve],
@filepath{serve.ss} and re-load: @filepath{serve.ss} and re-load:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(eval:alts (enter! "serve.ss") (show-reload)) (eval:alts (enter! "serve.ss") (show-load #t))
(eval:alts (serve 8080) (void)) (eval:alts (serve 8080) (void))
] ]
@ -206,7 +210,7 @@ interrupts the server loop:
@onscreen{Stop} button once.} @onscreen{Stop} button once.}
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(eval:alts (serve 8080) (show-break)) (eval:alts (serve 8080) (show-break))
(eval:alts code:blank (void)) (eval:alts code:blank (void))
] ]
@ -215,7 +219,7 @@ Unfortunately, we cannot now re-start the server with the same port
number: number:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(eval:alts (serve 8080) (show-fail 8080)) (eval:alts (serve 8080) (show-fail 8080))
] ]
@ -244,8 +248,8 @@ server thread and TCP listener:
Try the new one: Try the new one:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(eval:alts (enter! "serve.ss") (show-reload)) (eval:alts (enter! "serve.ss") (show-load #t))
(define stop (serve 8081)) (define stop (serve 8081))
] ]
@ -254,7 +258,7 @@ can shut down and restart the server on the same port number as often
as you like: as you like:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(stop) (stop)
(define stop (serve 8081)) (define stop (serve 8081))
(stop) (stop)
@ -283,10 +287,10 @@ thread, we can put each individual connection into its own thread:
With this change, our server can now handle multiple threads at With this change, our server can now handle multiple threads at
once. The handler is so fast that this fact will be difficult to once. The handler is so fast that this fact will be difficult to
detect, however, so try inserting @scheme[(sleep (random 10))] before detect, however, so try inserting @scheme[(sleep (random 10))] before
the @scheme[handle] call above. If you make multiple connects from the the @scheme[handle] call above. If you make multiple connections from
web browser at the same time, some will return right away, and some the web browser at roughly the same time, some will return soon, and
will take up to 10 seconds. The random delays will be independent of some will take up to 10 seconds. The random delays will be independent
the order in which you started the connections. of the order in which you started the connections.
@; ---------------------------------------------------------------------- @; ----------------------------------------------------------------------
@section{Terminating Connections} @section{Terminating Connections}
@ -298,7 +302,7 @@ like to implement a timeout for each connection thread.
One way to implement the timeout is to create a second thread that One way to implement the timeout is to create a second thread that
waits for 10 seconds, and then kills the thread that calls waits for 10 seconds, and then kills the thread that calls
@scheme[handle]. Threads are lightweight enough that this @scheme[handle]. Threads are lightweight enough in Scheme that this
watcher-thread strategy works well: watcher-thread strategy works well:
@schemeblock[ @schemeblock[
@ -321,15 +325,15 @@ could add code to the watcher thread to close the streams as well as
kill the thread, but Scheme offers a more general shutdown mechanism: kill the thread, but Scheme offers a more general shutdown mechanism:
@defterm{custodians}. A custodian is a kind of container for all @defterm{custodians}. A custodian is a kind of container for all
resources other than memory, and it supports a resources other than memory, and it supports a
@scheme[custodian-shutdown-all] that terminates and closes all @scheme[custodian-shutdown-all] operation that terminates and closes
resources within the container, whether they're threads, streams, or all resources within the container, whether they're threads, streams,
other kinds of limited resources. or other kinds of limited resources.
Whenever a thread or stream is created, it is placed into the current Whenever a thread or stream is created, it is placed into the current
custodian as determined by the @scheme[current-custodian] custodian as determined by the @scheme[current-custodian]
parameter. To place everything about a connection into a custodian, we parameter. To place everything about a connection into a custodian, we
@scheme[parameterize] all the resources creations to go into a new @scheme[parameterize] all the resource creations to go into a new
one: custodian:
@schemeblock[ @schemeblock[
(define (accept-and-handle listener) (define (accept-and-handle listener)
@ -347,11 +351,10 @@ one:
] ]
With this implementation, @scheme[in], @scheme[out], and the thread With this implementation, @scheme[in], @scheme[out], and the thread
that calls @scheme[handle] all belong to the @scheme[cust] that calls @scheme[handle] all belong to @scheme[cust]. In addition,
custodian. In addition, if we later change @scheme[handle] so that it, if we later change @scheme[handle] so that it, say, opens a file, then
say, opens a file, then the file handles will also belong to the file handles will also belong to @scheme[cust], so they will be
@scheme[cust], so they will be reliably closed when @scheme[cust] is reliably closed when @scheme[cust] is shut down.
shut down.
In fact, it's a good idea to change @scheme[serve] to that it uses a In fact, it's a good idea to change @scheme[serve] to that it uses a
custodian, too: custodian, too:
@ -372,7 +375,7 @@ custodian, too:
That way, the @scheme[main-cust] created in @scheme[serve] not only That way, the @scheme[main-cust] created in @scheme[serve] not only
owns the TCP listener and the main server thread, it also owns every owns the TCP listener and the main server thread, it also owns every
custodian created for a connection. Consequently, the revised shutdown custodian created for a connection. Consequently, the revised shutdown
procedure for the server immediately terminates any active connection, procedure for the server immediately terminates all active connections,
in addition to the main server loop. in addition to the main server loop.
@whole-prog["4"] @whole-prog["4"]
@ -381,8 +384,8 @@ After updating the @scheme[serve] and @scheme[accept-and-handle]
functions as above, here's how you can simulate a malicious client: functions as above, here's how you can simulate a malicious client:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(eval:alts (enter! "serve.ss") (show-reload)) (eval:alts (enter! "serve.ss") (show-load #t))
(define stop (serve 8081)) (define stop (serve 8081))
(eval:alts (define-values (cin cout) (tcp-connect "localhost" 8081)) (void)) (eval:alts (define-values (cin cout) (tcp-connect "localhost" 8081)) (void))
] ]
@ -392,7 +395,7 @@ the stream that sends data from the server back to the client, you'll
find that the server has shut down the connection: find that the server has shut down the connection:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(eval:alts (read-line cin) eof) (eval:alts (read-line cin) eof)
] ]
@ -400,7 +403,7 @@ Alternatively, you don't have to wait 10 seconds if you explicitly
shut down the server: shut down the server:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(eval:alts (define-values (cin2 cout2) (tcp-connect "localhost" 8081)) (void)) (eval:alts (define-values (cin2 cout2) (tcp-connect "localhost" 8081)) (void))
(stop) (stop)
(eval:alts (read-line cin2) eof) (eval:alts (read-line cin2) eof)
@ -426,7 +429,7 @@ takes a Scheme value that looks like HTML and turns it into actual
HTML: HTML:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(xexpr->string '(html (head (title "Hello")) (body "Hi!"))) (xexpr->string '(html (head (title "Hello")) (body "Hi!")))
] ]
@ -456,7 +459,7 @@ The @schememodname[net/url] library gives us @scheme[string->url],
for getting from a string to parts of the URL that it represents: for getting from a string to parts of the URL that it represents:
@interaction[ @interaction[
#:eval break-eval #:eval more-eval
(define u (string->url "http://localhost:8080/foo/bar?x=bye")) (define u (string->url "http://localhost:8080/foo/bar?x=bye"))
(url-path u) (url-path u)
(map path/param-path (url-path u)) (map path/param-path (url-path u))
@ -490,7 +493,7 @@ path element, like @scheme["foo"], to a handler function:
With the new @scheme[require] import and new @scheme[handle], With the new @scheme[require] import and new @scheme[handle],
@scheme[dispatch], and @scheme[dispatch-table] definitions, our @scheme[dispatch], and @scheme[dispatch-table] definitions, our
``Hello World!'' server has turn into an error server. You don't have ``Hello World!'' server has turned into an error server. You don't have
to stop the server to try it out. After modifying @filepath{serve.ss} to stop the server to try it out. After modifying @filepath{serve.ss}
with the new pieces, evaluate @scheme[(enter! "serve.ss")] and then with the new pieces, evaluate @scheme[(enter! "serve.ss")] and then
try again to connect to the server. The web browser should show an try again to connect to the server. The web browser should show an
@ -519,7 +522,7 @@ supplies through a form.
The following helper function constructs an HTML form. The The following helper function constructs an HTML form. The
@scheme[label] argument is a string to show the user. The @scheme[label] argument is a string to show the user. The
@scheme[next-url] argument is destination for the form results. The @scheme[next-url] argument is a destination for the form results. The
@scheme[hidden] argument is a value to propagate through the form as a @scheme[hidden] argument is a value to propagate through the form as a
hidden field. When the user responds, the @scheme["number"] field in hidden field. When the user responds, the @scheme["number"] field in
the form holds the user's value: the form holds the user's value:
@ -559,7 +562,7 @@ many ``hello''s as a user wants:
As usual, once you have added these to your program, update with As usual, once you have added these to your program, update with
@scheme[(enter! "serve.ss")], and then visit @scheme[(enter! "serve.ss")], and then visit
@tt{http://localhost:8081/many}. Provide a number, and then you'll get @tt{http://localhost:8081/many}. Provide a number, and you'll receive
a new page with that many ``hello''s. a new page with that many ``hello''s.
@; ---------------------------------------------------------------------- @; ----------------------------------------------------------------------
@ -574,7 +577,7 @@ The solution to this class of problems is to limit the memory use of a
connection. Inside @scheme[accept-and-handle], after the definition of connection. Inside @scheme[accept-and-handle], after the definition of
@scheme[cust], add the line @scheme[cust], add the line
@scheme[(custodian-limit-memory cust (* 50 1024 1024))] @schemeblock[(custodian-limit-memory cust (* 50 1024 1024))]
@whole-prog["7"] @whole-prog["7"]
@ -583,17 +586,19 @@ way that memory accounting is defined, @scheme[cust] might also be
charged for the core server implementation and all of the libraries charged for the core server implementation and all of the libraries
loaded on start-up, so the limit cannot be too small. Also, loaded on start-up, so the limit cannot be too small. Also,
garbage-collector overhead means that the actual memory use of the garbage-collector overhead means that the actual memory use of the
system can be some small multiple of 50 MB. The main guarantee is that system can be some small multiple of 50 MB. An important guarantee,
different connections will not be charged for each other's memory use. however, is that different connections will not be charged for each
other's memory use, so one misbehaving connection will not interfere
with a different one.
So, with the new line above, and assuming that you have a couple of So, with the new line above, and assuming that you have a couple of
hundred megabytes available for the @exec{mzscheme} process to use, hundred megabytes available for the @exec{mzscheme} process to use,
then with the above limit, you shouldn't be able to crash the web you shouldn't be able to crash the web server by requesting a
server by requesting a ridiculously large number of ``hello''s. ridiculously large number of ``hello''s.
Given the @scheme["many"] example, it's a small step to a web server Given the @scheme["many"] example, it's a small step to a web server
that accepts from clients arbitrary code to execute on the server. In that accepts arbitrary Scheme code to execute on the server. In that
that case, there are many additional security issues besides limiting case, there are many additional security issues besides limiting
processor time and memory consumption. The processor time and memory consumption. The
@schememodname[scheme/sandbox] library provides support to managing @schememodname[scheme/sandbox] library provides support to managing
all those other issues. all those other issues.
@ -608,10 +613,10 @@ topic: @defterm{continuations}. In fact, this facet of a web server
needs @defterm{delimited continuations}, which PLT Scheme provides. needs @defterm{delimited continuations}, which PLT Scheme provides.
The problem solved by continuations is related to servlet sessions and The problem solved by continuations is related to servlet sessions and
user input, where a computation spans multiple client user input, where a computation spans multiple client connections
connections. Often, client-side computation (as in AJAX) is the right @cite["Queinnec00"]. Often, client-side computation (as in AJAX) is the
solution to the problem, but many problems are best solved with a right solution to the problem, but many problems are best solved with
mixture of techniques (e.g., to take advantage of the browser's a mixture of techniques (e.g., to take advantage of the browser's
``back'' button). ``back'' button).
As the multi-connection computation becomes more complex, propagating As the multi-connection computation becomes more complex, propagating
@ -712,7 +717,7 @@ around the cal to @scheme[dispatch]:
Now, we can implement @scheme[send/suspend]. We use @scheme[call/cc] Now, we can implement @scheme[send/suspend]. We use @scheme[call/cc]
in the guise of @scheme[let/cc], which captures the current in the guise of @scheme[let/cc], which captures the current
computation up to an enclosing @scheme[prompt], and binds that computation up to an enclosing @scheme[prompt] and binds that
computation to an identifier---@scheme[k], in this case: computation to an identifier---@scheme[k], in this case:
@schemeblock[ @schemeblock[
@ -746,8 +751,8 @@ generated tag:
When the user submits the form, the handler associated with the form's When the user submits the form, the handler associated with the form's
URL is the old computation, stored as a continuation in the dispatch URL is the old computation, stored as a continuation in the dispatch
table. Invoking the continuation as a function restores the old table. Calling the continuation (like a function) restores the old
computation, passing the @scheme[query] arguments as back to that computation, passing the @scheme[query] argument back to that
computation. computation.
@whole-prog["9" #t] @whole-prog["9" #t]
@ -774,8 +779,9 @@ Scheme, including papers on MrEd @cite["Flatt99"], memory accounting
delimited continuations @cite["Flatt07"]. delimited continuations @cite["Flatt07"].
The PLT Scheme distribution includes a production-quality web server The PLT Scheme distribution includes a production-quality web server
that addresses all of the design points mentioned here and more. See that addresses all of the design points mentioned here and more
@other-manual['(lib "web-server/scribblings/web-server.scrbl")]. @cite["Krishnamurthi07"]. See @other-manual['(lib
"web-server/scribblings/web-server.scrbl")].
@; ---------------------------------------------------------------------- @; ----------------------------------------------------------------------
@ -803,6 +809,20 @@ that addresses all of the design points mentioned here and more. See
#:date "2007" #:date "2007"
#:url "http://www.cs.utah.edu/plt/publications/icfp07-fyff.pdf") #:url "http://www.cs.utah.edu/plt/publications/icfp07-fyff.pdf")
(bib-entry #:key "Krishnamurthi07"
#:author "Shriram Krishnamurthi, Peter Hopkins, Jay McCarthy, Paul T. Graunke, Greg Pettyjohn, and Matthias Felleisen"
#:title "Implementation and Use of the PLT Scheme Web Server"
#:location @italic{Higher-Order and Symbolic Computation}
#:date "2007"
#:url "http://www.cs.brown.edu/~sk/Publications/Papers/Published/khmgpf-impl-use-plt-web-server-journal/paper.pdf")
(bib-entry #:key "Queinnec00"
#:author "Christian Queinnec"
#:title "The Influence of Browsers on Evaluators or, Continuations to Program Web Servers"
#:location "International Conference on Functional Programming"
#:date "2000"
#:url "http://www-spi.lip6.fr/~queinnec/Papers/webcont.ps.gz")
(bib-entry #:key "Wick04" (bib-entry #:key "Wick04"
#:author "Adam Wick and Matthew Flatt" #:author "Adam Wick and Matthew Flatt"
#:title "Memory Accounting without Partitions" #:title "Memory Accounting without Partitions"

View File

@ -7,16 +7,33 @@
@note-lib-only[scheme/control] @note-lib-only[scheme/control]
The @scheme[scheme/control] library provides various control operators The @scheme[scheme/control] library provides various control operators
from the research literature on higher-order control operators. These from the research literature on higher-order control operators, plus a
control operators are implemented in terms of few extra convenience forms. These control operators are implemented
@scheme[call-with-continuation-prompt], in terms of @scheme[call-with-continuation-prompt],
@scheme[call-with-composable-continuations], etc., and they generally @scheme[call-with-composable-continuations], etc., and they generally
work sensibly together. Many are redundant; for example, work sensibly together. Many are redundant; for example,
@scheme[reset] and @scheme[shift] are aliases. @scheme[reset] and @scheme[shift] are aliases.
@; ----------------------------------------------------------------------
@defproc[(abort [v any/c] ...) any]{
Returns the @scheme[v]s to a prompt using the default continuation
prompt tag and the default abort handler.
That is, @scheme[(abort v ...)] is equivalent to
@schemeblock[
(abort-current-continuation
(default-continuation-prompt-tag)
(lambda () (values v ...)))
]}
@; ----------------------------------------------------------------------
@deftogether[( @deftogether[(
@defform[(% expr)] @defform*[[(% expr)
@defform/none[(% expr handler-expr)] (% expr handler-expr)]]
@defproc[(fcontrol [v any/c]) any] @defproc[(fcontrol [v any/c]) any]
)]{ )]{

View File

@ -0,0 +1,34 @@
#lang scribble/doc
@(require "mz.ss"
(for-label scheme/enter))
@title[#:tag "enter"]{Interactive Module Loading}
@note-lib[scheme/enter]
@defform*[[(enter! module-path)
(enter! #f)]]{
Intended for use in a @tech{REPL}, such as when @exec{mzscheme} is
started in interactive mode. When a @scheme[module-path] is provided
(in the same sense as for @scheme[require]), the corresponding module
is loaded or invoked, and the current @tech{namespace} is changed to
the body of the module via @scheme[module->namespace]. When
@scheme[#f] is provided, then the current @tech{namespace} is restored
to the original one.
If invoking @scheme[module-path] requires loading any files, then
modification dates of the files are recorded. If the file is modified,
then a later @scheme[enter!] re-loads the module from source; see also
@secref["module-redeclare"]. Similarly if a later @scheme[enter!]
transitively @scheme[require]s a modified module, then the required
module is re-loaded. Re-loading support works only for modules that
are first loaded (either directly or indirectly through transitive
@scheme[require]s) via @scheme[enter!].
After switching namespaces to the designated module, @scheme[enter!]
automatically requires @scheme[scheme/enter] into the namespace, so
that @scheme[enter!] can be used to switch namespaces again.
When it loads or re-loads a module from a file, @scheme[enter!] prints
a message to @scheme[(current-error-port)].}

View File

@ -9,3 +9,4 @@
@include-section["collects.scrbl"] @include-section["collects.scrbl"]
@include-section["info.scrbl"] @include-section["info.scrbl"]
@include-section["help.scrbl"] @include-section["help.scrbl"]
@include-section["enter.scrbl"]