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

Hi monks.

Background

I'm writing an application server, using POE::Component::Server::HTTP (Don't have a client yet) that accepts a request; say /index?foo=bar and calls a routine in a pre-defined package; say MyDocRoot::index.

I'm not trying to duplicate mod_perl's functionality -- this is only a testing-environment/proof-of-concept.

Problem

MyDocRoot.pm is loaded at startup and works fine. My code keeps track of all loaded modules and checks the time the module was loaded against the module's file mtime. When a module changes, the code does:

do $module;

With $module set to the path and filename of the module to reload. The thing does reload since I can see different output from the same request.

The trouble is, old sub-routines and variables are still defined according to what Data::Dumper on \%MyDocRoot:: shows.

I have read perlmod, perlref and various others but I have not yet found a way to reload (or unload) a module completely. I don't know where else to look. Does any monk have some wisdom/pointers to share ?

Update: Just in case, forgot to mention:

This is perl, v5.8.0 built for i386-linux-thread-multi Linux 2.4.21 i686 GNU/Linux (Debian woody)

Replies are listed 'Best First'.
•Re: Unloading a module completely
by merlyn (Sage) on Dec 07, 2003 at 15:49 UTC

      From a quick glimpse to the pod, that looks promising. I'll give it a try.

Re: Unloading a module completely
by PodMaster (Abbot) on Dec 07, 2003 at 15:17 UTC
    Search for load/reload on CPAN. What it comes down to is undef, and delete, as in undef *PaCkAGe;, and delete $INC{'PaCkAGe.pm'};

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      undef *PaCkAGe; does not work for me -- That's the first thing I've tried anyway.

        Right, try something like
        { no strict 'refs'; for( keys %PaCkAGe:: ){ undef *{"PaCkAGe::$_"}; delete $PaCkAGe::{$_}; delete $::PaCkAGe::{$_}; } }

        MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
        I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
        ** The third rule of perl club is a statement of fact: pod is sexy.

      use Symbol(); Symbol::delete_package("PaCkAGe") instead of undef *PaCkAGe; will remove all symbols of package, but you surely won't be able to remove all side effects of a package unless the package itself supports that.

      But if you fork (or start new ithread) before loading the package, then ending that process/thread will almost certainly remove the package completely.

Re: Unloading a module completely
by Courage (Parson) on Dec 07, 2003 at 15:33 UTC
    It was discussed a bit in p5p, I do not know deep details, but do remember that it is not possible to do a task absolutely correctly, at least because you'll never manage XS modules to unload correctly: they may load any DLLs or depend on outside libraries and outside temperature conditions.

    However doing some kind of cleaning for any particular case is of course doable: once you understand behaviour of module, you'll manage right unloading at correct time and stopping all required thermometers.

    Courage, the Cowardly Dog

Re: Unloading a module completely
by ysth (Canon) on Dec 07, 2003 at 16:27 UTC
    If I understand you, the new module's subs and variables are getting defined, but there are subs and variables in the old that aren't in the new?

    Perhaps you want Symbol::delete_package? But see its caveats regarding any code using the module's subs and variables that was compiled after the old module was.

    It also doesn't do the delete $INC{foo} for you, but if you are using do instead of require, that's ok.

      I already have worked with this way to clean a package at delete_package(), but this doesn't do all the job! Actually this code came from old Safe module, at erase() method.

      You really need to clean everything one by one, not only the symbol at the table! I say that because making memory comparations I saw that after 1000 times running a test code I use 150Mb when cleanning with delete_package(), and with my method (cleanning everything) I got 8Mb! ;-P

      Maybe I will post some fix to delete_package()...

      update: I'm saying that Safe::World::CLEAN() clean better the package, since I get only 8Mb in the tests with it.

      Graciliano M. P.
      "Creativity is the expression of the liberty".

        Are you saying you have a method based on Safe::erase that's better than Symbol::delete_package or saying that you have a delete_package based on Safe::erase and another method that is better than that? If the latter, can you explain more what it is that you do?
        For me delete_package() and Safe::erase() are the same (do the same thing). Looking for a better way to clean a package I have created a better method to clean a package, that you can see at Safe::World::CLEAN().

        Graciliano M. P.
        "Creativity is the expression of the liberty".

