/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- timer_create_reset_on_fork
- timer_create_sigev_thread
- timer_sigev_thread
- timer_helper_thread
- start_helper_thread
1 /* Copyright (C) 2003-2013 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Ulrich Drepper <drepper@redhat.com>, 2003.
4
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
9
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If
17 not, see <http://www.gnu.org/licenses/>. */
18
19 #include <errno.h>
20 #include <pthread.h>
21 #include <semaphore.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <unistd.h>
27 #include <sys/errno.h>
28 #include <sys/syscall.h>
29
30 #include "timerwrappers.h"
31
32 #include "jalloc.h"
33 #include "jassert.h"
34
35 // As defined in libc.
36 #define SIGCANCEL SIGRTMIN
37 #define SIGTIMER SIGCANCEL
38
39 /* Internal representation of timer. */
40 struct timer
41 {
42 /* Notification mechanism. */
43 int sigev_notify;
44
45 /* Parameters for the thread to be started for SIGEV_THREAD. */
46 void (*thrfunc) (sigval_t);
47 sigval_t sival;
48 pthread_attr_t attr;
49
50 /* Next element in list of active SIGEV_THREAD timers. */
51 struct timer *next;
52 };
53
54 struct thread_start_data
55 {
56 void (*thrfunc) (sigval_t);
57 sigval_t sival;
58 };
59
60 /* List of active SIGEV_THREAD timers. */
61 struct timer *active_timer_sigev_thread;
62
63 /* Lock for the active_timer_sigev_thread. */
64 pthread_mutex_t active_timer_sigev_thread_lock = PTHREAD_MUTEX_INITIALIZER;
65
66 /* Control variable for helper thread creation. */
67 static pthread_once_t helper_once;
68
69 /* TID of the helper thread. */
70 static pid_t helper_tid = 0;
71 static sem_t helper_notification;
72
73 static void *timer_helper_thread (void *arg);
74 static void start_helper_thread (void);
75
76 /* Reset variables so that after a fork a new helper thread gets started. */
77 static void timer_create_reset_on_fork(void)
78 {
79 helper_once = PTHREAD_ONCE_INIT;
80 helper_tid = 0;
81 }
82
83 LIB_PRIVATE
84 int timer_create_sigev_thread(clockid_t clock_id,
85 struct sigevent *evp,
86 timer_t *timerid,
87 struct sigevent *sevOut)
88 {
89 /* If the user wants notification via a thread we need to handle
90 this special. */
91 JASSERT(evp == NULL || evp->sigev_notify == SIGEV_THREAD);
92
93 /* Create the helper thread. */
94 pthread_once (&helper_once, start_helper_thread);
95 sem_wait(&helper_notification);
96 if (helper_tid == 0) {
97 /* No resources to start the helper thread. */
98 errno = EAGAIN;
99 return -1;
100 }
101
102 struct timer *newp;
103 newp = (struct timer *) JALLOC_MALLOC(sizeof (struct timer));
104 if (newp == NULL) {
105 return -1;
106 }
107
108 /* Copy the thread parameters the user provided. */
109 newp->sival = evp->sigev_value;
110 newp->thrfunc = evp->sigev_notify_function;
111 newp->sigev_notify = SIGEV_THREAD;
112
113 /* We cannot simply copy the thread attributes since the
114 implementation might keep internal information for
115 each instance. */
116 (void) pthread_attr_init (&newp->attr);
117
118 // TODO: Copy attributes from evp->sigev_notify_attributes to newp->attr.
119
120 /* In any case set the detach flag. */
121 (void) pthread_attr_setdetachstate (&newp->attr, PTHREAD_CREATE_DETACHED);
122
123 /* Create the event structure for the kernel timer. */
124 sevOut->sigev_value.sival_ptr = newp;
125 sevOut->sigev_signo = SIGTIMER;
126 sevOut->sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
127 sevOut->_sigev_un._tid = helper_tid;
128
129 /* Create the timer. */
130 int res = _real_timer_create(clock_id, sevOut, timerid);
131 if (res == 0) {
132 /* Add to the queue of active timers with thread delivery. */
133 pthread_mutex_lock (&active_timer_sigev_thread_lock);
134 newp->next = active_timer_sigev_thread;
135 active_timer_sigev_thread = newp;
136 pthread_mutex_unlock (&active_timer_sigev_thread_lock);
137 return 0;
138 }
139
140 /* Free the resources. */
141 JALLOC_FREE(newp);
142 return -1;
143 }
144
145 /* Helper thread to call the user-provided function. */
146 static void *timer_sigev_thread (void *arg)
147 {
148 /* The parent thread has all signals blocked. This is a bit
149 surprising for user code, although valid. We unblock all
150 signals. */
151 sigset_t ss;
152 sigemptyset (&ss);
153 pthread_sigmask(SIG_SETMASK, &ss, NULL);
154
155 struct thread_start_data *td = (struct thread_start_data *) arg;
156
157 void (*thrfunc) (sigval_t) = td->thrfunc;
158 sigval_t sival = td->sival;
159
160 /* The TD object was allocated in timer_helper_thread. */
161 JALLOC_FREE(td);
162
163 /* Call the user-provided function. */
164 thrfunc (sival);
165
166 return NULL;
167 }
168
169
170 /* Helper function to support starting threads for SIGEV_THREAD. */
171 static void *timer_helper_thread (void *arg)
172 {
173 helper_tid = syscall(SYS_gettid);
174 sem_post(&helper_notification);
175
176 /* Wait for the SIGTIMER signal, allowing the setXid signal, and
177 none else. */
178 sigset_t ss;
179 sigemptyset (&ss);
180 sigaddset (&ss, SIGTIMER);
181
182 /* Endless loop of waiting for signals. The loop is only ended when
183 the thread is canceled. */
184 while (1) {
185 siginfo_t si;
186
187 /* sigwaitinfo cannot be used here, since it deletes
188 SIGCANCEL == SIGTIMER from the set. */
189
190 pthread_testcancel();
191 //int oldtype = LIBC_CANCEL_ASYNC ();
192
193 /* XXX The size argument hopefully will have to be changed to the
194 real size of the user-level sigset_t. */
195 int result = sigtimedwait(&ss, &si, NULL);
196
197 //LIBC_CANCEL_RESET (oldtype);
198
199 if (result > 0) {
200 if (si.si_code == SI_TIMER) {
201 struct timer *tk = (struct timer *) si.si_ptr;
202
203 /* Check the timer is still used and will not go away
204 while we are reading the values here. */
205 pthread_mutex_lock (&active_timer_sigev_thread_lock);
206
207 struct timer *runp = active_timer_sigev_thread;
208 while (runp != NULL)
209 if (runp == tk)
210 break;
211 else
212 runp = runp->next;
213
214 if (runp != NULL) {
215 struct thread_start_data *td =
216 (struct thread_start_data*) JALLOC_MALLOC(sizeof (*td));
217
218 /* There is not much we can do if the allocation fails. */
219 if (td != NULL) {
220 /* This is the signal we are waiting for. */
221 td->thrfunc = tk->thrfunc;
222 td->sival = tk->sival;
223
224 pthread_t th;
225 (void) pthread_create (&th, &tk->attr, timer_sigev_thread, td);
226 }
227 }
228
229 pthread_mutex_unlock (&active_timer_sigev_thread_lock);
230 }
231 else if (si.si_code == SI_TKILL)
232 /* The thread is canceled. */
233 pthread_exit (NULL);
234 }
235 }
236 }
237
238
239 static void start_helper_thread (void)
240 {
241 sem_init(&helper_notification, 0, 0);
242 /* The helper thread needs only very little resources
243 and should go away automatically when canceled. */
244 pthread_attr_t attr;
245 (void) pthread_attr_init (&attr);
246 (void) pthread_attr_setstacksize (&attr, 2 * 1024 * 1024);
247
248 /* Block all signals in the helper thread but SIGSETXID. To do this
249 thoroughly we temporarily have to block all signals here. The
250 helper can lose wakeups if SIGCANCEL is not blocked throughout,
251 but sigfillset omits it SIGSETXID. So, we add SIGCANCEL back
252 explicitly here. */
253 sigset_t ss;
254 sigset_t oss;
255 sigfillset (&ss);
256 sigaddset (&ss, SIGCANCEL);
257 sigprocmask(SIG_SETMASK, &ss, &oss);
258
259 /* Create the helper thread for this timer. */
260 pthread_t th;
261 int res = pthread_create (&th, &attr, timer_helper_thread, NULL);
262 JASSERT(res == 0);
263 if (res != 0) {
264 sem_post(&helper_notification);
265 }
266
267 /* Restore the signal mask. */
268 sigprocmask(SIG_SETMASK, &oss, NULL);
269
270 /* No need for the attribute anymore. */
271 (void) pthread_attr_destroy (&attr);
272
273 /* We have to make sure that after fork()ing a new helper thread can
274 be created. */
275 pthread_atfork (NULL, NULL, timer_create_reset_on_fork);
276 }