Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

modified to use continuations

  • Loading branch information...
commit 62898a276c564995f3d0d155245be7c38e312542 1 parent d30f857
mneumann authored
View
10 benchmark/counter.rb
@@ -77,10 +77,12 @@ def render_content_on(r)
class MySession < Wee::Session
def initialize
- super do
- self.root_component = Main.new
- self.page_store = Wee::Utils::LRUCache.new(10) # backtrack up to 10 pages
- end
+ self.page_store = Wee::Utils::LRUCache.new(10) # backtrack up to 10 pages
+ super
+ end
+
+ def root_component
+ Main.new
end
end
View
38 lib/wee/component.rb
@@ -3,16 +3,34 @@ class Wee::Component
attr_accessor :caller # who is calling us
# call another component
- def call(component, return_method=nil)
- component.caller = [self, return_method]
- add_decoration(Wee::Delegate.new(component))
+ CallUsesContinuation = Object.new
+ def call(component, return_method=CallUsesContinuation)
+ if return_method == CallUsesContinuation
+ callcc {|cc|
+ component.caller = [self, cc]
+ add_decoration(Wee::Delegate.new(component))
+ throw :back_to_process_request_actions
+ }
+ else
+ component.caller = [self, return_method]
+ add_decoration(Wee::Delegate.new(component))
+ end
end
# return from a called component
def answer(*args)
- c, return_method = self.caller
- c.remove_first_decoration
- c.send(return_method, *args) if return_method
+ component, return_method = self.caller
+ component.remove_first_decoration
+ self.caller = nil
+
+ case return_method
+ when Continuation
+ return_method.call(*args)
+ when nil
+ # nothing
+ else
+ component.send(return_method, *args)
+ end
end
def process_request(context)
@@ -30,17 +48,21 @@ def process_request_inputs(context)
end
def process_request_actions(context)
+ was_cc = [false] # did we return from a continuation call or not?
+
# handle URL actions
if act = context.handler_registry.get_action(context.handler_id, self)
- act.invoke
+ was_cc[0] = true if catch(:back_to_process_request_actions) { act.invoke; true }.nil?
end
# handle form actions
context.request.query.each do |hid, value|
if act = context.handler_registry.get_action(hid, self)
- act.invoke
+ was_cc[0] = true if catch(:back_to_process_request_actions) { act.invoke; true }.nil?
end
end
+
+ throw :back_to_request_response_loop if was_cc[0]
end
# Creates a new renderer for this component and renders it.
View
1  lib/wee/context.rb
@@ -3,6 +3,7 @@ class Wee::Context
attr_accessor :request, :response, :session, :session_id
attr_accessor :page_id, :handler_id, :resource_id
attr_accessor :handler_registry
+ attr_accessor :redirect
def initialize(request, response, session, session_id)
@request, @response, @session, @session_id, @root = request, response, session, session_id
View
30 lib/wee/delegate_decoration.rb
@@ -1,5 +1,8 @@
-class Wee::Delegate
+class Wee::Decoration
attr_accessor :next # next decoration in chain
+end
+
+class Wee::Delegate < Wee::Decoration
def initialize(component)
@component = component
@@ -17,6 +20,31 @@ def render_with_context(rendering_context)
end
def renderer_class
+ # Wee::DummyRenderer
+ Wee::HtmlCanvas
+ end
+end
+
+class Wee::Once < Wee::Decoration
+ def initialize
+ @process_request_cnt = 0
+ @render_cnt = 0
+ end
+
+ def process_request(context)
+ raise "once" if @process_request_cnt > 0
+ @process_request_cnt += 1
+ @next.process_request(context)
+ end
+
+ # Creates a new renderer for this component and renders it.
+ def render_with_context(rendering_context)
+ #raise "once" if @render_cnt > 0
+ #@render_cnt += 1
+ @next.render_with_context(rendering_context)
+ end
+
+ def renderer_class
Wee::HtmlCanvas
end
end
View
100 lib/wee/session.rb
@@ -4,8 +4,18 @@
require 'thread'
class Wee::Session
- attr_accessor :root_component, :page_store
+ attr_accessor :page_store
attr_reader :state_registry
+ attr_reader :root_component
+
+ def initialize(&block)
+ @next_page_id = 0
+ @state_registry = Wee::StateRegistry.new
+ @in_queue, @out_queue = SizedQueue.new(1), SizedQueue.new(1)
+
+ @initial_snapshot = @state_registry.snapshot
+ start_request_response_loop
+ end
def self.current
sess = Thread.current['Wee::Session']
@@ -17,58 +27,61 @@ def register_object_for_backtracking(obj)
@state_registry << obj
end
- def initialize(&block)
- Thread.current['Wee::Session'] = self
-
- @next_page_id = 0
- @mutex = Mutex.new
- @state_registry = Wee::StateRegistry.new
+ # called by application to send the session a request
+ def handle_request(context)
- block.call(self)
+ # Send a request to the session. If the session is currently busy
+ # processing another request, this will block.
+ @in_queue.push(context)
- raise ArgumentError, "No root component specified" if @root_component.nil?
- raise ArgumentError, "No page_store specified" if @page_store.nil?
-
- @initial_snapshot = @state_registry.snapshot
+ # Wait for the response.
+ context = @out_queue.pop
- ensure
- Thread.current['Wee::Session'] = nil
+ # TODO: can't move into session?
+ if context.redirect
+ context.response.set_redirect(WEBrick::HTTPStatus::MovedPermanently, context.redirect)
+ end
end
- def setup(context)
+ def start_request_response_loop
+ Thread.abort_on_exception = true
+ Thread.new {
+ Thread.current['Wee::Session'] = self
+ @root_component = root_component()
+
+ loop {
+ @context = @in_queue.pop
+ process_request
+ @out_queue.push(@context)
+ }
+ }
end
- def handle_request(context)
- Thread.current['Wee::Session'] = self
-
- @mutex.synchronize do
-
- setup(context)
-
- if context.page_id.nil?
+ def process_request
+ if @context.page_id.nil?
# No page_id was specified in the URL. This means that we start with a
# fresh component and a fresh page_id, then redirect to render itself.
- handle_new_page_view(context, @initial_snapshot)
+ handle_new_page_view(@context, @initial_snapshot)
- elsif page = @page_store.fetch(context.page_id, false)
+ elsif page = @page_store.fetch(@context.page_id, false)
# A valid page_id was specified and the corresponding page exists.
- page.snapshot.apply unless context.resource_id
+ page.snapshot.apply unless @context.resource_id
- raise "invalid request URL! both request_id and handler_id given!" if context.resource_id and context.handler_id
+ raise "invalid request URL! both request_id and handler_id given!" if @context.resource_id and @context.handler_id
- if context.resource_id
+ if @context.resource_id
# This is a resource request
- res = page.handler_registry.get_resource(context.resource_id)
+ res = page.handler_registry.get_resource(@context.resource_id)
- context.response.status = 200
- context.response['Content-Type'] = res.content_type
- context.response.body = res.content
+ @context.response.status = 200
+ @context.response['Content-Type'] = res.content_type
+ @context.response.body = res.content
- elsif context.handler_id.nil?
+ elsif @context.handler_id.nil?
# No action/inputs were specified -> render page
#
@@ -79,9 +92,9 @@ def handle_request(context)
# stored in memory).
page = Wee::Page.new(page.snapshot, Wee::HandlerRegistry.new) # remove all action/input handlers
- context.handler_registry = page.handler_registry
- respond(context.freeze) # render
- @page_store[context.page_id] = page # store
+ @context.handler_registry = page.handler_registry
+ respond(@context) # render
+ @page_store[@context.page_id] = page # store
else
@@ -90,9 +103,11 @@ def handle_request(context)
# We process the request and invoke actions/inputs. Then we generate a
# new page view.
- context.handler_registry = page.handler_registry
- @root_component.decoration.process_request(context.freeze)
- handle_new_page_view(context)
+ @context.handler_registry = page.handler_registry
+ catch(:back_to_request_response_loop) {
+ @root_component.decoration.process_request(@context)
+ }
+ handle_new_page_view(@context)
end
@@ -108,11 +123,6 @@ def handle_request(context)
raise "Not yet implemented"
end
-
- end # mutex
-
- ensure
- Thread.current['Wee::Session'] = nil
end
private
@@ -123,7 +133,7 @@ def handle_new_page_view(context, snapshot=nil)
@page_store[new_page_id] = new_page
redirect_url = "#{ context.application.path }/s:#{ context.session_id }/p:#{ new_page_id }"
- context.response.set_redirect(WEBrick::HTTPStatus::MovedPermanently, redirect_url)
+ context.redirect = redirect_url
end
def respond(context)
Please sign in to comment.
Something went wrong with that request. Please try again.