Skip to content

Conversation

@mikkoi
Copy link

@mikkoi mikkoi commented Nov 19, 2023

I am developing a testing tool which creates testing resources and then collects them when their reference count goes to zero. This means my code has a lot of activity in DESTRUCT phase. Often the DESTROY methods get executed only at the end of the program. So they go into global destruction. I was often receiving these errors:

(in cleanup) Log::Any::Proxy requires an 'adapter' parameter at [..]/Log/Any/Proxy.pm

... but not every time!

When I specifically reclaimed my objects by undef'ing them or by letting them fall out of subroutine scope, the problem disappeared.

Global Destruction

The order in which objects are destroyed during the global destruction before the program exits is unpredictable. This means that any objects contained by your object may already have been destroyed. You should check that a contained object is defined before calling a method on it:

-- https://perldoc.perl.org/perlobj#Global-Destruction

Global Destruction

The order in which objects are destroyed during the global destruction before
the program exits is unpredictable. This means that any objects contained
by your object may already have been destroyed. You should check that
a contained object is defined before calling a method on it:

https://perldoc.perl.org/perlobj#Global-Destruction

Signed-off-by: Mikko Koivunalho <mikkoi@cpan.org>
@nrdvana
Copy link
Contributor

nrdvana commented Jan 21, 2026

(just a drive-by observation from another user)

Your change would result in creation of a new adapter during global destruction. That generally won't work, as other objects that the adapter might want to access might be destroyed or partly destroyed. The only sensible behavior for a change to Log::Any itself would be to simply drop the log message as un-deliverable, but that doesn't necessarily solve anyone's problem.

The real solution is to make sure all your destructors run before global destruction takes place, or make sure that you never access any object other than your own scalar fields during global destruction.

If you are writing a module that logs things in the destructor, and you don't have control over whether the user of your module cleans up before program exit, the solution is

our %instances;

sub new {
   ...
   # Store a weak reference to every object instance
   Scalar::Util::weaken($instances{refaddr $self}= $self);
   return $self;
}

sub DESTROY {
   # code here needs to be OK with being called multiple times
   # and be a no-op the second time.
   ...
}

END {
   # This line ensures that all your objects get the
   # destructor run before global destruction
   $_->DESTROY for grep defined, values %instances;
}

@mikkoi
Copy link
Author

mikkoi commented Jan 21, 2026

Thank you, @nrdvana . I will try that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants