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