diff --git a/contrib/concurrency/concurrency-examples.factor b/contrib/concurrency/concurrency-examples.factor index 9e5f7c7920..b1d9d76262 100644 --- a/contrib/concurrency/concurrency-examples.factor +++ b/contrib/concurrency/concurrency-examples.factor @@ -193,7 +193,7 @@ M: promised-label set-label-color set-promised-label-color ; M: promised-label set-label-font set-promised-label-font ; : fib ( n -- n ) - yield dup 2 < [ drop 1 ] [ dup 1 - fib swap 2 - fib + ] if ; + 1 sleep dup 2 < [ drop 1 ] [ dup 1 - fib swap 2 - fib + ] if ; : test-promise-ui ( -- ) - dup gadget. [ 30 fib unparse swap fulfill ] cons spawn drop ; + dup gadget. [ 15 fib unparse swap fulfill ] cons spawn drop ; diff --git a/contrib/cont-responder/cont.txt b/contrib/cont-responder/cont.txt deleted file mode 100644 index 22473552bc..0000000000 --- a/contrib/cont-responder/cont.txt +++ /dev/null @@ -1,375 +0,0 @@ -cont-responder v0.3 -=================== - -NOTE: This documentation is slightly out of date with respect to the -current code but contains the basic idea. - -In a continuation based web application the current position within -the web application is identified by a random URL. In this example the -URL is generated as a string of random digits: - -: get-random-id ( -- id ) - #! Generate a random id to use for continuation URL's - <% 16 [ random-digit % ] times %> ; - -Each URL is associated with a quotation or continuation. When that URL -is accessed, that quotation is executed. The quotation will receive an -alist on the top of the stack which holds any POST or query -parameters. A global table is maintained that holds the association of -quotations to id's. - -: continuation-table ( -- ) - #! Return the global table of continuations - "cont" get ; - -: reset-continuation-table ( -- ) - #! Create the initial global table - "cont" set ; - -: register-continuation ( quot -- id ) - #! Store a continuation in the table and associate it with - #! a random id. - continuation-table [ get-random-id dup [ set ] dip ] bind ; - -: get-registered-continuation ( id -- cont ) - #! Return the continuation associated with the given id. - continuation-table [ get ] bind ; - -: resume-continuation ( value id -- ) - #! Call the continuation associated with the given id, - #! with 'value' on the top of the stack. - get-registered-continuation call ; - -When a an URL is accessed, the continuation for the specific URL is -obtained and called. That continuation needs to exit back to the -caller when it has some HTML that it needs to display. returning that -HTML to the caller. To exit back to the caller it calls an 'exit -continuation' that is stored in an "exit" variable: - -: exit-continuation ( -- exit ) - #! Get the current exit continuation - "exit" get ; - -: call-exit-continuation ( value -- ) - #! Call the exit continuation, passing it the given value on the - #! top of the stack. - "exit" get call ; - -: with-exit-continuation ( quot -- ) - #! Call the quotation with the variable "exit" bound such that when - #! the exit continuation is called, computation will resume from the - #! end of this 'with-exit-continuation' call, with the value passed - #! to the exit continuation on the top of the stack. - [ "exit" set call call-exit-continuation ] callcc1 nip ; - -All this calling of continuations is hidden behind a single 'show' -call. 'show' will take a quotation on the stack. That quotation should -return an HTML string. 'show' will call it to generate the HTML and -call the exit continuation with this string on the stack so it gets -returned to the httpd responder. The quotation receives a 'url' on the -top of the stack which is the 'id' of the continuation to resume when -that URL is accessed. - -The HTML page that 'show' displays can contain links to -'callbacks'. These are links to other quotations that when called -will perform some action and return back to the calling page. To -return back to the calling page 'show' must capture and store the -current continuation before 'show' does anything so it can be later -return to by the callback. The following words store the current -continuation and retrieve it: - -: store-callback-cc ( -- ) - #! Store the current continuation in the variable 'callback-cc' - #! so it can be returned to later by callbacks. Note that it - #! recalls itself when the continuation is called to ensure that - #! it resets it's value back to the most recent show call. - [ - [ "callback-cc" set call ] callcc0 drop store-callback-cc - ] callcc0 ; - -To generate the string of HTML I use 'with-string-stream' which calls -a quotation and all output inside that call gets appended to a string: - -: with-string-stream ( quot -- string ) - #! Call the quotation with standard output bound to a string output - #! stream. Return the string on exit. - [ - "stdio" put call "stdio" get stream>str - ] bind ; - -: show ( quot -- alist ) - #! Call the quotation with the URL associated with the current - #! continuation. Return the HTML string generated by that code - #! to the exit continuation. When the URL is later referenced then - #! computation will resume from this 'show' call with a alist on - #! the stack containing any query or post parameters. - store-callback-cc - [ - register-continuation swap with-string-stream - call-exit-continuation - ] callcc1 - nip ; - -An httpd get responder is used that takes the ID as an argument, retrieves -the continuation associated with it and calls it. For the post -responder the post data is converted into an alist and it is put on -the top of the stack when calling the continuation. - -: cont-get-responder ( id -- ) - #! httpd responder that retrieves a continuation and calls it. - [ f swap resume-continuation ] with-exit-continuation - serving-html print drop ; - -: post-request>alist ( post-request -- alist ) - #! Return an alist containing name/value pairs from the - #! post data. - dup "&" swap str-contains [ - "(.+)&(.+)" groups [ "(.+)=(.+)" groups uncons car cons ] inject - ] [ - "(.+)=(.+)" groups uncons car cons unit - ] ifte ; - -: cont-post-responder ( id -- ) - #! httpd responder that retrieves a continuation for the given - #! id and calls it with the POST data as an alist on the top - #! of the stack. - [ - read-post-request post-request>alist swap resume-continuation - ] with-exit-continuation - serving-html print drop ; - -Some code to install the responder: - -: install-cont-responder ( -- ) - #! Install the cont-responder in the table of httpd responders - "httpd-responders" get [ - [ - [ cont-get-responder ] "get" set - [ cont-post-responder ] "post" set - reset-continuation-table - ] extend "cont" set - ] bind ; - -Now to test it. Here's a function that displays some text on an HTML page: - -: display-page ( title -- ) - #! Display a page with some text to test the cont-responder. - #! The page has a link to the 'next' continuation. - [ - swap [ - "Next" write - ] html-document - ] show drop ; - -Note that it contains an A HREF link to the URL that resumes the -computation (The quotation passed to show has this URL passed to it by -show). - -An example of a POST request is a page that requests input of a -name. The following function calls show to display it and returns the -result of the 'name' field by retrieving it from the alist. Notice how -the 'action' of the post request is set to the URL passed in to the -quotation passed to show. So the 'next' that happens here is not an A -HREF but the action field of the POST. - -: display-get-name-page ( -- name ) - #! Display a page prompting for input of a name and return that name. - [ - "Enter your name" [ - "
" write - "Name: " write - "" write - "
" write - ] html-document - ] show - "name" swap assoc ; - -A word that displays a sequence of these pages would be: - -: test-cont-responder ( alist - ) - #! Test the cont-responder responder by displaying a few pages in a row. - drop - "Page one" display-page - "Hello " display-get-name-page cat2 display-page - "Page three" display-page ; - -This displays the first page, then a page prompting for the -name. "Hello " is concatenated to the result of the page and a third -page is displayed. A fourth page is available as well. - -The following registers this word with the continuation system: - -: register-test-cont-responder ( -- id ) - #! Register the test-cont-responder word so that accessing the - #! URL with the returned ID will call it. - "httpd-responders" get [ - "cont" get [ - [ test-cont-responder ] register-continuation - ] bind - ] bind ; - -This returns an ID which can be used from the web server to resume -it. Start the web server: - -8888 httpd - -Now access the URL with that id: - - http://localhost:8888/cont/1234567890 - - (replace 1234567890 with the ID returned by register-continuation above) - -You'll see the first page and a link. Click the link and you'll see -the second page requesting a name. Enter the name and press 'Ok'. A -third page will display a message using that name. You can book mark, -refresh, go back, enter a new name, etc as expected. - -You can do any form of computation inside the handlers. Here's an -example of looping a set number of times: - -: test-cont-responder2 ( alist - ) - #! Test the cont-responder responder by displaying a few pages in a loop. - [ "one" "two" "three" "four" ] [ display-page ] each - "Done!" display-page ; - -: register-test-cont-responder2 ( -- id ) - #! Register the test-cont-responder2 word so that accessing the - #! URL with the returned ID will call it. - "httpd-responders" get [ - "cont" get [ - [ test-cont-responder2 ] register-continuation - ] bind - ] bind ; - -There is currently a limited ability to do 'callbacks'. You can -register a quotation as an HTML A HREF anchor thar when accessed by -the browser will run the quotation and then return to the most recent -'show' call. This has the effect of allowing 'subroutine' calls as -page links that can do anything (including display other pages and -complicated action) and will return back to the originating page. The -word to generate this link is: - -: quot-href ( text quot -- ) - #! Write to standard output an HTML HREF where the href, - #! when referenced, will call the quotation and then return - #! back to the most recent 'show' call (via the callback-cc). - #! The text of the link will be the 'text' argument on the - #! stack. - "" write - write - "" write ; - -An example of use is a simple menu page that displays links to the -words written previously: - -: test-cont-responder3 ( alist - ) - #! Test the quot-href word by displaying a menu of the current - #! test words. Note that we drop the 'url' argument to the show - #! quotation as we don't link to a 'next' page. - drop - [ - drop - "Menu" [ - "
    " write - "
  1. " write - "Test responder1" [ test-cont-responder ] quot-href - "
  2. " write - "
  3. " write - "Test responder2" [ test-cont-responder2 ] quot-href - "
  4. " write - "
