1 | /* Copyright (C) 2003-2023 Free Software Foundation, Inc. |
2 | This file is part of the GNU C Library. |
3 | |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU Lesser General Public License as |
6 | published by the Free Software Foundation; either version 2.1 of the |
7 | License, or (at your option) any later version. |
8 | |
9 | The GNU C Library is distributed in the hope that it will be useful, |
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | Lesser General Public License for more details. |
13 | |
14 | You should have received a copy of the GNU Lesser General Public |
15 | License along with the GNU C Library; see the file COPYING.LIB. If |
16 | not, see <https://www.gnu.org/licenses/>. */ |
17 | |
18 | #include <errno.h> |
19 | #include <setjmp.h> |
20 | #include <signal.h> |
21 | #include <stdbool.h> |
22 | #include <sysdep-cancel.h> |
23 | #include <pthreadP.h> |
24 | #include "kernel-posix-timers.h" |
25 | |
26 | |
27 | /* List of active SIGEV_THREAD timers. */ |
28 | struct timer *__timer_active_sigev_thread; |
29 | |
30 | /* Lock for _timer_active_sigev_thread. */ |
31 | pthread_mutex_t __timer_active_sigev_thread_lock = PTHREAD_MUTEX_INITIALIZER; |
32 | |
33 | struct thread_start_data |
34 | { |
35 | void (*thrfunc) (sigval_t); |
36 | sigval_t sival; |
37 | }; |
38 | |
39 | |
40 | /* Helper thread to call the user-provided function. */ |
41 | static void * |
42 | timer_sigev_thread (void *arg) |
43 | { |
44 | signal_unblock_sigtimer (); |
45 | |
46 | struct thread_start_data *td = (struct thread_start_data *) arg; |
47 | void (*thrfunc) (sigval_t) = td->thrfunc; |
48 | sigval_t sival = td->sival; |
49 | |
50 | /* The TD object was allocated in timer_helper_thread. */ |
51 | free (td); |
52 | |
53 | /* Call the user-provided function. */ |
54 | thrfunc (sival); |
55 | |
56 | return NULL; |
57 | } |
58 | |
59 | |
60 | /* Helper function to support starting threads for SIGEV_THREAD. */ |
61 | static _Noreturn void * |
62 | timer_helper_thread (void *arg) |
63 | { |
64 | /* Endless loop of waiting for signals. The loop is only ended when |
65 | the thread is canceled. */ |
66 | while (1) |
67 | { |
68 | siginfo_t si; |
69 | |
70 | while (__sigwaitinfo (&sigtimer_set, &si) < 0); |
71 | if (si.si_code == SI_TIMER) |
72 | { |
73 | struct timer *tk = (struct timer *) si.si_ptr; |
74 | |
75 | /* Check the timer is still used and will not go away |
76 | while we are reading the values here. */ |
77 | __pthread_mutex_lock (&__timer_active_sigev_thread_lock); |
78 | |
79 | struct timer *runp = __timer_active_sigev_thread; |
80 | while (runp != NULL) |
81 | if (runp == tk) |
82 | break; |
83 | else |
84 | runp = runp->next; |
85 | |
86 | if (runp != NULL) |
87 | { |
88 | struct thread_start_data *td = malloc (sizeof (*td)); |
89 | |
90 | /* There is not much we can do if the allocation fails. */ |
91 | if (td != NULL) |
92 | { |
93 | /* This is the signal we are waiting for. */ |
94 | td->thrfunc = tk->thrfunc; |
95 | td->sival = tk->sival; |
96 | |
97 | pthread_t th; |
98 | __pthread_create (&th, &tk->attr, timer_sigev_thread, td); |
99 | } |
100 | } |
101 | |
102 | __pthread_mutex_unlock (&__timer_active_sigev_thread_lock); |
103 | } |
104 | } |
105 | } |
106 | |
107 | |
108 | /* Control variable for helper thread creation. */ |
109 | pthread_once_t __timer_helper_once = PTHREAD_ONCE_INIT; |
110 | |
111 | |
112 | /* TID of the helper thread. */ |
113 | pid_t __timer_helper_tid; |
114 | |
115 | |
116 | /* Reset variables so that after a fork a new helper thread gets started. */ |
117 | void |
118 | __timer_fork_subprocess (void) |
119 | { |
120 | __timer_helper_once = PTHREAD_ONCE_INIT; |
121 | __timer_helper_tid = 0; |
122 | } |
123 | |
124 | |
125 | void |
126 | __timer_start_helper_thread (void) |
127 | { |
128 | /* The helper thread needs only very little resources |
129 | and should go away automatically when canceled. */ |
130 | pthread_attr_t attr; |
131 | __pthread_attr_init (&attr); |
132 | __pthread_attr_setstacksize (&attr, __pthread_get_minstack (&attr)); |
133 | |
134 | /* Block all signals in the helper thread but SIGSETXID. */ |
135 | sigset_t ss; |
136 | __sigfillset (&ss); |
137 | __sigdelset (&ss, SIGSETXID); |
138 | int res = __pthread_attr_setsigmask_internal (&attr, &ss); |
139 | if (res != 0) |
140 | { |
141 | __pthread_attr_destroy (&attr); |
142 | return; |
143 | } |
144 | |
145 | /* Create the helper thread for this timer. */ |
146 | pthread_t th; |
147 | res = __pthread_create (&th, &attr, timer_helper_thread, NULL); |
148 | if (res == 0) |
149 | /* We managed to start the helper thread. */ |
150 | __timer_helper_tid = ((struct pthread *) th)->tid; |
151 | |
152 | /* No need for the attribute anymore. */ |
153 | __pthread_attr_destroy (&attr); |
154 | } |
155 | |