Skip to content

Conversation

khwilliamson
Copy link
Contributor

@khwilliamson khwilliamson commented Aug 24, 2025

Instead of using assert() , use ASSUME(). It is a drop-in replacement for assert() and gives more hints to the compiler.

Further, when available, this arranges to instead use __attribute__nonnull__ to make sure a parameter being passed is non-NULL. This is compile-time, not run-time. It only does this for functions that don't have a thread context parameter, deferring those to a potential future p.r.

Doing this showed a small issue

util.c: In function ‘void Perl_set_context(void*)’:
perl.h:6412:26: warning: ‘nonnull’ argument ‘t’ compared to NULL [-Wnonnull-compare]
 6412 |             STMT_START { if (i) PERL_SET_LOCALE_CONTEXT(i); } STMT_END
      |                          ^~
util.c:3665:5: note: in expansion of macro ‘PERL_SET_NON_tTHX_CONTEXT’
 3665 |     PERL_SET_NON_tTHX_CONTEXT((PerlInterpreter *) t);

in which the compiler now catches that t can't be NULL. I don't know the best way to resolve this.

  • This set of changes does not require a perldelta entry.

... instead of assert().  ASSUME() is a drop-in replacement for assert()
and gives more hints to the compiler.
This got duplicated in recent rebasing
The next commit will want this to be available earlier.
proto.h contains a generated PERL_ARGS_ASSERT macro for every function.
Prior to this commit, each such macro will assert that each such parameter
isn't NULL.  Some compilers allow a compile-time assertion to be made
for this situation.  This commit uses that instead when available, but
only for functions that don't have a thread context.

The reason for that is it gets more complicated; so I'm starting with
this.
@bulk88
Copy link
Contributor

bulk88 commented Aug 25, 2025

Doing this showed a small issue

util.c: In function ‘void Perl_set_context(void*)’:
perl.h:6412:26: warning: ‘nonnull’ argument ‘t’ compared to NULL [-Wnonnull-compare]
 6412 |             STMT_START { if (i) PERL_SET_LOCALE_CONTEXT(i); } STMT_END
      |                          ^~
util.c:3665:5: note: in expansion of macro ‘PERL_SET_NON_tTHX_CONTEXT’
 3665 |     PERL_SET_NON_tTHX_CONTEXT((PerlInterpreter *) t);

in which the compiler now catches that t can't be NULL. I don't know the best way to resolve this.

tried PTR2nat() or macro NUM2PTR(size_t,ptr)?

#  define PERL_SET_CONTEXT(t)                                               \
    STMT_START {                                                            \
        int _eC_;                                                           \
        if ((_eC_ = pthread_setspecific(PL_thr_key,                         \
                                        PL_current_context = (void *)(t)))) \
            Perl_croak_nocontext("panic: pthread_setspecific (%d) [%s:%d]", \
                                 _eC_, __FILE__, __LINE__);                 \
        PERL_SET_NON_tTHX_CONTEXT(t);                                       \
    } STMT_END
    /* In some Configurations there may be per-thread information that is
     * carried in a library instead of perl's tTHX structure.  This macro is to
     * be used to handle those when tTHX is changed.  Only locale handling is
     * currently known to be affected. */
#  define PERL_SET_NON_tTHX_CONTEXT(i)                                      \
            STMT_START { if (i) PERL_SET_LOCALE_CONTEXT(i); } STMT_END

Looks like a bug with POSIX Perl's PERL_SET_CONTEXT(t)''s internals which is macro PERL_SET_NON_tTHX_CONTEXT(i).

Imagine is the wrong word b/c I've done this b4 for private biz XS.

So lets imagine, I am a CPAN XS module running on ithread-ed WinPerl, with only 1 Perl thread (my_perl) in the process, and I am using the Native OS's >= Win2000 Thread Pool feature with Perl.

So I am an XS->PP event handler executing inside a TP Thd, The root thread is frozen (blocked until I release control back to the root thread manually). At the end of my TP thread runner, after the call_sv(); but before I return control back to the OS, and send the kernel event to wakeup the root thread, I would be doing PERL_SET_CONTEXT((PerlInterpreter*)NULL); to detach a sleeping/frozen my_perl ptr from my current random OS TP thread, because I don't want that my_perl ptr to continue stay inside OS's Thread Local Storage for that totally random TID, random lifespan thread after I return control of my temporary thd back to the OS.

I'm avoiding an accident if some enumeration API gets smart (not really) and runs my C callback fn ptr asynchronously, in parallel, on multiple cores on multiple TP OS threads. Bad hygiene to leave your de allocated void ptrs in TLS.

So I would definitely want PERL_SET_CONTEXT(NULL); to work.

Or this is an optimization to const fold away all machine code associated with PERL_SET_CONTEXT() on single-threaded perls builds.

But then the question is, why was PERL_SET_CONTEXT() left compiled in, and not #if 0ed away in that XS module? Why is the Perl C API compatible with CPAN XS modules that ithread-aware but are not no-threads-aware and crash/hang/C syntax error on no-thread Perls??

You also wrote, or were the last person to clean it up.

6e13fe3

but the null test existed before the commit above, ill stop git blaming at this point.

diff --git a/locale.c b/locale.c
index 617119fdb8..20d49395fc 100644
--- a/locale.c
+++ b/locale.c
@@ -8538,19 +8538,38 @@ S_my_setlocale_debug_string_i(pTHX_
 #ifdef USE_PERL_SWITCH_LOCALE_CONTEXT
 
 void
-Perl_switch_locale_context()
+Perl_switch_locale_context(pTHX)
 {
     /* libc keeps per-thread locale status information in some configurations.
      * So, we can't just switch out aTHX to switch to a new thread.  libc has
      * to follow along.  This routine does that based on per-interpreter
-     * variables we keep just for this purpose */
-
-    /* Can't use pTHX, because we may be called from a place where that
-     * isn't available */
-    dTHX;
+     * variables we keep just for this purpose.
+     *
+     * There are two implementations where this is an issue.  For the other
+     * implementations, it doesn't matter because libc is using global values
+     * that all threads know about.
+     *
+     * The two implementations are where libc keeps thread-specific information
+     * on its own.  These are
+     *
+     * POSIX 2008:  The current locale is kept by libc as an object.  We save
+     *              a copy of that in the per-thread PL_cur_locale_obj, and so
+     *              this routine uses that copy to tell the thread it should be
+     *              operating with that object
+     * Windows thread-safe locales:  A given thread in Windows can be being run
+     *              with per-thread locales, or not.  When the thread context
+     *              changes, libc doesn't automatically know if the thread is
+     *              using per-thread locales, nor does it know what the new
+     *              thread's locale is.  We keep that information in the
+     *              per-thread variables:
+     *                  PL_controls_locale  indicates if this thread is using
+     *                                      per-thread locales or not
+     *                  PL_cur_LC_ALL       indicates what the the locale
+     *                                      should be if it is a per-thread
+     *                                      locale.
+     */
 
-    if (UNLIKELY(   aTHX == NULL
-                 || PL_veto_switch_non_tTHX_context
+    if (UNLIKELY(   PL_veto_switch_non_tTHX_context
                  || PL_phase == PERL_PHASE_CONSTRUCT))
     {
         return;

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants