Skip to content

Commit

Permalink
API change for async, now uses throw :async, and callback is in the r…
Browse files Browse the repository at this point in the history
…equest env.
  • Loading branch information
raggi committed Jun 18, 2008
1 parent 705c021 commit 85865e1
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 45 deletions.
61 changes: 30 additions & 31 deletions example/async_app.ru
Expand Up @@ -33,60 +33,59 @@
# Document Length: 12 bytes
#
# Concurrency Level: 100
# Time taken for tests: 5.244146 seconds
# Time taken for tests: 5.263089 seconds
# Complete requests: 500
# Failed requests: 0
# Write errors: 0
# Total transferred: 47000 bytes
# HTML transferred: 6000 bytes
# Requests per second: 95.34 [#/sec] (mean)
# Time per request: 1048.829 [ms] (mean)
# Time per request: 10.488 [ms] (mean, across all concurrent requests)
# Transfer rate: 8.58 [Kbytes/sec] received
# Requests per second: 95.00 [#/sec] (mean)
# Time per request: 1052.618 [ms] (mean)
# Time per request: 10.526 [ms] (mean, across all concurrent requests)
# Transfer rate: 8.55 [Kbytes/sec] received
#
# Connection Times (ms)
# min mean[+/-sd] median max
# Connect: 0 3 1.8 3 7
# Processing: 1034 1043 3.0 1044 1050
# Waiting: 1032 1039 3.8 1040 1049
# Total: 1041 1046 1.9 1047 1051
# Connect: 0 3 2.2 3 8
# Processing: 1042 1046 3.1 1046 1053
# Waiting: 1037 1042 3.6 1041 1050
# Total: 1045 1049 3.1 1049 1057
#
# Percentage of the requests served within a certain time (ms)
# 50% 1047
# 66% 1048
# 75% 1048
# 80% 1048
# 90% 1048
# 95% 1049
# 98% 1049
# 99% 1049
# 100% 1051 (longest request)

# 50% 1049
# 66% 1051
# 75% 1053
# 80% 1053
# 90% 1054
# 95% 1054
# 98% 1056
# 99% 1057
# 100% 1057 (longest request)

class AsyncApp

# Status code 100 means CONTINUE
AsyncResponse = [100,{},'']


def call(env)
@env = env
# If we have fibers / threads available, we could fire off the processing
# here, but if we're trying to linearize, it's just as easy to wait until
# async is called.
AsyncResponse
end

def async(instance, method)
# Semi-emulate a long db request, instead of a timer, in reality we'd be
# waiting for the response data. Whilst this happens, other connections
# can be serviced.
# This could be any callback based thing though, a deferrable waiting on
# IO data, a db request, an http request, an smtp send, whatever.
EventMachine::add_timer(1) {
instance.send(method, [200, {}, 'Woah, async!'])
env['async.connection'].send(env['async.callback'], [200, {}, 'Woah, async!'])
}
throw :async
end

end

# The additions to env for async.connection and async.callback absolutely
# destroy the speed of the request if Lint is doing it's checks on env.
# It is also important to note that an async response will not pass through
# any further middleware, as the async response notification has been passed
# right up to the webserver, and the callback goes directly there too.
# Middleware could possibly catch :async, and also provide a different
# async.connection and async.callback.

# use Rack::Lint
run AsyncApp.new
28 changes: 14 additions & 14 deletions lib/thin/connection.rb
Expand Up @@ -8,13 +8,7 @@ class Connection < EventMachine::Connection
include Logging

# Rack application (adapter) served by this connection.
attr_reader :app
attr_accessor :app_async
def app=(app)
@app = app
@app_async = app.respond_to?(:async) ? true : false
app
end
attr_accessor :app

# Backend to the server
attr_accessor :backend
Expand Down Expand Up @@ -60,9 +54,18 @@ def process
def pre_process
# Add client info to the request env
@request.remote_address = remote_address

# Add the async references to env, these can be use to construct
# callback targets for the async response.
@request.env['async.connection'] = self
@request.env['async.callback'] = :post_process

# Process the request calling the Rack adapter
@app.call(@request.env)
response = :async
catch(:async) do
response = @app.call(@request.env)
end
response
rescue Object
handle_error
terminate_request
Expand All @@ -71,13 +74,10 @@ def pre_process

def post_process(result)
return unless result

# An async app should return a [100, {}, ''], meaning continue.
return if result == :async

@response.status, @response.headers, @response.body = result

# If we're going async, then setup our callback on the app, and get outta here.
return @app.async(self, :post_process) if @app_async && @response.status == 100

# Make the response persistent if requested by the client
@response.persistent! if @request.persistent?

Expand All @@ -93,7 +93,7 @@ def post_process(result)
rescue Object
handle_error
ensure
terminate_request unless @response.status == 100
terminate_request unless result == :async
end

def handle_error
Expand Down

0 comments on commit 85865e1

Please sign in to comment.