0e6766
From a1e8f04634112d64383f0079421cf9cf5a154c0e Mon Sep 17 00:00:00 2001
0e6766
From: Vincent Pit <perl@profvince.com>
0e6766
Date: Fri, 28 Aug 2015 14:17:00 -0300
0e6766
Subject: [PATCH] Properly duplicate PerlIO::encoding objects
0e6766
MIME-Version: 1.0
0e6766
Content-Type: text/plain; charset=UTF-8
0e6766
Content-Transfer-Encoding: 8bit
0e6766
0e6766
Upstream commit ported to 5.16.3:
0e6766
0e6766
commit 0ee3fa26f660ac426e3e082f77d806c9d1471f93
0e6766
Author: Vincent Pit <perl@profvince.com>
0e6766
Date:   Fri Aug 28 14:17:00 2015 -0300
0e6766
0e6766
    Properly duplicate PerlIO::encoding objects
0e6766
0e6766
    PerlIO::encoding objects are usually initialized by calling Perl methods,
0e6766
    essentially from the pushed() and getarg() callbacks. During cloning, the
0e6766
    PerlIO API will by default call these methods to initialize the duplicate
0e6766
    struct when the PerlIOBase parent struct is itself duplicated. This does
0e6766
    not behave so well because the perl interpreter is not ready to call
0e6766
    methods at this point, for the stacks are not set up yet.
0e6766
0e6766
    The proper way to duplicate the PerlIO::encoding object is to call sv_dup()
0e6766
    on its members from the dup() PerlIO callback. So the only catch is to make
0e6766
    the getarg() and pushed() calls implied by the duplication of the underlying
0e6766
    PerlIOBase object aware that they are called during cloning, and make them
0e6766
    wait that the control flow returns to the dup() callback. Fortunately,
0e6766
    getarg() knows since its param argument is then non-null, and its return
0e6766
    value is passed immediately to pushed(), so it is enough to tag this
0e6766
    returned value with a custom magic so that pushed() can see it is being
0e6766
    called during cloning.
