[RFC,1/4] string: Make strsignal async-signal-safe
Commit Message
To accomplish it a per-thread static buffer is used for unknown
signals and the resulting message is not translated (this will
be covered by a new function).
To setup the per-thread static buffer, two strategies are used:
1. The default one uses a TLS structure, which will be place in the
static TLS space.
2. Linux allocates on struct pthread and access it through THREAD_*
macros.
The default strategy has the disadvantage of increasing libc.so static
TLS consumption and thus descreasing the possible surplus used in
some scenarios (which might be mitigated by BZ#25051 fix).
It is used only on Hurd, where accessing the thread point in single
thread case is not straightforward (afaiu, Hurd developers could
correct me here).
Checked on x86-64-linux-gnu, i686-linux-gnu, powerpc64le-linux-gnu,
and s390x-linux-gnu.
---
manual/signal.texi | 6 +-
nptl/descr.h | 5 +-
string/strsignal.c | 119 +++++++------------------
sysdeps/generic/Makefile | 1 +
sysdeps/generic/tls_internal-struct.h | 32 +++++++
sysdeps/generic/tls_internal.c | 3 +
sysdeps/generic/tls_internal.h | 32 +++++++
sysdeps/unix/sysv/linux/tls_internal.c | 1 +
sysdeps/unix/sysv/linux/tls_internal.h | 30 +++++++
time/strftime_l.c | 10 +--
10 files changed, 139 insertions(+), 100 deletions(-)
create mode 100644 sysdeps/generic/tls_internal-struct.h
create mode 100644 sysdeps/generic/tls_internal.c
create mode 100644 sysdeps/generic/tls_internal.h
create mode 100644 sysdeps/unix/sysv/linux/tls_internal.c
create mode 100644 sysdeps/unix/sysv/linux/tls_internal.h
Comments
On Mai 13 2020, Adhemerval Zanella via Libc-alpha wrote:
> + if (signum < 0)
> {
> - /* We don't use the static buffer and so we have a key. Use it
> - to get the thread-specific buffer. */
> - result = __libc_getspecific (key);
> - if (result == NULL)
> - {
> - /* No buffer allocated so far. */
> - result = malloc (BUFFERSIZ);
> - if (result == NULL)
> - /* No more memory available. We use the static buffer. */
> - result = local_buf;
> - else
> - __libc_setspecific (key, result);
> - }
> + *q++ = '-';
> + signum = abs (signum);
Since you already know signum < 0 you can just use -signum.
Andreas.
@@ -830,8 +830,8 @@ termination status of a child process (@pxref{Process Completion}) or it
may come from a signal handler in the same process.
@deftypefun {char *} strsignal (int @var{signum})
-@standards{GNU, string.h}
-@safety{@prelim{}@mtunsafe{@mtasurace{:strsignal} @mtslocale{}}@asunsafe{@asuinit{} @ascuintl{} @asucorrupt{} @ascuheap{}}@acunsafe{@acuinit{} @acucorrupt{} @acsmem{}}}
+@standards{POSIX-1.2008, string.h}
+@safety{@prelim{}@mtsafe{}@assafe{}@acsafe{}}
@c strsignal @mtasurace:strsignal @mtslocale @asuinit @ascuintl @asucorrupt @ascuheap @acucorrupt @acsmem
@c uses a static buffer if tsd key creation fails
@c [once] init
@@ -852,7 +852,7 @@ rewritten on subsequent calls, you should save a copy of it if you need
to reference it later.
@pindex string.h
-This function is a GNU extension, declared in the header file
+This function is declared in the header file
@file{string.h}.
@end deftypefun
@@ -34,6 +34,7 @@
#include <unwind.h>
#include <bits/types/res_state.h>
#include <kernel-features.h>
+#include <tls_internal-struct.h>
#ifndef TCB_ALIGNMENT
# define TCB_ALIGNMENT sizeof (double)
@@ -116,7 +117,6 @@ struct priority_protection_data
unsigned int priomap[];
};
-
/* Thread descriptor data structure. */
struct pthread
{
@@ -398,6 +398,9 @@ struct pthread
/* Indicates whether is a C11 thread created by thrd_creat. */
bool c11;
+ /* Used on strsignal. */
+ struct tls_internal_t tls_state;
+
/* This member must be last. */
char end_padding[];
@@ -16,110 +16,55 @@
<https://www.gnu.org/licenses/>. */
#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <libintl.h>
-#include <libc-lock.h>
-
-static __libc_key_t key;
-
-/* If nonzero the key allocation failed and we should better use a
- static buffer than fail. */
-#define BUFFERSIZ 100
-static char local_buf[BUFFERSIZ];
-static char *static_buf;
+#include <stdlib.h>
+#include <array_length.h>
+#include <tls_internal.h>
-/* Destructor for the thread-specific data. */
-static void init (void);
-static void free_key_mem (void *mem);
-static char *getbuffer (void);
+#define RT_STR "Real-time signal "
+_Static_assert (sizeof (RT_STR) <= strsignal_str_len,
+ RT_STR " > strsignal_str_len");
+#define UNK_STR "Unknown signal "
+_Static_assert (sizeof (UNK_STR) <= strsignal_str_len,
+ UNK_STR " > strsignal_str_len");
/* Return a string describing the meaning of the signal number SIGNUM. */
char *
strsignal (int signum)
{
- __libc_once_define (static, once);
- const char *desc;
+ const char *desc = NULL;
+
+ if (signum >= 0 && signum <= NSIG
+ && signum < array_length (__sys_siglist_internal))
+ desc = __sys_siglist_internal[signum];
- /* If we have not yet initialized the buffer do it now. */
- __libc_once (once, init);
+ if (desc != NULL)
+ return (char *) desc;
- if (
+ const char* prefix = UNK_STR;
#ifdef SIGRTMIN
- (signum >= SIGRTMIN && signum <= SIGRTMAX) ||
-#endif
- signum < 0 || signum >= NSIG
- || (desc = __sys_siglist_internal[signum]) == NULL)
+ if (signum >= SIGRTMIN && signum <= SIGRTMAX)
{
- char *buffer = getbuffer ();
- int len;
-#ifdef SIGRTMIN
- if (signum >= SIGRTMIN && signum <= SIGRTMAX)
- len = __snprintf (buffer, BUFFERSIZ - 1, _("Real-time signal %d"),
- signum - SIGRTMIN);
- else
-#endif
- len = __snprintf (buffer, BUFFERSIZ - 1, _("Unknown signal %d"),
- signum);
- if (len >= BUFFERSIZ)
- buffer = NULL;
- else
- buffer[len] = '\0';
-
- return buffer;
+ prefix = RT_STR;
+ signum -= SIGRTMIN;
}
+#endif
- return (char *) _(desc);
-}
-
-
-/* Initialize buffer. */
-static void
-init (void)
-{
- if (__libc_key_create (&key, free_key_mem))
- /* Creating the key failed. This means something really went
- wrong. In any case use a static buffer which is better than
- nothing. */
- static_buf = local_buf;
-}
-
-
-/* Free the thread specific data, this is done if a thread terminates. */
-static void
-free_key_mem (void *mem)
-{
- free (mem);
- __libc_setspecific (key, NULL);
-}
-
+ char *buffer = __glibc_tls_internal ()->strsignal_buf;
-/* Return the buffer to be used. */
-static char *
-getbuffer (void)
-{
- char *result;
+ char *q = __stpcpy (buffer, prefix);
- if (static_buf != NULL)
- result = static_buf;
- else
+ if (signum < 0)
{
- /* We don't use the static buffer and so we have a key. Use it
- to get the thread-specific buffer. */
- result = __libc_getspecific (key);
- if (result == NULL)
- {
- /* No buffer allocated so far. */
- result = malloc (BUFFERSIZ);
- if (result == NULL)
- /* No more memory available. We use the static buffer. */
- result = local_buf;
- else
- __libc_setspecific (key, result);
- }
+ *q++ = '-';
+ signum = abs (signum);
}
+ for (int d = signum; q++, (d /= 10) != 0; )
+ continue;
+ *q = '\0';
+ for (int d = signum; *--q = '0' + d % 10, (d /= 10) != 0; )
+ continue;
- return result;
+ return buffer;
}
@@ -16,6 +16,7 @@
# <https://www.gnu.org/licenses/>.
ifeq ($(subdir),string)
+sysdep_routines += tls_internal
CFLAGS-wordcopy.c += -Wno-uninitialized
endif
new file mode 100644
@@ -0,0 +1,32 @@
+/* Per-thread state. Generic version.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _TLS_INTERNAL_STRUCT_H
+#define _TLS_INTERNAL_STRUCT_H 1
+
+#include <intprops.h>
+
+enum { strsignal_str_len = 20 };
+
+struct tls_internal_t
+{
+ /* Used on strsignal.c. */
+ char strsignal_buf[strsignal_str_len + INT_STRLEN_BOUND (int) + 1];
+};
+
+#endif
new file mode 100644
@@ -0,0 +1,3 @@
+#include <tls_internal.h>
+
+__thread struct tls_internal_t tls_internal;
new file mode 100644
@@ -0,0 +1,32 @@
+/* Per-thread state. Generic version.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _TLS_INTERNAL_H
+#define _TLS_INTERNAL_H 1
+
+#include <tls_internal-struct.h>
+
+extern __thread struct tls_internal_t tls_internal attribute_hidden;
+
+static inline struct tls_internal_t *
+__glibc_tls_internal (void)
+{
+ return &tls_internal;
+}
+
+#endif
new file mode 100644
@@ -0,0 +1 @@
+/* Empty. */
new file mode 100644
@@ -0,0 +1,30 @@
+/* Per-thread state. Linux version.
+ Copyright (C) 2020 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#ifndef _TLS_INTERNAL_H
+#define _TLS_INTERNAL_H 1
+
+#include <nptl/pthreadP.h>
+
+static inline struct tls_internal_t *
+__glibc_tls_internal (void)
+{
+ return &THREAD_SELF->tls_state;
+}
+
+#endif
@@ -136,15 +136,7 @@ extern char *tzname[];
# define NULL 0
#endif
-#define TYPE_SIGNED(t) ((t) -1 < 0)
-
-/* Bound on length of the string representing an integer value of type t.
- Subtract one for the sign bit if t is signed;
- 302 / 1000 is log10 (2) rounded up;
- add one for integer division truncation;
- add one more for a minus sign if t is signed. */
-#define INT_STRLEN_BOUND(t) \
- ((sizeof (t) * CHAR_BIT - TYPE_SIGNED (t)) * 302 / 1000 + 1 + TYPE_SIGNED (t))
+#include <intprops.h>
#define TM_YEAR_BASE 1900