Bug #921

autoload is not thread-safe

Added by Charles Nutter over 5 years ago. Updated about 1 year ago.

[ruby-core:20797]
Status:Closed
Priority:Normal
Assignee:Hiroshi Nakamura
Category:core
Target version:1.9.4
ruby -v:- Backport:

Description

=begin
Currently autoload is not safe to use in a multi-threaded application. To put it more bluntly, it's broken.

The current logic for autoload is as follows:

  1. A special object is inserted into the target constant table, used as a marker for autoloading
  2. When that constant is looked up, the marker is found and triggers autoloading
  3. The marker is first removed, so the constant now appears to be undefined if retrieved concurrently
  4. The associated autoload resource is required, and presumably redefines the constant in question
  5. The constant lookup, upon completion of autoload, looks up the constant again and either returns its new value or proceeds with normal constant resolution

    The problem arises when two or more threads try to access the constant. Because autoload is stateful and unsynchronized, the second thread may encounter the constant table in any number of states:

  6. It may see the autoload has not yet fired, if the first thread has encountered the marker but not yet removed it. It would then proceed along the same autoload path, requiring the same file a second time.

  7. It may not find an autoload marker, and assume the constant does not exist.

  8. It may see the eventual constant the autoload was intended to define.

    Of these combinations, (3) is obviously the desired behavior. (1) can only happen on native-threaded implementations that do not have a global interpreter lock, since it requires concurrency during autoload's internal logic. (2) can happen on any implementation, since while the required file is processing the original autoload constant appears to be undefined.

    I have only come up with two solutions:

  • When the autoload marker is encountered, it is replaced (under lock) with an "autoload in progress" marker. All subsequent threads will then see this marker and wait for the autoloading process to complete. the mechanics of this are a little tricky, but it would guarantee concurrent autoloads would only load the target file once and would always return the intended value to concurrent readers.
  • A single autoload mutex, forcing all autoloads to happen in serial.

    There is a potential for deadlock in the first solution, unfortunately, since two threads autoloading two constants with circular autoloaded constant dependencies would ultimately deadlock, each waiting for the other to complete. Because of this, a single autoload mutex for all autoloads may be the only safe solution.
    =end

Associated revisions

Revision 33078
Added by Hiroshi Nakamura over 2 years ago

  • variable.c: Make autoload thread-safe. See #921.

    What's the problem?
    autoload is thread unsafe. When we define a constant to be
    autoloaded, we expect the constant construction is invariant. But
    current autoload implementation allows other threads to access the
    constant while the first thread is loading a file.

    What's happening inside?
    The current implementation uses Qundef as a marker of autoload in
    Constant table. Once the first thread find Qundef as a value at
    constant lookup, it starts loading a defined feature. Generally a
    loaded file overrides the Qundef in Constant table by module/class
    declaration at very beginning lines of the file, so other threads
    can see the new Module/Class object before feature loading is
    finished. It breaks invariant construction.

    How to solve?
    To ensure invariant constant construction, we need to override
    Qundef with defined Object after the feature loading. For keeping
    Qundef in Constant table, I expanded autoload_data struct in
    Module to have a slot for keeping the defined object while feature
    loading. And changed Module's constant lookup/update logic a
    little so that the slot is only visible from the thread which
    invokes feature loading. (== the first thread which accessed the
    autoload constant)

    Evaluation?
    All test passes (bootstrap test, test-all and RubySpec) and added
    8 tests for threading behavior. Extra logics are executed only
    when Qundef is found, so no perf drop should happen except
    autoloading.

  • variable.c (rbautoload): Prepare new autoloaddata struct.

  • variable.c (rbautoloadload): Load feature and update Constant
    table after feature loading is finished.

  • variable.c (rbconstget_0): When the fetched constant is under
    autoloading, it returns the object only for the thread which starts
    autoloading.

  • variable.c (rbconstdefined_0): Ditto.

  • variable.c (rbconstset): When the specified constant is under
    autoloading, it sets the object only for the thread which starts
    autoloading. Otherwise, simply overrides Qundef with constant
    override warning.

  • vminsnhelper.c (vmgetevconst): Apply same change as
    rbconstget_0 in variable.c.

  • test/ruby/test_autoload.rb: Added tests for threading behavior.

