Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow an inner handle to be hot-swapped from inside a callback. #49

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

robschaber
Copy link
Contributor

Hello!

I'd like to be able to swap out an inner handle for a new one from
inside a callback. This would allow me to test the connection and
replace it if necessary before dispatch. Currently, it's not possible.
This test script illustrates:

use DBI;
my $dbh = DBI->connect('dbi:ExampleP:');
printf "inner handle before dispatch: %s\n", tied %$dbh;
$dbh->{Callbacks} = { ping => \&swapper };
*DBD::ExampleP::db::ping = sub {
    my ($inner) = @_;
    printf "inner handle during dispatch: %s\n", $inner;
};
$dbh->ping;
printf "inner handle after dispatch:  %s\n", tied %$dbh;
exit;
sub swapper {
    my ($dbh) = @_;
    my $new_dbh = DBI->connect('dbi:ExampleP:');
    $dbh->swap_inner_handle($new_dbh);
    return;
}

On my machine this produces:

inner handle before dispatch: DBI::db=HASH(0x7ff2d383f8f0)
panic: attempt to copy freed scalar 7ff2d5080210 to 7ff2d5080960 at
    test.pl line 6.
perl(5734,0x7fff75766300) malloc: *** error for object 0x7ff2d3425940:
    incorrect checksum for freed object - object was probably modified
    after being freed.
*** set a breakpoint in malloc_error_break to debug
Abort trap: 6

The inner handle being used by the dispatcher was freed after my
callback exited because it had been associated with the newly-created
outer handle, which was garbage collected. I've patched the dispatcher
to detect this situation and update its internal state accordingly (h,
mg, imp_xxh). After applying the patch:

inner handle before dispatch: DBI::db=HASH(0x7fae998776f0)
inner handle during dispatch: DBI::db=HASH(0x7fae99877ff0)
inner handle after dispatch:  DBI::db=HASH(0x7fae99877ff0)

t/70callbacks.t has also been updated with a representative test case.

I've checked that no memory leaks have been introduced by running the
test snippet inside a loop while watching memory usage. I also ran it
in Xcode with all the memory-debugging features enabled, with no
issues reported.

Thanks for taking a look, and let me know if I missed anything.

A dispatched method receives the inner handle, not outer.
During development, forgot to fix the secondary lookup after fixing
the primary one.
@timbunce
Copy link
Member

timbunce commented Aug 6, 2017

Umm. That's quite a scary thing to be doing.
I don't have any fundamental objection but do have some concerns...

  • duplication of code to set mg, h, and imp_xxh
  • but it doesn't use dbih_getcom2(aTHX_ h, 0); without explaining why not
  • no check for same thread (imp_xxh && DBIc_THR_USER(imp_xxh) != my_perl)
  • needs a debug log message
  • some docs would be good, even better if they say it's not supported :)

@pali
Copy link
Member

pali commented Jan 25, 2019

Would not it better to introduce a $dbh->connect() method which just re-connect? When called without arguments it re-use arguments passed to DBI->connect() which returned $dbh.

@robschaber: I think that this solution is less ugly and better fit into DBI API as it already has $dbh->disconnect method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants