On Wed, 27 Oct 2004, Robert Klemme wrote:

> 
> "Markus" <markus / reality.com> schrieb im Newsbeitrag 
> news:1098808602.21256.755.camel / lapdog.reality.com...
> > On Tue, 2004-10-26 at 08:54, Robert Klemme wrote:
> >> "Markus" <markus / reality.com> schrieb im Newsbeitrag
> >> news:1098804673.21256.734.camel / lapdog.reality.com...
> >> > On Tue, 2004-10-26 at 02:14, Erwan Loisant wrote:
> >> > > What could be a better design? I there a way to design my C library
> >> > > better so I can tell Ruby's garbage collector about references 
> >> > > without
> >> > > introducing a Ruby dependency in my library's source code ?
> >> >
> >> >      One idea might be to give ruby "handles" of some sort that were
> >> > only usable/meaningful via calls to your library.  As far as ruby's GC
> >> > was concerned, they would just be integers.  You would then be able to
> >> > manage your storage as you saw fit, and ruby would be able to do
> >> > likewise.
> >>
> >> IMHO this is probably the most reasonable approach.  Additionally Erwan
> >> should then have some mechanism of verifying the handle stored in a
> >> wrapper object to be able to detect the situation that you have a wrapper
> >> whose corresponding C model instance is gone.  Unfortunately you would
> >> have to check the validity in every method that needs the C model 
> >> instance
> >> or even in *every* method.  Alternatively you need some kind of observer
> >> mechanism so your Ruby model instances get to know that their C model
> >> counterparts are gone.  But even then you still some other object might
> >> reference the Ruby model instance...
> >>
> >> Another issue is aliasing: depending on the application it might be
> >> crucial that you have at most one Ruby model instance per C model
> >> instance, i.e., you want the same representant on the higher level.  This
> >> is typically the case when you store additional state in the wrapper
> >> instance.  You can handle this with some sort of mapping - but you'll 
> >> have
> >> to be careful with respect to GC in order to not fix wrapper instances in
> >> mem: You might need a Hash where values are WeakReferences to the 
> >> wrapping
> >> instances.
> >>
> >> This is all not very nice and personally I haven't found a clean, nice 
> >> and
> >> efficient solution to this issue...
> >
> >     If he wants to be truly independent of ruby (so that his library
> > can be used elsewhere) I would suggest the following:
> >
> >      * Assign each C object a handle (int) on creation.
> >      * Store the handle in the object
> >      * Maintain an index mapping handle --> C object
> >      * Only talk to outsiders in terms of handles
> >      * When an outsider tries to talk to you about a C object, first
> >        check that the handle is valid (via the index)
> >
> >
> >     There are several operations that would need to be implemented in
> > the library:
> >
> >      * Assigning a handle
> >      * Adding a handle=>object to the index
> >      * Looking up an object given a handle
> >      * Cleaning up the index when an object is deleted
> >      * Detecting if an object is valid
> >
> >     All of them admit to rather simple, time & space efficient
> > solutions, provided you know what sort of load (lots of lookups, few
> > creations, or visa versa, etc.) to expect.  For example, if deletions
> > were going to be rare or done all at the end, the index could be a
> > hierarchical array and:
> >
> >      * Assigning a handle-- this_handle = (next_handle++)
> >      * Adding a handle=>object to the index -- 
> >                index[this_handle] = this_object
> >      * Looking up an object given a handle -- index[handle]
> >      * Cleaning up the index when an object is deleted --
> >                index[this_handle] = NULL
> >      * Detecting if an object is valid -- index[handle]
> >
> >
> >     If there was going to be more turnover, you could use a hash for
> > the index, or store an additional id with the object (and in the index)
> > to detect recycled handles.  In effect, the handle becomes a
> > (slot,version) pair.  The later may be a tad faster but a hash is less
> > ad hoc and might be easier to maintain.
> 
> Completely ACK!  Still, these solutions do not get rid of the problem of 
> orphaned handles, which renders them somehow ugly.  But the ugliness is in 
> the problem, not in the solution. *sigh*
> 
> Kind regards
> 
>     robert

     Why ACK?  This is a pretty standard solution, it works, it's clean 
and fast to implement, and does what is needed...and the semantics are a 
direct extention of the usual pointer semantics, adding only what is 
needed to solve the problem at hand.

     Specifically, a pointer is just an integer that (by convention) tells 
you how to find the object you want (e.g. that it is so many bytes from 
the start of your memory.  These handles are just integer that (by the 
proposed convention) tell you how to find the object that you want, but 
the details of their dereferencing (which can and should be hidden from 
the "user") are somewhat different.  Why is one more ugly than the other?

     As for the orphaned handles, that is the whole point.  He wants to 
decouple the library from the use of the library in a way that does not 
require the library to know anything about ruby's internals.  Which means 
that it will be possible to have orphaned handles.  So a means of 
detecting them is needed. 

     If I write something like:

     g = A_graph.new
     points.each { |p| g.add_node(p) }
     lines.each  { |p1,p2| g.add_node(p1,p2) }
     x = g.random_unconnected_node
     if x
          while p = g.random_unconnected_node
              g.delete_node(p)
              end
          end
     print x.inspect

I would _expect_ some sort of error in the final print statement, since x 
should have been deleted in the while loop, just as I would expect an 
error if I tried to cat a file after I deleted it, even if I had written 
its name down.

     -- Markus