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

Locale patch breaks dist/threads/t/kill.t by triggering attribute.pm XS version mismatch errors. #20155

Closed
demerphq opened this issue Aug 25, 2022 · 12 comments · Fixed by #20361
Assignees

Comments

@demerphq
Copy link
Collaborator

demerphq commented Aug 25, 2022

Description
Bisect tells me that ever since a7ff7ac I have been seeing test failures from dist/threads/t/kill.t:

attributes object version 0.35 does not match $attributes::VERSION 0 at lib/XSLoader.pm line 112. Compilation failed in require at lib/Thread/Queue.pm line 19. BEGIN failed--compilation aborted at lib/Thread/Queue.pm line 19. Compilation failed in require at dist/threads/t/kill.t line 31. BEGIN failed--compilation aborted at dist/threads/t/kill.t line 36.

This is the patch details:

git log -1 --stat a7ff7aca3826600e5832ffe161fe075182f19a82
commit a7ff7aca3826600e5832ffe161fe075182f19a82
Author: Karl Williamson <khw@cpan.org>
Date:   Thu Mar 11 12:24:56 2021 -0700

    locale.c: Use setlocale() for init, not P2008
    
    We have found bugs in the POSIX 2008 libc implementations on various
    platforms.  This code, which does the initialization of locale handling
    has always been very conservative, expecting possible failures due to
    bugs in it or the libc implementations, and backing out if necessary to
    a crippled, but workable state, if something goes wrong.
    
    I think we should use the oldest, most stable locale implementation in
    these circumstances

 locale.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

FWIW, I looked at the patch but beyond maybe an off by one error overwriting memory somewhere it is not clear to me why this patch would lead to this error. It is also not clear why it would fail for me and not in CI. I would be happy to provide any additional details required to debug.

Steps to Reproduce

$ git reset --hard a7ff7aca3826600e5832ffe161fe075182f19a82
HEAD is now at a7ff7aca38 locale.c: Use setlocale() for init, not P2008

$ make -j8 test_prep > /tmp/test_prep.log 2>&1; ./perl -Ilib dist/threads/t/kill.t
attributes object version 0.35 does not match $attributes::VERSION 0 at lib/XSLoader.pm line 112.
Compilation failed in require at lib/Thread/Queue.pm line 19.
BEGIN failed--compilation aborted at lib/Thread/Queue.pm line 19.
Compilation failed in require at dist/threads/t/kill.t line 31.
BEGIN failed--compilation aborted at dist/threads/t/kill.t line 36.

$ git reset --hard a7ff7aca3826600e5832ffe161fe075182f19a82^
HEAD is now at 50ffb02bd2 locale.c: Refactor some derived #defines

$ make -j8 test_prep > /tmp/test_prep.log 2>&1; ./perl -Ilib dist/threads/t/kill.t
1..18
ok 1 - Loaded
ok 2 - Created thread
ok 3 - Thread sleeping
ok 4 - Signalled thread
ok 5 - Thread received signal
ok 6 - No thread return value
ok 7 - Thread termination warning
ok 8 - Semaphore created
ok 9 - Created thread
ok 10 - Thread received semaphore
ok 11 - Suspended thread
ok 12 - Thread suspending
ok 13 - Thread resuming
ok 14 - Signalled thread to terminate
ok 15 - Thread caught termination signal
ok 16 - Thread done
ok 17 - Thread return value
ok 18 - Ignore signal to terminated thread

Expected behavior
The second output, kill.t should not fail.

Perl configuration

