Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

#inspect goes into infinite loop #22

Closed
mperham opened this Issue · 13 comments

7 participants

@mperham

An actor that has an ivar to an object that contains a circular reference to the same actor will go into an infinite loop when #inspect is called on the actor.

@benlangfeld
Collaborator

@tarcieri Do you have any thoughts on the way to approach the fix for this? I need to get it fixed within the next few days and would appreciate some pointers.

@mperham
@tarcieri
Owner

Short of that I don''t have a near-term solution, unfortunately. I need a recursion detection mechanism, and that won't be trivial. I've been mulling some sort of way to collect stack traces across actors. At the very least I need a UUID mechanism for call chains.

@benlangfeld
Collaborator
@tarcieri
Owner

I added Celluloid.uuid that I'd like to be able to use to track "call chains", that is the flow of calls between actors. Each call chain will have a unique UUID.

If you know the UUID of every active call chain, it shouldn't be too hard to detect "recursion", or perhaps more aptly reentrant calls from the same call chain.

@knewter

:+1: I'm a total noob and this infinite loop is trivial to run into and caused me moderately sad grief. My load spiked to > 1000 on a core i5 in about 60 seconds

@tarcieri
Owner

:(

The temporary workaround is to redefine "inspect" on one of the objects.

I have the rudiments of a permanent fix in place, but I have not yet wired them up

@tarcieri
Owner

In case anyone is wondering, this is still a known issue. I will try to get a fix into Celluloid 0.13

@jrochkind

Ruby stdlib Object#inspect avoids this with some kind of recursion detector. It may (or may not) be useful to look at how it does so as a model. It seems to use a C function 'rb_exec_recursive'. You may be well familiar with this and know exactly what ruby Object#inspect does and why it doesn't apply to celluloid, just didn't see it mentioned and got curious and went to look at the ruby stdlib code and behavior.

1.9.3p194 :001 > o = Object.new
 => #<Object:0x1f9dac> 
1.9.3p194 :002 > o.instance_variable_set("@foo", o)
 => #<Object:0x1f9dac @foo=#<Object:0x1f9dac ...>> 
1.9.3p194 :003 > o.inspect
 => "#<Object:0x1f9dac @foo=#<Object:0x1f9dac ...>>" 
@tarcieri
Owner

@jrochkind yeah, so I've been building the prerequisites for a recursion detector in Celluloid. All call chains are now tagged with a UUID, so in theory it's as simple as walking the task lists and see if another task for the same method name is participating in the same call chain as the current one (i.e. they have the same UUID).

Unfortunately there's not presently a way to inspect the call chain for a given task, although that information is being propagated throughout the system.

@chuckremes

What's the recommended work around for now? Is it sufficient to define an #inspect method on actors in the system? Or do I need to redefine inspect on non-actor objects too?

@tarcieri
Owner

Redefine #inspect for actors pretty much. You might also confirm you're seeing a spike in CPU usage

@tarcieri tarcieri referenced this issue from a commit
@tarcieri tarcieri Detect recursion in #inspect (fixes #22)
This detects recursion between actors during #inspect and similates Ruby's own
behavior for representing circular object graphs, replacing duplicate instances
of the original actor in the inspect output with "...", e.g.:

    #<Celluloid::ActorProxy(Foo:0x3fe79ea577f4) @bar=#<Celluloid::ActorProxy(
    Bar:0x3fe79e0585b4) @foo=...>>

The above shows a cyclical relationship between the Foo and Bar classes.

Without this sort of detection #inspect goes into an infinite loop which can
be both confusing and infuriating to newcomers and the experienced alike,
myself included!
2bee037
@halorgium
Owner

I'm gonna close this as we have a solution in #250.

@halorgium halorgium closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.