SlideShare a Scribd company logo
Object::Franger


 Wear a Raincoat
  in Your Code


   Steven Lembark
 Workhorse Computing
Why do you need one?
●   Frankly, because you can't control your object:  
    you never know where it might end up.
    ●   Other people can fiddle with it.
    ●   Forks can leave it in unintended places.
    ●   It might get bugs (or worse).
    ●   External issues can require special handling.
Why an object wrapper?
●   All of the code required to handle all of the 
    special cases ends up bloating your work.
●   Most wrappers are re­usable: forks, timeouts, 
    and signals all use common code.
●   The division of labor isolates the wrapper and 
    inner portions into re­usable pieces.
●   All of which make these good fodder for OO.
Requirements for Wrappers.
●   They should feel like the real thing.
●   They cannot leak.
●   These are not easy: making the wrapper feel 
    enough like the real thing to fool both your 
    object and the caller.
Where wrappers can help.
●   Re­usable sanity checks (what goes on here).
●   Compatibility layers for changing API's.
●   Localizing values on the call stack.
●   Modifying context.
●   Simplifying the wrapped code, which doesn't 
    have to deal with all of this in one place.
    ●   Example: Top­ & Bottom­half device drivers.
Perly wrappers.
●   There is (of course) more than one way:
    ●   Override a method in a derived class.
    ●   Replace a method in the symbol table.
    ●   AUTOLOAD from stub namespace.
●   Example:
    ●   Attribute Handlers replace the subroutine before 
        it is installed (functional).
    ●   Object::Trampoline replaces the object in­place.
    ●   Object::Wraper (AUTOLOAD).
My particular itch: DBI with forks.
●   DBI objects cannot be re­cycled across forks.
●   I was writing heavily forked code for high­
    volume database access.
●   Needed the forks, needed the DBI to handle 
    the children gracefully.
●   Wanted child proc's to fail gracefully – 
    hopefully without damaging the database.
Why forks hurt DBI
●   The points are sharp.
●   Database connections use PID's to bookkeep 
    connections.
●   Servers cannot handle multiple clients requests 
    on the same channel.
●   Destroying an object in one process brings 
    kicks its longer­lived sibling in the socket.
Cleaning Up $dbh
●   Forked process:
    ●   Disable destroy side effects on the channel for 
        each object.
    ●   Iterate the handle and cached kids.
●   Within process:
    ●   Call $kid­>finish for all cached kids.
    ●   Call $dbh­>disconnect.
Cleaning up $dbh
                              my @kidz
                              = do
●   Extract the list of       {
                                  my $drh = $dbh->{ Driver };

    handles.                       my $list
                                   = $drh
                                   ? $drh->{ CachedKids }
●   If the process did not         : '';


    create them, then              $list
                                   ? values %$list
                                   : ()
    inactivate the            };

    DESTROY side effects.     if( $$ != $pid )
                              {
                                  $_->{ InactiveDestroy } = 1
●   Otherwise finish the      }
                                  for ( $dbh, @kidz );

                              else
    kids and then             {
                                  $_->finish for @kidz;
    disconnect.                    $dbh->disconnect;
                              }
Sanity checking a PID
●   Perl stores he current Process ID (“PID”) in  
    “$$” (i.e., looks like the shell variable).
●   Storing this when the handle is created allows 
    re­checking it before dispatching the call.
●   If the stored PID and $$ don't agree then the 
    handle needs to be cleaned up.
●   This has to be sanity­checked on method calls, 
    dealt with carefully in DESTROY.
Looks objective:
●   The code is re­usable.
    ●   All of the handles are cleaned up the same way.
    ●   All of the fork checks are the same.
    ●   All of the wrapping is done the same way.
●   It can be parameterized.
    ●   $$ is $$ wherever you are.
●   Frangers are good for objects.
My Requirements
●   These are called for every method in the 
    wrapped class: they have to be fast.
●   They also cannot use bulky storage since they 
    are add to the requirements for all wrapped 
    objects.
●   They should also avoid unintended side effects 
    (e.g., modifying object values, calling context).
O::W is built in layers.
●   Object::Wrapper base class provides generic 
    new, DESTROY, and an AUTOLOAD.
●   The AUTOLOAD calls sanity check hooks in 
    the derived classes and re­dispatches the result 
    or croaks.
●   Object::Wrapper::Fork bookkeeps $$.
●   Object::Wrapper::Fork::DBI deals with the 
    cleanups.
A Place to Hang Your Hat
●   A few hooks are all that's kneaded:
    ●   pre­dispatch for the sanity check.
    ●   straight­jacket for failed sanity checks.
●   These accommodate all of the necessary 
    customizations for the basic wrapper.
Throwing a Hook
●   Perl's “can” is rather helpful:
    ●   It returns true if the object “can”.
    ●   Its true value is a subref to the  object's handler.
●   This makes:
        my $handler = $object­>can( $hook );
        $object­>$handler­>( @argz );
    synonymous with:
        $handler­>( $object, @argz );
Structure of a Franger
●   Remember the need for speed, flexibility, and 
    encapsulation of the wrapped object.
●   Take a look at the calling standard: generic 
    object with arguments.
●   The structure is obvious:
      bless [ $object, @sanity_argz ], $class;
●   Resulting in:
      $handler->( @$obj );
Constructing a Franger
●   Store the call stack for validation as­is.

    sub new
    {
        my $proto   = shift;
        my $class   = blessed $proto || $proto;

        my $object = shift
        or croak "Bogus franger: missing object";

        bless [ $object, @_ ], $class
    }
OK, but what do you do with it?
●   Whatever you want.
●   Object::Wrapper, in fact, does nothing but re­
    dispatch the method calls.
●   Useful for cases where the interesting part of 
    the wrapper is in the DESTROY, not the 
    individual calls.
Wrapper AUTOLOAD is Standard
●   Does nothing more than necessary.
●   Useful when the DESTROY check is enogh.

AUTOLOAD
{
    my $franger = shift;

    my $i       = rindex $AUTOLOAD, ':';
    my $name    = substr $AUTOLOAD, ++$i;

    my $sub     = $franger->[0]->can( $name )
    or confess "Bogus $AUTOLOAD: '$franger->[0]' cannot '$name'";

    $franger->[0]->$sub( @_ )
}
Oedipus Not­Complex: Forks
AUTOLOAD
{
    my $franger = shift;
    my ( $obj, $pid ) = @$franger;

    $pid == $$
     or confess "Bogus $AUTOLOAD: @{$franger} crosses fork.";

    my $i       = rindex $AUTOLOAD, ':';
    my $name    = substr $AUTOLOAD, ++$i;

    my $sub     = $obj->can( $name )
    or confess "Bogus $AUTOLOAD: '$obj' cannot '$name'";

    # goto &$obj is slower according to Benchmark...

    $obj->$sub( @_ )
}
Clean Up Your Mess: O::W::Destroy
DESTROY
{
    my $franger = shift;

    my $class   = blessed $franger || $franger;

    # $cleanupz{ $class } may be a method name or coderef to save time.

    my $cleanup = $cleanupz{ $class } || $franger->can( 'cleanup' )
    or confess "Bogus franger: no cleanup for '$franger' or '$class'";

    my $sub
    = ref $cleanup
    ? $cleanup
    : $franger->can( $cleanup )
    or confess "Bogus $class: no cleanup for '$franger' ($class)";

    'CODE' eq reftype $sub
    or confess "Bogus $class: not a coderef '$sub'";

    $cleanup->( @$franger );

    return
}
DBI: It's All How You Clean Up
●   Check for cached_kids.
●   Within the constructing PID: 
    ●   Finish all the kids.
    ●   Disconnect the parent.
●   Within child Proc's:
    ●   Disable destroy side effects in the kids & parent.
First Step: Find the Kids
sub cleanup
{
    my ( $dbh, $pid ) = @_;

   my $struct
   = do
   {
        my $drh    = $dbh->{ Driver };

        $drh
        ? $drh->{ CachedKids }
        : ''
   };

   my @kidz
   = $struct
   ? values %$struct
   : ()
   ;
Second Step: Do the Deed

    if( $$ != $pid )
    {
        # handle crossed a fork: turn off side
        # effects of destruction.

        $_->{ InactiveDestroy } = 1
        for
        (
            $dbh,
            @kidz
        );
    }
    else
    {
        $_->finish for @kidz;

        $dbh->disconnect;
    }

    # at this point the DBI object has been
    # prepared to go out of scope politely.

    return
}
Cleaning Up Statements Is Easier
sub cleanup
{
    my ( $sth, $pid ) = @_;

    if( $$ ~~ $pid )
    {
        # same process: finalize the handle and disconnect.
        # caller deals with clones.

           $sth->{ Active }
           and $sth->finish;
    }
    else
    {
           $sth->{ InactiveDestroy } = 1;
    }

    # at this point the DBD object has been
    # prepared to go out of scope politely.

    return
}
Getting What You Want: 
Overloading Constructors
●   For DBI this requires versions of connect and 
    connect_cached, prepare and prepare_cached.
●   Connect simply returns the wrapped $dbh:
    sub connect
    {
        shift;

        my $dbh     = DBI->connect( @_ )
        or croak 'Fail connect: ' . $DBI::errstr;

        Object::Wrapper::Fork::dbh->new( $dbh )
    }
Overloading STH Constructors
●   These get a DBI wrapper object.
●   Returning a wrapped DBD.
    sub prepare
    {
        my $franger = shift;

        my ( $dbh, $pid ) = @$franger;

        $pid == $$
        or confess "Bogus prepare: @{ $franger } crosses fork.";

        my $sth = $dbh->prepare( @_ )
        or croak 'Failed prepare: ' . $dbh->errstr;

        Object::Wrapper::Fork::sth->new( $sth )
    }
Wrappers are not 100% effective
●   DBI offers a tied­hash interface.
●   Kinda hard to handle this with a blessed array.
●   Fortunately, the hash interface is rarely 
    necessary.
●   There is also one more issue for destructors.
Making Happy ENDings
●   Perl destroys objects out­of­order on exit.
●   This means that we also have to wrap 
    DBI::DESTROY to get complete coverage.
    ●   Fortunately this isn't all that hard to do with the 
        Symbol module's qualify_to_ref.
    ●   This requires a map of $dbh → O::W::F::DBI 
        objects that can be used to dispatch destruction.
    ●   No time to describe it here.
Other Uses for Object::Wrappers
●   Maximum time:
     bless [ $obj, ( time + $window ) ];
     time < $franger->[1] or ...
●   Maximum reuse:
     bless [ $obj, $counter ];
     --$franger->[1] or ...
Only Your Wrapper Knows For Sure
●   Long­lived processes may not want to die after 
     the wrapped object hits its limit.
●   Nice thing is that they don't have to:
      --$franger->[ 1 ]
      or @$franger = ( $class->new( ... ), $counter );
●   This is handy for classes with memory leaks in 
    the objects.
Summary
●   Keeping your object safe is easy:
    ●   Use a franger.
    ●   Check before you use it.
    ●   Make sure you clean up afterwards.

More Related Content

PDF
Pim Elshoff "Technically DDD"
PDF
You code sucks, let's fix it
PDF
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
PDF
Alexander Makarov "Let’s talk about code"
PDF
Symfony2 from the Trenches
PDF
Metaprogramming in JavaScript
PDF
Doctrine MongoDB Object Document Mapper
PDF
Symfony Day 2010 Doctrine MongoDB ODM
Pim Elshoff "Technically DDD"
You code sucks, let's fix it
“Writing code that lasts” … or writing code you won’t hate tomorrow. - PHPKonf
Alexander Makarov "Let’s talk about code"
Symfony2 from the Trenches
Metaprogramming in JavaScript
Doctrine MongoDB Object Document Mapper
Symfony Day 2010 Doctrine MongoDB ODM

What's hot (20)

PDF
Desymfony2013.gonzalo123
PDF
Dependency Injection
PDF
SOLID Ruby SOLID Rails
PDF
Design Patterns in PHP5
PPT
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
PPTX
Lecture 6: Client Side Programming 2
PPTX
Lecture 5: Client Side Programming 1
PDF
Practical JavaScript Programming - Session 1/8
PDF
Dealing with Legacy PHP Applications
KEY
Grails UI Primer
PDF
Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"
PDF
Make it SOLID!
PDF
Entity api
PDF
CGI::Prototype (NPW 2006)
PDF
Advanced javascript
PPTX
ONE MORE TIME ABOUT CODE STANDARDS AND BEST PRACTICES
PDF
Object Features
PPTX
AngularJs $provide API internals & circular dependency problem.
PDF
Introducing jQuery
Desymfony2013.gonzalo123
Dependency Injection
SOLID Ruby SOLID Rails
Design Patterns in PHP5
ZFConf 2010: Zend Framework & MVC, Model Implementation (Part 2, Dependency I...
Lecture 6: Client Side Programming 2
Lecture 5: Client Side Programming 1
Practical JavaScript Programming - Session 1/8
Dealing with Legacy PHP Applications
Grails UI Primer
Architecting for PHP5 - Why "Runs on PHP5" is not "Written for PHP5"
Make it SOLID!
Entity api
CGI::Prototype (NPW 2006)
Advanced javascript
ONE MORE TIME ABOUT CODE STANDARDS AND BEST PRACTICES
Object Features
AngularJs $provide API internals & circular dependency problem.
Introducing jQuery
Ad

Viewers also liked (16)

PPTX
Raincoat survey findings
DOCX
Business plan (air umbrella)
PPTX
State of art
PPTX
認知症にやさしいまちづくり ~セクターを越えたつながり~
PDF
State of the Cloud 2017
PPTX
Projeto gelo
PDF
BigWeatherGear Group and Corporate Services Brochure 2013
PDF
Study: The Future of VR, AR and Self-Driving Cars
PDF
UX, ethnography and possibilities: for Libraries, Museums and Archives
PDF
Hype vs. Reality: The AI Explainer
PDF
Designing Teams for Emerging Challenges
PDF
Visual Design with Data
PDF
3 Things Every Sales Team Needs to Be Thinking About in 2017
PDF
Build Features, Not Apps
PDF
TEDx Manchester: AI & The Future of Work
PDF
How to Become a Thought Leader in Your Niche
Raincoat survey findings
Business plan (air umbrella)
State of art
認知症にやさしいまちづくり ~セクターを越えたつながり~
State of the Cloud 2017
Projeto gelo
BigWeatherGear Group and Corporate Services Brochure 2013
Study: The Future of VR, AR and Self-Driving Cars
UX, ethnography and possibilities: for Libraries, Museums and Archives
Hype vs. Reality: The AI Explainer
Designing Teams for Emerging Challenges
Visual Design with Data
3 Things Every Sales Team Needs to Be Thinking About in 2017
Build Features, Not Apps
TEDx Manchester: AI & The Future of Work
How to Become a Thought Leader in Your Niche
Ad

Similar to Object::Franger: Wear a Raincoat in your Code (20)

PDF
Object Trampoline: Why having not the object you want is what you need.
PDF
Object::Trampoline: Follow the bouncing object.
PDF
Our Friends the Utils: A highway traveled by wheels we didn't re-invent.
PDF
Os Wilhelm
PDF
PDF
Short Introduction To "perl -d"
PDF
Getting big without getting fat, in perl
PDF
Eversion 101: An Introduction to Inside-Out Objects
KEY
Good Evils In Perl (Yapc Asia)
PDF
WEB PROGRAMMING UNIT VI BY BHAVSINGH MALOTH
PDF
Regexp Master
PPTX
Ropython-windbg-python-extensions
PDF
Working Effectively With Legacy Perl Code
PPT
web programming Unit VI PPT by Bhavsingh Maloth
PDF
Perl at SkyCon'12
PDF
Object Exercise
ODP
Perl - laziness, impatience, hubris, and one liners
PDF
Introduction to Moose
PDF
24 uses for perl6
Object Trampoline: Why having not the object you want is what you need.
Object::Trampoline: Follow the bouncing object.
Our Friends the Utils: A highway traveled by wheels we didn't re-invent.
Os Wilhelm
Short Introduction To "perl -d"
Getting big without getting fat, in perl
Eversion 101: An Introduction to Inside-Out Objects
Good Evils In Perl (Yapc Asia)
WEB PROGRAMMING UNIT VI BY BHAVSINGH MALOTH
Regexp Master
Ropython-windbg-python-extensions
Working Effectively With Legacy Perl Code
web programming Unit VI PPT by Bhavsingh Maloth
Perl at SkyCon'12
Object Exercise
Perl - laziness, impatience, hubris, and one liners
Introduction to Moose
24 uses for perl6

More from Workhorse Computing (20)

PDF
Wheels we didn't re-invent: Perl's Utility Modules
PDF
mro-every.pdf
PDF
Paranormal statistics: Counting What Doesn't Add Up
PDF
The $path to knowledge: What little it take to unit-test Perl.
PDF
Unit Testing Lots of Perl
PDF
Generating & Querying Calendar Tables in Posgresql
PDF
Hypers and Gathers and Takes! Oh my!
PDF
BSDM with BASH: Command Interpolation
PDF
Findbin libs
PDF
Memory Manglement in Raku
PDF
BASH Variables Part 1: Basic Interpolation
PDF
Effective Benchmarks
PDF
Metadata-driven Testing
PDF
The W-curve and its application.
PDF
Keeping objects healthy with Object::Exercise.
PDF
Perl6 Regexen: Reduce the line noise in your code.
PDF
Smoking docker
PDF
Getting Testy With Perl6
PDF
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
PDF
Neatly folding-a-tree
Wheels we didn't re-invent: Perl's Utility Modules
mro-every.pdf
Paranormal statistics: Counting What Doesn't Add Up
The $path to knowledge: What little it take to unit-test Perl.
Unit Testing Lots of Perl
Generating & Querying Calendar Tables in Posgresql
Hypers and Gathers and Takes! Oh my!
BSDM with BASH: Command Interpolation
Findbin libs
Memory Manglement in Raku
BASH Variables Part 1: Basic Interpolation
Effective Benchmarks
Metadata-driven Testing
The W-curve and its application.
Keeping objects healthy with Object::Exercise.
Perl6 Regexen: Reduce the line noise in your code.
Smoking docker
Getting Testy With Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly folding-a-tree

Recently uploaded (20)

PDF
Heart disease approach using modified random forest and particle swarm optimi...
PDF
NewMind AI Weekly Chronicles - August'25-Week II
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PPTX
1. Introduction to Computer Programming.pptx
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
1 - Historical Antecedents, Social Consideration.pdf
PDF
Accuracy of neural networks in brain wave diagnosis of schizophrenia
PDF
Video forgery: An extensive analysis of inter-and intra-frame manipulation al...
PDF
A comparative analysis of optical character recognition models for extracting...
PDF
DP Operators-handbook-extract for the Mautical Institute
PDF
Hindi spoken digit analysis for native and non-native speakers
PDF
A comparative study of natural language inference in Swahili using monolingua...
PPTX
TLE Review Electricity (Electricity).pptx
PDF
MIND Revenue Release Quarter 2 2025 Press Release
PDF
From MVP to Full-Scale Product A Startup’s Software Journey.pdf
PDF
Zenith AI: Advanced Artificial Intelligence
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
Approach and Philosophy of On baking technology
PDF
Transform Your ITIL® 4 & ITSM Strategy with AI in 2025.pdf
PDF
Getting Started with Data Integration: FME Form 101
Heart disease approach using modified random forest and particle swarm optimi...
NewMind AI Weekly Chronicles - August'25-Week II
Digital-Transformation-Roadmap-for-Companies.pptx
1. Introduction to Computer Programming.pptx
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
1 - Historical Antecedents, Social Consideration.pdf
Accuracy of neural networks in brain wave diagnosis of schizophrenia
Video forgery: An extensive analysis of inter-and intra-frame manipulation al...
A comparative analysis of optical character recognition models for extracting...
DP Operators-handbook-extract for the Mautical Institute
Hindi spoken digit analysis for native and non-native speakers
A comparative study of natural language inference in Swahili using monolingua...
TLE Review Electricity (Electricity).pptx
MIND Revenue Release Quarter 2 2025 Press Release
From MVP to Full-Scale Product A Startup’s Software Journey.pdf
Zenith AI: Advanced Artificial Intelligence
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Approach and Philosophy of On baking technology
Transform Your ITIL® 4 & ITSM Strategy with AI in 2025.pdf
Getting Started with Data Integration: FME Form 101

Object::Franger: Wear a Raincoat in your Code

  • 1. Object::Franger Wear a Raincoat in Your Code Steven Lembark Workhorse Computing
  • 2. Why do you need one? ● Frankly, because you can't control your object:   you never know where it might end up. ● Other people can fiddle with it. ● Forks can leave it in unintended places. ● It might get bugs (or worse). ● External issues can require special handling.
  • 3. Why an object wrapper? ● All of the code required to handle all of the  special cases ends up bloating your work. ● Most wrappers are re­usable: forks, timeouts,  and signals all use common code. ● The division of labor isolates the wrapper and  inner portions into re­usable pieces. ● All of which make these good fodder for OO.
  • 4. Requirements for Wrappers. ● They should feel like the real thing. ● They cannot leak. ● These are not easy: making the wrapper feel  enough like the real thing to fool both your  object and the caller.
  • 5. Where wrappers can help. ● Re­usable sanity checks (what goes on here). ● Compatibility layers for changing API's. ● Localizing values on the call stack. ● Modifying context. ● Simplifying the wrapped code, which doesn't  have to deal with all of this in one place. ● Example: Top­ & Bottom­half device drivers.
  • 6. Perly wrappers. ● There is (of course) more than one way: ● Override a method in a derived class. ● Replace a method in the symbol table. ● AUTOLOAD from stub namespace. ● Example: ● Attribute Handlers replace the subroutine before  it is installed (functional). ● Object::Trampoline replaces the object in­place. ● Object::Wraper (AUTOLOAD).
  • 7. My particular itch: DBI with forks. ● DBI objects cannot be re­cycled across forks. ● I was writing heavily forked code for high­ volume database access. ● Needed the forks, needed the DBI to handle  the children gracefully. ● Wanted child proc's to fail gracefully –  hopefully without damaging the database.
  • 8. Why forks hurt DBI ● The points are sharp. ● Database connections use PID's to bookkeep  connections. ● Servers cannot handle multiple clients requests  on the same channel. ● Destroying an object in one process brings  kicks its longer­lived sibling in the socket.
  • 9. Cleaning Up $dbh ● Forked process: ● Disable destroy side effects on the channel for  each object. ● Iterate the handle and cached kids. ● Within process: ● Call $kid­>finish for all cached kids. ● Call $dbh­>disconnect.
  • 10. Cleaning up $dbh my @kidz = do ● Extract the list of  { my $drh = $dbh->{ Driver }; handles. my $list = $drh ? $drh->{ CachedKids } ● If the process did not  : ''; create them, then  $list ? values %$list : () inactivate the  }; DESTROY side effects. if( $$ != $pid ) { $_->{ InactiveDestroy } = 1 ● Otherwise finish the  } for ( $dbh, @kidz ); else kids and then  { $_->finish for @kidz; disconnect. $dbh->disconnect; }
  • 11. Sanity checking a PID ● Perl stores he current Process ID (“PID”) in   “$$” (i.e., looks like the shell variable). ● Storing this when the handle is created allows  re­checking it before dispatching the call. ● If the stored PID and $$ don't agree then the  handle needs to be cleaned up. ● This has to be sanity­checked on method calls,  dealt with carefully in DESTROY.
  • 12. Looks objective: ● The code is re­usable. ● All of the handles are cleaned up the same way. ● All of the fork checks are the same. ● All of the wrapping is done the same way. ● It can be parameterized. ● $$ is $$ wherever you are. ● Frangers are good for objects.
  • 13. My Requirements ● These are called for every method in the  wrapped class: they have to be fast. ● They also cannot use bulky storage since they  are add to the requirements for all wrapped  objects. ● They should also avoid unintended side effects  (e.g., modifying object values, calling context).
  • 14. O::W is built in layers. ● Object::Wrapper base class provides generic  new, DESTROY, and an AUTOLOAD. ● The AUTOLOAD calls sanity check hooks in  the derived classes and re­dispatches the result  or croaks. ● Object::Wrapper::Fork bookkeeps $$. ● Object::Wrapper::Fork::DBI deals with the  cleanups.
  • 15. A Place to Hang Your Hat ● A few hooks are all that's kneaded: ● pre­dispatch for the sanity check. ● straight­jacket for failed sanity checks. ● These accommodate all of the necessary  customizations for the basic wrapper.
  • 16. Throwing a Hook ● Perl's “can” is rather helpful: ● It returns true if the object “can”. ● Its true value is a subref to the  object's handler. ● This makes: my $handler = $object­>can( $hook ); $object­>$handler­>( @argz ); synonymous with: $handler­>( $object, @argz );
  • 17. Structure of a Franger ● Remember the need for speed, flexibility, and  encapsulation of the wrapped object. ● Take a look at the calling standard: generic  object with arguments. ● The structure is obvious: bless [ $object, @sanity_argz ], $class; ● Resulting in: $handler->( @$obj );
  • 18. Constructing a Franger ● Store the call stack for validation as­is. sub new { my $proto = shift; my $class = blessed $proto || $proto; my $object = shift or croak "Bogus franger: missing object"; bless [ $object, @_ ], $class }
  • 19. OK, but what do you do with it? ● Whatever you want. ● Object::Wrapper, in fact, does nothing but re­ dispatch the method calls. ● Useful for cases where the interesting part of  the wrapper is in the DESTROY, not the  individual calls.
  • 20. Wrapper AUTOLOAD is Standard ● Does nothing more than necessary. ● Useful when the DESTROY check is enogh. AUTOLOAD { my $franger = shift; my $i = rindex $AUTOLOAD, ':'; my $name = substr $AUTOLOAD, ++$i; my $sub = $franger->[0]->can( $name ) or confess "Bogus $AUTOLOAD: '$franger->[0]' cannot '$name'"; $franger->[0]->$sub( @_ ) }
  • 21. Oedipus Not­Complex: Forks AUTOLOAD { my $franger = shift; my ( $obj, $pid ) = @$franger; $pid == $$ or confess "Bogus $AUTOLOAD: @{$franger} crosses fork."; my $i = rindex $AUTOLOAD, ':'; my $name = substr $AUTOLOAD, ++$i; my $sub = $obj->can( $name ) or confess "Bogus $AUTOLOAD: '$obj' cannot '$name'"; # goto &$obj is slower according to Benchmark... $obj->$sub( @_ ) }
  • 22. Clean Up Your Mess: O::W::Destroy DESTROY { my $franger = shift; my $class = blessed $franger || $franger; # $cleanupz{ $class } may be a method name or coderef to save time. my $cleanup = $cleanupz{ $class } || $franger->can( 'cleanup' ) or confess "Bogus franger: no cleanup for '$franger' or '$class'"; my $sub = ref $cleanup ? $cleanup : $franger->can( $cleanup ) or confess "Bogus $class: no cleanup for '$franger' ($class)"; 'CODE' eq reftype $sub or confess "Bogus $class: not a coderef '$sub'"; $cleanup->( @$franger ); return }
  • 23. DBI: It's All How You Clean Up ● Check for cached_kids. ● Within the constructing PID:  ● Finish all the kids. ● Disconnect the parent. ● Within child Proc's: ● Disable destroy side effects in the kids & parent.
  • 24. First Step: Find the Kids sub cleanup { my ( $dbh, $pid ) = @_; my $struct = do { my $drh = $dbh->{ Driver }; $drh ? $drh->{ CachedKids } : '' }; my @kidz = $struct ? values %$struct : () ;
  • 25. Second Step: Do the Deed if( $$ != $pid ) { # handle crossed a fork: turn off side # effects of destruction. $_->{ InactiveDestroy } = 1 for ( $dbh, @kidz ); } else { $_->finish for @kidz; $dbh->disconnect; } # at this point the DBI object has been # prepared to go out of scope politely. return }
  • 26. Cleaning Up Statements Is Easier sub cleanup { my ( $sth, $pid ) = @_; if( $$ ~~ $pid ) { # same process: finalize the handle and disconnect. # caller deals with clones. $sth->{ Active } and $sth->finish; } else { $sth->{ InactiveDestroy } = 1; } # at this point the DBD object has been # prepared to go out of scope politely. return }
  • 27. Getting What You Want:  Overloading Constructors ● For DBI this requires versions of connect and  connect_cached, prepare and prepare_cached. ● Connect simply returns the wrapped $dbh: sub connect { shift; my $dbh = DBI->connect( @_ ) or croak 'Fail connect: ' . $DBI::errstr; Object::Wrapper::Fork::dbh->new( $dbh ) }
  • 28. Overloading STH Constructors ● These get a DBI wrapper object. ● Returning a wrapped DBD. sub prepare { my $franger = shift; my ( $dbh, $pid ) = @$franger; $pid == $$ or confess "Bogus prepare: @{ $franger } crosses fork."; my $sth = $dbh->prepare( @_ ) or croak 'Failed prepare: ' . $dbh->errstr; Object::Wrapper::Fork::sth->new( $sth ) }
  • 29. Wrappers are not 100% effective ● DBI offers a tied­hash interface. ● Kinda hard to handle this with a blessed array. ● Fortunately, the hash interface is rarely  necessary. ● There is also one more issue for destructors.
  • 30. Making Happy ENDings ● Perl destroys objects out­of­order on exit. ● This means that we also have to wrap  DBI::DESTROY to get complete coverage. ● Fortunately this isn't all that hard to do with the  Symbol module's qualify_to_ref. ● This requires a map of $dbh → O::W::F::DBI  objects that can be used to dispatch destruction. ● No time to describe it here.
  • 31. Other Uses for Object::Wrappers ● Maximum time: bless [ $obj, ( time + $window ) ]; time < $franger->[1] or ... ● Maximum reuse: bless [ $obj, $counter ]; --$franger->[1] or ...
  • 32. Only Your Wrapper Knows For Sure ● Long­lived processes may not want to die after   the wrapped object hits its limit. ● Nice thing is that they don't have to: --$franger->[ 1 ] or @$franger = ( $class->new( ... ), $counter ); ● This is handy for classes with memory leaks in  the objects.
  • 33. Summary ● Keeping your object safe is easy: ● Use a franger. ● Check before you use it. ● Make sure you clean up afterwards.