From ce6301c5c07ee3d98316cc24a522961b19140ed0 Mon Sep 17 00:00:00 2001 From: bulk88 Date: Sun, 3 Aug 2025 07:48:51 -0400 Subject: [PATCH] combine PL_strategy_* bools into 1 exported symbol These 9 symbols are just 2 bits wide bools. I presume each one is declared as a 4 byte int because either they are lvals taking a 4 byte enum assignment or because C doesn't acknowledge the SMP concept and doesn't describe any unit tests for threads or SMP address spaces. ISO C stopped describing what volatile and register do on a HW level long ago. Considering all current former and future CPU archs Perl has executed on, there is no money-back guarentee from all CPU mfgs that doing R/Ws to the same mem addr by 2 CPU cores will not shard or be atomic. As of 5.43, metaconfig and perl.h still don't know about the std::atomic API that first appeared in C11/C++11. PL_strategy_* is required de-jure, not de-facto, by C11/C++11 to be using the atomic_load() token. It is very overkill to use the atomic_load() token on these PL_strategy_* vars. atomic_load() can be added to PL_strategy_* after a verified and reproduced bug ticket on the correct permutation of hardware and compiler that needs it. Dr Memory, Asan, and Valgrind aren't hardware. These 9 different exported linker symbols take up significant screen space in various C dev tools. These vars will never be part of a copy paste backtrace SEGV report. They are undocumented, not public CPAN API. The only user is doio.c They take up alot of bytes inside of libperl since symbol PL_strategy_mkstemp can't be smaller than 24 == (sizeof("PL_strategy_mkstemp") + sizeof(U32)) Dynamic instrumenting all dereferences of PL_strategy_dup but not instrumenting PL_strategy_mkstemp, using LD_PRELOAD inside a private + obsolete + proprietary + closed source + lost source build of libperl.so ... All of the PL_strategy_* symbols have no references to them on Win32. The Win32 doio.o/doio.obj file doesn't link against the PL_strategy_* symbols. They would've been discarded on Windows, but their names appeared inside perldll.def so that is that and ABI is ABI. The easiest step is to combine the symbols into 1 ascii C string name. Any other improvements will be more complex and can be done another time. This step is beneficial on all OS since the shared library loaders on all OSes have to loop over the array of "Symbols" to connect 2 files, or 2 User Services (TM IBM), or 2 Subroutines (TM Larry Wall) atleast once on startup. If this io_strategy code needs to be stepped debeg (unlikely) keeping all the bools together in 1 struct is easier to see their state in the watch window since its 1 name to type in vs 9 names. The amount of vertical screen space these exported symbols occupy in C devel tools if they are drawn, and need to be scrolled or glanced by, or more partial match letters to type, vs what these syms/vars do and the state they hold, is the main reason this commit was written. --- doio.c | 22 +++++++++++----------- globvar.sym | 10 +--------- perl.h | 29 +++++++++++++++++++++++++++++ perlvars.h | 19 +++++++++---------- 4 files changed, 50 insertions(+), 30 deletions(-) diff --git a/doio.c b/doio.c index d0681ad6ccbe..d19a2a1a51f3 100644 --- a/doio.c +++ b/doio.c @@ -196,7 +196,7 @@ Perl_PerlLIO_dup_cloexec(pTHX_ int oldfd) * PERL_IMPLICIT_SYS builds. */ DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_dup, + PL_strategy.dup, fcntl(oldfd, F_DUPFD_CLOEXEC, 0), PerlLIO_dup(oldfd)); #else @@ -214,7 +214,7 @@ Perl_PerlLIO_dup2_cloexec(pTHX_ int oldfd, int newfd) * PERL_IMPLICIT_SYS builds. */ DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_dup2, + PL_strategy.dup2, dup3(oldfd, newfd, O_CLOEXEC), PerlLIO_dup2(oldfd, newfd)); #else @@ -279,7 +279,7 @@ Perl_PerlLIO_open_cloexec(pTHX_ const char *file, int flag) PERL_ARGS_ASSERT_PERLLIO_OPEN_CLOEXEC; #if defined(O_CLOEXEC) DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_open, + PL_strategy.open, PerlLIO_open(file, flag | O_CLOEXEC), PerlLIO_open(file, flag)); #else @@ -293,7 +293,7 @@ Perl_PerlLIO_open3_cloexec(pTHX_ const char *file, int flag, int perm) PERL_ARGS_ASSERT_PERLLIO_OPEN3_CLOEXEC; #if defined(O_CLOEXEC) DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_open3, + PL_strategy.open3, PerlLIO_open3(file, flag | O_CLOEXEC, perm), PerlLIO_open3(file, flag, perm)); #else @@ -310,7 +310,7 @@ static int Internal_Perl_my_mkstemp_cloexec(char *templte) PERL_ARGS_ASSERT_MY_MKSTEMP_CLOEXEC; # if defined(O_CLOEXEC) DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_mkstemp, + PL_strategy.mkstemp, Perl_my_mkostemp(templte, O_CLOEXEC), Perl_my_mkstemp(templte)); # else @@ -334,7 +334,7 @@ Perl_my_mkstemp_cloexec(char *templte) PERL_ARGS_ASSERT_MY_MKSTEMP_CLOEXEC; # if defined(O_CLOEXEC) DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_mkstemp, + PL_strategy.mkstemp, Perl_my_mkostemp(templte, O_CLOEXEC), Perl_my_mkstemp(templte)); # else @@ -349,7 +349,7 @@ Perl_my_mkostemp_cloexec(char *templte, int flags) PERL_ARGS_ASSERT_MY_MKOSTEMP_CLOEXEC; #if defined(O_CLOEXEC) DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_mkstemp, + PL_strategy.mkstemp, Perl_my_mkostemp(templte, flags | O_CLOEXEC), Perl_my_mkostemp(templte, flags)); #else @@ -368,7 +368,7 @@ Perl_PerlProc_pipe_cloexec(pTHX_ int *pipefd) * PERL_IMPLICIT_SYS builds. */ # if !defined(PERL_IMPLICIT_SYS) && defined(HAS_PIPE2) && defined(O_CLOEXEC) - DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(PL_strategy_pipe, pipefd, + DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(PL_strategy.pipe, pipefd, pipe2(pipefd, O_CLOEXEC), PerlProc_pipe(pipefd)); # else @@ -384,7 +384,7 @@ Perl_PerlSock_socket_cloexec(pTHX_ int domain, int type, int protocol) { # if defined(SOCK_CLOEXEC) DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_socket, + PL_strategy.socket, PerlSock_socket(domain, type | SOCK_CLOEXEC, protocol), PerlSock_socket(domain, type, protocol)); # else @@ -404,7 +404,7 @@ Perl_PerlSock_accept_cloexec(pTHX_ int listenfd, struct sockaddr *addr, * on PERL_IMPLICIT_SYS builds. */ DO_ONEOPEN_EXPERIMENTING_CLOEXEC( - PL_strategy_accept, + PL_strategy.accept, accept4(listenfd, addr, addrlen, SOCK_CLOEXEC), PerlSock_accept(listenfd, addr, addrlen)); # else @@ -423,7 +423,7 @@ Perl_PerlSock_socketpair_cloexec(pTHX_ int domain, int type, int protocol, { PERL_ARGS_ASSERT_PERLSOCK_SOCKETPAIR_CLOEXEC; # ifdef SOCK_CLOEXEC - DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(PL_strategy_socketpair, pairfd, + DO_PIPEOPEN_EXPERIMENTING_CLOEXEC(PL_strategy.socketpair, pairfd, PerlSock_socketpair(domain, type | SOCK_CLOEXEC, protocol, pairfd), PerlSock_socketpair(domain, type, protocol, pairfd)); # else diff --git a/globvar.sym b/globvar.sym index b9cea72af64b..798bc5f6857e 100644 --- a/globvar.sym +++ b/globvar.sym @@ -66,15 +66,7 @@ PL_sig_name PL_sig_num PL_simple PL_simple_bitmask -PL_strategy_dup -PL_strategy_dup2 -PL_strategy_open -PL_strategy_open3 -PL_strategy_mkstemp -PL_strategy_socket -PL_strategy_accept -PL_strategy_pipe -PL_strategy_socketpair +PL_strategy PL_strict_utf8_dfa_tab PL_subversion PL_utf8skip diff --git a/perl.h b/perl.h index 1154cb99dfaf..247119a6bdfb 100644 --- a/perl.h +++ b/perl.h @@ -5230,6 +5230,35 @@ EXTERN_C void PerlIO_teardown(void); # define PERLIO_TERM #endif +/* these record the best way to perform certain IO operations while + * atomically setting FD_CLOEXEC. On the first call, a probe is done + * and the result recorded for use by subsequent calls. + * In theory these variables aren't thread-safe, but the worst that can + * happen is that two threads will both do an initial probe or even less + * likley both ithreads will repeat the probe a couple times over the next + * 100 ms. + * + * If a cross platform atomic API gets added to perl.h, all reads and writes + * to PL_strategy need force a memory fence. Certain archs like ARM in SMP mode + * by design will never re-read or flush memory writes between their L2 cache + * central physical unless software commands the CPU core to do it. + * + * struct io_stategy {} can be reduced to a U32 with 18 bits if its known + * the CC guarentees (PL_strategy & 0xC == 0x8) is safe against sharding. + */ + +struct io_strategy { + int strategy_dup; /* doio.c */ + int strategy_dup2; /* doio.c */ + int strategy_open; /* doio.c */ + int strategy_open3; /* doio.c */ + int strategy_mkstemp; /* doio.c */ + int strategy_socket; /* doio.c */ + int strategy_accept; /* doio.c */ + int strategy_pipe; /* doio.c */ + int strategy_socketpair; /* doio.c */ +}; + #ifdef MYMALLOC # ifdef MUTEX_INIT_CALLS_MALLOC # define MALLOC_INIT \ diff --git a/perlvars.h b/perlvars.h index 7b0c01a43f30..ec7a5c25108c 100644 --- a/perlvars.h +++ b/perlvars.h @@ -400,21 +400,20 @@ PERLVAR(G, user_prop_mutex, perl_mutex) /* Mutex for manipulating */ PERLVARI(G, shutdownhook, shutdown_proc_t, &Perl_noshutdownhook) -/* these record the best way to perform certain IO operations while +/* This struct records the best way to perform certain IO operations while * atomically setting FD_CLOEXEC. On the first call, a probe is done * and the result recorded for use by subsequent calls. * In theory these variables aren't thread-safe, but the worst that can * happen is that two treads will both do an initial probe + * + * Boolean-like probe results for certain I/O operations and FD_CLOEXEC. + * Each flag has 3 states: enum CLOEXEC_EXPERIMENT (== 0), CLOEXEC_AT_OPEN, + * and CLOEXEC_AFTER_OPEN. + * + * doio.c is the only user of this var currently. This variable is exported + * in case experimental XS code needs to perfectly replicate an IO pp_*() func. */ -PERLVARI(G, strategy_dup, int, 0) /* doio.c */ -PERLVARI(G, strategy_dup2, int, 0) /* doio.c */ -PERLVARI(G, strategy_open, int, 0) /* doio.c */ -PERLVARI(G, strategy_open3, int, 0) /* doio.c */ -PERLVARI(G, strategy_mkstemp, int, 0) /* doio.c */ -PERLVARI(G, strategy_socket, int, 0) /* doio.c */ -PERLVARI(G, strategy_accept, int, 0) /* doio.c */ -PERLVARI(G, strategy_pipe, int, 0) /* doio.c */ -PERLVARI(G, strategy_socketpair, int, 0) /* doio.c */ +PERLVAR(G, strategy, struct io_strategy) PERLVARI(G, my_environ, char **, NULL) PERLVARI(G, origenviron, char **, NULL)