Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Weak References?

0 views
Skip to first unread message

Benjamin Goldberg

unread,
Aug 21, 2003, 9:27:16 PM8/21/03
to perl6-i...@perl.org

I would like for Parrot to have some way of creating Weak References; I
think that this is probably a vital feature.

The way I envision this is as follows. The following typedef and new
function would be added:

typedef void (*pobject_died_cb)(INTERP, PMC* who_asked,
Pobj* weakref, void *callback_info);
void pobject_weakref(INTERP, pobject_died_cb callback,
Pobj* weakref, void *callback_info);

Inside of a PMC*'s mark method, it registers callbacks (using the above
function) so that it will be informed about what to do if the object to
which it weakly refers to is found to not be alive at the end of the DOD
sweep.

The pobject_weakref function first checks if the 'weakref' argument has
been marked as alive -- if so, nothing happens. Then, it adds the Pobj*
to a lookup table, pointing from the Pobj*, to a list of registered
callbacks for that Pobj*.

After DOD finishes, the lookup table is walked; for each entry whose
Pobj* hasn't been marked as alive, the callbacks are called.

The effect of this of course is that a WeakRef has no cost except during
Dead Object Detection.


The first, perhaps most important use, would be to implement a
string-interning table.

You'd have a global (per-interpreter) pmc which contains a hashtable of
cstrings to perlstrings; if the cstring is present, the corresponding
perlstring is returned; otherwise a new perlstring would be created and
added to the table, then returned. If any of the perlstrings go out of
scope, then their hash entries should disappear from the table.
Obivously, if the table contained normal references to the strings, then
they won't ever go out of scope. And if the table simply doesn't mark
them as alive, it wouldn't know when they're dead. But with weakrefs,
this is easy.

--
$a=24;split//,240513;s/\B/ => /for@@=qw(ac ab bc ba cb ca
);{push(@b,$a),($a-=6)^=1 for 2..$a/6x--$|;print "$@[$a%6
]\n";((6<=($a-=6))?$a+=$_[$a%6]-$a%6:($a=pop @b))&&redo;}

Juergen Boemmels

unread,
Aug 22, 2003, 8:04:45 AM8/22/03
to Benjamin Goldberg, perl6-i...@perl.org
Benjamin Goldberg <ben.go...@hotpop.com> writes:

> I would like for Parrot to have some way of creating Weak References; I
> think that this is probably a vital feature.
>
> The way I envision this is as follows. The following typedef and new
> function would be added:
>
> typedef void (*pobject_died_cb)(INTERP, PMC* who_asked,
> Pobj* weakref, void *callback_info);
> void pobject_weakref(INTERP, pobject_died_cb callback,
> Pobj* weakref, void *callback_info);

This might be sometimes useful. Keeping a container of active objects
like filehandles or windows for example. In this case it can't mark
the reference because this would make the object active forever.

> Inside of a PMC*'s mark method, it registers callbacks (using the above
> function) so that it will be informed about what to do if the object to
> which it weakly refers to is found to not be alive at the end of the DOD
> sweep.

This does not need to go into the mark function. The weakref_callback
code can be called inside the destroy function. Im not sure if it
should be called befor or after the custom destroy-function is
run. And is the order of the weakref_callbacks defined.

> The pobject_weakref function first checks if the 'weakref' argument has
> been marked as alive -- if so, nothing happens. Then, it adds the Pobj*
> to a lookup table, pointing from the Pobj*, to a list of registered
> callbacks for that Pobj*.

This is far to complicated. Each object has a list of
destroy-functions, one of them is the costom-destroy function the
others are the weakref-callbacks.

But one other thing, what happens if the object holding the weakref
dies before the refrenced object? Then the callback-function will be
called for a dead object. So pobject_weakref() needs to return a handle
for the weakref and there needs to be a function
weakref_destroy(weakref_handle *handle).

