Introduce M.A.D. within the schema/source instance linkage
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / Schema.pm
index 77a7fe7..8270c27 100644 (file)
@@ -11,6 +11,7 @@ use File::Spec;
 use Sub::Name 'subname';
 use Module::Find();
 use Storable();
+use B qw/svref_2object/;
 use namespace::clean;
 
 use base qw/DBIx::Class/;
@@ -244,7 +245,9 @@ sub load_namespaces {
     use warnings 'redefine';
 
     # ensure classes are loaded and attached in inheritance order
-    $class->ensure_class_loaded($_) foreach(values %results);
+    for my $res (values %results) {
+      $class->ensure_class_loaded($res);
+    }
     my %inh_idx;
     my @subclass_last = sort {
 
@@ -898,6 +901,7 @@ sub compose_namespace {
   my $schema = $self->clone;
   {
     no warnings qw/redefine/;
+    no strict qw/refs/;
 #    local *Class::C3::reinitialize = sub { };
     foreach my $moniker ($schema->sources) {
       my $source = $schema->source($moniker);
@@ -906,8 +910,14 @@ sub compose_namespace {
         $target_class => $source->result_class, ($base ? $base : ())
       );
       $source->result_class($target_class);
-      $target_class->result_source_instance($source)
-        if $target_class->can('result_source_instance');
+      if ($target_class->can('result_source_instance')) {
+
+        # since the newly created classes are registered only with
+        # the instance of $schema, it should be safe to weaken
+        # the ref (it will GC when $schema is destroyed)
+        $target_class->result_source_instance($source);
+        weaken ${"${target_class}::__cag_result_source_instance"};
+      }
      $schema->register_source($moniker, $source);
     }
   }
@@ -1192,13 +1202,13 @@ sub thaw {
 
 =head2 freeze
 
-This doesn't actually do anything more than call L<Storable/freeze>, it is just
+This doesn't actually do anything more than call L<Storable/nfreeze>, it is just
 provided here for symmetry.
 
 =cut
 
 sub freeze {
-  return Storable::freeze($_[1]);
+  return Storable::nfreeze($_[1]);
 }
 
 =head2 dclone
@@ -1363,6 +1373,29 @@ sub _register_source {
   $self->class_mappings(\%map);
 }
 
+{
+  my $global_phase_destroy;
+
+  END { $global_phase_destroy++ }
+
+  sub DESTROY {
+    return if $global_phase_destroy;
+
+    my $self = shift;
+    my $srcs = $self->source_registrations;
+
+    for my $moniker (keys %$srcs) {
+      # find first source that is not about to be GCed (someone other than $self
+      # holds a reference to it) and reattach to it, weakening our own link
+      if (ref $srcs->{$moniker} and svref_2object($srcs->{$moniker})->REFCNT > 1) {
+        $srcs->{$moniker}->schema($self);
+        weaken $srcs->{$moniker};
+        last;
+      }
+    }
+  }
+}
+
 sub _unregister_source {
     my ($self, $moniker) = @_;
     my %reg = %{$self->source_registrations};