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

How can I delete an entire package, including its symbol table? Either through Perl or XS.

Context: I have an application with an embedded perl interpreter. To simplify the explanation, let's say the application is a "page browser", where each page happens to be written in Perl. In order to keep the pages from colliding with each other, I generate a unique package name for each page, and compile the page within that package. (If the page chooses to set its own package, then it's expected to know how to deal with the consequences.)

So far, so good. But when I switch away from a page, I want to clean things up. I don't want to just nuke the interpreter and create a new one, because there is some shared state in the application itself that I don't want to lose. So I'd like to be able to wipe out the entire package I created, and have the package variables' DESTROY methods called if applicable. (I would also kind of like the interpreter to forget about any required files, so that when I went back to the page, it would do the same thing it did the first time. But those files could manipulate global state, so I'd better not. I'll stick with use'ing things so that they'll just re-import stuff when reactivated.)

Is there some way to do this? Or am I approaching this the wrong way? General advice is appreciated too.

I have tried a couple variations of delete $main::{"MYPACKAGE::"}, but no luck so far.

Replies are listed 'Best First'.
Re: Deleting a package
by broquaint (Abbot) on Apr 27, 2004 at 08:06 UTC
    See. delete_package() from Symbol which does the necessary trickery to ensure a package is properly deleted.
    HTH

    _________
    broquaint

      Excellent, that's exactly what I was looking for. Now I just need to figure out how to apply it properly, and what constraints I'll need to impose on the users.

      Update: in searching for caveats to Symbol::delete_package, I encountered Mark-Jason Dominus's scrub_package:

      sub scrub_package { no strict 'refs'; my $pack = shift; die "Shouldn't delete main package" if $pack eq "" || $pack eq "main"; my $stash = *{$pack . '::'}{HASH}; my $name; foreach $name (keys %$stash) { my $fullname = $pack . '::' . $name; # Get rid of everything with that name. undef $$fullname; undef @$fullname; undef %$fullname; undef &$fullname; # or *$fullname = sub { } undef *$fullname; } }
      This is very similar to what delete_package does, at least in 5.8.1, but both have the unfortunate side effect of also deleting any imported subroutine -- so if &mypackage::glort is aliased to &main::glort, then you won't be able to call main::glort() after deleting "mypackage". If you use the above commented out line (*$fullname = sub {}), you will avoid this issue.

      Which says to me: "here be dragons". But I guess I knew that already. I hope they're friendly ones.

      Update2: But in actually testing the above problem, it does not appear to happen with 5.8.1's Symbol::delete_package (version 1.04). So never mind. It's only a problem with the above code. Life is good.

Re: Deleting a package
by Fletch (Bishop) on Apr 27, 2004 at 02:39 UTC

    Check what mod_perl does, specifically when PerlFreshRestart is enabled.

Re: Deleting a package
by adrianh (Chancellor) on Apr 27, 2004 at 08:34 UTC
    Or am I approaching this the wrong way?

    I have to admit it sounds rather complex to me. Why not just encapsulate your behaviour in objects?

      If every "object" has a completely different and independent set of subs, it makes sense not to rely on the object model.

      --
      [ e d @ h a l l e y . c c ]

        If every "object" has a completely different and independent set of subs, it makes sense not to rely on the object model.

        Without knowing more about the application who knows - but we can always store subroutines as object state if we want to.

      Because the "pages" really are close to web pages (or CGI scripts?). It is too difficult to explain without giving some more detail about the application, so I will: the application is an interactive projection system that plays what we call "spots", which are short interactive segments with graphics and behavior that users will play with. The spots are organized into a playlist that loops repeatedly. The authors of the spots are artists -- technically-minded artists, but still much stronger artistically than technically -- and I do not want to burden them with the overhead of constructing proper Perl objects. Also, the spots aren't really reusable in any useful way. (The fragments that are, I pull out and create objects out of, but the original authors don't know how to.)

      So although I don't think it would work to encapsulate them as objects, it might be a good idea to take a hint from Apache::Registry and wrap them in subroutines.

      Oh, and as long as a playlist is active, I don't really have any problems; all of the spots can stay alive in the interpreter. The problem comes when I switch to another playlist -- I want to free up everything from the first playlist. Also, when switching back to a playlist, I want it to be running in a clean environment, and not find little fragments of earlier incarnations of itself lying around. At the same time, the interpreter is used for other things, so I don't want to just kill and recreate it.

        So although I don't think it would work to encapsulate them as objects, it might be a good idea to take a hint from Apache::Registry and wrap them in subroutines.

        That's probably the way I'd approach it. The issues with non-obvious creation of closures are probably easier to deal with than the hassles of manually managing the packages.

        Template::Document might be another place for inspiration.

Re: Deleting a package
by bsb (Priest) on Apr 27, 2004 at 12:41 UTC
    Can you fork or clone an interpreter in XS? Spawn a new thread? That way the parent stays clean.

    You'd still be "nuking the interpreter" but it wouldn't have the state destroying consequences. I don't know how to do this, but mod_perl does something like it (I think)

    Brad

    Update: man perlapi
    perl_clone
    Create and return a new interpreter by cloning the current one.

    PerlInterpreter* perl_clone(PerlInterpreter* interp, UV flags)
Re: Deleting a package
by nothingmuch (Priest) on Apr 27, 2004 at 14:40 UTC
    Since you're maintaining class definitions per object, perhaps you should just move the classes into the object.

    By the smell of it I'm guessing you might want to look at classless objects on the CPAN.

    There are many implementations, many of which have a nice edge on the issue. I particularly like Class::Inner for what it's worth, which is a bit of a hybrid. I think it may work well in your scenario. Skip my module though - I believe it won't be useful in this context.

    -nuffin
    zz zZ Z Z #!perl