Other issue is who owns the data_structure of the weakref? The
referent, the referee, or will this be garbage-collected (which makes
the weakref_handle a PObj* and weakref_destroy its custom destroy function.

> After DOD finishes, the lookup table is walked; for each entry whose
> Pobj* hasn't been marked as alive, the callbacks are called.
>
> The effect of this of course is that a WeakRef has no cost except during
> Dead Object Detection.

It only has a cost at object destroy-time. (If the weakrefs are
garbagecollected they have an effect on DOD in the way that there are
more objects to trace)

> The first, perhaps most important use, would be to implement a
> string-interning table.
>
> You'd have a global (per-interpreter) pmc which contains a hashtable of
> cstrings to perlstrings; if the cstring is present, the corresponding
> perlstring is returned; otherwise a new perlstring would be created and
> added to the table, then returned. If any of the perlstrings go out of
> scope, then their hash entries should disappear from the table.
> Obivously, if the table contained normal references to the strings, then
> they won't ever go out of scope. And if the table simply doesn't mark
> them as alive, it wouldn't know when they're dead. But with weakrefs,
> this is easy.

this is only useful if a hashlookup is fast compared with
string_make.

bye
boe
--
Juergen Boemmels boem...@physik.uni-kl.de
Fachbereich Physik Tel: ++49-(0)631-205-2817
Universitaet Kaiserslautern Fax: ++49-(0)631-205-3906
PGP Key fingerprint = 9F 56 54 3D 45 C1 32 6F 23 F6 C7 2F 85 93 DD 47

Benjamin Goldberg

unread,
Aug 22, 2003, 8:09:17 PM8/22/03
to perl6-i...@perl.org

Benjamin Goldberg

unread,
Aug 22, 2003, 8:12:25 PM8/22/03
to perl6-i...@perl.org

But suppose that at the end of DoD, the object we're weakly referring to
gets marked as alive? Now, we would need to look through that object's
list of destroy-functions, and remove all of the weakref-callbacks.

But if the weakref-callbacks are stored in a seperate data structure,
created during DoD, then there's no problem; this data structure will
get re-created for each DoD pass, and thus always start empty.

Also, by keeping them seperate, we can walk all the callback functions
before destructing the dead objects.

(But you're right -- it is too complicated to do a lookup table. A
simple linked list should do fine.)

> But one other thing, what happens if the object holding the weakref
> dies before the refrenced object? Then the callback-function will be
> called for a dead object.

Each callback-function "belongs" to a pmc. The DoD should be able to
know this, and act on it. So if the pmc which registered the callback
is dead, (or if the object weakly referred has since then come alive),
then the callback isn't called.

> So pobject_weakref() needs to return a handle
> for the weakref and there needs to be a function
> weakref_destroy(weakref_handle *handle).
>
> Other issue is who owns the data_structure of the weakref? The
> referent, the referee, or will this be garbage-collected (which makes
> the weakref_handle a PObj* and weakref_destroy its custom destroy
> function.

The garbage collector owns everything except for callback_info, which
belongs to the pmc which registered the weakref-callback.

> > After DOD finishes, the lookup table is walked; for each entry whose
> > Pobj* hasn't been marked as alive, the callbacks are called.
> >
> > The effect of this of course is that a WeakRef has no cost except
> > during Dead Object Detection.
>
> It only has a cost at object destroy-time. (If the weakrefs are
> garbagecollected they have an effect on DOD in the way that there are
> more objects to trace)

*blink* More objects? Oh, you're assuming that pobject_weakref is
returning a Pobj* handle.

Keeping the callback data in a seperate list which only exists for the
duration of the dod prevents this. Or rather, you do have to clean up
the linked list, of course, but there's no extra bookkeeping.

> > The first, perhaps most important use, would be to implement a
> > string-interning table.
> >
> > You'd have a global (per-interpreter) pmc which contains a hashtable
> > of cstrings to perlstrings; if the cstring is present, the
> > corresponding perlstring is returned; otherwise a new perlstring would
> > be created and added to the table, then returned. If any of the
> > perlstrings go out of scope, then their hash entries should disappear
> > from the table. Obivously, if the table contained normal references
> > to the strings, then they won't ever go out of scope. And if the
> > table simply doesn't mark them as alive, it wouldn't know when they're
> > dead. But with weakrefs, this is easy.
>
> this is only useful if a hashlookup is fast compared with
> string_make.

Well, it might be. Hashing can be quite fast, ya know.

Here's a better idea, one you'll have more difficulty arguing with --
imagine a debugger, written in parrot.

We are going to have one, right? Hmm, p6tkdb :)

It needs to keep references to objects it's interested in, but if
they're strong references, then we would have trouble debugging objects
with custom destroys (or worse, objects needing timely destruction),
since the debugger's references to them would prevent them from being
cleaned up.

Changing to weakrefs removes this kind of horrible heisenbug.

Luke Palmer

unread,
Aug 23, 2003, 12:28:28 AM8/23/03
to Benjamin Goldberg, perl6-i...@perl.org
Benjamin Goldberg writes:
> Juergen Boemmels wrote:
> > Benjamin Goldberg <ben.go...@hotpop.com> writes:
> >
> > > The pobject_weakref function first checks if the 'weakref' argument
> > > has been marked as alive -- if so, nothing happens. Then, it adds the
> > > Pobj* to a lookup table, pointing from the Pobj*, to a list of
> > > registered callbacks for that Pobj*.
> >
> > This is far to complicated. Each object has a list of
> > destroy-functions, one of them is the costom-destroy function the
> > others are the weakref-callbacks.
>
> But suppose that at the end of DoD, the object we're weakly referring to
> gets marked as alive? Now, we would need to look through that object's
> list of destroy-functions, and remove all of the weakref-callbacks.
>
> But if the weakref-callbacks are stored in a seperate data structure,
> created during DoD, then there's no problem; this data structure will
> get re-created for each DoD pass, and thus always start empty.
>
> Also, by keeping them seperate, we can walk all the callback functions
> before destructing the dead objects.
>
> (But you're right -- it is too complicated to do a lookup table. A
> simple linked list should do fine.)

I may be missing the problem that you are talking about, but it seems to
me that since we have PMCs which mark themselves instead of being
automatically marked, a WeakRef PMC would be trivial... I don't think
it needs to be any more fundamental than a PMC.

Luke

Juergen Boemmels

unread,
Aug 23, 2003, 7:01:38 AM8/23/03
to Benjamin Goldberg, perl6-i...@perl.org
Benjamin Goldberg <ben.go...@hotpop.com> writes:


> But suppose that at the end of DoD, the object we're weakly referring to
> gets marked as alive? Now, we would need to look through that object's
> list of destroy-functions, and remove all of the weakref-callbacks.

At the end of DoD nobody gets marked alive anymore. The calling of
destroy-functions is done at sweep-time. There might be a problem when
you destroy referent and referee at the same DoD run. Then the
weakref-callback can be called on a dead but not destroyed object. It
is a matter of destruction ordering to destroy the referee first,
which destroyes the weakref.

> But if the weakref-callbacks are stored in a seperate data structure,
> created during DoD, then there's no problem; this data structure will
> get re-created for each DoD pass, and thus always start empty.

This is unnecessary complicated. And it slows down the DoD run by
creating a datastructure.

> Also, by keeping them seperate, we can walk all the callback functions
> before destructing the dead objects.

This is a problem of destruction ordering. You try to solve it by
introducing a seperate step, which works in this special case. But
destruction ordering is a much harder problem.

> (But you're right -- it is too complicated to do a lookup table. A
> simple linked list should do fine.)
>
> > But one other thing, what happens if the object holding the weakref
> > dies before the refrenced object? Then the callback-function will be
> > called for a dead object.
>
> Each callback-function "belongs" to a pmc. The DoD should be able to
> know this, and act on it. So if the pmc which registered the callback
> is dead, (or if the object weakly referred has since then come alive),
> then the callback isn't called.
>
> > So pobject_weakref() needs to return a handle
> > for the weakref and there needs to be a function
> > weakref_destroy(weakref_handle *handle).
> >
> > Other issue is who owns the data_structure of the weakref? The
> > referent, the referee, or will this be garbage-collected (which makes
> > the weakref_handle a PObj* and weakref_destroy its custom destroy
> > function.
>
> The garbage collector owns everything except for callback_info, which
> belongs to the pmc which registered the weakref-callback.

This is one possiblity. Therefor the destroy-function of the
registering PMC must be extended with freeing the callback_info. As we
already extend the destroy function of the PMC referenced by the
weakref this needs no new mechanics.

> > > After DOD finishes, the lookup table is walked; for each entry whose
> > > Pobj* hasn't been marked as alive, the callbacks are called.
> > >
> > > The effect of this of course is that a WeakRef has no cost except
> > > during Dead Object Detection.
> >
> > It only has a cost at object destroy-time. (If the weakrefs are
> > garbagecollected they have an effect on DOD in the way that there are
> > more objects to trace)
>
> *blink* More objects? Oh, you're assuming that pobject_weakref is
> returning a Pobj* handle.

Returning a PObj* handle is the other possibility. The registering PMC
holds a hard reference to the callback_info, and the callback_info
deregisters itself when it gets destroyed. The advantage of this
approach is there is no need to malloc/free the memory for
callback_info, it just uses the standard gc-allocator.

> Keeping the callback data in a seperate list which only exists for the
> duration of the dod prevents this. Or rather, you do have to clean up
> the linked list, of course, but there's no extra bookkeeping.

*blink too* You don't want to use a weakref you want a weak_MARK. The
callbacks are getting registered during each mark, and get used or
destroyed. But this registring and destructing has a cost too. This
cost is only payed by the weakmark-using objects, but they need to be
paid on every DoD run.

> > this is only useful if a hashlookup is fast compared with
> > string_make.
>
> Well, it might be. Hashing can be quite fast, ya know.

Only the profiler can tell you which one is faster.

> Here's a better idea, one you'll have more difficulty arguing with --
> imagine a debugger, written in parrot.
>
> We are going to have one, right? Hmm, p6tkdb :)
>
> It needs to keep references to objects it's interested in, but if
> they're strong references, then we would have trouble debugging objects
> with custom destroys (or worse, objects needing timely destruction),
> since the debugger's references to them would prevent them from being
> cleaned up.
>
> Changing to weakrefs removes this kind of horrible heisenbug.

Something totaly diffrent. DoD runs happen normaly in out of memory
situations. Doing things like running a debugger callback befor a
sweep finishes might ask for trouble. If the debugger is writen in
parrot, then the callback-function which deregisters the object is
surely also written in parrot. Recursive runloops with unfinished
sweeps, combined with early destruction in the inner runloop. This
will be really fun.

Juergen Boemmels

unread,
Aug 23, 2003, 7:14:52 AM8/23/03
to Luke Palmer, Benjamin Goldberg, perl6-i...@perl.org
Luke Palmer <fibo...@babylonia.flatirons.org> writes:

> I may be missing the problem that you are talking about, but it seems to
> me that since we have PMCs which mark themselves instead of being
> automatically marked, a WeakRef PMC would be trivial... I don't think
> it needs to be any more fundamental than a PMC.

Marking itself does not help here. The mark-function of a PMC is
called after it is already marked. Self-marking does nothing in the
best case, in the worst case it blocks the DoD in an endless loop.

Anyway a weakref needs to extend the destroy-function of the refered
object (on a per object basis). This is whats not possible in current
parrot. Parrot_exit/Parrot_on_exit does something similar but not for
the destruction of an object but for the end of the program.

Benjamin Goldberg

unread,
Aug 23, 2003, 8:17:03 PM8/23/03
to perl6-i...@perl.org

I dislike replying to myself, however, it seems I haven't been clear
enough in describing how I think this (c|sh)ould be implemented.

Let's abstract DoD to the following psuedocode:

function markalive(p) {
if(!p.is_alive)
interpreter->dod_queue.enqueue(p);
}
for p in all pmcs
p.is_alive = false
for p in rootset
interpreter->dod_queue.enqueue(p)
while interpreter->dod_queue.size()
p = interpreter->dod_queue.dequeue()
if(p.is_alive) continue
p.is_alive = true
if(p.has_special_mark)
p.mark()
for p in all pmcs
if !p.is_alive
if p.has_destruct
p.destruct();
add p to list of free pmcs
# end

With my proposal, we'd change it to:

function markalive(p) {
if(!p.is_alive)
interpreter->dod_queue.enqueue(p);
}
function weakmark(asker, asked_of, cb, cb_info) {
if(!asked_of.is_alive)
interpreter->weakref_cnt ++;
interpreter->weakref_list.push(cb);
interpreter->weakref_list.push(cb_info);
interpreter->weakref_list.push(asked_of);
}
interpreter->weakref_cnt = 0;
interpreter->weakref_list = new fast_unsafe_stack;
for p in all pmcs
p.is_alive = false
for p in rootset

q = interpreter's list of objs to sweep
while( interpreter->dod_queue.size() )
p = interpreter->dod_queue.dequeue()
p.is_alive = true;
if(p.has_special_mark)
p.mark()
if( interpreter->weakref_cnt )
interpreter->weakref_list.push(interpreter->weakref_cnt);
interpreter->weakref_list.push(p);
interpreter->weakref_cnt = 0;
while( interpreter->weakref_list.size() )
p = interpreter->weakref_list.pop();
i = interpreter->weakref_list.pop();
if( !p.is_alive ) {
while( i-- ) {
interpreter->weakref_list.pop();
interpreter->weakref_list.pop();
interpreter->weakref_list.pop();
}
} else {
while( i-- ) {
deadobj = interpreter->weakref_list.pop();
if( deadobj.is_alive ) {
interpreter->weakref_list.pop();
interpreter->weakref_list.pop();
} else {
cb_extra = interpreter->weakref_list.pop();
callback = interpreter->weakref_list.pop();
callback->(p, deadobj, cb_extra);
}
}
}
}
free up memory used by interpreter->weakref_list
interpreter->weakref_list = NULL;
for p in all pmcs
if(!p.is_alive) {
if( p.has_destruct ) p.destruct();
add p to list of free pmcs
}
# end

