Re: mro status, etc
Brandon Black [Mon, 30 Apr 2007 16:45:54 +0000 (11:45 -0500)]
From: "Brandon Black" <blblack@gmail.com>
Message-ID: <84621a60704301445y37e9b05ey235210a8e5547cc1@mail.gmail.com>

p4raw-id: //depot/perl@31122

embedvar.h
lib/mro.pm
mg.c
perlapi.h
pp_hot.c
t/mro/recursion_c3.t
t/mro/recursion_dfs.t
thrdvar.h

index 1a4ba0d..da82c87 100644 (file)
@@ -55,6 +55,7 @@
 #define PL_curstash            (vTHX->Tcurstash)
 #define PL_defoutgv            (vTHX->Tdefoutgv)
 #define PL_defstash            (vTHX->Tdefstash)
+#define PL_delayedisa          (vTHX->Tdelayedisa)
 #define PL_delaymagic          (vTHX->Tdelaymagic)
 #define PL_dirty               (vTHX->Tdirty)
 #define PL_dumpindent          (vTHX->Tdumpindent)
 #define PL_Tcurstash           PL_curstash
 #define PL_Tdefoutgv           PL_defoutgv
 #define PL_Tdefstash           PL_defstash
+#define PL_Tdelayedisa         PL_delayedisa
 #define PL_Tdelaymagic         PL_delaymagic
 #define PL_Tdirty              PL_dirty
 #define PL_Tdumpindent         PL_dumpindent
index 693a0ac..301f7a4 100644 (file)
@@ -246,6 +246,39 @@ In simple cases, it is equivalent to:
 But there are some cases where only this solution
 works (like C<goto &maybe::next::method>);
 
+=head1 PERFORMANCE CONSIDERATIONS
+
+Specifying the mro type of a class before setting C<@ISA> will
+be faster than the other way around.  Also, making all of your
+C<@ISA> manipulations in a single assignment statement will be
+faster that doing them one by one via C<push> (which is what
+C<use base> does currently).
+
+Examples:
+
+  # The slowest way
+  package Foo;
+  use base qw/A B C/;
+  use mro 'c3';
+
+  # The fastest way
+  # (not exactly equivalent to above,
+  #   as base.pm can do other magic)
+  use mro 'c3';
+  use A ();
+  use B ();
+  use C ();
+  our @ISA = qw/A B C/;
+
+Generally speaking, every time C<@ISA> is modified, the MRO
+of that class will be recalculated, because of the way array
+magic works.  Pushing multiple items onto C<@ISA> in one push
+statement still counts as multiple modifications.  However,
+assigning a list to C<@ISA> only counts as a single
+modification.  Thus if you really need to do C<push> as
+opposed to assignment, C<@ISA = (@ISA, qw/A B C/);>
+will still be faster than C<push(@ISA, qw/A B C/);>
+
 =head1 SEE ALSO
 
 =head2 The original Dylan paper
diff --git a/mg.c b/mg.c
index 21f671a..9617767 100644 (file)
--- a/mg.c
+++ b/mg.c
@@ -1529,19 +1529,26 @@ int
 Perl_magic_setisa(pTHX_ SV *sv, MAGIC *mg)
 {
     dVAR;
+    HV* stash;
     PERL_UNUSED_ARG(sv);
 
+    /* Bail out if destruction is going on */
+    if(PL_dirty) return 0;
+
     /* The first case occurs via setisa,
        the second via setisa_elem, which
        calls this same magic */
-    mro_isa_changed_in(
-        GvSTASH(
-            SvTYPE(mg->mg_obj) == SVt_PVGV
-                ? (GV*)mg->mg_obj
-                : (GV*)SvMAGIC(mg->mg_obj)->mg_obj
-        )
+    stash = GvSTASH(
+        SvTYPE(mg->mg_obj) == SVt_PVGV
+            ? (GV*)mg->mg_obj
+            : (GV*)SvMAGIC(mg->mg_obj)->mg_obj
     );
 
+    if(PL_delaymagic)
+        PL_delayedisa = stash;
+    else
+        mro_isa_changed_in(stash);
+
     return 0;
 }
 
index cf29a35..d09fc33 100644 (file)
--- a/perlapi.h
+++ b/perlapi.h
@@ -668,6 +668,8 @@ END_EXTERN_C
 #define PL_defoutgv            (*Perl_Tdefoutgv_ptr(aTHX))
 #undef  PL_defstash
 #define PL_defstash            (*Perl_Tdefstash_ptr(aTHX))
+#undef  PL_delayedisa
+#define PL_delayedisa          (*Perl_Tdelayedisa_ptr(aTHX))
 #undef  PL_delaymagic
 #define PL_delaymagic          (*Perl_Tdelaymagic_ptr(aTHX))
 #undef  PL_dirty
index 51f4967..35e8868 100644 (file)
--- a/pp_hot.c
+++ b/pp_hot.c
@@ -1151,6 +1151,15 @@ PP(pp_aassign)
        while (relem <= SP)
            *relem++ = (lelem <= lastlelem) ? *lelem++ : &PL_sv_undef;
     }
+
+    /* This is done at the bottom and in this order because
+       mro_isa_changed_in() can throw exceptions */
+    if(PL_delayedisa) {
+        HV* stash = PL_delayedisa;
+        PL_delayedisa = NULL;
+        mro_isa_changed_in(stash);
+    }
+
     RETURN;
 }
 
index 7aa4a7b..5429315 100644 (file)
@@ -10,7 +10,6 @@ BEGIN {
 }
 
 require './test.pl';
-use mro;
 
 plan(skip_all => "Your system has no SIGALRM") if !exists $SIG{ALRM};
 plan(tests => 8);
index 313a4ed..b7bf6d4 100644 (file)
@@ -10,7 +10,6 @@ BEGIN {
 }
 
 require './test.pl';
-use mro;
 
 plan(skip_all => "Your system has no SIGALRM") if !exists $SIG{ALRM};
 plan(tests => 8);
index 4f49ef8..3b5bbc1 100644 (file)
--- a/thrdvar.h
+++ b/thrdvar.h
@@ -182,6 +182,7 @@ PERLVAR(Tcolorset,  bool)           /* from regcomp.c */
 PERLVARI(Tdirty,       bool, FALSE)    /* in the middle of tearing things down? */
 PERLVAR(Tin_eval,      VOL U8) /* trap "fatal" errors? */
 PERLVAR(Ttainted,      bool)           /* using variables controlled by $< */
+PERLVARI(Tdelayedisa,  HV*, NULL)      /* stash for PL_delaymagic for magic_setisa */
 
 /* For historical reasons this file is followed by intrpvar.h in the interpeter
    struct. As this file currently ends with 7 bytes of variables, intrpvar.h