Artem
Artem

Reputation: 553

How can I thread-safely set STDOUT encoding in Perl?

According to the documentation it`s not thread safe for use encoding module with threads and it`s deprecated since perl 1.18. I use perl 5.16 on windows 7 machine. I need to covert output to windows 866 encoding. With the below code interpreter crash when try to run thread.

use utf8;
binmode(STDOUT, ':encoding(cp866)');
use threads;


my $thr = threads->create(sub {
    print "поток работает";
})->detach();

sleep 1;
print "основной поток также работает";

Without binmode(STDOUT, ':cp866');, I have "Wide characters in print" error and text shows in incorrect charset. So how should I correctly set encoding?

Upvotes: 1

Views: 180

Answers (1)

ikegami
ikegami

Reputation: 385916

I replicated the problem with ActivePerl 5.16.3 x64 on Win7

I was able to resolve it by upgrading to ActivePerl 5.24.2 x64.

Cleaned up code:

use utf8;
use open ':std', ':encoding(cp866)';
use threads;

my $thr = async {
    print "поток работает";
};

print "основной поток также работает";
$thr->join();

Workaround: You could encode manually instead of using PerlIO layers.

use utf8;
use threads;

use Encode qw( encode );

my $thr = async {
    print encode('cp866', "поток работает");
};

print encode('cp866', "основной поток также работает");
$thr->join();

Workaround: Clone STDOUT in each thread, and apply the encoding layer to the non-shared clone.

use utf8;
use threads;

my $thr = async {
    open(my $fh, '>&=:encoding(cp866)', \*STDOUT) or die $!;
    *STDOUT = $fh;

    print "поток работает";
};

open(my $fh, '>&=:encoding(cp866)', \*STDOUT) or die $!;
*STDOUT = $fh;

print "основной поток также работает";
$thr->join();

(This doesn't clone the underlying file descriptor, so the change is very transparent.)

Upvotes: 2

Related Questions