Notice that the marking phase of dod now does a single extra integer
check in the common case of objects not having weak references; I
believe that this won't be a significant speed penalty.

Further, note that the weakref_list doesn't need to know (record)
anything about the type of the data being stored in it -- push can take
a void*, and pop can return a void*, and if we want an int, we can just
use a cast.

The reason this is safe to do is because it's *only* created by the dod,
and *only* accessed through the weakmark function; since it's not
exposed to *arbitrary* code; there's no chance of it being accessed
incorrectly, since it's not exposed.

If we wanted, it could be defined as follows:
typedef struct {
ptrdiff_t * b;
ptrdiff_t * i;
} weaklreflist;
weaklreflist * new_weaklreflist() {
weaklreflist * list = malloc(sizeof(weaklreflist));
list->b = malloc(sizeof(ptrdiff_t) * 1024);
list->b[0] = 0;
list->i = b;
return list;
}
void wekref_list_push(weaklreflist * list, void* val) {
if( ++list->i == list->b+1023 ) {
ptrdiff_t * newb = malloc(sizeof(ptrdiff_t)*1024);
ptrdiff_t * oldb = list->b;
newb[0] = (ptrdiff_t)oldb;
oldb[1023] = (ptrdiff_t)newb;
list->b = newb;
list->i = newb + 1;
}
*(list->i) = (ptrdiff_t)val;
}
void* weakref_list_pop(weaklreflist * list) {
if( list->i-- == b ) {
list->b = (ptrdiff_t*)*(list->b);
list->i = &(list->b[1022]);
}
return (void*)*(list->i);
}
void free_weakreflist(weakreflist * list) {
ptrdiff_t * b = list->b, *n;
assert(list->i == list->b+1);
assert(list->b[0] == 0);
do {
n = (ptrdiff_t*)b[1023];
free(b);
b = n;
} while(b);
free(list);
}
[untested, might contain syntax and logical errors]

