Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #10803: rails-threading.patch

File rails-threading.patch, 17.3 kB (added by wesley.moxam, 9 months ago)
  • activesupport/test/dependencies/my_model.rb

    old new  
     1class MyModel < ActiveRecord::Base 
     2  sleep 0.5 
     3  def self.my_number 
     4    5 
     5  end 
     6end 
  • activesupport/test/dependencies_test.rb

    old new  
    3030    Dependencies.explicitly_unloadable_constants = [] 
    3131  end 
    3232 
     33  def test_concurrent_load_or_require 
     34    assert_nothing_raised do 
     35        with_loading 'autoloading_fixtures' do 
     36      threads = [] 
     37          5.times { |i|  
     38                threads[i] = Thread.new {  
     39                  require_dependency(File.dirname(__FILE__) + "/dependencies/my_model") 
     40                  assert MyModel.my_number == 5  
     41                } 
     42          } 
     43          threads.each &:join 
     44          end 
     45        end 
     46        assert_equal 1, Dependencies.loaded.size 
     47  end 
     48 
    3349  def test_tracking_loaded_files 
    3450    require_dependency 'dependencies/service_one' 
    3551    require_dependency 'dependencies/service_two' 
  • activesupport/lib/active_support/dependencies.rb

    old new  
    5050  # An internal stack used to record which constants are loaded by any block. 
    5151  mattr_accessor :constant_watch_stack 
    5252  self.constant_watch_stack = [] 
    53    
     53 
    5454  def load? 
    5555    mechanism == :load 
    5656  end 
     
    7373  end 
    7474 
    7575  def require_or_load(file_name, const_path = nil) 
    76     log_call file_name, const_path 
    77     file_name = $1 if file_name =~ /^(.*)\.rb$/ 
    78     expanded = File.expand_path(file_name) 
    79     return if loaded.include?(expanded) 
     76        Thread.exclusive { 
     77      log_call file_name, const_path 
     78      file_name = $1 if file_name =~ /^(.*)\.rb$/ 
     79      expanded = File.expand_path(file_name) 
     80      return if loaded.include?(expanded) 
    8081 
    81     # Record that we've seen this file *before* loading it to avoid an 
    82     # infinite loop with mutual dependencies. 
    83     loaded << expanded 
     82      # Record that we've seen this file *before* loading it to avoid an 
     83      # infinite loop with mutual dependencies. 
     84      loaded << expanded 
    8485     
    85     if load? 
    86       log "loading #{file_name}" 
    87       begin 
    88         # Enable warnings iff this file has not been loaded before and 
    89         # warnings_on_first_load is set. 
    90         load_args = ["#{file_name}.rb"] 
    91         load_args << const_path unless const_path.nil? 
     86      if load? 
     87        log "loading #{file_name}" 
     88        begin 
     89          # Enable warnings iff this file has not been loaded before and 
     90          # warnings_on_first_load is set. 
     91          load_args = ["#{file_name}.rb"] 
     92          load_args << const_path unless const_path.nil? 
    9293         
    93         if !warnings_on_first_load or history.include?(expanded) 
    94           result = load_file(*load_args) 
    95         else 
    96           enable_warnings { result = load_file(*load_args) } 
     94          if !warnings_on_first_load or history.include?(expanded) 
     95            result = load_file(*load_args) 
     96          else 
     97            enable_warnings { result = load_file(*load_args) } 
     98          end 
     99        rescue Exception 
     100          loaded.delete expanded 
     101          raise 
    97102        end 
    98       rescue Exception 
    99         loaded.delete expanded 
    100         rais
     103      else 
     104        log "requiring #{file_name}" 
     105        result = require file_nam
    101106      end 
    102     else 
    103       log "requiring #{file_name}" 
    104       result = require file_name 
    105     end 
    106107 
    107     # Record history *after* loading so first load gets warnings. 
    108     history << expanded 
    109     return result 
     108      # Record history *after* loading so first load gets warnings. 
     109      history << expanded 
     110      return result 
     111        } 
    110112  end 
    111113   
    112114  # Is the provided constant path defined? 
     
    219221  # it is not possible to load the constant into from_mod, try its parent module 
    220222  # using const_missing. 
    221223  def load_missing_constant(from_mod, const_name) 
    222     log_call from_mod, const_name 
    223     if from_mod == Kernel 
    224       if ::Object.const_defined?(const_name) 
    225         log "Returning Object::#{const_name} for Kernel::#{const_name}" 
    226         return ::Object.const_get(const_name) 
    227       else 
    228         log "Substituting Object for Kernel" 
    229         from_mod = Object 
     224        Thread.exclusive { 
     225      log_call from_mod, const_name 
     226      if from_mod == Kernel 
     227        if ::Object.const_defined?(const_name) 
     228          log "Returning Object::#{const_name} for Kernel::#{const_name}" 
     229          return ::Object.const_get(const_name) 
     230        else 
     231          log "Substituting Object for Kernel" 
     232          from_mod = Object 
     233        end 
    230234      end 
    231     end 
    232235 
    233     # If we have an anonymous module, all we can do is attempt to load from Object. 
    234     from_mod = Object if from_mod.name.blank? 
     236      # If we have an anonymous module, all we can do is attempt to load from Object. 
     237      from_mod = Object if from_mod.name.blank? 
    235238 
    236     unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id 
    237       raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!" 
    238     end 
     239      unless qualified_const_defined?(from_mod.name) && from_mod.name.constantize.object_id == from_mod.object_id 
     240        raise ArgumentError, "A copy of #{from_mod} has been removed from the module tree but is still active!" 
     241      end 
    239242 
    240     raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if from_mod.const_defined?(const_name) 
     243      raise ArgumentError, "#{from_mod} is not missing constant #{const_name}!" if from_mod.const_defined?(const_name) 
    241244     
    242     qualified_name = qualified_name_for from_mod, const_name 
    243     path_suffix = qualified_name.underscore 
    244     name_error = NameError.new("uninitialized constant #{qualified_name}") 
     245      qualified_name = qualified_name_for from_mod, const_name 
     246      path_suffix = qualified_name.underscore 
     247      name_error = NameError.new("uninitialized constant #{qualified_name}") 
    245248     
    246     file_path = search_for_file(path_suffix) 
    247     if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load 
    248       require_or_load file_path 
    249       raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless from_mod.const_defined?(const_name) 
    250       return from_mod.const_get(const_name) 
    251     elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix) 
    252       return mod 
    253     elsif (parent = from_mod.parent) && parent != from_mod && 
     249      file_path = search_for_file(path_suffix) 
     250      if file_path && ! loaded.include?(File.expand_path(file_path)) # We found a matching file to load 
     251        require_or_load file_path 
     252        raise LoadError, "Expected #{file_path} to define #{qualified_name}" unless from_mod.const_defined?(const_name) 
     253        return from_mod.const_get(const_name) 
     254      elsif mod = autoload_module!(from_mod, const_name, qualified_name, path_suffix) 
     255        return mod 
     256      elsif (parent = from_mod.parent) && parent != from_mod && 
    254257          ! from_mod.parents.any? { |p| p.const_defined?(const_name) } 
    255       # If our parents do not have a constant named +const_name+ then we are free 
    256       # to attempt to load upwards. If they do have such a constant, then this 
    257       # const_missing must be due to from_mod::const_name, which should not 
    258       # return constants from from_mod's parents. 
    259       begin 
    260         return parent.const_missing(const_name) 
    261       rescue NameError => e 
    262         raise unless e.missing_name? qualified_name_for(parent, const_name) 
     258        # If our parents do not have a constant named +const_name+ then we are free 
     259        # to attempt to load upwards. If they do have such a constant, then this 
     260        # const_missing must be due to from_mod::const_name, which should not 
     261        # return constants from from_mod's parents. 
     262        begin 
     263          return parent.const_missing(const_name) 
     264        rescue NameError => e 
     265          raise unless e.missing_name? qualified_name_for(parent, const_name) 
     266          raise name_error 
     267        end 
     268      else 
    263269        raise name_error 
    264270      end 
    265     else 
    266       raise name_error 
    267     end 
     271        } 
    268272  end 
    269273   
    270274  # Remove the constants that have been autoloaded, and those that have been 
  • actionpack/test/template/compiled_templates_test.rb

    old new  
    185185    end 
    186186  end 
    187187end 
    188  
    189 module ActionView 
    190   class Base 
    191     def compile_time 
    192       @@compile_time 
    193     end 
    194     def method_names 
    195       @@method_names 
    196     end 
    197   end 
    198 end 
  • actionpack/lib/action_controller/dispatcher.rb

    old new  
    33  # reloading the app after each request when Dependencies.load? is true. 
    44  class Dispatcher 
    55    @@guard = Mutex.new 
    6  
    76    class << self 
    87      # Backward-compatible class method takes CGI-specific args. Deprecated 
    98      # in favor of Dispatcher.new(output, request, response).dispatch. 
     
    113112    end 
    114113 
    115114    def dispatch 
    116       @@guard.synchronize do 
     115          @@guard.lock unless ActionController::Base.allow_concurrency 
    117116        begin 
    118117          run_callbacks :before 
    119118          handle_request 
     
    122121        ensure 
    123122          run_callbacks :after, :reverse_each 
    124123        end 
    125       end 
     124      @@guard.unlock unless ActionController::Base.allow_concurrency 
    126125    end 
    127126 
    128127    def dispatch_cgi(cgi, session_options) 
  • actionpack/lib/action_view/base.rb

    old new  
    158158    attr_internal :cookies, :flash, :headers, :params, :request, :response, :session 
    159159     
    160160    attr_writer :template_format 
     161         
     162        attr_reader :method_names, :compile_time 
    161163 
    162164    # Specify trim mode for the ERB compiler. Defaults to '-'. 
    163165    # See ERb documentation for suitable values. 
     
    189191     
    190192    delegate :request_forgery_protection_token, :to => :controller 
    191193 
    192     @@template_handlers = HashWithIndifferentAccess.new 
    193   
    194194    module CompiledTemplates #:nodoc: 
    195195      # holds compiled template code 
    196196    end 
    197197    include CompiledTemplates 
    198198 
    199     # Maps inline templates to their method names  
    200     @@method_names = {} 
    201     # Map method names to their compile time 
    202     @@compile_time = {} 
    203     # Map method names to the names passed in local assigns so far 
    204     @@template_args = {} 
    205     # Count the number of inline templates 
    206     @@inline_template_count = 0 
    207     # Maps template paths without extension to their file extension returned by pick_template_extension. 
    208     # If for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions 
    209     # used by pick_template_extension determines whether ext1 or ext2 will be stored. 
    210     @@cached_template_extension = {} 
    211     # Maps template paths / extensions to  
    212     @@cached_base_paths = {} 
    213  
    214199    # Cache public asset paths 
    215200    cattr_reader :computed_public_paths 
    216201    @@computed_public_paths = {} 
     
    269254      @assigns = assigns_for_first_render 
    270255      @assigns_added = nil 
    271256      @controller = controller 
    272       @logger = controller && controller.logger  
     257      @logger = controller && controller.logger 
     258      # Map method names to the names passed in local assigns so far 
     259          @template_args = {} 
     260      # Maps inline templates to their method names 
     261          @method_names = {} 
     262      # Map method names to their compile time 
     263          @compile_time = {} 
     264      # Count the number of inline templates 
     265          @inline_template_count = 0 
     266      # Maps template paths without extension to their file extension returned by pick_template_extension 
     267      # If for a given path, path.ext1 and path.ext2 exist on the file system, the order of extensions 
     268      # used by pick_template_extension determines whether ext1 or ext2 will be stored. 
     269          @cached_template_extension = {} 
     270      # Maps template paths / extensions to 
     271          @cached_base_paths = {} 
    273272    end 
    274273 
    275274    # Renders the template present at <tt>template_path</tt>. If <tt>use_full_path</tt> is set to true,  
     
    378377    # 
    379378    def full_template_path(template_path, extension) 
    380379      if @@cache_template_extensions 
    381         (@@cached_base_paths[template_path] ||= {})[extension.to_s] ||= find_full_template_path(template_path, extension) 
    382       else 
     380        (@cached_base_paths[template_path] ||= {})[extension.to_s] ||= find_full_template_path(template_path, extension) 
     381         else 
    383382        find_full_template_path(template_path, extension) 
    384383      end 
    385384    end 
     
    395394    # 
    396395    def pick_template_extension(template_path)#:nodoc: 
    397396      if @@cache_template_extensions 
    398         (@@cached_template_extension[template_path] ||= {})[template_format] ||= find_template_extension_for(template_path) 
     397        (@cached_template_extension[template_path] ||= {})[template_format] ||= find_template_extension_for(template_path) 
    399398      else 
    400399        find_template_extension_for(template_path) 
    401400      end 
     
    458457      # Asserts the existence of a template. 
    459458      def template_exists?(template_path, extension) 
    460459        file_path = full_template_path(template_path, extension) 
    461         !file_path.blank? && @@method_names.has_key?(file_path) || File.exist?(file_path) 
     460        !file_path.blank? && @method_names.has_key?(file_path) || File.exist?(file_path) 
    462461      end 
    463462 
    464463      # Splits the path and extension from the given template_path and returns as an array. 
     
    522521      # Return true if the given template was compiled for a superset of the keys in local_assigns 
    523522      def supports_local_assigns?(render_symbol, local_assigns) 
    524523        local_assigns.empty? || 
    525           ((args = @@template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) }) 
     524          ((args = @template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) }) 
    526525      end 
    527526 
    528527      # Method to check whether template compilation is necessary. 
     
    531530      # or if the file has changed on disk and checking file mods hasn't been disabled. 
    532531      def compile_template?(template, file_name, local_assigns) 
    533532        method_key    = file_name || template 
    534         render_symbol = @@method_names[method_key] 
     533        render_symbol = @method_names[method_key] 
    535534 
    536         compile_time = @@compile_time[render_symbol] 
     535        compile_time = @compile_time[render_symbol] 
    537536        if compile_time && supports_local_assigns?(render_symbol, local_assigns) 
    538537          if file_name && !@@cache_template_loading 
    539538            template_changed_since?(file_name, compile_time) 
     
    555554      def create_template_source(handler, template, render_symbol, locals) 
    556555        body = handler.compile(template) 
    557556 
    558         @@template_args[render_symbol] ||= {} 
    559         locals_keys = @@template_args[render_symbol].keys | locals 
    560         @@template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h } 
    561  
     557        @template_args[render_symbol] ||= {} 
     558        locals_keys = @template_args[render_symbol].keys | locals 
     559        @template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h } 
    562560        locals_code = "" 
    563561        locals_keys.each do |key| 
    564562          locals_code << "#{key} = local_assigns[:#{key}]\n" 
     
    569567 
    570568      def assign_method_name(handler, template, file_name) 
    571569        method_key = file_name || template 
    572         @@method_names[method_key] ||= compiled_method_name(handler, template, file_name) 
     570        @method_names[method_key] ||= compiled_method_name(handler, template, file_name) 
    573571      end 
    574572 
    575573      def compiled_method_name(handler, template, file_name) 
     
    583581          s.gsub!(/([^a-zA-Z0-9_])/) { $1.ord } 
    584582          s 
    585583        else 
    586           (@@inline_template_count += 1).to_s 
     584          (@inline_template_count += 1).to_s 
    587585        end 
    588586      end 
    589587 
     
    591589      def compile_template(handler, template, file_name, local_assigns) 
    592590        render_symbol = assign_method_name(handler, template, file_name) 
    593591        render_source = create_template_source(handler, template, render_symbol, local_assigns.keys) 
    594         line_offset   = @@template_args[render_symbol].size + handler.line_offset 
     592        line_offset   = @template_args[render_symbol].size + handler.line_offset 
    595593 
    596594        begin 
    597595          file_name = 'compiled-template' if file_name.blank? 
     
    606604          raise TemplateError.new(extract_base_path_from(file_name) || view_paths.first, file_name || template, @assigns, template, e) 
    607605        end 
    608606 
    609         @@compile_time[render_symbol] = Time.now 
     607        @compile_time[render_symbol] = Time.now 
    610608        # logger.debug "Compiled template #{file_name || template}\n  ==> #{render_symbol}" if logger 
    611609      end 
    612610 
     
    627625        end 
    628626 
    629627        # Get the method name for this template and run it 
    630         method_name = @@method_names[file_path || template] 
     628        method_name = @method_names[file_path || template] 
    631629        evaluate_assigns 
    632630 
    633631        send(method_name, local_assigns) do |*name|