@@ -2176,6 +2176,22 @@ autoload_data_mark(void *ptr)
21762176
21772177 rb_gc_mark_movable (p -> feature );
21782178 rb_gc_mark_movable (p -> mutex );
2179+
2180+ // Allow GC to free us if no modules refer to this via autoload_const.autoload_data
2181+ if (ccan_list_empty (& p -> constants )) {
2182+ rb_hash_delete (autoload_features , p -> feature );
2183+ }
2184+ }
2185+
2186+ static void
2187+ autoload_data_free (void * ptr )
2188+ {
2189+ struct autoload_data * p = ptr ;
2190+
2191+ // We may leak some memory at VM shutdown time, no big deal...?
2192+ if (ccan_list_empty (& p -> constants )) {
2193+ ruby_xfree (p );
2194+ }
21792195}
21802196
21812197static size_t
@@ -2186,12 +2202,12 @@ autoload_data_memsize(const void *ptr)
21862202
21872203static const rb_data_type_t autoload_data_type = {
21882204 "autoload_data" ,
2189- {autoload_data_mark , ruby_xfree , autoload_data_memsize , autoload_data_compact },
2205+ {autoload_data_mark , autoload_data_free , autoload_data_memsize , autoload_data_compact },
21902206 0 , 0 , RUBY_TYPED_FREE_IMMEDIATELY
21912207};
21922208
21932209static void
2194- autoload_c_compact (void * ptr )
2210+ autoload_const_compact (void * ptr )
21952211{
21962212 struct autoload_const * ac = ptr ;
21972213
@@ -2202,7 +2218,7 @@ autoload_c_compact(void *ptr)
22022218}
22032219
22042220static void
2205- autoload_c_mark (void * ptr )
2221+ autoload_const_mark (void * ptr )
22062222{
22072223 struct autoload_const * ac = ptr ;
22082224
@@ -2213,41 +2229,52 @@ autoload_c_mark(void *ptr)
22132229}
22142230
22152231static size_t
2216- autoload_c_memsize (const void * ptr )
2232+ autoload_const_memsize (const void * ptr )
22172233{
22182234 return sizeof (struct autoload_const );
22192235}
22202236
2237+ static void
2238+ autoload_const_free (void * ptr )
2239+ {
2240+ struct autoload_const * autoload_const = ptr ;
2241+
2242+ ccan_list_del (& autoload_const -> cnode );
2243+ ruby_xfree (ptr );
2244+ }
2245+
22212246static const rb_data_type_t autoload_const_type = {
22222247 "autoload_const" ,
2223- {autoload_c_mark , ruby_xfree , autoload_c_memsize , autoload_c_compact ,},
2248+ {autoload_const_mark , autoload_const_free , autoload_const_memsize , autoload_const_compact ,},
22242249 0 , 0 , RUBY_TYPED_FREE_IMMEDIATELY
22252250};
22262251
22272252static struct autoload_data *
2228- get_autoload_data (VALUE acv , struct autoload_const * * acp )
2253+ get_autoload_data (VALUE autoload_const_value , struct autoload_const * * autoload_const_pointer )
22292254{
2230- struct autoload_const * ac = rb_check_typeddata (acv , & autoload_const_type );
2231- struct autoload_data * ele ;
2255+ struct autoload_const * autoload_const = rb_check_typeddata (autoload_const_value , & autoload_const_type );
2256+
2257+ struct autoload_data * autoload_data = rb_check_typeddata (autoload_const -> autoload_data_value , & autoload_data_type );
22322258
2233- ele = rb_check_typeddata (ac -> autoload_data_value , & autoload_data_type );
22342259 /* do not reach across stack for ->state after forking: */
2235- if (ele && ele -> fork_gen != GET_VM ()-> fork_gen ) {
2236- ele -> mutex = Qnil ;
2237- ele -> fork_gen = 0 ;
2260+ if (autoload_data && autoload_data -> fork_gen != GET_VM ()-> fork_gen ) {
2261+ autoload_data -> mutex = Qnil ;
2262+ autoload_data -> fork_gen = 0 ;
22382263 }
2239- if (acp ) * acp = ac ;
2240- return ele ;
2264+
2265+ if (autoload_const_pointer ) * autoload_const_pointer = autoload_const ;
2266+
2267+ return autoload_data ;
22412268}
22422269
22432270RUBY_FUNC_EXPORTED void
2244- rb_autoload (VALUE mod , ID id , const char * file )
2271+ rb_autoload (VALUE module , ID name , const char * feature )
22452272{
2246- if (!file || !* file ) {
2247- rb_raise (rb_eArgError , "empty file name" );
2273+ if (!feature || !* feature ) {
2274+ rb_raise (rb_eArgError , "empty feature name" );
22482275 }
22492276
2250- rb_autoload_str (mod , id , rb_fstring_cstr (file ));
2277+ rb_autoload_str (module , name , rb_fstring_cstr (feature ));
22512278}
22522279
22532280static void const_set (VALUE klass , ID id , VALUE val );
@@ -2256,25 +2283,47 @@ static void const_added(VALUE klass, ID const_name);
22562283struct autoload_arguments {
22572284 VALUE module ;
22582285 ID name ;
2259-
2260- VALUE path ;
2286+ VALUE feature ;
22612287};
22622288
22632289static VALUE
2264- autoload_feature_lookup_or_create (VALUE file )
2290+ autoload_feature_lookup_or_create (VALUE feature , struct autoload_data * * autoload_data_pointer )
22652291{
2266- VALUE ad = rb_hash_aref ( autoload_features , file );
2267- struct autoload_data * ele ;
2292+ RUBY_ASSERT_MUTEX_OWNED ( autoload_mutex );
2293+ RUBY_ASSERT_CRITICAL_SECTION_ENTER () ;
22682294
2269- if (NIL_P (ad )) {
2270- ad = TypedData_Make_Struct (0 , struct autoload_data , & autoload_data_type , ele );
2271- ele -> feature = file ;
2272- ele -> mutex = Qnil ;
2273- ccan_list_head_init (& ele -> constants );
2274- rb_hash_aset (autoload_features , file , ad );
2295+ VALUE autoload_data_value = rb_hash_aref (autoload_features , feature );
2296+ struct autoload_data * autoload_data ;
2297+
2298+ if (NIL_P (autoload_data_value )) {
2299+ autoload_data_value = TypedData_Make_Struct (0 , struct autoload_data , & autoload_data_type , autoload_data );
2300+ autoload_data -> feature = feature ;
2301+ autoload_data -> mutex = Qnil ;
2302+ ccan_list_head_init (& autoload_data -> constants );
2303+
2304+ if (autoload_data_pointer ) * autoload_data_pointer = autoload_data ;
2305+
2306+ rb_hash_aset (autoload_features , feature , autoload_data_value );
2307+ } else if (autoload_data_pointer ) {
2308+ * autoload_data_pointer = rb_check_typeddata (autoload_data_value , & autoload_data_type );
22752309 }
22762310
2277- return ad ;
2311+ RUBY_ASSERT_CRITICAL_SECTION_LEAVE ();
2312+ return autoload_data_value ;
2313+ }
2314+
2315+ static VALUE
2316+ autoload_feature_clear_if_empty (VALUE argument )
2317+ {
2318+ RUBY_ASSERT_MUTEX_OWNED (autoload_mutex );
2319+
2320+ struct autoload_data * autoload_data = (struct autoload_data * )argument ;
2321+
2322+ if (ccan_list_empty (& autoload_data -> constants )) {
2323+ rb_hash_delete (autoload_features , autoload_data -> feature );
2324+ }
2325+
2326+ return Qnil ;
22782327}
22792328
22802329static struct st_table * autoload_table_lookup_or_create (VALUE module ) {
@@ -2313,10 +2362,10 @@ autoload_synchronized(VALUE _arguments)
23132362 struct st_table * autoload_table = autoload_table_lookup_or_create (arguments -> module );
23142363
23152364 // Ensure the string is uniqued since we use an identity lookup:
2316- VALUE path = rb_fstring (arguments -> path );
2365+ VALUE feature = rb_fstring (arguments -> feature );
23172366
2318- VALUE autoload_data_value = autoload_feature_lookup_or_create ( path ) ;
2319- struct autoload_data * autoload_data = rb_check_typeddata ( autoload_data_value , & autoload_data_type );
2367+ struct autoload_data * autoload_data ;
2368+ VALUE autoload_data_value = autoload_feature_lookup_or_create ( feature , & autoload_data );
23202369
23212370 {
23222371 struct autoload_const * autoload_const ;
@@ -2334,59 +2383,63 @@ autoload_synchronized(VALUE _arguments)
23342383}
23352384
23362385void
2337- rb_autoload_str (VALUE mod , ID id , VALUE file )
2386+ rb_autoload_str (VALUE module , ID name , VALUE feature )
23382387{
2339- if (!rb_is_const_id (id )) {
2340- rb_raise (rb_eNameError , "autoload must be constant name: %" PRIsVALUE "" , QUOTE_ID (id ));
2388+ if (!rb_is_const_id (name )) {
2389+ rb_raise (rb_eNameError , "autoload must be constant name: %" PRIsVALUE "" , QUOTE_ID (name ));
23412390 }
23422391
2343- Check_Type (file , T_STRING );
2344- if (!RSTRING_LEN (file )) {
2345- rb_raise (rb_eArgError , "empty file name" );
2392+ Check_Type (feature , T_STRING );
2393+ if (!RSTRING_LEN (feature )) {
2394+ rb_raise (rb_eArgError , "empty feature name" );
23462395 }
23472396
23482397 struct autoload_arguments arguments = {
2349- .module = mod ,
2350- .name = id ,
2351- .path = file ,
2398+ .module = module ,
2399+ .name = name ,
2400+ .feature = feature ,
23522401 };
23532402
23542403 VALUE result = rb_mutex_synchronize (autoload_mutex , autoload_synchronized , (VALUE )& arguments );
23552404
23562405 if (result == Qtrue ) {
2357- const_added (mod , id );
2406+ const_added (module , name );
23582407 }
23592408}
23602409
23612410static void
2362- autoload_delete (VALUE mod , ID id )
2411+ autoload_delete (VALUE module , ID name )
23632412{
2364- st_data_t val , load = 0 , n = id ;
2413+ RUBY_ASSERT_CRITICAL_SECTION_ENTER ();
2414+
2415+ st_data_t value , load = 0 , key = name ;
23652416
2366- if (st_lookup (RCLASS_IV_TBL (mod ), (st_data_t )autoload , & val )) {
2367- struct st_table * tbl = check_autoload_table ((VALUE )val );
2368- struct autoload_data * ele ;
2369- struct autoload_const * ac ;
2417+ if (st_lookup (RCLASS_IV_TBL (module ), (st_data_t )autoload , & value )) {
2418+ struct st_table * table = check_autoload_table ((VALUE )value );
23702419
2371- st_delete (tbl , & n , & load );
2420+ st_delete (table , & key , & load );
23722421
23732422 /* Qfalse can indicate already deleted */
23742423 if (load != Qfalse ) {
2375- ele = get_autoload_data ((VALUE )load , & ac );
2376- if (!ele ) VM_ASSERT (ele );
2424+ struct autoload_const * autoload_const ;
2425+ get_autoload_data ((VALUE )load , & autoload_const );
2426+
23772427 /*
23782428 * we must delete here to avoid "already initialized" warnings
23792429 * with parallel autoload. Using list_del_init here so list_del
2380- * works in autoload_c_free
2430+ * works in autoload_const_free
23812431 */
2382- ccan_list_del_init (& ac -> cnode );
2432+ ccan_list_del_init (& autoload_const -> cnode );
23832433
2384- if (tbl -> num_entries == 0 ) {
2385- n = autoload ;
2386- st_delete (RCLASS_IV_TBL (mod ), & n , & val );
2434+ // If the autoload table is empty, we can delete it.
2435+ if (table -> num_entries == 0 ) {
2436+ name = autoload ;
2437+ st_delete (RCLASS_IV_TBL (module ), & name , & value );
23872438 }
23882439 }
23892440 }
2441+
2442+ RUBY_ASSERT_CRITICAL_SECTION_LEAVE ();
23902443}
23912444
23922445static int
@@ -2589,9 +2642,9 @@ autoload_feature_require(VALUE _arguments)
25892642static VALUE
25902643autoload_apply_constants (VALUE _arguments )
25912644{
2592- struct autoload_load_arguments * arguments = ( struct autoload_load_arguments * ) _arguments ;
2645+ RUBY_ASSERT_CRITICAL_SECTION_ENTER () ;
25932646
2594- RUBY_DEBUG_THREAD_SCHEDULE () ;
2647+ struct autoload_load_arguments * arguments = ( struct autoload_load_arguments * ) _arguments ;
25952648
25962649 if (arguments -> result == Qtrue ) {
25972650 struct autoload_const * autoload_const ;
@@ -2605,8 +2658,7 @@ autoload_apply_constants(VALUE _arguments)
26052658 }
26062659 }
26072660
2608- // Since the feature is now loaded, delete the autoload data for it:
2609- rb_hash_delete (autoload_features , arguments -> autoload_data -> feature );
2661+ RUBY_ASSERT_CRITICAL_SECTION_LEAVE ();
26102662
26112663 return Qtrue ;
26122664}
@@ -2623,7 +2675,7 @@ autoload_try_load(VALUE _arguments)
26232675 struct autoload_load_arguments * arguments = (struct autoload_load_arguments * )_arguments ;
26242676
26252677 // We have tried to require the autoload feature, so we shouldn't bother trying again in any other threads. More specifically, `arguments->result` starts of as nil, but then contains the result of `require` which is either true or false. Provided it's not nil, it means some other thread has got as far as evaluating the require statement completely.
2626- if (arguments -> result != Qnil ) arguments -> result ;
2678+ if (arguments -> result != Qnil ) return arguments -> result ;
26272679
26282680 // Try to require the autoload feature:
26292681 rb_ensure (autoload_feature_require , _arguments , autoload_feature_require_ensure , _arguments );
@@ -2672,6 +2724,8 @@ rb_autoload_load(VALUE module, ID name)
26722724
26732725 // Only one thread will enter here at a time:
26742726 return rb_mutex_synchronize (arguments .mutex , autoload_try_load , (VALUE )& arguments );
2727+
2728+ // rb_mutex_synchronize(autoload_mutex, autoload_feature_clear_if_empty, (VALUE)&arguments.autoload_data);
26752729}
26762730
26772731VALUE
0 commit comments