Permalink
Browse files

Make the new code reloading behavior work with multithreaded environm…

…ents such as Mongrel.

[#2948 state:incomplete]

Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net>
  • Loading branch information...
1 parent 17f336e commit d37ac7958fc88fdbf37a8948102f6b4e45c530b3 @FooBarWidget FooBarWidget committed with jeremy Jul 31, 2009
Showing with 53 additions and 5 deletions.
  1. +11 −3 actionpack/lib/action_controller/reloader.rb
  2. +42 −2 actionpack/test/controller/reloader_test.rb
View
14 actionpack/lib/action_controller/reloader.rb
@@ -1,14 +1,20 @@
+require 'thread'
+
module ActionController
class Reloader
+ @@lock = Mutex.new
+
class BodyWrapper
- def initialize(body)
+ def initialize(body, lock)
@body = body
+ @lock = lock
end
def close
@body.close if @body.respond_to?(:close)
ensure
Dispatcher.cleanup_application
+ @lock.unlock
end
def method_missing(*args, &block)
@@ -20,11 +26,13 @@ def respond_to?(symbol, include_private = false)
end
end
- def initialize(app)
+ def initialize(app, lock = @@lock)
@app = app
+ @lock = lock
end
def call(env)
+ @lock.lock
Dispatcher.reload_application
status, headers, body = @app.call(env)
# We do not want to call 'cleanup_application' in an ensure block
@@ -39,7 +47,7 @@ def call(env)
# completely finished. So we wrap the body in a BodyWrapper class so that
# when the Rack handler calls #close during the end of the request, we get to
# run our cleanup code.
- [status, headers, BodyWrapper.new(body)]
+ [status, headers, BodyWrapper.new(body, @lock)]
end
end
end
View
44 actionpack/test/controller/reloader_test.rb
@@ -22,9 +22,27 @@ def close
end
end
+ class MyLock
+ def lock
+ @locked = true
+ end
+
+ def unlock
+ @locked = false
+ end
+
+ def locked?
+ @locked
+ end
+ end
+
+ def setup
+ @lock = Mutex.new
+ end
+
def setup_and_return_body(app = lambda { })
Dispatcher.expects(:reload_application)
- reloader = Reloader.new(app)
+ reloader = Reloader.new(app, @lock)
headers, status, body = reloader.call({ })
body
end
@@ -33,10 +51,32 @@ def test_it_reloads_the_application_before_the_request
Dispatcher.expects(:reload_application)
reloader = Reloader.new(lambda {
[200, { "Content-Type" => "text/html" }, [""]]
- })
+ }, @lock)
reloader.call({ })
end
+ def test_it_locks_before_calling_app
+ lock = MyLock.new
+ Dispatcher.expects(:reload_application)
+ reloader = Reloader.new(lambda {
+ [200, { "Content-Type" => "text/html" }, [""]]
+ }, lock)
+ assert !lock.locked?
+ reloader.call({ })
+ assert lock.locked?
+ end
+
+ def it_unlocks_upon_calling_close_on_body
+ lock = MyLock.new
+ Dispatcher.expects(:reload_application)
+ reloader = Reloader.new(lambda {
+ [200, { "Content-Type" => "text/html" }, [""]]
+ }, lock)
+ headers, status, body = reloader.call({ })
+ body.close
+ assert !lock.locked?
+ end
+
def test_returned_body_object_always_responds_to_close
body = setup_and_return_body(lambda {
[200, { "Content-Type" => "text/html" }, [""]]

0 comments on commit d37ac79

Please sign in to comment.