b8876f
From 99b847695211f825df6299aa9da91f9494f741e2 Mon Sep 17 00:00:00 2001
b8876f
From: Tony Cook <tony@develop-help.com>
b8876f
Date: Thu, 1 Jun 2017 15:11:27 +1000
b8876f
Subject: [PATCH] [perl #131221] improve duplication of :via handles
b8876f
MIME-Version: 1.0
b8876f
Content-Type: text/plain; charset=UTF-8
b8876f
Content-Transfer-Encoding: 8bit
b8876f
b8876f
Previously duplication (as with open ... ">&...") would fail
b8876f
unless the user supplied a GETARG, which wasn't documented, and
b8876f
resulted in an attempt to free and unreferened scalar if supplied.
b8876f
b8876f
Cloning on thread creation was simply broken.
b8876f
b8876f
We now handle GETARG correctly, and provide a useful default if it
b8876f
returns nothing.
b8876f
b8876f
Cloning on thread creation now duplicates the appropriate parts of the
b8876f
parent thread's handle.
b8876f
b8876f
Signed-off-by: Petr Písař <ppisar@redhat.com>
b8876f
---
b8876f
 MANIFEST                  |  1 +
b8876f
 ext/PerlIO-via/t/thread.t | 73 +++++++++++++++++++++++++++++++++++++++++++++++
b8876f
 ext/PerlIO-via/t/via.t    | 56 +++++++++++++++++++++++++++++++++++-
b8876f
 ext/PerlIO-via/via.pm     |  2 +-
b8876f
 ext/PerlIO-via/via.xs     | 55 +++++++++++++++++++++++++++++++----
b8876f
 5 files changed, 179 insertions(+), 8 deletions(-)
b8876f
 create mode 100644 ext/PerlIO-via/t/thread.t
b8876f
b8876f
diff --git a/MANIFEST b/MANIFEST
b8876f
index 8c4950e..d39f992 100644
b8876f
--- a/MANIFEST
b8876f
+++ b/MANIFEST
b8876f
@@ -4056,6 +4056,7 @@ ext/PerlIO-scalar/scalar.xs	PerlIO layer for scalars
b8876f
 ext/PerlIO-scalar/t/scalar.t	See if PerlIO::scalar works
b8876f
 ext/PerlIO-scalar/t/scalar_ungetc.t	Tests for PerlIO layer for scalars
b8876f
 ext/PerlIO-via/hints/aix.pl	Hint for PerlIO::via for named architecture
b8876f
+ext/PerlIO-via/t/thread.t		See if PerlIO::via works with threads
b8876f
 ext/PerlIO-via/t/via.t		See if PerlIO::via works
b8876f
 ext/PerlIO-via/via.pm		PerlIO layer for layers in perl
b8876f
 ext/PerlIO-via/via.xs		PerlIO layer for layers in perl
b8876f
diff --git a/ext/PerlIO-via/t/thread.t b/ext/PerlIO-via/t/thread.t
b8876f
new file mode 100644
b8876f
index 0000000..e4358f9
b8876f
--- /dev/null
b8876f
+++ b/ext/PerlIO-via/t/thread.t
b8876f
@@ -0,0 +1,73 @@
b8876f
+#!perl
b8876f
+BEGIN {
b8876f
+    unless (find PerlIO::Layer 'perlio') {
b8876f
+	print "1..0 # Skip: not perlio\n";
b8876f
+	exit 0;
b8876f
+    }
b8876f
+    require Config;
b8876f
+    unless ($Config::Config{'usethreads'}) {
b8876f
+        print "1..0 # Skip -- need threads for this test\n";
b8876f
+        exit 0;
b8876f
+    }
b8876f
+    if (($Config::Config{'extensions'} !~ m!\bPerlIO/via\b!) ){
b8876f
+        print "1..0 # Skip -- Perl configured without PerlIO::via module\n";
b8876f
+        exit 0;
b8876f
+    }
b8876f
+}
b8876f
+
b8876f
+use strict;
b8876f
+use warnings;
b8876f
+use threads;
b8876f
+
b8876f
+my $tmp = "via$$";
b8876f
+
b8876f
+END {
b8876f
+    1 while unlink $tmp;
b8876f
+}
b8876f
+
b8876f
+use Test::More tests => 2;
b8876f
+
b8876f
+our $push_count = 0;
b8876f
+
b8876f
+{
b8876f
+    open my $fh, ">:via(Test1)", $tmp
b8876f
+      or die "Cannot open $tmp: $!";
b8876f
+    $fh->autoflush;
b8876f
+
b8876f
+    print $fh "AXAX";
b8876f
+
b8876f
+    # previously this would crash
b8876f
+    threads->create(
b8876f
+        sub {
b8876f
+            print $fh "XZXZ";
b8876f
+        })->join;
b8876f
+
b8876f
+    print $fh "BXBX";
b8876f
+    close $fh;
b8876f
+
b8876f
+    open my $in, "<", $tmp;
b8876f
+    my $line = <$in>;
b8876f
+    close $in;
b8876f
+
b8876f
+    is($line, "AYAYYZYZBYBY", "check thread data delivered");
b8876f
+
b8876f
+    is($push_count, 1, "PUSHED not called for dup on thread creation");
b8876f
+}
b8876f
+
b8876f
+package PerlIO::via::Test1;
b8876f
+
b8876f
+sub PUSHED {
b8876f
+    my ($class) = @_;
b8876f
+    ++$main::push_count;
b8876f
+    bless {}, $class;
b8876f
+}
b8876f
+
b8876f
+sub WRITE {
b8876f
+    my ($self, $data, $fh) = @_;
b8876f
+    $data =~ tr/X/Y/;
b8876f
+    $fh->autoflush;
b8876f
+    print $fh $data;
b8876f
+    return length $data;
b8876f
+}
b8876f
+
b8876f
+
b8876f
diff --git a/ext/PerlIO-via/t/via.t b/ext/PerlIO-via/t/via.t
b8876f
index 6787e11..80577df 100644
b8876f
--- a/ext/PerlIO-via/t/via.t
b8876f
+++ b/ext/PerlIO-via/t/via.t
b8876f
@@ -17,7 +17,7 @@ use warnings;
b8876f
 
b8876f
 my $tmp = "via$$";
b8876f
 
b8876f
-use Test::More tests => 18;
b8876f
+use Test::More tests => 26;
b8876f
 
b8876f
 my $fh;
b8876f
 my $a = join("", map { chr } 0..255) x 10;
b8876f
@@ -84,6 +84,60 @@ is( $obj, 'Foo', 'search for package Foo' );
b8876f
 open $fh, '<:via(Bar)', "bar";
b8876f
 is( $obj, 'PerlIO::via::Bar', 'search for package PerlIO::via::Bar' );
b8876f
 
b8876f
+{
b8876f
+    # [perl #131221]
b8876f
+    ok(open(my $fh1, ">", $tmp), "open $tmp");
b8876f
+    ok(binmode($fh1, ":via(XXX)"), "binmode :via(XXX) onto it");
b8876f
+    ok(open(my $fh2, ">&", $fh1), "dup it");
b8876f
+    close $fh1;
b8876f
+    close $fh2;
b8876f
+
b8876f
+    # make sure the old workaround still works
b8876f
+    ok(open($fh1, ">", $tmp), "open $tmp");
b8876f
+    ok(binmode($fh1, ":via(YYY)"), "binmode :via(YYY) onto it");
b8876f
+    ok(open($fh2, ">&", $fh1), "dup it");
b8876f
+    print $fh2 "XZXZ";
b8876f
+    close $fh1;
b8876f
+    close $fh2;
b8876f
+
b8876f
+    ok(open($fh1, "<", $tmp), "open $tmp for check");
b8876f
+    { local $/; $b = <$fh1> }
b8876f
+    close $fh1;
b8876f
+    is($b, "XZXZ", "check result is from non-filtering class");
b8876f
+
b8876f
+    package PerlIO::via::XXX;
b8876f
+
b8876f
+    sub PUSHED {
b8876f
+        my $class = shift;
b8876f
+        bless {}, $class;
b8876f
+    }
b8876f
+
b8876f
+    sub WRITE {
b8876f
+        my ($self, $buffer, $handle) = @_;
b8876f
+
b8876f
+        print $handle $buffer;
b8876f
+        return length($buffer);
b8876f
+    }
b8876f
+    package PerlIO::via::YYY;
b8876f
+
b8876f
+    sub PUSHED {
b8876f
+        my $class = shift;
b8876f
+        bless {}, $class;
b8876f
+    }
b8876f
+
b8876f
+    sub WRITE {
b8876f
+        my ($self, $buffer, $handle) = @_;
b8876f
+
b8876f
+        $buffer =~ tr/X/Y/;
b8876f
+        print $handle $buffer;
b8876f
+        return length($buffer);
b8876f
+    }
b8876f
+
b8876f
+    sub GETARG {
b8876f
+        "XXX";
b8876f
+    }
b8876f
+}
b8876f
+
b8876f
 END {
b8876f
     1 while unlink $tmp;
b8876f
 }
b8876f
diff --git a/ext/PerlIO-via/via.pm b/ext/PerlIO-via/via.pm
b8876f
index e477dcc..30083fe 100644
b8876f
--- a/ext/PerlIO-via/via.pm
b8876f
+++ b/ext/PerlIO-via/via.pm
b8876f
@@ -1,5 +1,5 @@
b8876f
 package PerlIO::via;
b8876f
-our $VERSION = '0.16';
b8876f
+our $VERSION = '0.17';
b8876f
 require XSLoader;
b8876f
 XSLoader::load();
b8876f
 1;
b8876f
diff --git a/ext/PerlIO-via/via.xs b/ext/PerlIO-via/via.xs
b8876f
index 8a7f1fc..61953c8 100644
b8876f
--- a/ext/PerlIO-via/via.xs
b8876f
+++ b/ext/PerlIO-via/via.xs
b8876f
@@ -38,6 +38,8 @@ typedef struct
b8876f
  CV *UTF8;
b8876f
 } PerlIOVia;