Re: Unloading a module completely
by gmpassos (Priest) on Dec 08, 2003 at 10:46 UTC
    Unload a module, actually unload a package, is hard. You need to scan all the package to undef all the data types (SCALAR, ARRAY, HASH, GLOB) and close the IO too. Also you need to overload the subs with a DUMMY sub. But when you "undef" a package you can't use that package name again, or you will have a core dump easy in the future codes!

    For XS code you can use some unload_file() method (I don't remember now the method name) of Dynaloader, but this only work for Linux and isn't stable. Soo, I have skiped that!

    But the problem is that when you load a code, or module, it generally set new symbols in the other packages (in other symbols tables). Soo, I think that the best way is to create a Safe compartment and load your code there.

    But to make your code load modules inside a Safe compartment is hard, since you need to implement by hand the output, %INC and @INC handlers. Soo, I have created Safe::World, that implements all the World and already have the CLEAN() resource that you are looking. It also has other cool things, but I will let them to be read in the POD.

    Get it at: http://search.cpan.org/~gmpassos/Safe-World-0.03/

    I will been releasing the 0.04 version in some days, soo be updated...

    Graciliano M. P.
    "Creativity is the expression of the liberty".

Re: Unloading a module completely
by holo (Monk) on Dec 08, 2003 at 13:31 UTC

    Finally got it to work (with lots of warnings) by using plain old Safe. I couldn't get a proper run with Safe::World. I guess is not ready for stable use yet. It has a similar interface to Safe though so one can easilly port to it once it's a bit more stable. Keep up the good work gmpassos.

    Thanks to all the monks that have helped me.

    Update:

    I might be doing it all wrong. I have replacated the failing part(s) here.

    This should print 144.

    #!/usr/bin/perl use strict; use warnings 'all'; use Safe::World; my ($stdout, $stderr); my $world = Safe::World->new( stdout => \$stdout, stderr => \$stderr, ); print "World created\n"; $world->eval('print 12 ** 2, "\n";'); die $@ if $@; print $stdout, "***End***\n"; # CLEANup should follow

    The output produced is the followng (paths foobared):

    Scalar value @_[0] better written as $_[0] at .../Safe/World/ScanPack. +pm line 88. Scalar value @_[0] better written as $_[0] at .../Safe/World.pm line 3 +45. Useless use of hash element in void context at .../Safe/World.pm line +1007. Use of uninitialized value in string eq at .../Safe/World.pm line 170. Use of uninitialized value in numeric ne (!=) at .../Safe/World.pm lin +e 344. World created Use of uninitialized value in numeric ne (!=) at .../Safe/World.pm lin +e 386. syntax error at (eval 18) line 1, near "=" Use of uninitialized value in numeric ne (!=) at .../Safe/World.pm lin +e 943. Use of uninitialized value in numeric ne (!=) at .../Safe/World.pm lin +e 956. Use of uninitialized value in numeric ne (!=) at .../Safe/World.pm lin +e 386. Use of uninitialized value in numeric ne (!=) at .../Safe/World.pm lin +e 386. Use of uninitialized value in string ne at .../Safe/World/stdout.pm li +ne 218. Use of uninitialized value in string ne at .../Safe/World/stdout.pm li +ne 218.
      Note that you need to make a close() after run your code, or you won't have the output flushed! Or you can just use flush in the creation.

      Soo, your code should be:

      use Safe::World; my ($stdout, $stderr); my $world = Safe::World->new( stdout => \$stdout, stderr => \$stderr, ); $world->eval('print 12 ** 2, "\n";'); $world->close ; print $stdout ;
      Or using flush:
      use Safe::World; my ($stdout, $stderr); my $world = Safe::World->new( stdout => \$stdout, stderr => \$stderr, flush => 1 , ); $world->eval('print 12 ** 2, "\n";'); print $stdout ;

      Graciliano M. P.
      "Creativity is the expression of the liberty".