Add comprehensive concurrent-test-locking logging to aid future debugging
[dbsrgits/DBIx-Class.git] / lib / DBIx / Class / _Util.pm
index bf8b830..4829539 100644 (file)
@@ -21,8 +21,7 @@ BEGIN {
 
     HAS_ITHREADS => $Config{useithreads} ? 1 : 0,
 
-    # ::Runmode would only be loaded by DBICTest, which in turn implies t/
-    DBICTEST => eval { DBICTest::RunMode->is_author } ? 1 : 0,
+    DBICTEST => $INC{"DBICTest/Util.pm"} ? 1 : 0,
 
     # During 5.13 dev cycle HELEMs started to leak on copy
     # add an escape for these perls ON SMOKERS - a user will still get death
@@ -71,7 +70,7 @@ use base 'Exporter';
 our @EXPORT_OK = qw(
   sigwarn_silencer modver_gt_or_eq modver_gt_or_eq_and_lt
   fail_on_internal_wantarray fail_on_internal_call
-  refdesc refcount hrefaddr is_exception detect_reinvoked_destructor
+  refdesc refcount hrefaddr is_exception detected_reinvoked_destructor
   quote_sub qsub perlstring serialize deep_clone
   UNRESOLVABLE_CONDITION
 );
@@ -165,6 +164,27 @@ sub is_exception ($) {
       die $suberror
     }
   }
+  elsif (
+    # a ref evaluating to '' is definitively a "null object"
+    ( not $not_blank )
+      and
+    length( my $class = ref $e )
+  ) {
+    carp_unique( sprintf(
+      "Objects of external exception class '%s' stringify to '' (the "
+    . 'empty string), implementing the so called null-object-pattern. '
+    . 'Given Perl\'s "globally cooperative" exception handling using this '
+    . 'class of exceptions is extremely dangerous, as it may (and often '
+    . 'does) result in silent discarding of errors. DBIx::Class tries to '
+    . 'work around this as much as possible, but other parts of your '
+    . 'software stack may not be even aware of the problem. Please submit '
+    . 'a bugreport against the distribution containing %s.',
+
+      ($class) x 2,
+    ));
+
+    $not_blank = 1;
+  }
 
   return $not_blank;
 }
@@ -181,22 +201,21 @@ sub is_exception ($) {
 
   # This is almost invariably invoked from within DESTROY
   # throwing exceptions won't work
-  sub detect_reinvoked_destructor {
+  sub detected_reinvoked_destructor {
 
     # quick "garbage collection" pass - prevents the registry
     # from slowly growing with a bunch of undef-valued keys
     defined $destruction_registry->{$_} or delete $destruction_registry->{$_}
       for keys %$destruction_registry;
 
-    unless (length ref $_[0]) {
-      printf STDERR '%s() expects a reference %s',
+    if (! length ref $_[0]) {
+      printf STDERR '%s() expects a blessed reference %s',
         (caller(0))[3],
         Carp::longmess,
       ;
       return undef; # don't know wtf to do
     }
-
-    if (! defined $destruction_registry->{ my $addr = refaddr($_[0]) } ) {
+    elsif (! defined $destruction_registry->{ my $addr = refaddr($_[0]) } ) {
       weaken( $destruction_registry->{$addr} = $_[0] );
       return 0;
     }
@@ -261,7 +280,7 @@ sub modver_gt_or_eq_and_lt ($$$) {
     }
 
     my $cf = 1;
-    while ( ( (caller($cf+1))[3] || '' ) =~ / :: (?:
+    while ( ( (CORE::caller($cf+1))[3] || '' ) =~ / :: (?:
 
       # these are public API parts that alter behavior on wantarray
       search | search_related | slice | search_literal
@@ -279,8 +298,8 @@ sub modver_gt_or_eq_and_lt ($$$) {
     my ($fr, $want, $argdesc);
     {
       package DB;
-      $fr = [ caller($cf) ];
-      $want = ( caller($cf-1) )[5];
+      $fr = [ CORE::caller($cf) ];
+      $want = ( CORE::caller($cf-1) )[5];
       $argdesc = ref $DB::args[0]
         ? DBIx::Class::_Util::refdesc($DB::args[0])
         : 'non '
@@ -306,7 +325,7 @@ sub fail_on_internal_call {
   my ($fr, $argdesc);
   {
     package DB;
-    $fr = [ caller(1) ];
+    $fr = [ CORE::caller(1) ];
     $argdesc = ref $DB::args[0]
       ? DBIx::Class::_Util::refdesc($DB::args[0])
       : undef