stas 2004/02/09 11:32:43
Modified: . Changes src/modules/perl modperl_callback.c modperl_filter.c modperl_handler.c modperl_handler.h modperl_mgv.c modperl_mgv.h modperl_types.h t/filter both_str_req_add.t t/filter/TestFilter both_str_req_add.pm t/hooks push_handlers.t t/hooks/TestHooks push_handlers.pm todo release xs/tables/current/ModPerl FunctionTable.pm Log: Anonymous subs are now supported in push_handlers, set_handlers, add_input_filter, etc. A fast cached cv is used with non-ithreaded perl. A slower deparse/eval approach (via B::Deparse) is used with ithreads enabled perls. Further optimizations are planned for the latter case. Revision Changes Path 1.325 +6 -0 modperl-2.0/Changes Index: Changes =================================================================== RCS file: /home/cvs/modperl-2.0/Changes,v retrieving revision 1.324 retrieving revision 1.325 diff -u -u -r1.324 -r1.325 --- Changes 9 Feb 2004 19:25:01 -0000 1.324 +++ Changes 9 Feb 2004 19:32:42 -0000 1.325 @@ -12,6 +12,12 @@ =item 1.99_13-dev +Anonymous subs are now supported in push_handlers, set_handlers, +add_input_filter, etc. A fast cached cv is used with non-ithreaded +perl. A slower deparse/eval approach (via B::Deparse) is used with +ithreads enabled perls. Further optimizations are planned for the +latter case. [Stas] + ht_time w/o the pool is now available only via override/restore compat API. format_time, has been renamed back to ht_time, and the default values for fmt, time and gmt are now supported. [Stas] 1.68 +15 -1 modperl-2.0/src/modules/perl/modperl_callback.c Index: modperl_callback.c =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_callback.c,v retrieving revision 1.67 retrieving revision 1.68 diff -u -u -r1.67 -r1.68 --- modperl_callback.c 9 Feb 2004 19:09:34 -0000 1.67 +++ modperl_callback.c 9 Feb 2004 19:32:42 -0000 1.68 @@ -36,8 +36,22 @@ PUTBACK; if (MpHandlerANON(handler)) { - SV *sv = eval_pv(handler->name, TRUE); /* XXX: cache */ +#ifdef USE_ITHREADS + /* it's possible that the interpreter that is running the anon + * cv, isn't the one that compiled it. so to be safe need to + * re-eval the deparsed form before using it. + * XXX: possible optimizations, see modperl_handler_new_anon */ + SV *sv = eval_pv(handler->name, TRUE); cv = (CV*)SvRV(sv); +#else + /* the same interpreter that has compiled the anon cv is used + * to run it */ + if (!handler->cv) { + SV *sv = eval_pv(handler->name, TRUE); + handler->cv = (CV*)SvRV(sv); /* cache */ + } + cv = handler->cv; +#endif } else { GV *gv = modperl_mgv_lookup_autoload(aTHX_ handler->mgv_cv, s, p); 1.81 +9 -12 modperl-2.0/src/modules/perl/modperl_filter.c Index: modperl_filter.c =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_filter.c,v retrieving revision 1.80 retrieving revision 1.81 diff -u -u -r1.80 -r1.81 --- modperl_filter.c 23 Dec 2003 15:56:01 -0000 1.80 +++ modperl_filter.c 9 Feb 2004 19:32:42 -0000 1.81 @@ -353,21 +353,19 @@ char *code = apr_pstrcat(p, "package ", package_name, ";", init_handler_pv_code, NULL); SV *sv = eval_pv(code, TRUE); - char *init_handler_name; /* fprintf(stderr, "code: %s\n", code); */ - - if ((init_handler_name = modperl_mgv_name_from_sv(aTHX_ p, sv))) { - modperl_handler_t *init_handler = - modperl_handler_new(p, apr_pstrdup(p, init_handler_name)); + modperl_handler_t *init_handler = + modperl_handler_new_from_sv(aTHX_ p, sv); + if (init_handler) { MP_TRACE_h(MP_FUNC, "found init handler %s\n", - init_handler->name); + modperl_handler_name(init_handler)); - if (! init_handler->attrs & MP_FILTER_INIT_HANDLER) { + if (!init_handler->attrs & MP_FILTER_INIT_HANDLER) { Perl_croak(aTHX_ "handler %s doesn't have " "the FilterInitHandler attribute set", - init_handler->name); + modperl_handler_name(init_handler)); } handler->next = init_handler; @@ -1091,12 +1089,11 @@ SV *callback, const char *type) { apr_pool_t *pool = r ? r->pool : c->pool; - char *handler_name; + modperl_handler_t *handler = + modperl_handler_new_from_sv(aTHX_ pool, callback); - if ((handler_name = modperl_mgv_name_from_sv(aTHX_ pool, callback))) { + if (handler) { ap_filter_t *f; - modperl_handler_t *handler = - modperl_handler_new(pool, apr_pstrdup(pool, handler_name)); modperl_filter_ctx_t *ctx = (modperl_filter_ctx_t *)apr_pcalloc(pool, sizeof(*ctx)); 1.21 +143 -4 modperl-2.0/src/modules/perl/modperl_handler.c Index: modperl_handler.c =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_handler.c,v retrieving revision 1.20 retrieving revision 1.21 diff -u -u -r1.20 -r1.21 --- modperl_handler.c 9 Feb 2004 18:19:09 -0000 1.20 +++ modperl_handler.c 9 Feb 2004 19:32:42 -0000 1.21 @@ -1,5 +1,64 @@ #include "mod_perl.h" + +static +char *modperl_coderef2text(pTHX_ apr_pool_t *p, CV *cv) +{ + dSP; + int count; + SV *bdeparse; + char *text; + + /* B::Deparse >= 0.61 needed for blessed code references */ + + load_module(PERL_LOADMOD_NOIMPORT, + newSVpvn("B::Deparse", 10), + newSVnv(0.61)); + + ENTER; + SAVETMPS; + + /* create the B::Deparse object */ + PUSHMARK(sp); + XPUSHs(sv_2mortal(newSVpvn("B::Deparse", 10))); + PUTBACK; + count = call_method("new", G_SCALAR); + SPAGAIN; + if (count != 1) { + Perl_croak(aTHX_ "Unexpected return value from B::Deparse::new\n"); + } + if (SvTRUE(ERRSV)) { + Perl_croak(aTHX_ "error: %s", SvPVX(ERRSV)); + } + bdeparse = POPs; + + PUSHMARK(sp); + XPUSHs(bdeparse); + XPUSHs(sv_2mortal(newRV_inc((SV*)cv))); + PUTBACK; + count = call_method("coderef2text", G_SCALAR); + SPAGAIN; + if (count != 1) { + Perl_croak(aTHX_ "Unexpected return value from " + "B::Deparse::coderef2text\n"); + } + if (SvTRUE(ERRSV)) { + Perl_croak(aTHX_ "error: %s", SvPVX(ERRSV)); + } + + { + STRLEN n_a; + text = POPpx; + } + + PUTBACK; + + FREETMPS; + LEAVE; + + return apr_pstrcat(p, "sub ", text, NULL); +} + modperl_handler_t *modperl_handler_new(apr_pool_t *p, const char *name) { modperl_handler_t *handler = @@ -20,6 +79,7 @@ break; } + handler->cv = NULL; handler->name = name; MP_TRACE_h(MP_FUNC, "[%s] new handler %s\n", modperl_pid_tid(p), handler->name); @@ -27,6 +87,60 @@ return handler; } + +static +modperl_handler_t *modperl_handler_new_anon(pTHX_ apr_pool_t *p, CV *cv) +{ + modperl_handler_t *handler = + (modperl_handler_t *)apr_pcalloc(p, sizeof(*handler)); + MpHandlerPARSED_On(handler); + MpHandlerANON_On(handler); + +#ifdef USE_ITHREADS + /* XXX: perhaps we can optimize this further. At the moment when + * perl w/ ithreads is used, we always deparse the anon subs + * before storing them and then eval them each time they are + * used. This is because we don't know whether the same perl that + * compiled the anonymous sub is used to run it. + * + * A possible optimization is to cache the CV and use that cached + * value w/ or w/o deparsing at all if: + * + * - the mpm is non-threaded mpm and no +Clone/+Parent is used + * (i.e. no perl pools) (no deparsing is needed at all) + * + * - the interpreter that has supplied the anon cv is the same + * interpreter that is executing that cv (requires storing aTHX + * in the handler's struct) (need to deparse in case the + * interpreter gets switched) + * + * - other cases? + */ + handler->cv = NULL; + handler->name = modperl_coderef2text(aTHX_ p, cv); + MP_TRACE_h(MP_FUNC, "[%s] new deparsed anon handler:\n%s\n", + modperl_pid_tid(p), handler->name); +#else + /* it's safe to cache and later use the cv, since the same perl + * interpeter is always used */ + handler->cv = cv; + handler->name = NULL; + MP_TRACE_h(MP_FUNC, "[%s] new cached cv anon handler\n", + modperl_pid_tid(p)); +#endif + + return handler; +} + +MP_INLINE +const char *modperl_handler_name(modperl_handler_t *handler) +{ + /* a handler containing an anonymous sub doesn't have a normal sub + * name */ + return handler->name ? handler->name : "anonymous sub"; +} + + int modperl_handler_resolve(pTHX_ modperl_handler_t **handp, apr_pool_t *p, server_rec *s) { @@ -320,14 +434,39 @@ action, NULL); } +modperl_handler_t *modperl_handler_new_from_sv(pTHX_ apr_pool_t *p, SV *sv) +{ + char *name = NULL; + GV *gv; + + if (SvROK(sv)) { + sv = SvRV(sv); + } + + switch (SvTYPE(sv)) { + case SVt_PV: + name = SvPVX(sv); + return modperl_handler_new(p, apr_pstrdup(p, name)); + break; + case SVt_PVCV: + if (CvANON((CV*)sv)) { + return modperl_handler_new_anon(aTHX_ p, (CV*)sv); + } + gv = CvGV((CV*)sv); + name = apr_pstrcat(p, HvNAME(GvSTASH(gv)), "::", GvNAME(gv), NULL); + return modperl_handler_new(p, apr_pstrdup(p, name)); + break; + }; + + return NULL; +} + int modperl_handler_push_handlers(pTHX_ apr_pool_t *p, MpAV *handlers, SV *sv) { - char *handler_name; + modperl_handler_t *handler = modperl_handler_new_from_sv(aTHX_ p, sv); - if ((handler_name = modperl_mgv_name_from_sv(aTHX_ p, sv))) { - modperl_handler_t *handler = - modperl_handler_new(p, apr_pstrdup(p, handler_name)); + if (handler) { modperl_handler_array_push(handlers, handler); return TRUE; } 1.9 +4 -0 modperl-2.0/src/modules/perl/modperl_handler.h Index: modperl_handler.h =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_handler.h,v retrieving revision 1.8 retrieving revision 1.9 diff -u -u -r1.8 -r1.9 --- modperl_handler.h 19 Oct 2001 16:40:44 -0000 1.8 +++ modperl_handler.h 9 Feb 2004 19:32:42 -0000 1.9 @@ -21,6 +21,10 @@ modperl_handler_t *modperl_handler_new(apr_pool_t *p, const char *name); +modperl_handler_t *modperl_handler_new_from_sv(pTHX_ apr_pool_t *p, SV *sv); + +MP_INLINE const char *modperl_handler_name(modperl_handler_t *handler); + int modperl_handler_resolve(pTHX_ modperl_handler_t **handp, apr_pool_t *p, server_rec *s); 1.32 +0 -25 modperl-2.0/src/modules/perl/modperl_mgv.c Index: modperl_mgv.c =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_mgv.c,v retrieving revision 1.31 retrieving revision 1.32 diff -u -u -r1.31 -r1.32 --- modperl_mgv.c 9 Feb 2004 18:51:07 -0000 1.31 +++ modperl_mgv.c 9 Feb 2004 19:32:42 -0000 1.32 @@ -84,31 +84,6 @@ return symbol; } -char *modperl_mgv_name_from_sv(pTHX_ apr_pool_t *p, SV *sv) -{ - char *name = NULL; - GV *gv; - - if (SvROK(sv)) { - sv = SvRV(sv); - } - - switch (SvTYPE(sv)) { - case SVt_PV: - name = SvPVX(sv); - break; - case SVt_PVCV: - if (CvANON((CV*)sv)) { - Perl_croak(aTHX_ "anonymous handlers not (yet) supported"); - } - gv = CvGV((CV*)sv); - name = apr_pstrcat(p, HvNAME(GvSTASH(gv)), "::", GvNAME(gv), NULL); - break; - }; - - return name; -} - void modperl_mgv_append(pTHX_ apr_pool_t *p, modperl_mgv_t *symbol, const char *name) { 1.7 +0 -2 modperl-2.0/src/modules/perl/modperl_mgv.h Index: modperl_mgv.h =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_mgv.h,v retrieving revision 1.6 retrieving revision 1.7 diff -u -u -r1.6 -r1.7 --- modperl_mgv.h 6 Dec 2002 07:58:22 -0000 1.6 +++ modperl_mgv.h 9 Feb 2004 19:32:42 -0000 1.7 @@ -8,8 +8,6 @@ modperl_mgv_t *modperl_mgv_compile(pTHX_ apr_pool_t *p, const char *name); -char *modperl_mgv_name_from_sv(pTHX_ apr_pool_t *p, SV *sv); - GV *modperl_mgv_lookup(pTHX_ modperl_mgv_t *symbol); GV *modperl_mgv_lookup_autoload(pTHX_ modperl_mgv_t *symbol, 1.71 +1 -0 modperl-2.0/src/modules/perl/modperl_types.h Index: modperl_types.h =================================================================== RCS file: /home/cvs/modperl-2.0/src/modules/perl/modperl_types.h,v retrieving revision 1.70 retrieving revision 1.71 diff -u -u -r1.70 -r1.71 --- modperl_types.h 13 Dec 2003 23:40:31 -0000 1.70 +++ modperl_types.h 9 Feb 2004 19:32:42 -0000 1.71 @@ -164,6 +164,7 @@ modperl_mgv_t *mgv_obj; modperl_mgv_t *mgv_cv; const char *name; /* original name from .conf if any */ + CV *cv; U8 flags; U32 attrs; modperl_handler_t *next; 1.3 +1 -0 modperl-2.0/t/filter/both_str_req_add.t Index: both_str_req_add.t =================================================================== RCS file: /home/cvs/modperl-2.0/t/filter/both_str_req_add.t,v retrieving revision 1.2 retrieving revision 1.3 diff -u -u -r1.2 -r1.3 --- both_str_req_add.t 18 Apr 2003 06:18:56 -0000 1.2 +++ both_str_req_add.t 9 Feb 2004 19:32:42 -0000 1.3 @@ -10,6 +10,7 @@ my $data = join ' ', 'A'..'Z', 0..9; my $expected = lc $data; # that's what the input filter does $expected =~ s/\s+//g; # that's what the output filter does +$expected .= "end"; # that's what the anon output filter does my $location = '/TestFilter__both_str_req_add'; my $response = POST_BODY $location, content => $data; ok t_cmp($expected, $response, "lc input and reverse output filters"); 1.4 +12 -0 modperl-2.0/t/filter/TestFilter/both_str_req_add.pm Index: both_str_req_add.pm =================================================================== RCS file: /home/cvs/modperl-2.0/t/filter/TestFilter/both_str_req_add.pm,v retrieving revision 1.3 retrieving revision 1.4 diff -u -u -r1.3 -r1.4 --- both_str_req_add.pm 18 Sep 2003 08:09:06 -0000 1.3 +++ both_str_req_add.pm 9 Feb 2004 19:32:42 -0000 1.4 @@ -20,6 +20,18 @@ # test adding by sub's name $r->add_output_filter("out_filter"); + # test adding anon sub + $r->add_output_filter(sub { + my $filter = shift; + + while ($filter->read(my $buffer, 1024)) { + $buffer .= "end"; + $filter->print($buffer); + } + + return Apache::OK; + }); + return Apache::DECLINED; } 1.4 +1 -4 modperl-2.0/t/hooks/push_handlers.t Index: push_handlers.t =================================================================== RCS file: /home/cvs/modperl-2.0/t/hooks/push_handlers.t,v retrieving revision 1.3 retrieving revision 1.4 diff -u -u -r1.3 -r1.4 --- push_handlers.t 18 Apr 2003 06:18:57 -0000 1.3 +++ push_handlers.t 9 Feb 2004 19:32:43 -0000 1.4 @@ -11,10 +11,7 @@ full_coderef coderef1 coderef2 coderef3); my @anon = qw(anonymous anonymous1 coderef4 anonymous3); -my @strings = @refs; - -# XXX: anon-handlers unsupported yet -# push @strings, @anon +my @strings = (@refs, @anon); my $location = "/TestHooks__push_handlers"; my $expected = join "\n", @strings, ''; 1.7 +8 -9 modperl-2.0/t/hooks/TestHooks/push_handlers.pm Index: push_handlers.pm =================================================================== RCS file: /home/cvs/modperl-2.0/t/hooks/TestHooks/push_handlers.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -u -u -r1.6 -r1.7 --- push_handlers.pm 11 Aug 2003 20:34:22 -0000 1.6 +++ push_handlers.pm 9 Feb 2004 19:32:43 -0000 1.7 @@ -21,17 +21,16 @@ \&TestHooks::push_handlers::full_coderef); $r->push_handlers(PerlResponseHandler => - [\&coderef1, \&coderef2, \&coderef3]); + [\&coderef1, __PACKAGE__.'::coderef2', \&coderef3]); -# XXX: anon-handlers unsupported yet -# $r->push_handlers(PerlResponseHandler => -# sub { return say(shift, "anonymous") }); + $r->push_handlers(PerlResponseHandler => + sub { return say(shift, "anonymous") }); -# $r->push_handlers(PerlResponseHandler => -# [sub { return say(shift, "anonymous1") }, -# \&coderef4, -# sub { return say(shift, "anonymous3") }, -# ]); + $r->push_handlers(PerlResponseHandler => + [sub { return say(shift, "anonymous1") }, + \&coderef4, + sub { return say(shift, "anonymous3") }, + ]); $r->push_handlers(PerlResponseHandler => \&end); 1.15 +0 -5 modperl-2.0/todo/release Index: release =================================================================== RCS file: /home/cvs/modperl-2.0/todo/release,v retrieving revision 1.14 retrieving revision 1.15 diff -u -u -r1.14 -r1.15 --- release 9 Feb 2004 19:25:01 -0000 1.14 +++ release 9 Feb 2004 19:32:43 -0000 1.15 @@ -45,9 +45,6 @@ the whole thread is here: http://marc.theaimsgroup.com/?t=103713532800003&r=1&w=2 -- anonymous handler (for push_handlers, add_input_filter, etc), see - modperl_mgv.c: modperl_mgv_name_from_sv - - PerlModule, PerlRequire, Perl{Set,Add}Var in .htaccess is missing Owner: geoff @@ -105,5 +102,3 @@ * Apache->unescape_url{_info}: not yet implemented. should be moved to Apache::Util - - 1.145 +12 -2 modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm Index: FunctionTable.pm =================================================================== RCS file: /home/cvs/modperl-2.0/xs/tables/current/ModPerl/FunctionTable.pm,v retrieving revision 1.144 retrieving revision 1.145 diff -u -u -r1.144 -r1.145 --- FunctionTable.pm 9 Feb 2004 19:05:59 -0000 1.144 +++ FunctionTable.pm 9 Feb 2004 19:32:43 -0000 1.145 @@ -3197,8 +3197,8 @@ ] }, { - 'return_type' => 'char *', - 'name' => 'modperl_mgv_name_from_sv', + 'return_type' => 'modperl_handler_t *', + 'name' => 'modperl_handler_new_from_sv', 'args' => [ { 'type' => 'PerlInterpreter *', @@ -3211,6 +3211,16 @@ { 'type' => 'SV *', 'name' => 'sv' + } + ] + }, + { + 'return_type' => 'const char *', + 'name' => 'modperl_handler_name', + 'args' => [ + { + 'type' => 'modperl_handler_t *', + 'name' => 'handler' } ] },