History

#1 Updated by Dave Thomas over 5 years ago

=begin

On Dec 22, 2008, at 11:10 AM, Charles Nutter wrote:

  • When the autoload marker is encountered, it is replaced (under
    lock) with an "autoload in progress" marker. All subsequent threads
    will then see this marker and wait for the autoloading process to
    complete. the mechanics of this are a little tricky, but it would
    guarantee concurrent autoloads would only load the target file once
    and would always return the intended value to concurrent readers.
  • A single autoload mutex, forcing all autoloads to happen in serial.

There is a potential for deadlock in the first solution,

unfortunately, since two threads autoloading two constants with

circular autoloaded constant dependencies would ultimately deadlock,

each waiting for the other to complete. Because of this, a single

autoload mutex for all autoloads may be the only safe solution.

Why not use the same lock that's used by require? The two are clearly

related, and if there's a require in progress, you'd want to suspend

autoload until it has finished, and the result of the require might

affect the behavior of the autoload.

Dave

=end

#2 Updated by Charles Nutter over 5 years ago

=begin
Dave Thomas wrote:

Why not use the same lock that's used by require? The two are clearly
related, and if there's a require in progress, you'd want to suspend
autoload until it has finished, and the result of the require might
affect the behavior of the autoload.

It probably could use the same lock; the trickier bit is that in between
encountering the autoload and requiring the file there's already state
changes happening. So I think we still need to ensure the autoload
marker is mutated under lock as well.

To be honest, I'm not sure autoload is really even possible to make safe
in the presence of threads...the design of it may be inherently
incompatible. But I'm willing to talk through some possibilities and see
what we can come up with.

=end

#3 Updated by Dave Thomas over 5 years ago

=begin

On Dec 22, 2008, at 1:44 PM, Charles Oliver Nutter wrote:

It probably could use the same lock; the trickier bit is that in

between encountering the autoload and requiring the file there's

already state changes happening. So I think we still need to ensure

the autoload marker is mutated under lock as well.

To be honest, I'm not sure autoload is really even possible to make

safe in the presence of threads...the design of it may be inherently

incompatible. But I'm willing to talk through some possibilities and

see what we can come up with.

Yeah--you'd need to make require transactional somehow.

Stupid question: does anyone actually use autoload?

Dave

=end

#4 Updated by Eric Hodel over 5 years ago

=begin
On Dec 23, 2008, at 05:53 AM, Dave Thomas wrote:

On Dec 22, 2008, at 1:44 PM, Charles Oliver Nutter wrote:

It probably could use the same lock; the trickier bit is that in

between encountering the autoload and requiring the file there's

already state changes happening. So I think we still need to ensure

the autoload marker is mutated under lock as well.

To be honest, I'm not sure autoload is really even possible to make

safe in the presence of threads...the design of it may be

inherently incompatible. But I'm willing to talk through some

possibilities and see what we can come up with.

Yeah--you'd need to make require transactional somehow.

Stupid question: does anyone actually use autoload?

RubyGems now does, but the code wasn't introduced by me.

=end

#5 Updated by Roger Pack over 5 years ago

=begin

Currently autoload is not safe to use in a multi-threaded application.

My preference would be for it at most one thread to be "in an
autoload" at a time, so similar to require, no chance for confusion.
But then again that's just me :)

-r-

=end

#6 Updated by Austin Ziegler over 5 years ago

=begin
On Tue, Dec 23, 2008 at 3:51 PM, Christian Neukirchen
chneukirchen@gmail.com wrote:

Dave Thomas dave@pragprog.com writes:

On Dec 22, 2008, at 1:44 PM, Charles Oliver Nutter wrote:

It probably could use the same lock; the trickier bit is that in
between encountering the autoload and requiring the file there's
already state changes happening. So I think we still need to ensure
the autoload marker is mutated under lock as well.

To be honest, I'm not sure autoload is really even possible to make
safe in the presence of threads...the design of it may be inherently
incompatible. But I'm willing to talk through some possibilities and
see what we can come up with.

