|
Packit |
549706 |
package prefork;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=pod
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 NAME
|
|
Packit |
549706 |
|
|
Packit |
549706 |
prefork - Optimized module loading for forking or non-forking processes
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 SYNOPSIS
|
|
Packit |
549706 |
|
|
Packit |
549706 |
In a module that normally delays module loading with require
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Module Foo::Bar only uses This::That 25% of the time.
|
|
Packit |
549706 |
# We want to preload in in forking scenarios (like mod_perl), but
|
|
Packit |
549706 |
# we want to delay loading in non-forking scenarios (like CGI)
|
|
Packit |
549706 |
use prefork 'This::That';
|
|
Packit |
549706 |
|
|
Packit |
549706 |
sub do_something {
|
|
Packit |
549706 |
my $arg = shift;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Load the module at run-time as normal
|
|
Packit |
549706 |
if ( $special_case ) {
|
|
Packit |
549706 |
require This::That;
|
|
Packit |
549706 |
This::That::blah(@_);
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Register a module to be loaded before forking directly
|
|
Packit |
549706 |
prefork::prefork('Module::Name');
|
|
Packit |
549706 |
|
|
Packit |
549706 |
In a script or module that is going to be forking.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
package Module::Forker;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Enable forking mode
|
|
Packit |
549706 |
use prefork ':enable';
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Or call it directly
|
|
Packit |
549706 |
prefork::enable();
|
|
Packit |
549706 |
|
|
Packit |
549706 |
In a third-party run-time loader
|
|
Packit |
549706 |
|
|
Packit |
549706 |
package Runtime::Loader;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
use prefork ();
|
|
Packit |
549706 |
prefork::notify( \&load_everything );
|
|
Packit |
549706 |
|
|
Packit |
549706 |
...
|
|
Packit |
549706 |
|
|
Packit |
549706 |
sub load_everything { ... }
|
|
Packit |
549706 |
|
|
Packit |
549706 |
1;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 INTRODUCTION
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The task of optimizing module loading in Perl tends to move in two different
|
|
Packit |
549706 |
directions, depending on the context.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
In a procedural context, such as scripts and CGI-type situations, you can
|
|
Packit |
549706 |
improve the load times and memory usage by loading a module at run-time,
|
|
Packit |
549706 |
only once you are sure you will need it.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
In the other common load profile for perl applications, the application
|
|
Packit |
549706 |
will start up and then fork off various worker processes. To take full
|
|
Packit |
549706 |
advantage of memory copy-on-write features, the application should load
|
|
Packit |
549706 |
as many modules as possible before forking to prevent them consuming memory
|
|
Packit |
549706 |
in multiple worker processes.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Unfortunately, the strategies used to optimise for these two load profiles
|
|
Packit |
549706 |
are diametrically opposed. What improves a situation for one tends to
|
|
Packit |
549706 |
make life worse for the other.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 DESCRIPTION
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The C<prefork> pragma is intended to allow module writers to optimise
|
|
Packit |
549706 |
module loading for B<both> scenarios with as little additional code as
|
|
Packit |
549706 |
possible.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
prefork.pm is intended to serve as a central and optional marshalling
|
|
Packit |
549706 |
point for state detection (are we running in compile-time or run-time
|
|
Packit |
549706 |
mode) and to act as a relatively light-weight module loader.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head2 Loaders and Forkers
|
|
Packit |
549706 |
|
|
Packit |
549706 |
C<prefork> is intended to be used in two different ways.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The first is by a module that wants to indicate that another module should
|
|
Packit |
549706 |
be loaded before forking. This is known as a "Loader".
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The other is a script or module that will be initiating the forking. It
|
|
Packit |
549706 |
will tell prefork.pm that it is either going to fork, or is about to fork,
|
|
Packit |
549706 |
or for some other reason all modules previously mentioned by the Loaders
|
|
Packit |
549706 |
should be loaded immediately.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head2 Usage as a Pragma
|
|
Packit |
549706 |
|
|
Packit |
549706 |
A Loader can register a module to be loaded using the following
|
|
Packit |
549706 |
|
|
Packit |
549706 |
use prefork 'My::Module';
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The same thing can be done in such a way as to not require prefork
|
|
Packit |
549706 |
being installed, but taking advantage of it if it is.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
eval "use prefork 'My::Module';";
|
|
Packit |
549706 |
|
|
Packit |
549706 |
A Forker can indicate that it will be forking with the following
|
|
Packit |
549706 |
|
|
Packit |
549706 |
use prefork ':enable';
|
|
Packit |
549706 |
|
|
Packit |
549706 |
In any use of C<prefork> as a pragma, you can only pass a single value
|
|
Packit |
549706 |
as argument. Any additional arguments will be ignored. (This may throw
|
|
Packit |
549706 |
an error in future versions).
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head2 Compatbility with mod_perl and others
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Part of the design of C<prefork>, and its minimalistic nature, is that it
|
|
Packit |
549706 |
is intended to work easily with existing modules, needing only small
|
|
Packit |
549706 |
changes.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
For example, C<prefork> itself will detect the C<$ENV{MOD_PERL}>
|
|
Packit |
549706 |
environment variable and automatically start in forking mode.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
prefork has support for integrating with third-party modules, such as
|
|
Packit |
549706 |
L<Class::Autouse>. The C<notify> function allows these run-time loaders
|
|
Packit |
549706 |
to register callbacks, to be called once prefork enters forking mode.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The synopsis entry above describes adding support for prefork.pm as a
|
|
Packit |
549706 |
dependency. To allow your third-party module loader without a dependency
|
|
Packit |
549706 |
and only if it is installed use the following:
|
|
Packit |
549706 |
|
|
Packit |
549706 |
eval { require prefork; }
|
|
Packit |
549706 |
prefork::notify( \&function ) unless $@;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head2 Using prefork.pm
|
|
Packit |
549706 |
|
|
Packit |
549706 |
From the Loader side, it is fairly simple. prefork becomes a dependency
|
|
Packit |
549706 |
for your module, and you use it as a pragma as documented above.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
For the Forker, you have two options. Use as a dependency or optional use.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
In the dependency case, you add prefork as a dependency and use it as a
|
|
Packit |
549706 |
pragma with the ':enable' option.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
To add only optional support for prefork, without requiring it to be
|
|
Packit |
549706 |
installed, you should wait until the moment just before you fork and then
|
|
Packit |
549706 |
call C<prefork::enable> directly ONLY if it is loaded.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Load modules if any use the prefork pragma.
|
|
Packit |
549706 |
prefork::enable() if $INC{prefork.pm};
|
|
Packit |
549706 |
|
|
Packit |
549706 |
This will cause the modules to be loaded ONLY if there are any modules that
|
|
Packit |
549706 |
need to be loaded. The main advantage of the dependency version is that you
|
|
Packit |
549706 |
only need to enable the module once, and not before each fork.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
If you wish to have your own module leverage off the forking-detection that
|
|
Packit |
549706 |
prefork provides, you can also do the following.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
use prefork;
|
|
Packit |
549706 |
if ( $prefork::FORKING ) {
|
|
Packit |
549706 |
# Complete some preparation task
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head2 Modules that are prefork-aware
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=over 4
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=item mod_perl/mod_perl2
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=item Class::Autouse
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=back
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 FUNCTIONS
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=cut
|
|
Packit |
549706 |
|
|
Packit |
549706 |
use 5.005;
|
|
Packit |
549706 |
use strict;
|
|
Packit |
549706 |
use Carp ();
|
|
Packit |
549706 |
use List::Util 0.18 ();
|
|
Packit |
549706 |
use Scalar::Util 0.18 ();
|
|
Packit |
549706 |
|
|
Packit |
549706 |
use vars qw{$VERSION $FORKING %MODULES @NOTIFY};
|
|
Packit |
549706 |
BEGIN {
|
|
Packit |
549706 |
$VERSION = '1.04';
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# The main state variable for this package.
|
|
Packit |
549706 |
# Are we in preforking mode.
|
|
Packit |
549706 |
$FORKING = '';
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# The queue of modules to load
|
|
Packit |
549706 |
%MODULES = ();
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# The queue of notification callbacks
|
|
Packit |
549706 |
@NOTIFY = (
|
|
Packit |
549706 |
sub {
|
|
Packit |
549706 |
# Do a hash copy of Config to get everything
|
|
Packit |
549706 |
# inside of it preloaded.
|
|
Packit |
549706 |
require Config;
|
|
Packit |
549706 |
eval {
|
|
Packit |
549706 |
# Sometimes there is no Config_heavy.pl
|
|
Packit |
549706 |
require 'Config_heavy.pl';
|
|
Packit |
549706 |
};
|
|
Packit |
549706 |
my $copy = { %Config::Config };
|
|
Packit |
549706 |
return 1;
|
|
Packit |
549706 |
},
|
|
Packit |
549706 |
);
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Look for situations that need us to start in forking mode
|
|
Packit |
549706 |
$FORKING = 1 if $ENV{MOD_PERL};
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
sub import {
|
|
Packit |
549706 |
return 1 unless $_[1];
|
|
Packit |
549706 |
($_[1] eq ':enable') ? enable() : prefork($_[1]);
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=pod
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head2 prefork $module
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The 'prefork' function indicates that a module should be loaded before
|
|
Packit |
549706 |
the process will fork. If already in forking mode the module will be
|
|
Packit |
549706 |
loaded immediately.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Otherwise it will be added to a queue to be loaded later if it recieves
|
|
Packit |
549706 |
instructions that it is going to be forking.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Returns true on success, or dies on error.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=cut
|
|
Packit |
549706 |
|
|
Packit |
549706 |
sub prefork ($) {
|
|
Packit |
549706 |
# Just hand straight to require if enabled
|
|
Packit |
549706 |
my $module = defined $_[0] ? "$_[0]" : ''
|
|
Packit |
549706 |
or Carp::croak('You did not pass a module name to prefork');
|
|
Packit |
549706 |
$module =~ /^[^\W\d]\w*(?:(?:\'|::)[^\W\d]\w*)*$/
|
|
Packit |
549706 |
or Carp::croak("'$module' is not a module name");
|
|
Packit |
549706 |
my $file = join( '/', split /(?:\'|::)/, $module ) . '.pm';
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Is it already loaded or queued
|
|
Packit |
549706 |
return 1 if $INC{$file};
|
|
Packit |
549706 |
return 1 if $MODULES{$module};
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Load now if enabled, or add to the module list
|
|
Packit |
549706 |
return require $file if $FORKING;
|
|
Packit |
549706 |
$MODULES{$module} = $file;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
1;
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=pod
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head2 enable
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The C<enable> function indicates to the prefork module that the process is
|
|
Packit |
549706 |
going to fork, possibly immediately.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
When called, prefork.pm will immediately load all outstanding modules, and
|
|
Packit |
549706 |
will set a flag so that any further 'prefork' calls will load the module
|
|
Packit |
549706 |
at that time.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Returns true, dieing as normal is there is a problem loading a module.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=cut
|
|
Packit |
549706 |
|
|
Packit |
549706 |
sub enable () {
|
|
Packit |
549706 |
# Turn on the PREFORK flag, so any additional
|
|
Packit |
549706 |
# 'use prefork ...' calls made during loading
|
|
Packit |
549706 |
# will load immediately.
|
|
Packit |
549706 |
return 1 if $FORKING;
|
|
Packit |
549706 |
$FORKING = 1;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Load all of the modules not yet loaded
|
|
Packit |
549706 |
foreach my $module ( sort keys %MODULES ) {
|
|
Packit |
549706 |
my $file = $MODULES{$module};
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Has it been loaded since we were told about it
|
|
Packit |
549706 |
next if $INC{$file};
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Load the module.
|
|
Packit |
549706 |
require $file;
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Clear the modules list
|
|
Packit |
549706 |
%MODULES = ();
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Execute the third-party callbacks
|
|
Packit |
549706 |
while ( my $callback = shift @NOTIFY ) {
|
|
Packit |
549706 |
$callback->();
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
1;
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=pod
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head2 notify &function
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The C<notify> function is used to integrate support for modules other than
|
|
Packit |
549706 |
prefork.pm itself.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
A module loader calls the notify function, passing it a reference to a
|
|
Packit |
549706 |
C reference (either anon or a function reference). C<prefork> will
|
|
Packit |
549706 |
store this CODE reference, and execute it immediately as soon as it knows
|
|
Packit |
549706 |
it is in forking-mode, but after it loads its own modules.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Callbacks are called in the order they are registered.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Normally, this will happen as soon as the C<enable> function is called.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
However, you should be aware that if prefork is B<already> in preforking
|
|
Packit |
549706 |
mode at the time that the notify function is called, prefork.pm will
|
|
Packit |
549706 |
execute the function immediately.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
This means that any third party module loader should be fully loaded and
|
|
Packit |
549706 |
initialised B<before> the callback is provided to C<notify>.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Returns true if the function is stored, or dies if not passed a C
|
|
Packit |
549706 |
reference, or the callback is already set in the notify queue.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=cut
|
|
Packit |
549706 |
|
|
Packit |
549706 |
sub notify ($) {
|
|
Packit |
549706 |
# Get the CODE ref callback param
|
|
Packit |
549706 |
my $function = shift;
|
|
Packit |
549706 |
my $reftype = Scalar::Util::reftype($function);
|
|
Packit |
549706 |
unless ( $reftype and $reftype eq 'CODE' ) {
|
|
Packit |
549706 |
Carp::croak("prefork::notify was not passed a CODE reference");
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Call it immediately is already in forking mode
|
|
Packit |
549706 |
if ( $FORKING ) {
|
|
Packit |
549706 |
$function->();
|
|
Packit |
549706 |
return 1;
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Is it already defined?
|
|
Packit |
549706 |
if ( List::Util::first { Scalar::Util::refaddr($function) == Scalar::Util::refaddr($_) } @NOTIFY ) {
|
|
Packit |
549706 |
Carp::croak("Callback function already registered");
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Add to the queue
|
|
Packit |
549706 |
push @NOTIFY, $function;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
1;
|
|
Packit |
549706 |
}
|
|
Packit |
549706 |
|
|
Packit |
549706 |
|
|
Packit |
549706 |
|
|
Packit |
549706 |
|
|
Packit |
549706 |
|
|
Packit |
549706 |
#####################################################################
|
|
Packit |
549706 |
# Built-in Notifications
|
|
Packit |
549706 |
|
|
Packit |
549706 |
# Compile CGI functions automatically
|
|
Packit |
549706 |
prefork::notify( sub {
|
|
Packit |
549706 |
CGI->compile() if $INC{'CGI.pm'};
|
|
Packit |
549706 |
} );
|
|
Packit |
549706 |
|
|
Packit |
549706 |
1;
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=pod
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 TO DO
|
|
Packit |
549706 |
|
|
Packit |
549706 |
- Add checks for more pre-forking situations
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 SUPPORT
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Bugs should be always submitted via the CPAN bug tracker, located at
|
|
Packit |
549706 |
|
|
Packit |
549706 |
L<http://rt.cpan.org/NoAuth/ReportBug.html?Queue=prefork>
|
|
Packit |
549706 |
|
|
Packit |
549706 |
For other issues, or commercial enhancement or support, contact the author.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 AUTHOR
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Adam Kennedy <adamk@cpan.org>
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=head1 COPYRIGHT
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Thank you to Phase N Australia (L<http://phase-n.com/>) for
|
|
Packit |
549706 |
permitting the open sourcing and release of this distribution.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
Copyright 2004 - 2009 Adam Kennedy.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
This program is free software; you can redistribute
|
|
Packit |
549706 |
it and/or modify it under the same terms as Perl itself.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
The full text of the license can be found in the
|
|
Packit |
549706 |
LICENSE file included with this module.
|
|
Packit |
549706 |
|
|
Packit |
549706 |
=cut
|