./Configure -Dusethreads -Doptimize=-g -d -Dusedevel -Dcc=ccache\ gcc -Dld=gcc -DDEBUGGING
./perl -Ilib -V
Summary of my perl5 (revision 5 version 37 subversion 3) configuration:
  Commit id: 50ffb02bd2b94a3e1ca31fc74825149050e13940
  Platform:
    osname=linux
    osvers=5.14.0-1049-oem
    archname=x86_64-linux-thread-multi
    uname='linux oncidium 5.14.0-1049-oem #56-ubuntu smp fri aug 12 10:23:08 utc 2022 x86_64 x86_64 x86_64 gnulinux '
    config_args='-Dusethreads -Doptimize=-g -d -Dusedevel -Dcc=ccache gcc -Dld=gcc -DDEBUGGING'
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=define
    usemultiplicity=define
    use64bitint=define
    use64bitall=define
    uselongdouble=undef
    usemymalloc=n
    default_inc_excludes_dot=define
  Compiler:
    cc='gcc'
    ccflags ='-D_REENTRANT -D_GNU_SOURCE -fwrapv -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'
    optimize='-g'
    cppflags='-D_REENTRANT -D_GNU_SOURCE -fwrapv -DDEBUGGING -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
    ccversion=''
    gccversion='9.4.0'
    gccosandvers=''
    intsize=4
    longsize=8
    ptrsize=8
    doublesize=8
    byteorder=12345678
    doublekind=3
    d_longlong=define
    longlongsize=8
    d_longdbl=define
    longdblsize=16
    longdblkind=3
    ivtype='long'
    ivsize=8
    nvtype='double'
    nvsize=8
    Off_t='off_t'
    lseeksize=8
    alignbytes=8
    prototype=define
  Linker and Libraries:
    ld='gcc'
    ldflags =' -fstack-protector-strong -L/usr/local/lib'
    libpth=/usr/local/lib /usr/lib/x86_64-linux-gnu /usr/lib /usr/lib64
    libs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    perllibs=-lpthread -lnsl -ldl -lm -lcrypt -lutil -lc
    libc=libc-2.31.so
    so=so
    useshrplib=false
    libperl=libperl.a
    gnulibc_version='2.31'
  Dynamic Linking:
    dlsrc=dl_dlopen.xs
    dlext=so
    d_dlsymun=undef
    ccdlflags='-Wl,-E'
    cccdlflags='-fPIC'
    lddlflags='-shared -g -L/usr/local/lib -fstack-protector-strong'


Characteristics of this binary (from libperl): 
  Compile-time options:
    DEBUGGING
    HAS_TIMES
    MULTIPLICITY
    PERLIO_LAYERS
    PERL_COPY_ON_WRITE
    PERL_DONT_CREATE_GVSV
    PERL_HASH_FUNC_SIPHASH13
    PERL_HASH_USE_SBOX32
    PERL_MALLOC_WRAP
    PERL_OP_PARENT
    PERL_PRESERVE_IVUV
    PERL_TRACK_MEMPOOL
    PERL_USE_DEVEL
    PERL_USE_SAFE_PUTENV
    USE_64_BIT_ALL
    USE_64_BIT_INT
    USE_ITHREADS
    USE_LARGE_FILES
    USE_LOCALE
    USE_LOCALE_COLLATE
    USE_LOCALE_CTYPE
    USE_LOCALE_NUMERIC
    USE_LOCALE_TIME
    USE_PERLIO
    USE_PERL_ATOF
    USE_REENTRANT_API
    USE_THREAD_SAFE_LOCALE
  Built under linux
  Compiled at Aug 25 2022 15:09:49
  %ENV:
    PERLBREW_CONFIGURE_FLAGS="-de -Dcc=ccache\ gcc -Dld=gcc"
    PERLBREW_HOME="/home/yorton/.perlbrew"
    PERLBREW_MANPATH="/home/yorton/perl5/perlbrew/perls/perl-5.34.1/man"
    PERLBREW_PATH="/home/yorton/perl5/perlbrew/bin:/home/yorton/perl5/perlbrew/perls/perl-5.34.1/bin"
    PERLBREW_PERL="perl-5.34.1"
    PERLBREW_ROOT="/home/yorton/perl5/perlbrew"
    PERLBREW_SHELLRC_VERSION="0.88"
    PERLBREW_VERSION="0.88"
  @INC:
    lib
    /usr/local/lib/perl5/site_perl/5.37.3/x86_64-linux-thread-multi
    /usr/local/lib/perl5/site_perl/5.37.3
    /usr/local/lib/perl5/5.37.3/x86_64-linux-thread-multi
    /usr/local/lib/perl5/5.37.3
@demerphq demerphq changed the title a7ff7aca38 breaks dist/threads/t/kill.t by triggering attribute.pm XS version mismatch errors. Locale patch breaks dist/threads/t/kill.t by triggering attribute.pm XS version mismatch errors. Aug 25, 2022
@jkeenan
Copy link
Contributor

jkeenan commented Aug 25, 2022

Where I customarily test blead I am unable to reproduce these failures.

$ uname -mrs;gcc --version | head -1; gitcurr; git describe;./perl -Ilib -V:config_args
FreeBSD 12.3-RELEASE-p1 amd64
gcc (FreeBSD Ports Collection) 10.3.0
blead
v5.37.2-338-gc74a928a43
config_args='-des -Dusedevel -Duseithreads -DDEBUGGING -Doptimize=-g -Dcc=gcc -Dld=gcc';

