I have a page like this:
<html>
<head>
</head>
<body>
<img src="/images/new_header.gif">
<% 1.upto(20) do %>
<iframe src="<%= url_for(:action => :child)
%>"></iframe>
<% end %>
</body>
</html>
When I run this using Webrick, I experience a race condition, where
I''ll get various kinds of errors, such as "uninitialized constant
ApplicationController", or "uninitialized constant User" in the
IFRAMEs.
I have "ActionController::Base.allow_concurrency = false" and
"Dependencies.mechanism = :load". When I set
"Dependencies.mechanism
= :require" it no longer experience this, which leads me to this
hypothesis:
* When reloading dependencies, all the models and controllers are
first deleted, the recreated. If Webrick creates the nem thread for
one of the IFRAME requests _after_ the classes have been deleted, but
_before_ they have been reloaded, they will not be available to that
thread, hence the error I see.
Says the Pickaxe p136: "A thread shares all global, instance, and
local variables that are in existence at the time the thread starts."
Indeed, I concocted a test to verify that when one thread calls
either "reset_after_dispatch" or "prepare_application",
other threads
are affected, too.
Here''s the code for that:
#!/usr/local/bin/ruby
require File.dirname(__FILE__) + ''/../config/environment''
require ''dispatcher''
class Dispatcher
class << self
public :prepare_application, :reset_after_dispatch
end
end
class TestRace
def initialize
t = []
t << Thread.start{
0.upto(5) do
Dispatcher.prepare_application
puts "A: PREPARED"
sleep(2)
Dispatcher.reset_after_dispatch
puts "A: RESET"
sleep(2)
end
}
t << Thread.start{
0.upto(40) do
puts " B: #{defined? ApplicationController}"
sleep(0.5)
end
}
t.each {|thr| thr.join}
puts "Done"
end
end
TestRace.new()
Does this ring a bell with anyone? Any suggested fixes?
/Lars
Hm ... looking over that test again, it doesn''t quite make sense ... that''s what "ActionController::Base.allow_concurrency" is supposed to take care of with the mutex. Nonetheless, there *is* a race condition, and that makes me uncomfortable. Another data point: It doesn''t happen with apache/fastcgi, which also supports the hypothesis that it''s a thread problem. Any thoughts? /Lars On Jul 14, 2005, at 9:06 PM, Lars Pind wrote:> I have a page like this: > > <html> > <head> > </head> > <body> > <img src="/images/new_header.gif"> > <% 1.upto(20) do %> > <iframe src="<%= url_for(:action => :child) %>"></iframe> > <% end %> > </body> > </html> > > When I run this using Webrick, I experience a race condition, where > I''ll get various kinds of errors, such as "uninitialized constant > ApplicationController", or "uninitialized constant User" in the > IFRAMEs. > > I have "ActionController::Base.allow_concurrency = false" and > "Dependencies.mechanism = :load". When I set > "Dependencies.mechanism = :require" it no longer experience this, > which leads me to this hypothesis: > > * When reloading dependencies, all the models and controllers are > first deleted, the recreated. If Webrick creates the nem thread for > one of the IFRAME requests _after_ the classes have been deleted, > but _before_ they have been reloaded, they will not be available to > that thread, hence the error I see. > > Says the Pickaxe p136: "A thread shares all global, instance, and > local variables that are in existence at the time the thread starts." > > Indeed, I concocted a test to verify that when one thread calls > either "reset_after_dispatch" or "prepare_application", other > threads are affected, too. > > Here''s the code for that: > > #!/usr/local/bin/ruby > > require File.dirname(__FILE__) + ''/../config/environment'' > require ''dispatcher'' > > class Dispatcher > class << self > public :prepare_application, :reset_after_dispatch > end > end > class TestRace > def initialize > t = [] > t << Thread.start{ > 0.upto(5) do > Dispatcher.prepare_application > puts "A: PREPARED" > sleep(2) > Dispatcher.reset_after_dispatch > puts "A: RESET" > sleep(2) > end > } > t << Thread.start{ > 0.upto(40) do > puts " B: #{defined? ApplicationController}" > sleep(0.5) > end > } > t.each {|thr| thr.join} > puts "Done" > end > end > > TestRace.new() > > > Does this ring a bell with anyone? Any suggested fixes? > > /Lars > _______________________________________________ > Rails mailing list > Rails-1W37MKcQCpIf0INCOvqR/iCwEArCW2h5@public.gmane.org > http://lists.rubyonrails.org/mailman/listinfo/rails >
David Heinemeier Hansson
2005-Jul-14 23:28 UTC
Re: Race condition with Webrick and dependency reloading
> Hm ... looking over that test again, it doesn''t quite make sense ... > that''s what "ActionController::Base.allow_concurrency" is supposed to > take care of with the mutex. Nonetheless, there *is* a race > condition, and that makes me uncomfortable. > > Another data point: It doesn''t happen with apache/fastcgi, which also > supports the hypothesis that it''s a thread problem.WEBrick is currently going through a mutex and the ActionController::Base.allow_concurrency option is quite experimental. This is not a problem with cgi/fcgi since they use separate processes to handle requests. WEBrick is just a single process, so when we choke the threading there''s just a single dynamic request at the time. So no in-request requests. -- David Heinemeier Hansson http://www.loudthinking.com -- Broadcasting Brain http://www.basecamphq.com -- Online project management http://www.backpackit.com -- Personal information manager http://www.rubyonrails.com -- Web-application framework