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