http://www.perlmonks.org?node_id=864769

Boldra has asked for the wisdom of the Perl Monks concerning the following question:

I have a problem cleaning the cache of Class::Std::Fast. Here's my test code, which dies while joining the threads:
package STR; use Class::Std::Fast cache => 1; ## ---------------- ---------------- ---------------- ## package main; use threads; use 5.010; use strict; use warnings; STR->new; # create and immediately throw away. # Now do something completely different with threads: threads->create( sub { sleep 2 && say "thread $_" } ) for 1..4; $_->join for threads->list;
My STR class has caching enabled, which means Class::Std::Fast doesn't really destroy instances of that class, it squirrels them away to be reused next time STR->new is called. This breaks when threads are joined anywhere in the program, even if no STR objects are in scope.

What doesn't work:

That last idea seems the most promising. Class::Std::Fast::_cache_class_ref looks like this:
sub _cache_class_ref () { croak q{you can't call this method in your namespace} if 0 != index caller, 'Class::Std::'; return \%do_cache_class_of; }
Which means that just calling delete Class::Std::Fast::_cache_class_ref()->{STR} works beautifully, if I can only get around the croak!

Another avenue seems to be to dynamically override Class::Std::Fast::DESTROY, but doing this in a way that preserves all the original behaviour except caching would get very complex. Finding out if these behaviours even need to be preserved is even more complex.

Note: I'm not actually using Class::Std::Fast directly, I'm using SOAP::WSDL, which via 5 intermediate steps comes back to Class::Std::Fast. My real code is dying with

DESTROY created new reference to dead object 'SOAP::WSDL::XSD::Typelib::Builtin::string' during global dest +ruction (#1)

Does anyone have any suggestions?

Replies are listed 'Best First'.
Re: Class::Std::Fast cache and threads
by Anonymous Monk on Oct 12, 2010 at 08:50 UTC
    Does anyone have any suggestions?

    You have 3 options

      Thanks for your reply

      I don't actually want anything from Class::Std::Fast in my child threads. The children are only using thread-safe classes. I'm trying to use Class::Std::Fast in the main thread, and then later do something completely unrelated in child threads.

      I understand that I could create a thread especially for Class::Std::Fast, using require instead of use,

      my $s = STR->new; $s->one; $s->two; $s->three;
      becomes
      threads->create( sub { require STR; STR->import; my $s = STR->new; $s->one; $s->two; $s->three; } )->join;
      This would get STR out of my main thread and into it's own private child thread, but this hardly seems like an elegant or maintainable solution. I'm going to want to import STR at least twice in my code, and create a new thread just for this each time.

      As for rewriting CPAN modules to meet my requirements, yes, using moose would be ideal, but I am lazy, and I'll comment out the croak in Class::Std::Fast first. I see no good reason why it should be there.

      Is there really no way to :

      1. trick caller()
      2. load a module in only the main thread
      ?
        load a module in only the main thread

        Use require in your main thread after you've started all your other threads.

Re: Class::Std::Fast cache and threads
by Boldra (Deacon) on Oct 12, 2010 at 11:09 UTC
    Currently I'm tending to this hack:
    clean_cache: { no warnings qw<redefine once>; local *Class::Std::Fast::croak = sub {}; use warnings; delete Class::Std::Fast::_cache_class_ref()->{STR}; }
    Recall that _cache_class_ref refuses to give me access to the list of cached classes if the caller is not a member of 'Class::Std', but this refusal is via croak, which I can redefine.

      Have you (or anyone) actually run any benchmarks to see how much time using this cache option actually saves?

      Because in my (admittedly simplistic) tests, it actually runs slower! Only by a microsecond or two, but still slower.

      And when I look inside and see that the only thing it caches is the scalar ref used as an id, that makes sense. That is, it makes sense that it is slower.

      It makes no sense (to me) to cache this at all. Indeed, if the caching was removed completely, it would run faster still because of the absence of the 'is-it-cached' tests.

      And without the caching, the OP problem just 'goes away'.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
        Definitely a good point. I haven't tested it myself, insteadI'm trusting the claims of the CSF author who says it's "an order of magnitude faster". You may have missed is that it's caching a blessed scalar ref, which means that on the next new call, it can just pop an item of the cache instead of blessing a new object. This is apparently where the speed saving comes from.

        I'm also a bit stuck with the problem that the caching is set at import in about 20 SOAP::WSDL::XSD::Typelib::Builtin::* modules (and possibly other modules autogenerated by wsdl2perl). I'm sticking with my "caching is faster" excuse for now though - I'll switch to "I'm too lazy" when I get desperate :)