b8876f
 
b8876f
+static const MGVTBL PerlIOVia_tag = { 0, 0, 0, 0, 0, 0, 0, 0 };
b8876f
+
b8876f
 #define MYMethod(x) #x,&s->x
b8876f
 
b8876f
 static CV *
b8876f
@@ -131,8 +133,14 @@ PerlIOVia_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg,
b8876f
 		 PerlIO_funcs * tab)
b8876f
 {
b8876f
     IV code = PerlIOBase_pushed(aTHX_ f, mode, Nullsv, tab);
b8876f
+
b8876f
+    if (SvTYPE(arg) >= SVt_PVMG
b8876f
+		&& mg_findext(arg, PERL_MAGIC_ext, &PerlIOVia_tag)) {
b8876f
+	return code;
b8876f
+    }
b8876f
+
b8876f
     if (code == 0) {
b8876f
-	PerlIOVia *s = PerlIOSelf(f, PerlIOVia);
b8876f
+        PerlIOVia *s = PerlIOSelf(f, PerlIOVia);
b8876f
 	if (!arg) {
b8876f
 	    if (ckWARN(WARN_LAYER))
b8876f
 		Perl_warner(aTHX_ packWARN(WARN_LAYER),
b8876f
@@ -583,20 +591,55 @@ static SV *
b8876f
 PerlIOVia_getarg(pTHX_ PerlIO * f, CLONE_PARAMS * param, int flags)
b8876f
 {
b8876f
     PerlIOVia *s = PerlIOSelf(f, PerlIOVia);
b8876f
-    PERL_UNUSED_ARG(param);
b8876f
+    SV *arg;
b8876f
     PERL_UNUSED_ARG(flags);
b8876f
-    return PerlIOVia_method(aTHX_ f, MYMethod(GETARG), G_SCALAR, Nullsv);
b8876f
+
b8876f
+    /* During cloning, return an undef token object so that _pushed() knows
b8876f
+     * that it should not call methods and wait for _dup() to actually dup the
b8876f
+     * object. */
b8876f
+    if (param) {
b8876f
+	SV *sv = newSV(0);
b8876f
+	sv_magicext(sv, NULL, PERL_MAGIC_ext, &PerlIOVia_tag, 0, 0);
b8876f
+	return sv;
b8876f
+    }
b8876f
+
b8876f
+    arg = PerlIOVia_method(aTHX_ f, MYMethod(GETARG), G_SCALAR, Nullsv);
b8876f
+    if (arg) {
b8876f
+        /* arg is a temp, and PerlIOBase_dup() will explicitly free it */
b8876f
+        SvREFCNT_inc(arg);
b8876f
+    }
b8876f
+    else {
b8876f
+        arg = newSVpvn(HvNAME(s->stash), HvNAMELEN(s->stash));
b8876f
+    }
b8876f
+
b8876f
+    return arg;
b8876f
 }
b8876f
 
b8876f
 static PerlIO *
b8876f
 PerlIOVia_dup(pTHX_ PerlIO * f, PerlIO * o, CLONE_PARAMS * param,
b8876f
 	      int flags)
b8876f
 {
b8876f
-    if ((f = PerlIOBase_dup(aTHX_ f, o, param, flags))) {
b8876f
-	/* Most of the fields will lazily set themselves up as needed
b8876f
-	   stash and obj have been set up by the implied push
b8876f
+    if ((f = PerlIOBase_dup(aTHX_ f, o, param, flags)) && param) {
b8876f
+	/* For a non-interpreter dup stash and obj have been set up
b8876f
+	   by the implied push.
b8876f
+
b8876f
+           But if this is a clone for a new interpreter we need to
b8876f
+           translate the objects to their dups.
b8876f
 	 */
b8876f
+
b8876f
+        PerlIOVia *fs = PerlIOSelf(f, PerlIOVia);
b8876f
+        PerlIOVia *os = PerlIOSelf(o, PerlIOVia);
b8876f
+
b8876f
+        fs->obj = sv_dup_inc(os->obj, param);
b8876f
+        fs->stash = (HV*)sv_dup((SV*)os->stash, param);
b8876f
+        fs->var = sv_dup_inc(os->var, param);
b8876f
+        fs->cnt = os->cnt;
b8876f
+
b8876f
+        /* fh, io, cached CVs left as NULL, PerlIOVia_method()
b8876f
+           will reinitialize them if needed */
b8876f
     }
b8876f
+    /* for a non-threaded dup fs->obj and stash should be set by _pushed() */
b8876f
+
b8876f
     return f;
b8876f
 }
b8876f
 
b8876f
-- 
b8876f
2.9.4
b8876f