Skip to content

#inspect goes into infinite loop #22

mperham opened this Issue Jan 29, 2012 · 13 comments

7 participants

mperham commented Jan 29, 2012

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.

Celluloid member

@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.

Celluloid member

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.

Celluloid member
Celluloid member

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 commented Apr 24, 2012

👍 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

Celluloid member


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

Celluloid member

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


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: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 ...>>" 
Celluloid member

@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.


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?

Celluloid member

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

@tarcieri tarcieri added a commit that referenced this issue May 12, 2013
@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!
Celluloid member

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

@halorgium halorgium closed this May 13, 2013
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.