" write - ] html-document - ] show drop ; - -: register-test-cont-responder3 ( -- id ) - #! Register the test-cont-responder3 word so that accessing the - #! URL with the returned ID will call it. - "httpd-responders" get [ - "cont" get [ - [ test-cont-responder3 ] register-continuation - ] bind - ] bind ; - -You should now be able to click on the menu items, navigate through -those pages and when the sequence of pages ends, return back to the -original menu page. - -Note that this is just a proof of concept. In a real framework the -continuations need to be expired after time. It would also enable -generating links to other pages, etc rather than just a sequence of -pages. I plan to flesh this out over the next few days and present -some more useful examples. My main point was to see if it was possible -to do this type of thing in Factor. - -The number of words required to get things going is amazingly small -and it was very easy to develop this far. - -The code is contained in cont-responder.factor. Here are the steps to -run all the examples starting with a default Factor 0.60 install: - -1) java -cp Factor.jar factor.FactorInterpreter - -db:factor.db.FileStore:factor.db - - At the prompt enter: - - ---8<--- - USE: httpd-responder - default-responders - exit - ---8<--- - -2) java -cp Factor.jar factor.FactorInterpreter - -db:factor.db.FileStore:factor.db - -no-compile - - At the prompt enter: - - ---8<--- - USE: httpd - "cont-responder.factor" run-file - USE: cont-responder - init-cont-responder - register-test-cont-responder . - ---8<--- - - Make note of the number returned by the last line call this [1]. - - ---8<--- - register-test-cont-responder2 . - ---8<--- - - Make note of the number returned by the last line call this [2]. - - ---8<--- - register-test-cont-responder3 . - ---8<--- - - Make note of the number returned by the last line call this [3]. - - ---8<--- - 8888 httpd - ---8<--- - - For the first example go to http://localhost:8888/cont/[1] - where you replace [1] with the number from [1] above. In my system - it was: - - http://localhost:8888/cont/0406763029866672 - - For the second example go to http://localhost:8888/cont/[2] - where you replace [2] with the number from [2] above. In my system - it was: - - http://localhost:8888/cont/6018237533813007 - - For the menu example go to http://localhost:8888/cont/[3] - where you replace [3] with the number from [3] above. In my system - it was: - - http://localhost:8888/cont/3568874223456634 - -3) You can use the inspector to look at the continuation table: - - http://localhost:8888/inspect/global'httpd-responders'cont'cont \ No newline at end of file