autoload: always wait on loading thread

We cannot assume autoload_provided/rb_feature_provided returning
TRUE means it is safe to proceed without waiting.  Another
thread may call rb_provide_feature before setting the constant
(via autoload_const_set).  So we must wait until autoload is
completed by another thread.

Note: this patch was tested with an explicit rb_thread_schedule
in rb_provide_feature to make the race condition more apparent
as suggested by <s.wanabe@gmail.com>:
> --- a/load.c
> +++ b/load.c
> @@ -563,6 +563,7 @@ rb_provide_feature(VALUE feature)
>      rb_str_freeze(feature);
>
>      rb_ary_push(features, rb_fstring(feature));
> +rb_thread_schedule();
>      features_index_add(feature, INT2FIX(RARRAY_LEN(features)-1));
>      reset_loaded_features_snapshot();
>  }

* variable.c (check_autoload_required): do not assume a provided
  feature means autoload is complete, always wait if autoload is
  being performed by another thread.
  [ruby-core:81105] [Bug #11384] Thanks to <s.wanabe@gmail.com>

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@58696 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
normal 2017-05-12 21:52:04 +00:00
parent 98746acff3
commit 94a7a4e908

View File

@ -1987,6 +1987,17 @@ check_autoload_required(VALUE mod, ID id, const char **loadingpath)
if (!RSTRING_LEN(file) || !*RSTRING_PTR(file)) { if (!RSTRING_LEN(file) || !*RSTRING_PTR(file)) {
rb_raise(rb_eArgError, "empty file name"); rb_raise(rb_eArgError, "empty file name");
} }
/*
* if somebody else is autoloading, we MUST wait for them, since
* rb_provide_feature can provide a feature before autoload_const_set
* completes. We must wait until autoload_const_set finishes in
* the other thread.
*/
if (ele->state && ele->state->thread != rb_thread_current()) {
return load;
}
loading = RSTRING_PTR(file); loading = RSTRING_PTR(file);
safe = rb_safe_level(); safe = rb_safe_level();
rb_set_safe_level_force(0); rb_set_safe_level_force(0);