I used ptrdiff_t instead of void*, since dealing with pointers to void**
always confuses me and makes my head hurt, not to mention making my code
harder for me to read.

Benjamin Goldberg

unread,
Aug 23, 2003, 9:30:28 PM8/23/03
to perl6-i...@perl.org

Juergen Boemmels wrote:
>
> Benjamin Goldberg <ben.go...@hotpop.com> writes:
>
> > But suppose that at the end of DoD, the object we're weakly referring
> > to gets marked as alive? Now, we would need to look through that
> > object's list of destroy-functions, and remove all of the
> > weakref-callbacks.
>
> At the end of DoD nobody gets marked alive anymore. The calling of
> destroy-functions is done at sweep-time. There might be a problem when
> you destroy referent and referee at the same DoD run. Then the
> weakref-callback can be called on a dead but not destroyed object. It
> is a matter of destruction ordering to destroy the referee first,
> which destroyes the weakref.

Erm, I had meant to say, "suppose that *by* the time DoD has finished
sweeping, the object we're weakly referring to *has been* marked as
alive some time (after we've done pobject_weakref)," not "what happens
if it gets marked as alive after the end of DoD"

> > But if the weakref-callbacks are stored in a seperate data structure,
> > created during DoD, then there's no problem; this data structure will
> > get re-created for each DoD pass, and thus always start empty.
>
> This is unnecessary complicated. And it slows down the DoD run by
> creating a datastructure.

As opposed to attaching another destructor (which is, after all, a data
structure) to the object being weakly referred to?

Plus, since the data structure for the weakref callbacks gets created at
the start of the DoD function, and we're finished with it at the end of
the DoD function, then it doesn't need to be a PMC or any other
"generic" structure; it's allocation and deallocation will be quite
explicit, and it can be a special-purpose data structure written for
compactness and speed instead of generality.

> > Also, by keeping them seperate, we can walk all the callback functions
> > before destructing the dead objects.
>
> This is a problem of destruction ordering. You try to solve it by
> introducing a seperate step, which works in this special case. But
> destruction ordering is a much harder problem.

Well, it wouldn't be *harmful* to call the callbacks after the dead
objects are destructed (or even when *some* but not all of them are
destructed) ... however, to now be safe, we'd have to make it illegal to
operate on the Pobj pointer as anything other than opaque pointer, which
we may only perform equality comparisons with. Caching the information
about what the Pobj *used* to be would become necessary, if we needed to
know that info.

> > (But you're right -- it is too complicated to do a lookup table. A
> > simple linked list should do fine.)
> >
> > > But one other thing, what happens if the object holding the weakref
> > > dies before the refrenced object? Then the callback-function will be
> > > called for a dead object.
> >
> > Each callback-function "belongs" to a pmc. The DoD should be able to
> > know this, and act on it. So if the pmc which registered the callback
> > is dead, (or if the object weakly referred has since then come alive),
> > then the callback isn't called.
> >
> > > So pobject_weakref() needs to return a handle
> > > for the weakref and there needs to be a function
> > > weakref_destroy(weakref_handle *handle).
> > >
> > > Other issue is who owns the data_structure of the weakref? The
> > > referent, the referee, or will this be garbage-collected (which
> > > makes the weakref_handle a PObj* and weakref_destroy its custom
> > > destroy function.
> >
> > The garbage collector owns everything except for callback_info, which
> > belongs to the pmc which registered the weakref-callback.
>
> This is one possiblity. Therefor the destroy-function of the
> registering PMC must be extended with freeing the callback_info. As we
> already extend the destroy function of the PMC referenced by the
> weakref this needs no new mechanics.

? Where have we extended the destroy function of ... ? Hmm, oh, in your
proposed modification of ... . Ok. I suppose that works, though it
sounds like it's getting to be rather more complicated than my idea.
And bits of the weakref stuff is now scattered all over the place, stuck
onto each pmc being dealt with, instead of collected all in one nicely
managable location.

> > > > After DOD finishes, the lookup table is walked; for each entry
> > > > whose Pobj* hasn't been marked as alive, the callbacks are called.
> > > >
> > > > The effect of this of course is that a WeakRef has no cost except
> > > > during Dead Object Detection.
> > >
> > > It only has a cost at object destroy-time. (If the weakrefs are
> > > garbagecollected they have an effect on DOD in the way that there
> > > are more objects to trace)
> >
> > *blink* More objects? Oh, you're assuming that pobject_weakref is
> > returning a Pobj* handle.
>
> Returning a PObj* handle is the other possibility. The registering PMC
> holds a hard reference to the callback_info, and the callback_info
> deregisters itself when it gets destroyed. The advantage of this
> approach is there is no need to malloc/free the memory for
> callback_info, it just uses the standard gc-allocator.

Either you're confused, or you're confusing me. The callback_info is
just a void* pointer, which can be anything, ranging from an integer
cast to a pointer, or a pointer to global or static memory, or a pointer
into the object doing the registering, or a pointer into the interpreter
struct, or a pointer into some other pmc (one which we're marking as
alive, I hope!), or a pointer to whole PMC, or a pointer into the object
we're weakly referring to, or a mem_sys_allocate()d pointer.

If it's anything *other* than a mem_sys_allocate()d pointer, then
there's no need whatsoever to do anything to free it -- that'll happen
when necessary (or in the case of an int cast to a pointer, won't *be*
necessary).

If it *is* a mem_sys_allocate()d pointer, then of course someone needs
to free it. It had better not be one which was allocated by the pmc
being weakly referenced (and which will be freed by that pmc), or you're
in trouble (or at least, it's trouble if the callback may happen after
the referenced pmc is destructed)!

I'll assume that it's the referencing pmc that allocated it, and that
this was done inside of the mark() function, soley for the purposes of
the marking effect. Obviously, we'll need to keep a copy of this
pointer somewhere in our structure, so that *we* can free it in our
destroy, if necessary.

It's annoying though that if the weakly referenced object is found to be
alive, and we stay alive for a long while, this allocated memory stays
allocated for a long while, since we have no chance to free it earlier.

Hmm... I suppose that if the callback is called *whether or not* the
weakly referenced object dies, (and the callback then has to do a check
of whether the referenced object is really dead) then there's no
problem: it can free the sys_allocate()d memory inside the callback, and
we don't need to store any pointer to it anywhere.

> > Keeping the callback data in a seperate list which only exists for the
> > duration of the dod prevents this. Or rather, you do have to clean up
> > the linked list, of course, but there's no extra bookkeeping.
>
> *blink too* You don't want to use a weakref you want a weak_MARK. The
> callbacks are getting registered during each mark, and get used or
> destroyed. But this registring and destructing has a cost too. This
> cost is only payed by the weakmark-using objects, but they need to be
> paid on every DoD run.

Yes. What I meant was, no extra data is attached to pmcs, which we
would then have to keep track of.

> > > this is only useful if a hashlookup is fast compared with
> > > string_make.
> >
> > Well, it might be. Hashing can be quite fast, ya know.
>
> Only the profiler can tell you which one is faster.

Indeed. Anyway, doing this to strings *automatically* was merely a
thought -- we don't *have* to do it. (Well, we will have to when we
implement Java, since that's part of the spec. But we don't have to for
imcc, or perl6, unless we want to.

> > Here's a better idea, one you'll have more difficulty arguing with --
> > imagine a debugger, written in parrot.
> >
> > We are going to have one, right? Hmm, p6tkdb :)
> >
> > It needs to keep references to objects it's interested in, but if
> > they're strong references, then we would have trouble debugging
> > objects with custom destroys (or worse, objects needing timely
> > destruction), since the debugger's references to them would prevent
> > them from being cleaned up.
> >
> > Changing to weakrefs removes this kind of horrible heisenbug.
>
> Something totaly diffrent. DoD runs happen normaly in out of memory
> situations. Doing things like running a debugger callback befor a
> sweep finishes might ask for trouble. If the debugger is writen in
> parrot, then the callback-function which deregisters the object is
> surely also written in parrot. Recursive runloops with unfinished
> sweeps, combined with early destruction in the inner runloop. This
> will be really fun.

Blech, you're right.

Ok, how about this: If the callbacks are only called *after* everything
else is done (destructors called, memory compacted and maybe freed,
etc.), then the GC will be complete when the callback functions are
called.

Copy the pointer to the list of callback stuff into a C auto variable,
and then set the interpreter-> version of it to NULL. Thus, it should
be safe for a callback function to do something which might happen to
trigger DoD.

Since we cannot at this point in time deal with the dead objects as
actual Pobj* things (since they've been cleaned up, and their memory
presumably freed), that parameter would have to disappear from the
callback function's parameters (or maybe, rename the parameter as "void*
opaque", and forbid users (in the docs) from casting it into a Pobj*).
All the information needed to tell us what we *had been* weakly
referencing will now have to go into the callback_info pointer.

Gordon Henriksen

unread,
Aug 23, 2003, 11:32:30 PM8/23/03
to Benjamin Goldberg, perl6-i...@perl.org
On Saturday, August 23, 2003, at 08:17 , Benjamin Goldberg wrote:

> The reason this is safe to do is because it's *only* created by the dod,

Allocating memory during garbage collection?

Gordon Henriksen
mali...@mac.com

Dan Sugalski

unread,
Aug 24, 2003, 1:30:49 PM8/24/03
to perl6-i...@perl.org
At 9:27 PM -0400 8/21/03, Benjamin Goldberg wrote:
>I would like for Parrot to have some way of creating Weak References; I
>think that this is probably a vital feature.

No, it isn't, and we've discussed this before. (You were involved, as
I recall) Weak references can be done entirely with notifications,
there doesn't have to be any special core functionality for them
outside a weak reference PMC type and general notification support.

I'll wade through the rest of this thread to see if anything else has
come up from it, but if not then dig through the archives for the
last go-round and read what came of it.
--
Dan

--------------------------------------"it's like this"-------------------
Dan Sugalski even samurai
d...@sidhe.org have teddy bears and even
teddy bears get drunk

Benjamin Goldberg

unread,
Aug 24, 2003, 6:39:07 PM8/24/03
to perl6-i...@perl.org

Gordon Henriksen wrote:
>
> On Saturday, August 23, 2003, at 08:17 , Benjamin Goldberg wrote:
>
> > The reason this is safe to do is because it's *only* created by the
> > dod,
>
> Allocating memory during garbage collection?

Why not? Or at least, why not for sys_mem_allocate()d memory?

Benjamin Goldberg

unread,
Aug 24, 2003, 6:47:29 PM8/24/03
to perl6-i...@perl.org

Dan Sugalski wrote:
>
> At 9:27 PM -0400 8/21/03, Benjamin Goldberg wrote:
> >I would like for Parrot to have some way of creating Weak References; I
> >think that this is probably a vital feature.
>
> No, it isn't, and we've discussed this before. (You were involved, as
> I recall) Weak references can be done entirely with notifications,
> there doesn't have to be any special core functionality for them
> outside a weak reference PMC type and general notification support.
>
> I'll wade through the rest of this thread to see if anything else has
> come up from it, but if not then dig through the archives for the
> last go-round and read what came of it.

Googling doesn't seem to find it:
http://groups.google.com/groups?
q=group:perl.perl6.internals+weak+references
Shows only this thread. Searching for "notifications":
http://groups.google.com/groups?
q=group:perl.perl6.internals+notifications
Shows this thread, and an older, apparently unrelated thread.

Piers Cawley

unread,
Aug 26, 2003, 10:05:23 AM8/26/03
to Benjamin Goldberg, perl6-i...@perl.org
Benjamin Goldberg <ben.go...@hotpop.com> writes:
>> this is only useful if a hashlookup is fast compared with
>> string_make.
>
> Well, it might be. Hashing can be quite fast, ya know.
>
> Here's a better idea, one you'll have more difficulty arguing with --
> imagine a debugger, written in parrot.
>
> We are going to have one, right? Hmm, p6tkdb :)
>
> It needs to keep references to objects it's interested in, but if
> they're strong references, then we would have trouble debugging objects
> with custom destroys (or worse, objects needing timely destruction),
> since the debugger's references to them would prevent them from being
> cleaned up.

In general, I'd expect the Perl 6 debugger to use symbolic references
to variables and use %MY based introspection to get at
values. Watchlists could be implemented by via properties with
closures that refered back to the debugger object. In general it
should be possible to implement a remarkably powerful perl 6 debugger
without needing weakrefs (though weakrefs wouldn't hurt).

0 new messages