Yeah--you'd need to make require transactional somehow.

Stupid question: does anyone actually use autoload?

Rack does. Merb does as well.

So does RbGCCXML, a random library that I'm using (I see no point as
to why it's doing so, to be honest).

-austin
--
Austin Ziegler * halostatue@gmail.com * http://www.halostatue.ca/
* austin@halostatue.ca * http://www.halostatue.ca/feed/
* austin@zieglers.ca

=end

#7 Updated by Charles Nutter over 5 years ago

=begin
Dave Thomas wrote:

To be honest, I'm not sure autoload is really even possible to make
safe in the presence of threads...the design of it may be inherently
incompatible. But I'm willing to talk through some possibilities and
see what we can come up with.

Yeah--you'd need to make require transactional somehow.

Yeah, that's essentially it (though it's more specific to autoload).
You'd need to lock the constant under autoload in such a way that other
threads would block until autoloading completed. And unless it were a
single global lock for all autoload logic, there's a strong potential
for deadlock if two threads encounter opposing constants at the same time.

Stupid question: does anyone actually use autoload?

They do, but they probably shouldn't. It's broken.

=end

#8 Updated by Dave Thomas over 5 years ago

=begin

On Dec 23, 2008, at 4:16 PM, Charles Oliver Nutter wrote:

Yeah--you'd need to make require transactional somehow.

Yeah, that's essentially it (though it's more specific to autoload).

You'd need to lock the constant under autoload in such a way that

other threads would block until autoloading completed. And unless it

were a single global lock for all autoload logic, there's a strong

potential for deadlock if two threads encounter opposing constants

at the same time.

Would it be sufficient to add field to each constant to say "loading

in progress in thread n"

Have the lock we've been talking about in autoload and require.

During require, set the current thread id into every newly defined

constant, keeping a list of the ones that are set. Maintain a stack of

the current requires. (These will all be in the same thread because of

the mutex).

When the last require finishes, go though all constants in the list

and unset the thread id in them. Finally, signal a condition variable.

Constant lookup then works like this: If the constant isn't defined,

claim the require mutex and then check for autoloading stuff.

If the constant is defined, check to see if the thread id is set in

the constant. If it is, wait on the condition variable. That way, you

won't pick up partially initialized constants that are being

autoloaded or required by other threads.

And, I know, this sounds ridiculously complex. But the only

alternative I see is locking the entire interpreter during requires.

Dave

=end

#9 Updated by Nobuyoshi Nakada over 5 years ago

=begin
Hi,

At Tue, 23 Dec 2008 02:10:34 +0900,
Charles Nutter wrote in :

The current logic for autoload is as follows:

This description is old. Already autoload sees the same lock
used by require, as Dave Thomas mentioned in .

So I guess the autoload specific issue has gone, and we could
focus on the issue of recursive/circular require, i.e.,
.

--
Nobu Nakada

=end

#10 Updated by Charles Nutter over 5 years ago

=begin
Nobuyoshi Nakada wrote:

Hi,

At Tue, 23 Dec 2008 02:10:34 +0900,
Charles Nutter wrote in :

The current logic for autoload is as follows:

This description is old. Already autoload sees the same lock
used by require, as Dave Thomas mentioned in .

So I guess the autoload specific issue has gone, and we could
focus on the issue of recursive/circular require, i.e.,
.

So if two threads lookup the same autoload constant at the same time, is
the second thread guaranteed to wait until the first thread has finished
requiring?

I was not aware this had been fixed or decided.

=end

#11 Updated by Nobuyoshi Nakada over 5 years ago

=begin
Hi,

At Wed, 24 Dec 2008 14:56:37 +0900,
Charles Oliver Nutter wrote in :

So I guess the autoload specific issue has gone, and we could
focus on the issue of recursive/circular require, i.e.,
.

So if two threads lookup the same autoload constant at the same time, is
the second thread guaranteed to wait until the first thread has finished
requiring?

It's same as two threads requiring without autoloading.

--
Nobu Nakada

=end

