[perl #98204] Shared objects not destoryed
authorFather Chrysostomos <sprout@cpan.org>
Fri, 9 Sep 2011 05:35:44 +0000 (22:35 -0700)
committerFather Chrysostomos <sprout@cpan.org>
Fri, 9 Sep 2011 05:50:06 +0000 (22:50 -0700)
commit7d585d2f3001003ff22d7f5f373629cd46607c36
treeacbd3138cd928fc4b57af1eda9e9be0101706d85
parentab3a355e8adbf2a0abfe6972f2d194e5becfb2e8
[perl #98204] Shared objects not destoryed

Jerry wrote:
> threads::shared objects stored inside other
> threads::shared structures are not properly destroyed.
>  When a threads::shared object is 'removed' from a
> threads::shared structure (e.g., a hash), the object's
> DESTROY method is not called.

Later, he said:
> When PL_destroyhook and Perl_shared_object_destroy were
> added, the problem they were overcoming was that the
> destruction of each threads::shared proxy was causing the
> underlying shared object's DESTROY method to be called. The
> fix provided a refcount check on the shared object so that
> the DESTROY method was only called with the shared object
> was no longer in use.
>
> The above works fine when delete() and pop() are used,
> because a proxy is created for the stored shared object that
> is being deleted (i.e., the value returned by the delete()
> call), and when the proxy is destroyed, the object's DESTROY
> method is called.
>
> However, when the stored shared object is 'removed' in some
> other manner (e.g., setting the storage location to
> 'undef'), there is no proxy involved, and hence DESTROY does
> not get called for the object.

This commit fixes that by modifying sharedsv_scalar_store,
sharedsv_scalar_mg_free and sharedsv_array_mg_CLEAR.

Each of those functions now checks whether the current item being
freed has sub-items with reference counts of 1.  If so, that means the
sub-item will be freed as a result of the outer SV’s being freed.  It
also means that there are no proxy objects and that destructors will
hence not be called.  So it pushes a new proxy on to the calling con-
text’s mortals stack.  If there are multiple levels of nested objects,
then, when the proxy on the mortals stack is freed, it triggers
sharedsv_scalar_mg_free, which goes through the process again.

This does not fix the problem for shared objects that still exist
(without proxies) at global destruction time.  I cannot make that
work, as circularities will cause new proxies to be created continu-
ously and pushed on to the mortals stack.  Also, the proxies may end
up being created too late during global destruction, after the mor-
tals stack has been emptied, and when there is not enough of the run-
time environment left for destructors to run.  That will happen if
the shared object is referenced by a shared SV that is not an object.
The calling context doesn’t know about the object, so it won’t fire
the destructor at the object-destroying stage of global destruction.
Detecting circularities is also problematic: We would have to keep
a hash of ‘seen’ objects in the shared space, but then how would we
know when to free that?  Letting it leak would affect embedded
environments.

So this whole trick of creating mortal proxy objects is skipped during
global destruction.
dist/threads-shared/lib/threads/shared.pm
dist/threads-shared/shared.xs
dist/threads-shared/t/object2.t