$ cd t;TEST_JOBS=1 ./perl harness ../dist/threads/t/*.t; cd -
...
All tests successful.
Files=25, Tests=591, 39 wallclock secs ( 0.16 usr  0.05 sys +  6.53 cusr  0.42 csys =  7.16 CPU)
Result: PASS

@demerphq
Copy link
Collaborator Author

@jkeenan notice your Configure args are different.

./Configure -Dusethreads -Doptimize=-g -d -Dusedevel -DDEBUGGING

notice I have "usethreads" not "useithreads".

@Tux
Copy link
Contributor

Tux commented Aug 25, 2022

Unless you explicitly specify both, they are inherently the same: the default for usethreads is useithreads and useithreads is set to usethreads after its specification is done, so it should not matter

@demerphq
Copy link
Collaborator Author

@Tux ok, thanks.

@khwilliamson I have narrowed this down a bit. If I add use attributes; to threads::shared the problem goes away. I believe that somehow the a7ff7ac patch meant that something changed with how attributes are loaded, so that attributes.pm does not get loaded before it is used. I also stepped through the code in gdb to be sure. It is not clear to me if in the older code it loaded attributes as a side-effect and now it no longer does and somehow this meant nobody noticed that threads::shared is actually broken in not explicitly using attributes, or if it is because somehow module loading logic has been broken by this change. Given it only affects this single test im inclined to think you removed a side-effect that was hiding this bug.

@jkeenan
Copy link
Contributor

jkeenan commented Aug 25, 2022

@jkeenan notice your Configure args are different.

./Configure -Dusethreads -Doptimize=-g -d -Dusedevel -DDEBUGGING

notice I have "usethreads" not "useithreads".

No difference here.

$ ./perl -Ilib -V:config_args
config_args='-des -Dusedevel -Dusethreads -DDEBUGGING -Doptimize=-g -Dcc=gcc -Dld=gcc';
[perlmonger: perl] $ cd t; TEST_JOBS=1 ./perl harness ../dist/threads/t/kill*.t; cd -
../dist/threads/t/kill.t ... ok     
../dist/threads/t/kill2.t .. ok   
../dist/threads/t/kill3.t .. ok   
All tests successful.
Files=3, Tests=24,  9 wallclock secs ( 0.02 usr  0.01 sys +  0.29 cusr  0.07 csys =  0.38 CPU)
Result: PASS

demerphq added a commit that referenced this issue Aug 25, 2022
It seems it is possible that we load the attributes XS code
without loading the .pm code first. This causes threads/t/kill.t to
die with an error about XS version mismatch. Explicitly loading
the attributes module from threads::shared seems to fix the problem.

This resolves #20155.
@demerphq
Copy link
Collaborator Author

FWIW, it seems that #20157 fixes the issue, although it is still not clear to me if it should be required or why your patch exposed this issue this way. It does seem plausible that the old locale code loaded attributes.pm implicitly and stuff just worked that now doesn't. But why it only fails here and not elsewhere, and etc, are all open questions.

demerphq added a commit that referenced this issue Aug 25, 2022
It seems it is possible that we load the attributes XS code
without loading the .pm code first. This causes threads/t/kill.t to
die with an error about XS version mismatch. Explicitly loading
the attributes module from threads::shared seems to fix the problem.

This resolves #20155.
@bram-perl
Copy link

bram-perl commented Aug 25, 2022

Number parsing fails after a thread was created..
So it's a lot more generic then just attributes.pm failing

A reduced test case (which could be used as a starting point for an extra test):

use strict;
use warnings;
use Test::More;

BEGIN {
    use Config;
    if (! $Config{'useithreads'}) {
        plan skip_all => "Perl not compiled with 'useithreads'";
    }
}


use threads;

my $x = eval "1.25";
is("$x", "1.25", "number is ok before thread");
my $str_x = "$x";

my $thr = threads->create(sub {});
$thr->join();

is("$x", "$str_x", "number stringifies the same after thread");

my $y = eval "1.25";
is("$y", "1.25", "number is ok after threads");
is("$y", "$str_x", "new number stringifies the same as old number");


done_testing();

Running it:

$ LC_ALL= LC_NUMERIC=nl_NL ./perl -Ilib t-20155.pl
ok 1 - number is ok before thread
not ok 2 - number stringifies the same after thread
#   Failed test 'number stringifies the same after thread'
#   at t-20155.pl line 23.
#          got: '1,25'
#     expected: '1.25'
not ok 3 - number is ok after threads
#   Failed test 'number is ok after threads'
#   at t-20155.pl line 26.
#          got: '1'
#     expected: '1.25'
not ok 4 - new number stringifies the same as old number
#   Failed test 'new number stringifies the same as old number'
#   at t-20155.pl line 27.
#          got: '1'
#     expected: '1.25'
1..4
# Looks like you failed 3 tests of 4.

and

$ LC_ALL= LC_NUMERIC=en_US ./perl -Ilib t-20155.pl
ok 1 - number is ok before thread
ok 2 - number stringifies the same after thread
ok 3 - number is ok after threads
ok 4 - new number stringifies the same as old number
1..4

For those unaware:

  • 'nl_NL' uses , as decimal separator
  • 'en_US' uses . as decimal separator

@bram-perl
Copy link

One final observation: adding a use locale; alters the behavior and causes the number to be correctly parsed

use threads;


my $x = eval "1.25";
print "Before thread: $x\n";

my $thr = threads->create(sub {});
$thr->join();

print "After thread: $x\n";

{
        my $y = eval "1.25";
        print "New number (outside `use locale`-block): $y\n";
}
{
        use locale;
        my $y = eval "1.25";
        print "New number (inside `use locale`-block): $y\n";
}

Running:

$ LC_ALL= LC_NUMERIC=nl_NL ./perl -Ilib ../t2.pl
Before thread: 1.25
After thread: 1,25
New number (outside `use locale`-block): 1
New number (inside `use locale`-block): 1,25

@khwilliamson
Copy link
Contributor

This is a bug only in boxes running Posix 2008 threaded locales, and has to do with the thread not being switched soon enough from the global locale. I had commits already in my pipeline to fix this; I'll try to move them up.

In the meantime, just don't set LC_NUMERIC to a non-dot radix locale in your environment at perl initiation

@bram-perl
Copy link

I had commits already in my pipeline to fix this; I'll try to move them up.

Do you have an estimate on that?
IMO it should be fixed before 5.37.4 is released..

Right now it just - silently - fails at basic math when there was a thread (and LC_NUMERIC is set to a non-dot radix)
Example:

$ cat gh-20515.pl
use strict;
use warnings;

BEGIN {
    use Config;
    if (! $Config{'useithreads'}) {
        die "Perl not compiled with 'useithreads'\n";
    }
}

use threads;

print "Before threads:\n";
do "./gh-20155-b.pl";

my $thr = threads->create(sub {});
$thr->join();

print "After threads:\n";
do "./gh-20155-b.pl";

and

$ cat gh-20155-b.pl
print "5.55 + 5.55 = " . (5.55 + 5.55) . "\n";

Running:

$ LC_ALL=  LC_NUMERIC=nl_NL.UTF-8 ./perl -Ilib gh-20155.pl
Before threads:
5.55 + 5.55 = 11.1
After threads:
5.55 + 5.55 = 10

khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Aug 28, 2022
The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

Prior to this commit, there was a bug in which, when a thread
terminates, the master thread was switched into the global locale.  That
meant that that thread was no longer thread-safe with regards to
locales.

This bug stems from the fact that perl assumes that all you need to do
to switch between threads (or embedded interpreters) is to change out
aTHX.  Indeed much effort was expended in crafting perl to make this the
case.  But it breaks down in the case of some alien library that keeps
per-thread information.  That library needs to be informed of the
switch.  In this case it is libc keeping per-thread locale information.
We change the thread context, but the library still retains the old
thread's locale.

One cannot be using a given locale object and successfully free it.
Therefore the code switches to the global locale (which isn't
deletable) before freeing.  There was no apparent need to do more
switching, as the thread is in the process of dying.  What I was unaware
of is that it is the parent thread pretending to be the dying one for the
purposes of destruction.  So switching to the global locale affected the
parent, leaving it there.

The parent thread called the locale.c thread locale termination
function, and then called the perl.c perl_destruct() on the thread.  This
commit moves all the code for thread destruction from perl.c into the
locale.c code, and calls it.  Thus the thread initiation and termination
is moved into locale.c

The thread termination is also called from thread.c.  This cleans up a
dying thread.  The perl.c call is needed for thread0 and
non-multiplicity builds.  A check is done to prevent duplicate work.

This commit adds a new per-interpreter variable which maps aTHX to its
locale.  This is used to get the terminating thread's locale instead of
the master.  And the master locale is switched back to at the end.

This commit is incomplete.  Something similar needs to be done for
Windows where the libc knows the per-thread locale.

I'm unsure of if this is the full correct approach.  It only works for
thread termination.  Perhaps a better solution would be to change the
locale every time aTHX is changed.  PERL_SET_INTERP, PERL_SET_CONTEXT,
and PERL_SET_THX all seem to do the aTHX change, and I can't figure out
when you would prefer one over the other.  But maybe one of them should
then arrange also to change the locale when aTHX is changed.

Perhaps you can think of other libraries and functions that have a
similar problem that also would need something like this.

This commit causes Perl#20155 to go away.  The triggering failure is merely
a symptom of the deeper problem.  A proper test will need to be done in
XS.
@khwilliamson
Copy link
Contributor

#20178 fixes this, but needs more work, and advice

khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Aug 29, 2022
The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

Prior to this commit, there was a bug in which, when a thread
terminates, the master thread was switched into the global locale.  That
meant that that thread was no longer thread-safe with regards to
locales.

This bug stems from the fact that perl assumes that all you need to do
to switch between threads (or embedded interpreters) is to change out
aTHX.  Indeed much effort was expended in crafting perl to make this the
case.  But it breaks down in the case of some alien library that keeps
per-thread information.  That library needs to be informed of the
switch.  In this case it is libc keeping per-thread locale information.
We change the thread context, but the library still retains the old
thread's locale.

One cannot be using a given locale object and successfully free it.
Therefore the code switches to the global locale (which isn't
deletable) before freeing.  There was no apparent need to do more
switching, as the thread is in the process of dying.  What I was unaware
of is that it is the parent thread pretending to be the dying one for the
purposes of destruction.  So switching to the global locale affected the
parent, leaving it there.

The parent thread called the locale.c thread locale termination
function, and then called the perl.c perl_destruct() on the thread.  This
commit moves all the code for thread destruction from perl.c into the
locale.c code, and calls it.  Thus the thread initiation and termination
is moved into locale.c

The thread termination is also called from thread.c.  This cleans up a
dying thread.  The perl.c call is needed for thread0 and
non-multiplicity builds.  A check is done to prevent duplicate work.

This commit adds a new per-interpreter variable which maps aTHX to its
locale.  This is used to get the terminating thread's locale instead of
the master.  And the master locale is switched back to at the end.

This commit is incomplete.  Something similar needs to be done for
Windows where the libc knows the per-thread locale.

I'm unsure of if this is the full correct approach.  It only works for
thread termination.  Perhaps a better solution would be to change the
locale every time aTHX is changed.  PERL_SET_INTERP, PERL_SET_CONTEXT,
and PERL_SET_THX all seem to do the aTHX change, and I can't figure out
when you would prefer one over the other.  But maybe one of them should
then arrange also to change the locale when aTHX is changed.

Perhaps you can think of other libraries and functions that have a
similar problem that also would need something like this.

This commit causes Perl#20155 to go away.  The triggering failure is merely
a symptom of the deeper problem.  A proper test will need to be done in
XS.
@bram-perl
Copy link

@khwilliamson release of 5.37.4 is getting closer.. have you been able to make progress on this? (/on a better fix then PR #20178 ?)

khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Sep 20, 2022
See Perl#20155

The root cause of that problem is that under POSIX 2008, when a thread
terminates, it causes thread 0 (the controller) to change to the global
locale.  Commit a7ff7ac caused perl to pay attention to the environment
variables in effect at startup for setting the global locale when using
the POSIX 2008 locale API.  (Previously only the initial per-thread
locale was affected.)

This causes problems when the initial setting was for a locale that uses
a comma as the radix character, but the thread 0 is set to a locale that
is expecting a dot as a radix character.  Whenever another thread
terminates, thread 0 was silently changed to using the global locake,
and hence a comma.  This caused parse errors.

The real solution is to fix thread 0 to remain in its chosen locale.
But that fix is not ready in time for 5.37.4, and it is deemed important
to get something working for this monthly development release.

This commit changes the initial global LC_NUMERIC locale to always be C,
hence uses a dot radix.  The vast majority of code is expecting a dot.
This is not the ultimate fix, but it works around the immediate problem
at hand.

The test case is courtesy @bram-perl
khwilliamson added a commit that referenced this issue Sep 20, 2022
See #20155

The root cause of that problem is that under POSIX 2008, when a thread
terminates, it causes thread 0 (the controller) to change to the global
locale.  Commit a7ff7ac caused perl to pay attention to the environment
variables in effect at startup for setting the global locale when using
the POSIX 2008 locale API.  (Previously only the initial per-thread
locale was affected.)

This causes problems when the initial setting was for a locale that uses
a comma as the radix character, but the thread 0 is set to a locale that
is expecting a dot as a radix character.  Whenever another thread
terminates, thread 0 was silently changed to using the global locake,
and hence a comma.  This caused parse errors.

The real solution is to fix thread 0 to remain in its chosen locale.
But that fix is not ready in time for 5.37.4, and it is deemed important
to get something working for this monthly development release.

This commit changes the initial global LC_NUMERIC locale to always be C,
hence uses a dot radix.  The vast majority of code is expecting a dot.
This is not the ultimate fix, but it works around the immediate problem
at hand.

The test case is courtesy @bram-perl
khwilliamson added a commit that referenced this issue Sep 29, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to.
This catches anything missed by locale_term().

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Sep 29, 2022
This is a step in solving Perl#20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to.
This catches anything missed by locale_term().

This fixes the symptoms associtated with Perl#20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 1, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 1, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be called with the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing #20155, EXCEPT for Windows boxes.  That
comes in the next commit.
khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Oct 2, 2022
This is a step in solving Perl#20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with Perl#20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Oct 2, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be called with the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing Perl#20155, EXCEPT for Windows boxes.  That
comes in the next commit.
khwilliamson added a commit that referenced this issue Oct 3, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 3, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be called with the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing #20155, EXCEPT for Windows boxes.  That
comes in the next commit.
khwilliamson added a commit that referenced this issue Oct 3, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 3, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be informed about the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

For POSIX 2008, the function just calls uselocale() using the
per-interpreter object introduced previously.

For Windows, this commit adds a per-interpreter string of the current
LC_ALL, and the function calls setlocale on that.  We keep the same
string for POSIX 2008 implementations that lack querylocale(), so this
commit just enables that variable on Windows as well.  The code is
already in place to free the memory the string occupies when done.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing #20155.
khwilliamson added a commit that referenced this issue Oct 7, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 7, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be informed about the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

For POSIX 2008, the function just calls uselocale() using the
per-interpreter object introduced previously.

For Windows, this commit adds a per-interpreter string of the current
LC_ALL, and the function calls setlocale on that.  We keep the same
string for POSIX 2008 implementations that lack querylocale(), so this
commit just enables that variable on Windows as well.  The code is
already in place to free the memory the string occupies when done.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing #20155.
khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Oct 7, 2022
This is a step in solving Perl#20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with Perl#20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Oct 7, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be informed about the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

For POSIX 2008, the function just calls uselocale() using the
per-interpreter object introduced previously.

For Windows, this commit adds a per-interpreter string of the current
LC_ALL, and the function calls setlocale on that.  We keep the same
string for POSIX 2008 implementations that lack querylocale(), so this
commit just enables that variable on Windows as well.  The code is
already in place to free the memory the string occupies when done.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing Perl#20155.
khwilliamson added a commit that referenced this issue Oct 10, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 10, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be informed about the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

For POSIX 2008, the function just calls uselocale() using the
per-interpreter object introduced previously.

For Windows, this commit adds a per-interpreter string of the current
LC_ALL, and the function calls setlocale on that.  We keep the same
string for POSIX 2008 implementations that lack querylocale(), so this
commit just enables that variable on Windows as well.  The code is
already in place to free the memory the string occupies when done.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing #20155.
khwilliamson added a commit that referenced this issue Oct 11, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 11, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be informed about the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

For POSIX 2008, the function just calls uselocale() using the
per-interpreter object introduced previously.

For Windows, this commit adds a per-interpreter string of the current
LC_ALL, and the function calls setlocale on that.  We keep the same
string for POSIX 2008 implementations that lack querylocale(), so this
commit just enables that variable on Windows as well.  The code is
already in place to free the memory the string occupies when done.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing #20155.
khwilliamson added a commit that referenced this issue Oct 12, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 12, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be informed about the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

For POSIX 2008, the function just calls uselocale() using the
per-interpreter object introduced previously.

For Windows, this commit adds a per-interpreter string of the current
LC_ALL, and the function calls setlocale on that.  We keep the same
string for POSIX 2008 implementations that lack querylocale(), so this
commit just enables that variable on Windows as well.  The code is
already in place to free the memory the string occupies when done.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing #20155.
khwilliamson added a commit that referenced this issue Oct 18, 2022
This is a step in solving #20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with #20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
khwilliamson added a commit that referenced this issue Oct 18, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be informed about the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

For POSIX 2008, the function just calls uselocale() using the
per-interpreter object introduced previously.

For Windows, this commit adds a per-interpreter string of the current
LC_ALL, and the function calls setlocale on that.  We keep the same
string for POSIX 2008 implementations that lack querylocale(), so this
commit just enables that variable on Windows as well.  The code is
already in place to free the memory the string occupies when done.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing #20155.
scottchiefbaker pushed a commit to scottchiefbaker/perl5 that referenced this issue Nov 3, 2022
See Perl#20155

The root cause of that problem is that under POSIX 2008, when a thread
terminates, it causes thread 0 (the controller) to change to the global
locale.  Commit a7ff7ac caused perl to pay attention to the environment
variables in effect at startup for setting the global locale when using
the POSIX 2008 locale API.  (Previously only the initial per-thread
locale was affected.)

This causes problems when the initial setting was for a locale that uses
a comma as the radix character, but the thread 0 is set to a locale that
is expecting a dot as a radix character.  Whenever another thread
terminates, thread 0 was silently changed to using the global locake,
and hence a comma.  This caused parse errors.

The real solution is to fix thread 0 to remain in its chosen locale.
But that fix is not ready in time for 5.37.4, and it is deemed important
to get something working for this monthly development release.

This commit changes the initial global LC_NUMERIC locale to always be C,
hence uses a dot radix.  The vast majority of code is expecting a dot.
This is not the ultimate fix, but it works around the immediate problem
at hand.

The test case is courtesy @bram-perl
scottchiefbaker pushed a commit to scottchiefbaker/perl5 that referenced this issue Nov 3, 2022
This is a step in solving Perl#20155

The POSIX 2008 locale API introduces per-thread locales.  But the
previous global locale system is retained, probably for backward
compatibility.

The POSIX 2008 interface causes memory to be malloc'd that needs to be
freed.  In order to do this, the caller must first stop using that
memory, by switching to another locale.  perl accomplishes this during
termination by switching to the global locale, which is always available
and doesn't need to be freed.

Perl has long assumed that all that was needed to switch threads was to
change out tTHX.  That's because that structure was intended to hold all
the information for a given thread.  But it turns out that this doesn't
work when some library independently holds information about the
thread's state.  And there are now some libraries that do that.

What was happening in this case was that perl thought that it was
sufficient to switch tTHX to change to a different thread in order to do
the freeing of memory, and then used the POSIX 2008 function to change
to the global locale so that the memory could be safely freed.  But the
POSIX 2008 function doesn't care about tTHX, and actually was typically
operating on a different thread, and so changed that thread to the global
locale instead of the intended thread.  Often that was the top-level
thread, thread 0.  That caused whatever thread it was to no longer be in
the expected locale, and to no longer be thread-safe with regards to
localess,

This commit causes locale_term(), which has always been called from the
actual terminating thread that POSIX 2008 knows about, to change to the
global thread and free the memory.

It also creates a new per-interpreter variable that effectively maps the
tTHX thread to the associated POSIX 2008 memory.  During
perl_destruct(), it frees the memory this variable points to, instead of
blindly assuming the memory to free is the current tTHX thread's.

This fixes the symptoms associtated with Perl#20155, but doesn't solve the
whole problem.  In general, a library that has independent thread status
needs to be updated to the new thread when Perl changes threads using
tTHX.  Future commits will do this.
scottchiefbaker pushed a commit to scottchiefbaker/perl5 that referenced this issue Nov 3, 2022
As noted in the previous commit, some library functions now keep
per-thread state.  So far the only ones we care about are libc
locale-changing ones.

When perl changes threads by swapping out tTHX, those library functions
need to be informed about the new value so that they remain in sync with
what perl thinks the locale should be.

This commit creates a function to do this, and changes the
thread-changing macros to also call this as part of the change.

For POSIX 2008, the function just calls uselocale() using the
per-interpreter object introduced previously.

For Windows, this commit adds a per-interpreter string of the current
LC_ALL, and the function calls setlocale on that.  We keep the same
string for POSIX 2008 implementations that lack querylocale(), so this
commit just enables that variable on Windows as well.  The code is
already in place to free the memory the string occupies when done.

The commit also creates a mechanism to skip this during thread
destruction.  A thread in its death throes doesn't need to have accurate
locale information, and the information needed to map from thread to
what libc needs to know gets destroyed as part of those throes, while
relics of the thread remain.  I couldn't find a way to accurately know
if we are dealing with a relic or not, so the solution I adopted was to
just not switch during destruction.

This commit completes fixing Perl#20155.
khwilliamson added a commit that referenced this issue May 7, 2023
This reverts commit 9e254b0.
Date:   Wed Apr 5 12:26:26 2023 -0600

This fixes GH #21040

The reverted commit caused failures in platforms using the musl library,
notably Alpine Linux.  I came up with a fix for that, which instead
broke Windows.  In looking at that I realized the original fix is
incomplete, and that things are too precarious to try to fix so close to
5.38.0.  For example, I spent hours, due to a %p format printing 0 for
what turned out to be a non-NULL string pointer.  I think it has to do
do with the fact that the failing code is in the middle of transitioning
between threads, and the printing got confused as a result.

The reverted commit was part of a series fixing #20155 and #20231.  But
the earlier part of the series succeeded in fixing those, without that
commit, so reverting it should not cause things to break as a result.

This whole issue has to do with locales and threading.  Those still
don't play well together.  I have a series of well over 200 commits that
address this situation, for applying in early 5.39.  My point is that we
are a long way from solving these kinds of issues; and they don't come
up that much in the field because they just don't get used.  The
reverted commit would help if it worked properly, but it's not the only
thing wrong by a long shot.
khwilliamson added a commit that referenced this issue May 8, 2023
This reverts commit 9e254b0.
Date:   Wed Apr 5 12:26:26 2023 -0600

This fixes GH #21040

The reverted commit caused failures in platforms using the musl library,
notably Alpine Linux.  I came up with a fix for that, which instead
broke Windows.  In looking at that I realized the original fix is
incomplete, and that things are too precarious to try to fix so close to
5.38.0.  For example, I spent hours, due to a %p format printing 0 for
what turned out to be a non-NULL string pointer.  I think it has to do
do with the fact that the failing code is in the middle of transitioning
between threads, and the printing got confused as a result.

The reverted commit was part of a series fixing #20155 and #20231.  But
the earlier part of the series succeeded in fixing those, without that
commit, so reverting it should not cause things to break as a result.

This whole issue has to do with locales and threading.  Those still
don't play well together.  I have a series of well over 200 commits that
address this situation, for applying in early 5.39.  My point is that we
are a long way from solving these kinds of issues; and they don't come
up that much in the field because they just don't get used.  The
reverted commit would help if it worked properly, but it's not the only
thing wrong by a long shot.
pjacklam pushed a commit to pjacklam/perl5 that referenced this issue May 20, 2023
This reverts commit 9e254b0.
Date:   Wed Apr 5 12:26:26 2023 -0600

This fixes GH Perl#21040

The reverted commit caused failures in platforms using the musl library,
notably Alpine Linux.  I came up with a fix for that, which instead
broke Windows.  In looking at that I realized the original fix is
incomplete, and that things are too precarious to try to fix so close to
5.38.0.  For example, I spent hours, due to a %p format printing 0 for
what turned out to be a non-NULL string pointer.  I think it has to do
do with the fact that the failing code is in the middle of transitioning
between threads, and the printing got confused as a result.

The reverted commit was part of a series fixing Perl#20155 and Perl#20231.  But
the earlier part of the series succeeded in fixing those, without that
commit, so reverting it should not cause things to break as a result.

This whole issue has to do with locales and threading.  Those still
don't play well together.  I have a series of well over 200 commits that
address this situation, for applying in early 5.39.  My point is that we
are a long way from solving these kinds of issues; and they don't come
up that much in the field because they just don't get used.  The
reverted commit would help if it worked properly, but it's not the only
thing wrong by a long shot.
pjacklam pushed a commit to pjacklam/perl5 that referenced this issue May 20, 2023
This reverts commit 9e254b0.
Date:   Wed Apr 5 12:26:26 2023 -0600

This fixes GH Perl#21040

The reverted commit caused failures in platforms using the musl library,
notably Alpine Linux.  I came up with a fix for that, which instead
broke Windows.  In looking at that I realized the original fix is
incomplete, and that things are too precarious to try to fix so close to
5.38.0.  For example, I spent hours, due to a %p format printing 0 for
what turned out to be a non-NULL string pointer.  I think it has to do
do with the fact that the failing code is in the middle of transitioning
between threads, and the printing got confused as a result.

The reverted commit was part of a series fixing Perl#20155 and Perl#20231.  But
the earlier part of the series succeeded in fixing those, without that
commit, so reverting it should not cause things to break as a result.

This whole issue has to do with locales and threading.  Those still
don't play well together.  I have a series of well over 200 commits that
address this situation, for applying in early 5.39.  My point is that we
are a long way from solving these kinds of issues; and they don't come
up that much in the field because they just don't get used.  The
reverted commit would help if it worked properly, but it's not the only
thing wrong by a long shot.
khwilliamson added a commit to khwilliamson/perl5 that referenced this issue Jul 10, 2023
This reverts commit 9e254b0.
Date:   Wed Apr 5 12:26:26 2023 -0600

This fixes GH Perl#21040

The reverted commit caused failures in platforms using the musl library,
notably Alpine Linux.  I came up with a fix for that, which instead
broke Windows.  In looking at that I realized the original fix is
incomplete, and that things are too precarious to try to fix so close to
5.38.0.  For example, I spent hours, due to a %p format printing 0 for
what turned out to be a non-NULL string pointer.  I think it has to do
do with the fact that the failing code is in the middle of transitioning
between threads, and the printing got confused as a result.

The reverted commit was part of a series fixing Perl#20155 and Perl#20231.  But
the earlier part of the series succeeded in fixing those, without that
commit, so reverting it should not cause things to break as a result.

This whole issue has to do with locales and threading.  Those still
don't play well together.  I have a series of well over 200 commits that
address this situation, for applying in early 5.39.  My point is that we
are a long way from solving these kinds of issues; and they don't come
up that much in the field because they just don't get used.  The
reverted commit would help if it worked properly, but it's not the only
thing wrong by a long shot.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
5 participants