#12 Updated by Charles Nutter over 5 years ago

=begin
Nobuyoshi Nakada wrote:

It's same as two threads requiring without autoloading.

I'm not sure this is safe enough. If the first thread has already set
the constant, the second thread would not know it is an autoload and
would return its value immediately. The resource the constant points at
may not be done loading.

my_file.rb:

module XXX
class Foo
# HERE
def bar; ... end
end
end

my_autoload.rb:

module XXX
autoload :Foo, "my_file.rb"
end

# Thread 1
Thread.new { XXX::Foo.new.bar }

# Thread 2
Thread.new { XXX::Foo.new.bar }

Thread 1 tries to look up XXX::Foo and starts requiring my_file.rb. It
reaches # HERE above and the XXX::Foo constant now points at the Foo
class. Now thread 2 looks up XXX::Foo, and since it is not an autoload
marker it gets the incomplete Foo class. Thread 2 blows up when it tries
to call bar because bar is not yet defined.

=end

#13 Updated by Stephen Bannasch over 5 years ago

=begin
At 10:53 PM +0900 12/23/08, Dave Thomas wrote:

Stupid question: does anyone actually use autoload?

Since the 2.2.2 release the rails trunk codebase has been doing a mass replacement of require with autoload.
=end

#14 Updated by Shyouhei Urabe about 5 years ago

  • Assignee set to Nobuyoshi Nakada
  • ruby -v set to -

=begin

=end

#15 Updated by Nobuyoshi Nakada over 4 years ago

  • Status changed from Open to Closed

=begin

=end

#16 Updated by Hiroshi Nakamura almost 3 years ago

Sorry for commenting this dated ticket.

Charles: I agree with Nobu that it's not autoload specific issue but a thereaded require issue.

my_file.rb:

module XXX
class Foo
# HERE
def bar; ... end
end
end

my_load.rb:

Thread 1

Thread.new { require 'my_file'; XXX::Foo.new.bar }

Thread 2

Thread.new { XXX::Foo.new.bar }

Thread 2 can get NoMethodError(for bar) instead of NameError(for XXX) I think and it's Ruby's behavior at this moment.

I'm just commenting this for closing similar ticket of JRuby: http://jira.codehaus.org/browse/JRUBY-3194. Sorry for the noise.

#17 Updated by Charles Nutter almost 3 years ago

The concurrent require issue is separate. I would like to understand what 1.9.3 does to make concurrent requires safe. A global lock around any require? I know there was a lock against specific filenames added at some point (which JRuby also does)...is there something more?

I do not understand how the autoload problem was fixed. Here is a simpler example...

autoload.rb:

class Object
autoload :X, 'constant.rb'
end

Thread.abortonexception = true

Thread.new {
puts "thread #{Thread.current} accessing X; defined? X == #{(defined? X).inspect}"
X
}
Thread.new {
puts "thread #{Thread.current} accessing X; defined? X == #{(defined? X).inspect}"
X
}
sleep

constant.rb:

simulate a slow file load or a deep chain of requires

puts "thread #{Thread.current} in constant.rb"

check that X is not defined

puts "X defined: #{(defined? X).inspect}"

1000000.times { Thread.pass }

class Object
# define X
X = 1
end

I will review the problem.

When autoloading is triggered, the first step is to remove the autoload constant. This allows the file being required to see a blank slate when (presumably) defining the constant attached to autoloading. The above example does indeed print out "X defined: nil".

Now if we have two threads that encounter an autoloaded constant at roughly the same time, I would expect this sequence is possible:

For our autoloaded constant X:

  1. Thread A encounters the autoload for X and removes the constant. It proceeds to require the associated file which (eventually) will define X.
  2. Because thread A is now in Ruby code, a context switch can occur to thread B.
  3. Thread B attempts to access the autoloaded constant X; however it has been removed by A, and thread B gets a NameError. It does not wait for the require to complete, because it never attempts to perform a require.

This is what happens in JRuby, Rubinius, and Ruby 1.8.7. It does not happen in Ruby 1.9.2 or MacRuby. Why?

Here is the output under 1.8.7:

~/projects/jruby ➔ ruby autoload.rb
thread #Thread:0x100169dc8 accessing X; defined? X == "constant"
thread #Thread:0x100169dc8 in constant.rb
X defined: nil
thread #Thread:0x100169080 accessing X; defined? X == nil
autoload.rb:13: uninitialized constant X (NameError)
from autoload.rb:11:in initialize'
from autoload.rb:11:in
new'
from autoload.rb:11

What appears to happen is that thread B encounters the X autoload and pauses. This could be explained for concurrent requires of the same file (if synchronizing against a filename), or for concurrent requires of different files (if there's a single global lock) but that still does not explain why autoload behaves this way in 1.9.2. Why? Because if thread A is actually removing the constant, it should never know that it's an autoload constant and should fail to block on a require lock of any kind. And it does appear that the constant has been removed...so how does thread B know to pause?

I could dig through the MRI code to find this, but perhaps can someone describe in simple terms how 1.9.2 prevents thread B in the example above from running when it encounters X?

#18 Updated by Hiroshi Nakamura almost 3 years ago

Thanks. I've understand the issue is separated. The problem is not for method definition bar as you wrote in the previous example but for constant definition.

Anyway it looks fixed for CRuby, I'll back to http://jira.codehaus.org/browse/JRUBY-3194. The patch posted to JRUBY-3194 still doesn't solve the problem. Sigh.

#19 Updated by Hiroshi Nakamura almost 3 years ago

  • Status changed from Closed to Open
  • Assignee changed from Nobuyoshi Nakada to Shyouhei Urabe
  • ruby -v changed from - to ruby 1.8.7 (2011-02-18 patchlevel 334) [x86_64-linux]

For JRuby 1.9, I fixed this issue (autoload thread safety.)

Regards to autoload, CRuby 1.9 is thread-safe. CRuby 1.8 is not thread-safe.

% cat autoload.rb
class Foo
autoload :X, 'constant.rb'
end

Thread.abortonexception = true

t1 = Thread.new {
puts "thread #{Thread.current} accessing X"
p Foo::X
}
t2 = Thread.new {
puts "thread #{Thread.current} accessing X"
p Foo::X
}

t1.join
t2.join

% cat constant.rb
# simulate a slow file load or a deep chain of requires
puts "#{Thread.current} in constant.rb"

1000000.times { Thread.pass }

class Foo
# define X
X = 1
end

% ruby187 autoload.rb
thread #Thread:0x7f2e2e301de0 accessing X
#Thread:0x7f2e2e301de0 in constant.rb
thread #Thread:0x7f2e2e301390 accessing X
autoload.rb:13: uninitialized constant Foo::X (NameError)
from autoload.rb:11:in initialize'
from autoload.rb:11:in
new'
from autoload.rb:11
zsh: exit 1 ruby187 autoload.rb

After talking to Shyouhei at Asakusa.rb meetup last night, we agreed that
it's a bug and it should be fixed if we can.

And here's another problem arises. 1.8 removes autoload a constant first as
Charles stated above, then requires the specified file. It's the cause of
threaded autoload issue but it has another side effect. When the require fails
with some Exception, the defined constant is removed afterwards.

% ruby187 -I. -e 'autoload(:X, "X"); begin; X; rescue LoadError; end; p Object.constants.include?("X")'
false

1.9 does not remove the constant.

% ruby -I. -e 'autoload(:X, "X"); begin; X; rescue LoadError; end; p Object.constants.include?(:X)'

true

I'm guessing this behavior would be affected when we fix 1.8's thread safety.
Unfortunately, there're "spec"s in RubySpec which expects that constant to be
removed in 1.8.

Can we change this 1.8.7 behavior?

#20 Updated by Hiroshi Nakamura almost 3 years ago

FYI: shyouhei is working for the fix. Here's commits for ruby18 branch.
http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?revision=31732&view=revision
http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?revision=31734&view=revision
And a little modification for ruby18_7 adjustment by me.
https://gist.github.com/992380

This patch intends;
- do not remove an autoload constant before autoloading.
- additional stop condition for edge case; when autoloading does not define the constant.

As I wrote above, this fix changes the following behavior.

% ruby187 -I. -e 'autoload(:X, "X"); begin; X; rescue LoadError; end; p Object.constants.include?("X")'
false #=> true like 1.9

Ruby developers, please let us know if you have any thoughts on this change.
With JRuby dev hat, I think it's OK. How other Ruby impls devs think?

#21 Updated by Akira Tanaka almost 3 years ago

  • Project changed from Ruby to ruby-trunk

#22 Updated by Hiroshi Nakamura almost 3 years ago

  • Assignee changed from Shyouhei Urabe to Hiroshi Nakamura
  • Target version set to 2.0.0

I discussed this with Shyouhei, Sasada, Tanaka and Naruse. And we don't have a consensus.

#23 Updated by Hiroshi Nakamura over 2 years ago

  • Category set to core

(Still no progress, just adding a note for future work)

I proposed the fix for JRuby at http://bit.ly/npPi66 (http://jira.codehaus.org/browse/JRUBY-3194)
And I had a talk about this issue in front of other Japanese CRuby committers last night.
http://prezi.com/ff9yptxhohjz/making-autoload-thread-safe/

As the result of discussing, we are going to fix CRuby as the same way.

#24 Updated by Hiroshi Nakamura over 2 years ago

I created a patch: https://github.com/nahi/ruby/compare/48bc63fa...54327abc
Here's the branch: https://github.com/nahi/ruby/commits/autoload-threadsafe

  • let Module keep 'UNDEF' in constant map until the end of autoloading
  • let Module keep actual object in another map (autoloading map)
  • add extra checks for constant looku

#25 Updated by Hiroshi Nakamura over 2 years ago

Updated the patch: https://github.com/nahi/ruby/compare/11667b9c...8b78a18e
Rewritten with strust instead of st_table.

#26 Updated by Hiroshi Nakamura over 2 years ago

=begin

Here's the updated patch: [[https://github.com/nahi/ruby/compare/11667b9c...03ddf439]]

Summary

  • ((What's the problem?)) autoload is thread unsafe. When we define a constant to be autoloaded, we expect the constant construction is invariant. But current autoload implementation allows other threads to access the constant while the first thread is loading a file. See http://prezi.com/ff9yptxhohjz/making-autoload-thread-safe/ for an example.

  • ((What's happening inside?)) The current implementation uses Qundef as a marker of autoload in Constant table. Once the first thread find Qundef as a value at constant lookup, it starts loading a defined feature. Generally a loaded file overrides the Qundef in Constant table by module/class declaration at very beginning lines of the file, so other threads can see the new Module/Class object before feature loading is finished. It breaks invariant construction.

  • ((How to solve?)) To ensure invariant constant construction, we need to override Qundef with defined Object after the feature loading. For keeping Qundef in Constant table, I expanded autoload_data struct in Module to have a slot for keeping the defined object while feature loading. And changed Module's constant lookup/update logic a little so that the slot is only visible from the thread which invokes feature loading. (== the first thread which accessed the autoload constant)

  • ((Evaluation?)) All test passes (bootstrap test, test-all and RubySpec) and added 8 tests for threading behavior. Extra logics are executed only when Qundef is found, so no perf drop should happen except autoloading.

I'll commit this soon. Committers, please evaluate this.
=end

#27 Updated by Eric Wong over 2 years ago

Hiroshi Nakamura nakahiro@gmail.com wrote:

I'll commit this soon. Committers, please evaluate this.

I noticed this got committed (r33078) and reverted (r33093).
Is a better commit planned? Thanks for working on this!

#28 Updated by Hiroshi Nakamura over 2 years ago

  • ruby -v changed from ruby 1.8.7 (2011-02-18 patchlevel 334) [x86_64-linux] to -

On Tue, Aug 30, 2011 at 07:14, Eric Wong normalperson@yhbt.net wrote:

Hiroshi Nakamura nakahiro@gmail.com wrote:

I'll commit this soon. Committers, please evaluate this.

I noticed this got committed (r33078) and reverted (r33093).
Is a better commit planned?  Thanks for working on this!

Yes, it broke Rails X-( I forgot a path to invoke rbautoloadload
from insns.def.

I fixed it my local repo but I need to ensure it passes Rails test
before committing it. :)

Thanks for your concern!

#29 Updated by Hiroshi Nakamura over 2 years ago

Hmm. Updating via reply e-mail seems to remove 'ruby -v'...

#30 Updated by Hiroshi Nakamura over 2 years ago

I re-apply r33078 and additional fixes at r33147. I tested it against Rails 3.1.0 and confirmed that it passes all of full tests except PG/MySQL/SQLite things which I don't installed properly.

Please evaluate it!

P.S. It won't included in 1.9.3 GA, but tell me if you find autoload threading issues on 193 as well, since RubyGems, RDoc, etc starts using autoload from 193. I think there's a chance to backport it. :)

#31 Updated by Eric Wong over 2 years ago

Hiroshi Nakamura nakahiro@gmail.com wrote:

I re-apply r33078 and additional fixes at r33147. I tested it against
Rails 3.1.0 and confirmed that it passes all of full tests except
PG/MySQL/SQLite things which I don't installed properly.

Please evaluate it!

I got the following with r33147 on gcc (Debian 4.6.1-4) 4.6.1:

../variable.c: In function ‘autoloaddefinedp’:
../variable.c:1613:5: error: implicit declaration of function ‘rbautoloadingvalue’ [-Werror=implicit-function-declaration]
cc1: some warnings being treated as errors

Moving the definition of autoloaddefinedp() after the definition of
rbautoloadingvalue() trivially fixes my build:

--- a/variable.c
+++ b/variable.c
@@ -1601,18 +1601,6 @@ checkautoloadrequired(VALUE mod, ID id, const char **loadingpath)
return 0;
}

-static int
-autoloaddefinedp(VALUE mod, ID id)
-{
- struct sttable *tbl = RCLASSCONSTTBL(mod);
- st
datat val;
-
- if (!tbl || !st
lookup(tbl, (stdatat)id, &val) || ((rbconstentryt*)val)->value != Qundef) {
- return 0;
- }
- return !rb
autoloadingvalue(mod, id, NULL);
-}
-
int
rb
autoloadingvalue(VALUE mod, ID id, VALUE* value)
{
@@ -1633,6 +1621,18 @@ rb
autoloading_value(VALUE mod, ID id, VALUE* value)
return 0;
}

+static int
+autoloaddefinedp(VALUE mod, ID id)
+{
+ struct sttable *tbl = RCLASSCONSTTBL(mod);
+ st
datat val;
+
+ if (!tbl || !st
lookup(tbl, (stdatat)id, &val) || ((rbconstentryt*)val)->value != Qundef) {
+ return 0;
+ }
+ return !rb
autoloadingvalue(mod, id, NULL);
+}
+
struct autoload
constsetargs {
VALUE mod;
ID id;
--
Eric Wong

#32 Updated by Hiroshi Nakamura over 2 years ago

Eric Wong wrote:

I got the following with r33147 on gcc (Debian 4.6.1-4) 4.6.1:

../variable.c: In function ‘autoloaddefinedp’:
../variable.c:1613:5: error: implicit declaration of function ‘rbautoloadingvalue’ [-Werror=implicit-function-declaration]
cc1: some warnings being treated as errors

Moving the definition of autoloaddefinedp() after the definition of
rbautoloadingvalue() trivially fixes my build:
[snip]

NARUSE-san applied this at r33151. Thank, guys!

#33 Updated by Hiroshi Nakamura over 2 years ago

  • Status changed from Open to Closed
  • Target version changed from 2.0.0 to 1.9.4

Closing. Please reopen this ticket if you find any regression.

#34 Updated by Ian MacLeod about 1 year ago

Apologies in advance if this is the wrong place to get clarification:

It's been widely circulated that autoload is deprecated - http://www.ruby-forum.com/topic/3036681 - does this patch change that stance?

I ask because autoload still appears to be the best route for libraries to avoid heavy load times; especially now that it is thread safe in JRuby and MRI.

Also available in: Atom PDF