From patchwork Fri Apr 4 18:05:44 2025 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 878203 Delivered-To: patch@linaro.org Received: by 2002:a5d:6dae:0:b0:38f:210b:807b with SMTP id u14csp3919066wrs; Fri, 4 Apr 2025 11:07:25 -0700 (PDT) X-Forwarded-Encrypted: i=3; AJvYcCVDGPGdrpGzlFMPYBgTBNdMdiaTawt88GPfPYz2E9wDXOoTiWw36cPqXNBjOY1TUOEOHUeblw==@linaro.org X-Google-Smtp-Source: AGHT+IEESWTO7jsI9EhY8O8ivM23jm6wzl28w7MxVpT9eBbZsB0ESAGCoTmn4UjGHMlgDN/HKsqc X-Received: by 2002:ad4:5ba1:0:b0:6ee:b77c:7dbe with SMTP id 6a1803df08f44-6f058433cecmr60154656d6.12.1743790045737; Fri, 04 Apr 2025 11:07:25 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1743790045; cv=pass; d=google.com; s=arc-20240605; b=U48TAuk1mLbYYZkQUXZplijUm0RBdcIhLQifK/kVLr0GXTL6Cdi3rGeMP+PtiKyz5Y yp5XcGKZM0Imd+w6KAz+p+mXA9Csm1UvaBhAydWGT1aOs5BnrbD24vcQ3i/yfePXEVvm 9Ctqe9N3etOAS2MNy3sy9InqGLCx6pFH38Li9nCqdwFfofV2vMvwtuwHLdxHJDmPftcx AgozVjlcQDfqXkl5MGKt7CG4smamJ3C6VdjTcj8nj3IxbdLV/AArY7tP6kmyNUD1awPL XRn3pyfO3ucd5PncuUWxR4LKIH+i7a1AUyvOXhTBqU0EgU2Mg8bnHU+ophNVklP2RRBj g1XA== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20240605; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:content-transfer-encoding :mime-version:message-id:date:subject:cc:to:from:dkim-signature :dkim-filter:arc-filter:dmarc-filter:delivered-to:dkim-filter; bh=mRwEvkH7ijIMWFi3q1RlQwpbjkilXpSlNGQpmPkSu0o=; fh=A9su4qsAMQ+K407mZUkct2keygEHaoajyoFiGXaGOoM=; b=X1Sxb+jjj9B+ssXIa+3tI6uMuj2Go/pruIdakZDPAcH/MfnVatoraB1xq6fq5CGUtY V3F1JHJGsqv1PbJvVy9VkGPkMaTXWr/B5d3O+m6PksFodBVTxy9KBLNnu1NqWpjrozJ5 d7i43HIguq+NS750YmgRwKtsvifp/EkN7lLUBy8Xn2Rp0lSW7WjvBNzLUU7hSNHXIIXe MYtN5H6p6AI9NesAU4vwWl25RX3tmXFU8uIlV9sFSC4cVBFQ/rLlxr+UtK77YhnRK07L sJowKqJs6NT3DWFEj/Rx4H/1SJZH5MUjHR+0BeJWg+DtfBupIEiNVCOyxQ6NGCNOCEIC 2ASw==; dara=google.com ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=d08Q64C8; arc=pass (i=1); spf=pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="libc-alpha-bounces~patch=linaro.org@sourceware.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Return-Path: Received: from server2.sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id af79cd13be357-7c76ea8616esi353641985a.365.2025.04.04.11.07.25 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Apr 2025 11:07:25 -0700 (PDT) Received-SPF: pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@linaro.org header.s=google header.b=d08Q64C8; arc=pass (i=1); spf=pass (google.com: domain of libc-alpha-bounces~patch=linaro.org@sourceware.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="libc-alpha-bounces~patch=linaro.org@sourceware.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=linaro.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 221903845858 for ; Fri, 4 Apr 2025 18:07:25 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 221903845858 Authentication-Results: sourceware.org; dkim=pass (2048-bit key, unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=d08Q64C8 X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pl1-x62a.google.com (mail-pl1-x62a.google.com [IPv6:2607:f8b0:4864:20::62a]) by sourceware.org (Postfix) with ESMTPS id 7CD3F3857B9B for ; Fri, 4 Apr 2025 18:07:01 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 7CD3F3857B9B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linaro.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 7CD3F3857B9B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::62a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1743790022; cv=none; b=ZGI+uvT7lHhndAQfiQp0TfrA1pycD9wDJ4hpdiU5CQE5HS1n3OXQLVmSZ9JD0j+v5LJAiFJmZNuz5x0aQfz7Sx0kaJgs3gDRRRJUE5xBKJ+xZ01g6sAJ4e/rddkRONfHi3VufI/9kOQsHIhlrknC72CLVwyEKX2LiJfGDj3Hrt0= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1743790022; c=relaxed/simple; bh=IH57E4CZz1PrMrnNoBkms0yCLesYlssvxAVoSebceiQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=GELAMLzbkH235yfgCb1O83MNFCdhNs5lvNKkmbC/ALDoFDZHNr4kWgqq+nlpjT0f/FGtGho+BJjCH3Q7173mKOOSwo6cFGYXUTMITES6qAhVjLhtdrl75pi2ug7+FnrkRkHuWBFSwFXtk7GespoaQ7oJ0mehn/kS03+z+VWQdS8= ARC-Authentication-Results: i=1; server2.sourceware.org DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 7CD3F3857B9B Received: by mail-pl1-x62a.google.com with SMTP id d9443c01a7336-22403cbb47fso25388445ad.0 for ; Fri, 04 Apr 2025 11:07:01 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1743790020; x=1744394820; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=mRwEvkH7ijIMWFi3q1RlQwpbjkilXpSlNGQpmPkSu0o=; b=d08Q64C8rq/QzH7H8B5WCGsR+g+zEGO5cdE9NW0bxLsGkzU0OandaFJlEe23eLdNYD J0G+ifKrJJZKq6LDLpyRVFxsD5pGSbA30ZVrnAz1WfFdL1ivcdIoQIYkUSWzyw4HSwLJ 8cTMytTZW5HDxu3YmkHmaGdUy9bKnzSWZiYuqbB9aoMIbnq9+dFJP5mr5IcRrfc9RwL/ Iw2Bc0Og9DqGKelH/2/G2HTvBpDjdSzb8lPJRW9KwPCuAuzUMVkF0y+EupwF5IN0uUbV zJjy/fiHDcS932Wc2Dt86Em8EWLjWvsVglBU748TcRxHmFDNieocWMiR+Gl1SwXpiwbh 8h1Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1743790020; x=1744394820; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=mRwEvkH7ijIMWFi3q1RlQwpbjkilXpSlNGQpmPkSu0o=; b=Gik7HQ9+tOja31zxqVnSPQGdcwID9wNrFZuQiEOfL+gZYp63sXKTAIFlDTGLnRwKUJ XNlWI7G66fAoupGBa3kJcGSLgg2vQ0Uq/tTYYaPGSKeFAWhXuk640RAvheWU8PZ1nwlv 640VXCCRUVSCsBHrTUwaHv7DQ/eABTw8+bcFUTUAbx57TdIkPsPU6jtRaMboBheAPXHm ICyemH0QZVlHWltwg0NfT5rvcrsTUUnzHhkjXfFpvM8UgI9tQpjkYW6gwr3CvSZOSX0c tEV1fwNc4ZbL8okhSh5dYO8ENdhfSTyzbwHikhxDS0HhJmUMAJy3NJveVB2EIPatasna 4VFw== X-Gm-Message-State: AOJu0YwZDIdHKCloaMWw+ElAiUd1teaDlqh6+spv6wmLYayYlwawNzH5 heSXR13UNCKv7QUzOtK44AQ2flUpJ5sfGL/01+EGgSEEyhhMnvdL3wAPoBO31EPBp36B7zjM7Dn t X-Gm-Gg: ASbGncsIH76FJBtdv7MXVmWFIqhYxBbg9EY7dbasU1H5OQY15KL7Ke9nsyQLK98H4/y x77eMHoTkZR6Rck5NRvwh8dq/rNW68UNTsPeqxOpKAbr2p6MbWnQHW1XftGob25wY4o4MRLlYmv vyGCvmdT3QzEwPTeLgyAJJbjzts1T3+SFd4bbV1pPmTYD+7ohwud4WtT7dvA7cKr2bRBgQ3HSTA jCHXFXgE7L/ZxSFiWWNA7UhafwbCqcuTjfRhuLVQ5iv4hKZsmQP8F3HvaGe+gJHXlOz9PTLyQto DNaPXCY/LDtrY3f0llPVmuYjnsEolvkY2UXJzAURnzvaDzpKmJFci5EXeOSHUNDxSzg= X-Received: by 2002:a17:902:ebc8:b0:223:325c:89de with SMTP id d9443c01a7336-22a8a85a1camr53227835ad.1.1743790019423; Fri, 04 Apr 2025 11:06:59 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c2:c84d:1406:233b:d6e:49ef]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-22978660966sm35376265ad.156.2025.04.04.11.06.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Apr 2025 11:06:58 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: Tim Starling Subject: [PATCH v2] linux: Do not spawn a new thread for SIGEV_THREAD (BZ 30558, 27895, 29705, 32833) Date: Fri, 4 Apr 2025 15:05:44 -0300 Message-ID: <20250404180654.649510-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces~patch=linaro.org@sourceware.org The current timer_create SIGEV_THREAD implementation has some downsides: 1. There is no way to report failure at thread creation when a timer triggers. It means that it might occur unreported and with missed events depending of the system load. 2. The backgroup thread also kept in backgroun even when there is no more timers, consuming resources and also misleading memory profile tools (BZ 29705). 3. There is a lot of metadata that required to be kept: a control variable for helper thread creation, a list of active SIGEV_THREAD timers, atfork handlers to cleanup the list. 4. timer_create does not propagate all thread attributes to the new thread (BZ 27895). 5. Kernel might deliver in-flight events for a timer after it was destroyed by timer_delete. The timer_helper_thread mechanism to handle it does not cover all possible issue, which leads to callbacks being wrong triggered (BZ 32833). This new implementation moves the thread creation to timer_create, so any failure is reported to the caller. Also, the same thread will issues the multiple timers, thus there is no unreported missed events. Also, avoiding parallel timer activation also avoid possible parallel timer invocation to see the same overrun value. To implement using SIGTIMER internally as SIGCANCEL, it requires to mask out SIGCANCEL on thread creation. It essentially disable async thread cancellation, but POSIX requires that SIGEV_THREAD is always created in detached mode and cancelling detached thread s UB (glibc check the internal tid, but the memory referenced by pthread_t might not always be valid as the momento of pthread_cancel call). And to avoid the need to recreate the thread for pthread_exit call (and having possible unreported missed due failed thread creation), the SIGEV_THREAD install a cleanup handler that reset all internal thread state. It also prevents the re-use issue when a newly-allocated timer has in-flight event being delivered by the kernel (BZ 32833). Performance-wise it see it uses less CPU timer for multiple thread activation, although each thread now requires a sigwaitinfo which generate more context-switches/page-faults (check comment 7 from BZ 30558). I would expect that latency should improve, since it avoid a thread creation for each timer expiration. Checked on aarch64-linux-gnu, x86_64-linux-gnu and i686-linux-gnu. --- Changes from v2: - Fixed some issues with timer_delete due using timeid to signal the thread. - Added BZ#32833 as fixed bug. - Rebased against master. --- nptl/descr.h | 3 + rt/Makefile | 4 +- rt/tst-timer-sigmask.c | 7 +- rt/tst-timer6.c | 79 +++++ sysdeps/nptl/Makefile | 2 - sysdeps/nptl/fork.h | 2 - sysdeps/unix/sysv/linux/internal-signals.h | 8 - .../unix/sysv/linux/kernel-posix-cpu-timers.h | 2 + sysdeps/unix/sysv/linux/kernel-posix-timers.h | 75 ++--- sysdeps/unix/sysv/linux/timer_create.c | 289 ++++++++++++------ sysdeps/unix/sysv/linux/timer_delete.c | 46 +-- sysdeps/unix/sysv/linux/timer_routines.c | 154 ---------- 12 files changed, 323 insertions(+), 348 deletions(-) create mode 100644 rt/tst-timer6.c delete mode 100644 sysdeps/unix/sysv/linux/timer_routines.c diff --git a/nptl/descr.h b/nptl/descr.h index ada6867a19..07eedda067 100644 --- a/nptl/descr.h +++ b/nptl/descr.h @@ -413,6 +413,9 @@ struct pthread /* getrandom vDSO per-thread opaque state. */ void *getrandom_buf; + /* POSIX per-process timer. */ + int timerid; + /* Amount of end padding, if any, in this structure. This definition relies on getrandom_buf being last. */ #define PTHREAD_STRUCT_END_PADDING \ diff --git a/rt/Makefile b/rt/Makefile index 8880e25b64..bdda9dd660 100644 --- a/rt/Makefile +++ b/rt/Makefile @@ -79,7 +79,8 @@ tests := tst-shm tst-timer tst-timer2 \ tst-cpuclock2 tst-cputimer1 tst-cputimer2 tst-cputimer3 \ tst-clock_nanosleep2 \ tst-shm-cancel \ - tst-mqueue10 + tst-mqueue10 \ + tst-timer6 tests-internal := tst-timer-sigmask tests-time64 := \ @@ -101,6 +102,7 @@ include ../Rules CFLAGS-aio_suspend.c += -fexceptions CFLAGS-mq_timedreceive.c += -fexceptions -fasynchronous-unwind-tables CFLAGS-mq_timedsend.c += -fexceptions -fasynchronous-unwind-tables +CFLAGS-timer_create.c += -fexceptions -fasynchronous-unwind-tables # Exclude fortified routines from being built with _FORTIFY_SOURCE routines_no_fortify += \ diff --git a/rt/tst-timer-sigmask.c b/rt/tst-timer-sigmask.c index d8a576bba7..61b7927863 100644 --- a/rt/tst-timer-sigmask.c +++ b/rt/tst-timer-sigmask.c @@ -39,12 +39,9 @@ thread_handler (union sigval sv) for (int sig = 1; sig < NSIG; sig++) { /* POSIX timers threads created to handle SIGEV_THREAD block all - signals except SIGKILL, SIGSTOP and glibc internals ones. */ + signals except SIGKILL, SIGSTOP, and SIGSETXID. */ if (sigismember (&ss, sig)) - { - TEST_VERIFY (sig != SIGKILL && sig != SIGSTOP); - TEST_VERIFY (!is_internal_signal (sig)); - } + TEST_VERIFY (sig != SIGKILL && sig != SIGSTOP && sig != SIGSETXID); if (test_verbose && sigismember (&ss, sig)) printf ("%d, ", sig); } diff --git a/rt/tst-timer6.c b/rt/tst-timer6.c new file mode 100644 index 0000000000..d0f3b030b6 --- /dev/null +++ b/rt/tst-timer6.c @@ -0,0 +1,79 @@ +/* Check re-use timer id for SIGEV_THREAD (BZ 32833) + Copyright (C) 2025 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; see the file COPYING.LIB. If + not, see . */ + +#include +#include +#include + +/* The test depends of the system load and scheduler pressure, so the + number of iteration is arbitrary to not take too much time. */ +enum { niters = 1<<13 }; + +static void +on_good_timer (union sigval sv) +{ +} + +static void +on_bad_timer (union sigval sv) +{ + FAIL_EXIT1 ("triggered bad timer"); +} + +static int +do_test (void) +{ + struct itimerspec its_long = {. it_value = { .tv_sec = 180 } }; + struct itimerspec its_short = { .it_value = { .tv_nsec = 1000 } }; + struct itimerspec its_zero = { .it_interval = { .tv_sec = 0} }; + + struct sigevent ev_short = + { + .sigev_notify = SIGEV_THREAD, + .sigev_notify_function = on_good_timer, + }; + + struct sigevent ev_long = + { + .sigev_notify = SIGEV_THREAD, + .sigev_notify_function = on_bad_timer, + }; + + for (int which = 0; which < niters; which++) + { + struct sigevent * ev = which & 0x1 ? &ev_short : &ev_long; + struct itimerspec * its = which & 0x1? &its_short : &its_long; + + timer_t timerid; + if (timer_create (CLOCK_REALTIME, ev, &timerid) == -1) + FAIL_EXIT1 ("timer_create: %m"); + + if (timer_settime (timerid, 0, its, NULL) == -1) + FAIL_EXIT1 ("timer_settime: %m"); + + if (timer_settime (timerid, 0, &its_zero, NULL) == -1) + FAIL_EXIT1 ("timer_settime: %m"); + + if (timer_delete (timerid) == -1) + FAIL_EXIT1 ("time_delete: %m"); + } + + return 0; +} + +#include diff --git a/sysdeps/nptl/Makefile b/sysdeps/nptl/Makefile index c6e15d2351..12b7cb5bc2 100644 --- a/sysdeps/nptl/Makefile +++ b/sysdeps/nptl/Makefile @@ -16,8 +16,6 @@ # . ifeq ($(subdir),rt) -sysdep_routines += timer_routines - tests += tst-mqueue8x CFLAGS-tst-mqueue8x.c += -fexceptions endif diff --git a/sysdeps/nptl/fork.h b/sysdeps/nptl/fork.h index c7b4a195c1..b241ffaffa 100644 --- a/sysdeps/nptl/fork.h +++ b/sysdeps/nptl/fork.h @@ -20,7 +20,6 @@ #define _FORK_H #include -#include #include #include #include @@ -46,7 +45,6 @@ fork_system_setup_after_fork (void) __default_pthread_attr_lock = LLL_LOCK_INITIALIZER; call_function_static_weak (__mq_notify_fork_subprocess); - call_function_static_weak (__timer_fork_subprocess); call_function_static_weak (__getrandom_fork_subprocess); } diff --git a/sysdeps/unix/sysv/linux/internal-signals.h b/sysdeps/unix/sysv/linux/internal-signals.h index ecb00f5f3c..13b840ca08 100644 --- a/sysdeps/unix/sysv/linux/internal-signals.h +++ b/sysdeps/unix/sysv/linux/internal-signals.h @@ -108,12 +108,4 @@ static const sigset_t sigtimer_set = { } }; -/* Unblock only SIGTIMER. */ -static inline void -signal_unblock_sigtimer (void) -{ - INTERNAL_SYSCALL_CALL (rt_sigprocmask, SIG_UNBLOCK, &sigtimer_set, NULL, - __NSIG_BYTES); -} - #endif diff --git a/sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h b/sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h index bea1e0e62d..eda53be167 100644 --- a/sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h +++ b/sysdeps/unix/sysv/linux/kernel-posix-cpu-timers.h @@ -8,6 +8,8 @@ - A clockid is invalid if bits 2, 1, and 0 are all set. */ +#include + #define CPUCLOCK_PID(clock) ((pid_t) ~((clock) >> 3)) #define CPUCLOCK_PERTHREAD(clock) \ (((clock) & (clockid_t) CPUCLOCK_PERTHREAD_MASK) != 0) diff --git a/sysdeps/unix/sysv/linux/kernel-posix-timers.h b/sysdeps/unix/sysv/linux/kernel-posix-timers.h index 3000953754..046cd3c993 100644 --- a/sysdeps/unix/sysv/linux/kernel-posix-timers.h +++ b/sysdeps/unix/sysv/linux/kernel-posix-timers.h @@ -19,29 +19,7 @@ #include #include #include - - -/* Nonzero if the system calls are not available. */ -extern int __no_posix_timers attribute_hidden; - -/* Callback to start helper thread. */ -extern void __timer_start_helper_thread (void) attribute_hidden; - -/* Control variable for helper thread creation. */ -extern pthread_once_t __timer_helper_once attribute_hidden; - -/* Called from fork so that the new subprocess re-creates the - notification thread if necessary. */ -void __timer_fork_subprocess (void) attribute_hidden; - -/* TID of the helper thread. */ -extern pid_t __timer_helper_tid attribute_hidden; - -/* List of active SIGEV_THREAD timers. */ -extern struct timer *__timer_active_sigev_thread attribute_hidden; - -/* Lock for __timer_active_sigev_thread. */ -extern pthread_mutex_t __timer_active_sigev_thread_lock attribute_hidden; +#include extern __typeof (timer_create) __timer_create; libc_hidden_proto (__timer_create) @@ -53,25 +31,12 @@ libc_hidden_proto (__timer_getoverrun) /* Type of timers in the kernel. */ typedef int kernel_timer_t; -/* Internal representation of SIGEV_THREAD timer. */ -struct timer -{ - kernel_timer_t ktimerid; - - void (*thrfunc) (sigval_t); - sigval_t sival; - pthread_attr_t attr; - - /* Next element in list of active SIGEV_THREAD timers. */ - struct timer *next; -}; - - /* For !SIGEV_THREAD, the resulting 'timer_t' is the returned kernel timer - identifier (kernel_timer_t), while for SIGEV_THREAD it uses the fact malloc - returns at least _Alignof (max_align_t) pointers plus that valid - kernel_timer_t are always positive to set the MSB bit of the returned - 'timer_t' to indicate the timer handles a SIGEV_THREAD. */ + identifier (kernel_timer_t), while for SIGEV_THREAD it assumes the + pthread_t at least 8-bytes aligned. + + For SIGEV_THREAD, the MSB bit (INT_MAX) is used on timer_delete to + signal the helper thread to stop and issue the timer_delete syscall. */ static inline timer_t kernel_timer_to_timerid (kernel_timer_t ktimerid) @@ -80,7 +45,7 @@ kernel_timer_to_timerid (kernel_timer_t ktimerid) } static inline timer_t -timer_to_timerid (struct timer *ptr) +pthread_to_timerid (pthread_t ptr) { return (timer_t) (INTPTR_MIN | (uintptr_t) ptr >> 1); } @@ -91,19 +56,33 @@ timer_is_sigev_thread (timer_t timerid) return (intptr_t) timerid < 0; } -static inline struct timer * -timerid_to_timer (timer_t timerid) +static inline struct pthread * +timerid_to_pthread (timer_t timerid) { - return (struct timer *)((uintptr_t) timerid << 1); + return (struct pthread *)((uintptr_t) timerid << 1); } static inline kernel_timer_t timerid_to_kernel_timer (timer_t timerid) { if (timer_is_sigev_thread (timerid)) - return timerid_to_timer (timerid)->ktimerid; - else - return (kernel_timer_t) ((uintptr_t) timerid); + { + struct pthread *pthr = timerid_to_pthread (timerid); + return pthr->timerid & INT_MAX; + } + return (uintptr_t) timerid; +} + +static inline void +timerid_signal_delete (kernel_timer_t *timerid) +{ + atomic_fetch_or_relaxed (timerid, INT_MIN); +} + +static inline kernel_timer_t +timerid_clear (kernel_timer_t timerid) +{ + return timerid & INT_MAX; } /* New targets use int instead of timer_t. The difference only diff --git a/sysdeps/unix/sysv/linux/timer_create.c b/sysdeps/unix/sysv/linux/timer_create.c index ca377a69f4..8ec71254a7 100644 --- a/sysdeps/unix/sysv/linux/timer_create.c +++ b/sysdeps/unix/sysv/linux/timer_create.c @@ -15,46 +15,196 @@ License along with the GNU C Library; see the file COPYING.LIB. If not, see . */ -#include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include -#include "kernel-posix-timers.h" -#include "kernel-posix-cpu-timers.h" #include +struct timer_helper_thread_args_t +{ + /* The barrier is used to synchronize the arguments copy from timer_create + and the SIGEV_THREAD thread and to instruct the thread to exit if the + timer_create syscall fails. */ + pthread_barrier_t b; + struct sigevent *evp; +}; + +struct cleanup_args_t +{ + struct pthread_unwind_buf *cleanup_jmp_buf; + jmp_buf jb; +}; + +/* Reset internal thread state if the callback issues pthread_exit. It avoids + recreating the thread and having possible unreported missed events due + thread creation failure. */ +static void +timer_helper_thread_cleanup (void *arg) +{ + struct pthread *self = THREAD_SELF; + + /* Call destructors for the thread_local TLS variables. */ + call_function_static_weak (__call_tls_dtors); + + /* Run the destructor for the thread-local data. */ + __nptl_deallocate_tsd (); + + /* Clean up any state libc stored in thread-local variables. */ + __libc_thread_freeres (); + + /* Reset internal TCB state. */ + struct cleanup_args_t *args = arg; + self->cleanup_jmp_buf = args->cleanup_jmp_buf; + self->cleanup_jmp_buf->priv.data.prev = NULL; + self->cleanup_jmp_buf->priv.data.cleanup = NULL; + self->cleanup_jmp_buf->priv.data.canceltype = 0; + self->cleanup = NULL; + self->exc = (struct _Unwind_Exception) { 0 }; + self->cancelhandling = 0; + self->nextevent = NULL; + + /* Re-initialize the TLS. */ + _dl_allocate_tls_init (TLS_TPADJ (self), true); + + /* Reset to the expected initial signal mask. */ + internal_sigset_t ss; + internal_sigfillset (&ss); + internal_sigdelset (&ss, SIGSETXID); + internal_sigprocmask (SIG_SETMASK, &ss, NULL); + + /* There is no need to perform any additional cleanup by the frames. */ + struct __jmp_buf_tag *env = args->jb; + __longjmp (env[0].__jmpbuf, 1); +} + +static void * +timer_helper_thread (void *arg) +{ + struct pthread *self = THREAD_SELF; + struct timer_helper_thread_args_t *args = arg; + struct cleanup_args_t clargs = { + .cleanup_jmp_buf = self->cleanup_jmp_buf + }; + + void (*thrfunc) (sigval_t) = args->evp->sigev_notify_function; + sigval_t sival = args->evp->sigev_value; + + __pthread_barrier_wait (&args->b); + /* timer_create syscall failed. */ + if (self->exiting) + return 0; + + while (1) + { + siginfo_t si; + while (__sigwaitinfo (&sigtimer_set, &si) < 0); + + if (si.si_code == SI_TIMER && !setjmp (clargs.jb)) + { + pthread_cleanup_push (timer_helper_thread_cleanup, &clargs); + thrfunc (sival); + pthread_cleanup_pop (0); + } + + /* timer_delete will set the MSB and signal the thread. */ + if (self->timerid < 0) + break; + } + + /* Clear the MSB bit set by timer_delete. */ + INTERNAL_SYSCALL_CALL (timer_delete, timerid_clear (self->timerid)); + + return NULL; +} + +static int +timer_create_sigev_thread (clockid_t clockid, struct sigevent *evp, + timer_t *timerid, pthread_attr_t *attr) +{ + /* Block all signals in the helper thread but SIGSETXID. */ + sigset_t ss; + __sigfillset (&ss); + __sigdelset (&ss, SIGSETXID); + if (__pthread_attr_setsigmask_internal (attr, &ss) < 0) + return -1; + + struct timer_helper_thread_args_t args; + __pthread_barrier_init (&args.b, NULL, 2); + args.evp = evp; + + pthread_t th; + int r = __pthread_create (&th, attr, timer_helper_thread, &args); + if (r != 0) + { + __set_errno (r); + return -1; + } + + struct pthread *pthr = (struct pthread *)th; + struct sigevent kevp = + { + .sigev_value.sival_ptr = NULL, + .sigev_signo = SIGTIMER, + .sigev_notify = SIGEV_THREAD_ID, + ._sigev_un = { ._tid = pthr->tid }, + }; + + kernel_timer_t ktimerid; + if (INLINE_SYSCALL_CALL (timer_create, clockid, &kevp, &ktimerid) < 0) + { + ktimerid = -1; + /* On timer creation failure we need to signal the helper thread to + exit and we can not use the an negative timerid value after the + ptherad_barrier_wait because we can not distinguish between + a timer creation failure and request to delete a timer if it happens + to arrive quickly (for where two timers are create in sequence, + where first succeeds). + + We re-use the 'exiting' member to signal the failure, it is set only + at pthread_create to avoid pthread_kill to send further signals. + Since the thread should not be user-visible, signal are only sent + during timer_delete. */ + pthr->exiting = true; + } + pthr->timerid = ktimerid; + /* Signal the thread to continue execution after it copies the arguments + or exit if the timer can not be created. */ + __pthread_barrier_wait (&args.b); + + if (ktimerid < 0) + return -1; + + *timerid = pthread_to_timerid (th); + + return 0; +} + int ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid) { - { - clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID - ? PROCESS_CLOCK - : clock_id == CLOCK_THREAD_CPUTIME_ID - ? THREAD_CLOCK - : clock_id); + clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID + ? PROCESS_CLOCK + : clock_id == CLOCK_THREAD_CPUTIME_ID + ? THREAD_CLOCK + : clock_id); - /* If the user wants notification via a thread we need to handle - this special. */ - if (evp == NULL - || __builtin_expect (evp->sigev_notify != SIGEV_THREAD, 1)) + switch (evp != NULL ? evp->sigev_notify : SIGEV_SIGNAL) + { + case SIGEV_NONE: + case SIGEV_SIGNAL: + case SIGEV_THREAD_ID: { - struct sigevent local_evp; - + struct sigevent kevp; if (evp == NULL) { - /* The kernel has to pass up the timer ID which is a - userlevel object. Therefore we cannot leave it up to - the kernel to determine it. */ - local_evp.sigev_notify = SIGEV_SIGNAL; - local_evp.sigev_signo = SIGALRM; - local_evp.sigev_value.sival_ptr = NULL; - - evp = &local_evp; + kevp.sigev_notify = SIGEV_SIGNAL; + kevp.sigev_signo = SIGALRM; + kevp.sigev_value.sival_ptr = NULL; + evp = &kevp; } kernel_timer_t ktimerid; @@ -64,75 +214,28 @@ ___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid) *timerid = kernel_timer_to_timerid (ktimerid); } - else + break; + case SIGEV_THREAD: { - /* Create the helper thread. */ - __pthread_once (&__timer_helper_once, __timer_start_helper_thread); - if (__timer_helper_tid == 0) - { - /* No resources to start the helper thread. */ - __set_errno (EAGAIN); - return -1; - } - - struct timer *newp = malloc (sizeof (struct timer)); - if (newp == NULL) - return -1; - - /* Copy the thread parameters the user provided. */ - newp->sival = evp->sigev_value; - newp->thrfunc = evp->sigev_notify_function; - - /* We cannot simply copy the thread attributes since the - implementation might keep internal information for - each instance. */ - __pthread_attr_init (&newp->attr); + pthread_attr_t attr; if (evp->sigev_notify_attributes != NULL) - { - struct pthread_attr *nattr; - struct pthread_attr *oattr; + __pthread_attr_copy (&attr, evp->sigev_notify_attributes); + else + __pthread_attr_init (&attr); + __pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED); - nattr = (struct pthread_attr *) &newp->attr; - oattr = (struct pthread_attr *) evp->sigev_notify_attributes; + int r = timer_create_sigev_thread (syscall_clockid, evp, timerid, + &attr); - nattr->schedparam = oattr->schedparam; - nattr->schedpolicy = oattr->schedpolicy; - nattr->flags = oattr->flags; - nattr->guardsize = oattr->guardsize; - nattr->stackaddr = oattr->stackaddr; - nattr->stacksize = oattr->stacksize; - } + if (&attr != evp->sigev_notify_attributes) + __pthread_attr_destroy (&attr); - /* In any case set the detach flag. */ - __pthread_attr_setdetachstate (&newp->attr, PTHREAD_CREATE_DETACHED); - - /* Create the event structure for the kernel timer. */ - struct sigevent sev = - { .sigev_value.sival_ptr = newp, - .sigev_signo = SIGTIMER, - .sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID, - ._sigev_un = { ._pad = { [0] = __timer_helper_tid } } }; - - /* Create the timer. */ - int res; - res = INTERNAL_SYSCALL_CALL (timer_create, syscall_clockid, &sev, - &newp->ktimerid); - if (INTERNAL_SYSCALL_ERROR_P (res)) - { - free (newp); - __set_errno (INTERNAL_SYSCALL_ERRNO (res)); - return -1; - } - - /* Add to the queue of active timers with thread delivery. */ - __pthread_mutex_lock (&__timer_active_sigev_thread_lock); - newp->next = __timer_active_sigev_thread; - __timer_active_sigev_thread = newp; - __pthread_mutex_unlock (&__timer_active_sigev_thread_lock); - - *timerid = timer_to_timerid (newp); + return r; } - } + default: + __set_errno (EINVAL); + return -1; + } return 0; } diff --git a/sysdeps/unix/sysv/linux/timer_delete.c b/sysdeps/unix/sysv/linux/timer_delete.c index 69f26b266b..0fd3cb15f1 100644 --- a/sysdeps/unix/sysv/linux/timer_delete.c +++ b/sysdeps/unix/sysv/linux/timer_delete.c @@ -15,10 +15,8 @@ License along with the GNU C Library; see the file COPYING.LIB. If not, see . */ -#include -#include +#include #include -#include #include "kernel-posix-timers.h" #include #include @@ -26,42 +24,20 @@ int ___timer_delete (timer_t timerid) { - kernel_timer_t ktimerid = timerid_to_kernel_timer (timerid); - int res = INLINE_SYSCALL_CALL (timer_delete, ktimerid); - - if (res == 0) + if (timer_is_sigev_thread (timerid)) { - if (timer_is_sigev_thread (timerid)) - { - struct timer *kt = timerid_to_timer (timerid); - - /* Remove the timer from the list. */ - __pthread_mutex_lock (&__timer_active_sigev_thread_lock); - if (__timer_active_sigev_thread == kt) - __timer_active_sigev_thread = kt->next; - else - { - struct timer *prevp = __timer_active_sigev_thread; - while (prevp->next != NULL) - if (prevp->next == kt) - { - prevp->next = kt->next; - break; - } - else - prevp = prevp->next; - } - __pthread_mutex_unlock (&__timer_active_sigev_thread_lock); - - free (kt); - } + struct pthread *th = timerid_to_pthread (timerid); + /* The helper thread itself will be responsible to call the + timer_delete syscall. */ + timerid_signal_delete (&th->timerid); + /* We can send the signal directly instead of through + __pthread_kill_internal because the thread is not user-visible + and it blocks SIGTIMER. */ + INTERNAL_SYSCALL_CALL (tgkill, __getpid (), th->tid, SIGTIMER); return 0; } - - /* The kernel timer is not known or something else bad happened. - Return the error. */ - return -1; + return INLINE_SYSCALL_CALL (timer_delete, timerid); } versioned_symbol (libc, ___timer_delete, timer_delete, GLIBC_2_34); libc_hidden_ver (___timer_delete, __timer_delete) diff --git a/sysdeps/unix/sysv/linux/timer_routines.c b/sysdeps/unix/sysv/linux/timer_routines.c deleted file mode 100644 index b2affaac91..0000000000 --- a/sysdeps/unix/sysv/linux/timer_routines.c +++ /dev/null @@ -1,154 +0,0 @@ -/* Copyright (C) 2003-2025 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; see the file COPYING.LIB. If - not, see . */ - -#include -#include -#include -#include -#include -#include -#include "kernel-posix-timers.h" - - -/* List of active SIGEV_THREAD timers. */ -struct timer *__timer_active_sigev_thread; - -/* Lock for _timer_active_sigev_thread. */ -pthread_mutex_t __timer_active_sigev_thread_lock = PTHREAD_MUTEX_INITIALIZER; - -struct thread_start_data -{ - void (*thrfunc) (sigval_t); - sigval_t sival; -}; - - -/* Helper thread to call the user-provided function. */ -static void * -timer_sigev_thread (void *arg) -{ - signal_unblock_sigtimer (); - - struct thread_start_data *td = (struct thread_start_data *) arg; - void (*thrfunc) (sigval_t) = td->thrfunc; - sigval_t sival = td->sival; - - /* The TD object was allocated in timer_helper_thread. */ - free (td); - - /* Call the user-provided function. */ - thrfunc (sival); - - return NULL; -} - - -/* Helper function to support starting threads for SIGEV_THREAD. */ -static _Noreturn void * -timer_helper_thread (void *arg) -{ - /* Endless loop of waiting for signals. The loop is only ended when - the thread is canceled. */ - while (1) - { - siginfo_t si; - - while (__sigwaitinfo (&sigtimer_set, &si) < 0); - if (si.si_code == SI_TIMER) - { - struct timer *tk = (struct timer *) si.si_ptr; - - /* Check the timer is still used and will not go away - while we are reading the values here. */ - __pthread_mutex_lock (&__timer_active_sigev_thread_lock); - - struct timer *runp = __timer_active_sigev_thread; - while (runp != NULL) - if (runp == tk) - break; - else - runp = runp->next; - - if (runp != NULL) - { - struct thread_start_data *td = malloc (sizeof (*td)); - - /* There is not much we can do if the allocation fails. */ - if (td != NULL) - { - /* This is the signal we are waiting for. */ - td->thrfunc = tk->thrfunc; - td->sival = tk->sival; - - pthread_t th; - __pthread_create (&th, &tk->attr, timer_sigev_thread, td); - } - } - - __pthread_mutex_unlock (&__timer_active_sigev_thread_lock); - } - } -} - - -/* Control variable for helper thread creation. */ -pthread_once_t __timer_helper_once = PTHREAD_ONCE_INIT; - - -/* TID of the helper thread. */ -pid_t __timer_helper_tid; - - -/* Reset variables so that after a fork a new helper thread gets started. */ -void -__timer_fork_subprocess (void) -{ - __timer_helper_once = PTHREAD_ONCE_INIT; - __timer_helper_tid = 0; -} - - -void -__timer_start_helper_thread (void) -{ - /* The helper thread needs only very little resources - and should go away automatically when canceled. */ - pthread_attr_t attr; - __pthread_attr_init (&attr); - __pthread_attr_setstacksize (&attr, __pthread_get_minstack (&attr)); - - /* Block all signals in the helper thread but SIGSETXID. */ - sigset_t ss; - __sigfillset (&ss); - __sigdelset (&ss, SIGSETXID); - int res = __pthread_attr_setsigmask_internal (&attr, &ss); - if (res != 0) - { - __pthread_attr_destroy (&attr); - return; - } - - /* Create the helper thread for this timer. */ - pthread_t th; - res = __pthread_create (&th, &attr, timer_helper_thread, NULL); - if (res == 0) - /* We managed to start the helper thread. */ - __timer_helper_tid = ((struct pthread *) th)->tid; - - /* No need for the attribute anymore. */ - __pthread_attr_destroy (&attr); -}