@@ -35,6 +35,9 @@ module FeatureLoader
3535 # A snapshot of $LOADED_FEATURES, to check if the @loaded_features_index cache is up to date.
3636 @loaded_features_version = -1
3737
38+ @features_realpath_cache = { }
39+ @loaded_features_realpaths = { }
40+
3841 @expanded_load_path = [ ]
3942 # A snapshot of $LOAD_PATH, to check if the @expanded_load_path cache is up to date.
4043 @load_path_version = -1
@@ -140,6 +143,16 @@ def self.find_feature_or_file(feature, use_feature_provided = true)
140143 end
141144 end
142145
146+ def self . load_unless_realpath_loaded ( feature , path )
147+ # TODO: does this need to be synchronized?
148+ realpath = ( @features_realpath_cache [ path ] ||= File . realpath ( path ) || path )
149+ return false if @loaded_features_realpaths . key? ( realpath )
150+
151+ result = Primitive . load_feature ( feature , path )
152+ @loaded_features_realpaths [ realpath ] = path
153+ result
154+ end
155+
143156 def self . expanded_path_provided ( path , ext , use_feature_provided )
144157 if use_feature_provided && feature_provided? ( path , true )
145158 [ :feature_loaded , path , ext ]
@@ -292,13 +305,24 @@ def self.get_loaded_features_index
292305 unless @loaded_features_version == $LOADED_FEATURES. version
293306 raise '$LOADED_FEATURES is frozen; cannot append feature' if $LOADED_FEATURES. frozen?
294307 @loaded_features_index . clear
308+ previous_realpaths = @features_realpath_cache . dup
309+ @features_realpath_cache . clear
310+ @loaded_features_realpaths . clear
295311 $LOADED_FEATURES. map! do |val |
296312 val = StringValue ( val )
297313 #val.freeze # TODO freeze these but post-boot.rb issue using replace
298314 val
299315 end
300316 $LOADED_FEATURES. each_with_index do |val , idx |
301317 features_index_add ( val , idx )
318+ # TODO: do we need to do this in a separate loop on a copy to avoid
319+ # concurrency issues? it's already called from with_synchronized_features.
320+ # https://github.com/ruby/ruby/pull/4887/commits/972f2744d8145db965f1c4218313bd200ea0a740#:~:text=To%20avoid%20concurrency%20issues%20when%20rebuilding%20the%20loaded%20features%0Aindex%2C%20the%20building%20of%20the%20index%20itself%20is%20left%20alone%2C%20and%0Aafterwards%2C%20a%20separate%20loop%20is%20done%20on%20a%20copy%20of%20the%20loaded%20feature%0Asnapshot%20in%20order%20to%20rebuild%20the%20realpaths%20hash.
321+ # end
322+ # $LOADED_FEATURES.dup.each_with_index do |val, idx|
323+ realpath = previous_realpaths [ val ] || ( File . exist? ( val ) ? File . realpath ( val ) : val )
324+ @features_realpath_cache [ val ] = realpath
325+ @loaded_features_realpaths [ realpath ] = val
302326 end
303327 @loaded_features_version = $LOADED_FEATURES. version
304328 end
0 commit comments