in reply to Re: Re: RFC : Pragma vs. Module
in thread RFC : Pragma vs. Module

The problem can be easily described with the following code:
use warnings; eval "INIT { 1 }"; __END__ Too late to run INIT block at (eval 1) line 1.
mod_perl basically loads all modules as a string eval. And you can't have INIT code blocks in eval. See also the CGI to mod_perl Porting. mod_perl Coding guidelines.

I faced a similar dilemma with Thread::Bless: in the end I made sure initializations would only actually initialize once (even if called multiple times), renamed the INIT code block to sub unitialize and added an INIT block that just does a goto &initialize.

Liz

Replies are listed 'Best First'.
Re: Re: Re: Re: RFC : Pragma vs. Module
by stvn (Monsignor) on Mar 15, 2004 at 01:55 UTC
    Liz,

    My apologies. You are completely correct, INIT and CHECK will not run anywhere in mod_perl. I checked every possible situation I could think of in one of my current mod_perl projects, and nothing worked. So I will now put my foot in my mouth.

    Apparently even modules loaded with PerlModule and PerlRequire are at some level eval-ed. Which to me seems ugly, but not being a C programmer or Stas Beckmen, who am I to complain.

    After some reading though, it seems to me that the PerlRestartHandler and PerlChildInitHandler could potentially be used to accomplish similar functionality. PerlRestartHandler happens before Apache pre-forks all the children, and PerlChildInitHandler runs at the begining of each child process. Has anyone ever tried to use these as CHECK/INIT replacements?

    -stvn
      These pseudohandlers could offer the right timing semantics, I guess. But how do you go about actually calling the CHECK and INIT blocks? Trying to call Foo::INIT() is a runtime error, because no such subroutine exists. I opine that it was a mistake to allow a sub INIT {} syntax, because their semantics are entirely distinct from subroutines — in particular, you can define any number of INIT or other special blocks and they'll accumulate, instead of overriding previous ones.

      Makeshifts last the longest.

        I agree. Personally, I never use the sub INIT {} form, I always do the block form INIT {} it makes them look (a little) more distinct in the code.

        But how do you go about actually calling the CHECK and INIT blocks?

        I would likely move the code in INIT out into another sub, and have INIT call that (allowing it to fail in mod_perl since it only produces a warning, although this would not be okay if you had set the warnings to be fatal). Then check for the mod_perl env, and push a handler that calls the sub-formerly-known-as-INIT. This is of course a simple and untested approach, but it certainly a surmountable problem.

        -stvn
Re: Re: Re: Re: RFC : Pragma vs. Module
by stvn (Monsignor) on Mar 14, 2004 at 23:16 UTC
    Liz,

    This makes sense that any of the compilation phases BEGIN, CHECK, INIT etc. would not work 100% as expected when eval-ed. They are for affecting the compilation during compile-time, and eval is a run-time construct.

    As for the assertion that this will not work with mod_perl, you are partially right. To start, Apache::Registry and similar CGI-to-mod_perl tools do eval the files as strings in order to run them in the persistant mod_perl environment. But this is only one very small (but very commonly used) aspect of mod_perl. The rest of mod_perl is handlers (for each of the Apache request cycles), and while I have not tested this assertion, I am pretty sure that INIT blocks as well as BEGIN, CHECK & END all run perfectly fine.

    -stvn

    UPDATE:
    Wow, was I wrong. Apparently mod_perl evals everything, handlers and all, see my apology to Liz below.

      BEGIN and END work in eval "". It's just CHECK and INIT that have to be set up before the compile of the main script ends or they are "too late".

        By the way, it looks like perl 5.9.1 (and thus the eventual 5.10.0) will get a UNITCHECK that is defined to run at the end of compliation of that "compilation unit" (string eval, do file, require). That should fix the problem.