0e6766
0e6766
    This fixes [RT #31923].
0e6766
0e6766
Signed-off-by: Petr Písař <ppisar@redhat.com>
0e6766
---
0e6766
 MANIFEST                        |  1 +
0e6766
 ext/PerlIO-encoding/encoding.xs | 25 +++++++++++++++++++++++--
0e6766
 ext/PerlIO-encoding/t/threads.t | 35 +++++++++++++++++++++++++++++++++++
0e6766
 3 files changed, 59 insertions(+), 2 deletions(-)
0e6766
 create mode 100644 ext/PerlIO-encoding/t/threads.t
0e6766
0e6766
diff --git a/MANIFEST b/MANIFEST
0e6766
index 02e8234..5caa981 100644
0e6766
--- a/MANIFEST
0e6766
+++ b/MANIFEST
0e6766
@@ -3791,6 +3791,7 @@ ext/PerlIO-encoding/MANIFEST	PerlIO::encoding list of files
0e6766
 ext/PerlIO-encoding/t/encoding.t	See if PerlIO encoding conversion works
0e6766
 ext/PerlIO-encoding/t/fallback.t	See if PerlIO fallbacks work
0e6766
 ext/PerlIO-encoding/t/nolooping.t	Tests for PerlIO::encoding
0e6766
+ext/PerlIO-encoding/t/threads.t		Tests PerlIO::encoding and threads
0e6766
 ext/PerlIO-mmap/mmap.pm	PerlIO layer for memory maps
0e6766
 ext/PerlIO-mmap/mmap.xs	PerlIO layer for memory maps
0e6766
 ext/PerlIO-scalar/scalar.pm	PerlIO layer for scalars
0e6766
diff --git a/ext/PerlIO-encoding/encoding.xs b/ext/PerlIO-encoding/encoding.xs
0e6766
index 98d89e9..d5efb62 100644
0e6766
--- a/ext/PerlIO-encoding/encoding.xs
0e6766
+++ b/ext/PerlIO-encoding/encoding.xs
0e6766
@@ -49,13 +49,23 @@ typedef struct {
0e6766
 
0e6766
 #define NEEDS_LINES	1
0e6766
 
0e6766
+static const MGVTBL PerlIOEncode_tag = { 0, 0, 0, 0, 0, 0, 0, 0 };
0e6766
+
0e6766
 SV *
0e6766
 PerlIOEncode_getarg(pTHX_ PerlIO * f, CLONE_PARAMS * param, int flags)
0e6766
 {
0e6766
     PerlIOEncode *e = PerlIOSelf(f, PerlIOEncode);
0e6766
-    SV *sv = &PL_sv_undef;
0e6766
-    PERL_UNUSED_ARG(param);
0e6766
+    SV *sv;
0e6766
     PERL_UNUSED_ARG(flags);
0e6766
+    /* During cloning, return an undef token object so that _pushed() knows
0e6766
+     * that it should not call methods and wait for _dup() to actually dup the
0e6766
+     * encoding object. */
0e6766
+    if (param) {
0e6766
+	sv = newSV(0);
0e6766
+	sv_magicext(sv, NULL, PERL_MAGIC_ext, &PerlIOEncode_tag, 0, 0);
0e6766
+	return sv;
0e6766
+    }
0e6766
+    sv = &PL_sv_undef;
0e6766
     if (e->enc) {
0e6766
 	dSP;
0e6766
 	/* Not 100% sure stack swap is right thing to do during dup ... */
0e6766
@@ -86,6 +96,14 @@ PerlIOEncode_pushed(pTHX_ PerlIO * f, const char *mode, SV * arg, PerlIO_funcs *
0e6766
     IV  code = PerlIOBuf_pushed(aTHX_ f, mode, Nullsv,tab);
0e6766
     SV *result = Nullsv;
0e6766
 
0e6766
+    if (SvTYPE(arg) >= SVt_PVMG
0e6766
+		&& mg_findext(arg, PERL_MAGIC_ext, &PerlIOEncode_tag)) {
0e6766
+	e->enc = NULL;
0e6766
+	e->chk = NULL;
0e6766
+	e->inEncodeCall = 0;
0e6766
+	return code;
0e6766
+    }
0e6766
+
0e6766
     PUSHSTACKi(PERLSI_MAGIC);
0e6766
     SPAGAIN;
0e6766
 
0e6766
@@ -558,6 +576,9 @@ PerlIOEncode_dup(pTHX_ PerlIO * f, PerlIO * o,
0e6766
 	if (oe->enc) {
0e6766
 	    fe->enc = PerlIO_sv_dup(aTHX_ oe->enc, params);
0e6766
 	}
0e6766
+	if (oe->chk) {
0e6766
+	    fe->chk = PerlIO_sv_dup(aTHX_ oe->chk, params);
0e6766
+	}
0e6766
     }
0e6766
     return f;
0e6766
 }
0e6766
diff --git a/ext/PerlIO-encoding/t/threads.t b/ext/PerlIO-encoding/t/threads.t
0e6766
new file mode 100644
0e6766
index 0000000..64f0e55
0e6766
--- /dev/null
0e6766
+++ b/ext/PerlIO-encoding/t/threads.t
0e6766
@@ -0,0 +1,35 @@
0e6766
+#!perl
0e6766
+
0e6766
+use strict;
0e6766
+use warnings;
0e6766
+
0e6766
+BEGIN {
0e6766
+    use Config;
0e6766
+    if ($Config{extensions} !~ /\bEncode\b/) {
0e6766
+	print "1..0 # Skip: no Encode\n";
0e6766
+	exit 0;
0e6766
+    }
0e6766
+    unless ($Config{useithreads}) {
0e6766
+	print "1..0 # Skip: no threads\n";
0e6766
+	exit 0;
0e6766
+    }
0e6766
+}
0e6766
+
0e6766
+use threads;
0e6766
+
0e6766
+use Test::More tests => 3 + 1;
0e6766
+
0e6766
+binmode *STDOUT, ':encoding(UTF-8)';
0e6766
+
0e6766
+SKIP: {
0e6766
+    local $@;
0e6766
+    my $ret = eval {
0e6766
+	my $thread = threads->create(sub { pass 'in thread'; return 1 });
0e6766
+	skip 'test thread could not be spawned' => 3 unless $thread;
0e6766
+	$thread->join;
0e6766
+    };
0e6766
+    is $@, '', 'thread did not croak';
0e6766
+    is $ret, 1, 'thread returned the right value';
0e6766
+}
0e6766
+
0e6766
+pass 'passes at least one test';
0e6766
-- 
0e6766
2.5.5
0e6766