1/* Copyright (C) 2003-2021 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 <https://www.gnu.org/licenses/>. */
18
19#include <errno.h>
20#include <pthread.h>
21#include <signal.h>
22#include <stdlib.h>
23#include <string.h>
24#include <time.h>
25#include <sysdep.h>
26#include <internaltypes.h>
27#include <pthreadP.h>
28#include "kernel-posix-timers.h"
29#include "kernel-posix-cpu-timers.h"
30#include <shlib-compat.h>
31
32int
33___timer_create (clockid_t clock_id, struct sigevent *evp, timer_t *timerid)
34{
35 {
36 clockid_t syscall_clockid = (clock_id == CLOCK_PROCESS_CPUTIME_ID
37 ? MAKE_PROCESS_CPUCLOCK (0, CPUCLOCK_SCHED)
38 : clock_id == CLOCK_THREAD_CPUTIME_ID
39 ? MAKE_THREAD_CPUCLOCK (0, CPUCLOCK_SCHED)
40 : clock_id);
41
42 /* If the user wants notification via a thread we need to handle
43 this special. */
44 if (evp == NULL
45 || __builtin_expect (evp->sigev_notify != SIGEV_THREAD, 1))
46 {
47 struct sigevent local_evp;
48
49 if (evp == NULL)
50 {
51 /* The kernel has to pass up the timer ID which is a
52 userlevel object. Therefore we cannot leave it up to
53 the kernel to determine it. */
54 local_evp.sigev_notify = SIGEV_SIGNAL;
55 local_evp.sigev_signo = SIGALRM;
56 local_evp.sigev_value.sival_ptr = NULL;
57
58 evp = &local_evp;
59 }
60
61 kernel_timer_t ktimerid;
62 if (INLINE_SYSCALL_CALL (timer_create, syscall_clockid, evp,
63 &ktimerid) == -1)
64 return -1;
65
66 *timerid = kernel_timer_to_timerid (ktimerid);
67 }
68 else
69 {
70 /* Create the helper thread. */
71 __pthread_once (&__timer_helper_once, __timer_start_helper_thread);
72 if (__timer_helper_tid == 0)
73 {
74 /* No resources to start the helper thread. */
75 __set_errno (EAGAIN);
76 return -1;
77 }
78
79 struct timer *newp = malloc (sizeof (struct timer));
80 if (newp == NULL)
81 return -1;
82
83 /* Copy the thread parameters the user provided. */
84 newp->sival = evp->sigev_value;
85 newp->thrfunc = evp->sigev_notify_function;
86
87 /* We cannot simply copy the thread attributes since the
88 implementation might keep internal information for
89 each instance. */
90 __pthread_attr_init (&newp->attr);
91 if (evp->sigev_notify_attributes != NULL)
92 {
93 struct pthread_attr *nattr;
94 struct pthread_attr *oattr;
95
96 nattr = (struct pthread_attr *) &newp->attr;
97 oattr = (struct pthread_attr *) evp->sigev_notify_attributes;
98
99 nattr->schedparam = oattr->schedparam;
100 nattr->schedpolicy = oattr->schedpolicy;
101 nattr->flags = oattr->flags;
102 nattr->guardsize = oattr->guardsize;
103 nattr->stackaddr = oattr->stackaddr;
104 nattr->stacksize = oattr->stacksize;
105 }
106
107 /* In any case set the detach flag. */
108 __pthread_attr_setdetachstate (&newp->attr, PTHREAD_CREATE_DETACHED);
109
110 /* Create the event structure for the kernel timer. */
111 struct sigevent sev =
112 { .sigev_value.sival_ptr = newp,
113 .sigev_signo = SIGTIMER,
114 .sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID,
115 ._sigev_un = { ._pad = { [0] = __timer_helper_tid } } };
116
117 /* Create the timer. */
118 int res;
119 res = INTERNAL_SYSCALL_CALL (timer_create, syscall_clockid, &sev,
120 &newp->ktimerid);
121 if (INTERNAL_SYSCALL_ERROR_P (res))
122 {
123 free (newp);
124 __set_errno (INTERNAL_SYSCALL_ERRNO (res));
125 return -1;
126 }
127
128 /* Add to the queue of active timers with thread delivery. */
129 __pthread_mutex_lock (&__timer_active_sigev_thread_lock);
130 newp->next = __timer_active_sigev_thread;
131 __timer_active_sigev_thread = newp;
132 __pthread_mutex_unlock (&__timer_active_sigev_thread_lock);
133
134 *timerid = timer_to_timerid (newp);
135 }
136 }
137
138 return 0;
139}
140versioned_symbol (libc, ___timer_create, timer_create, GLIBC_2_34);
141libc_hidden_ver (___timer_create, __timer_create)
142
143#if TIMER_T_WAS_INT_COMPAT
144# if OTHER_SHLIB_COMPAT (librt, GLIBC_2_3_3, GLIBC_2_34)
145compat_symbol (librt, ___timer_create, timer_create, GLIBC_2_3_3);
146# endif
147
148# if OTHER_SHLIB_COMPAT (librt, GLIBC_2_2, GLIBC_2_3_3)
149timer_t __timer_compat_list[OLD_TIMER_MAX];
150
151int
152__timer_create_old (clockid_t clock_id, struct sigevent *evp, int *timerid)
153{
154 timer_t newp;
155
156 int res = __timer_create (clock_id, evp, &newp);
157 if (res == 0)
158 {
159 int i;
160 for (i = 0; i < OLD_TIMER_MAX; ++i)
161 if (__timer_compat_list[i] == NULL
162 && ! atomic_compare_and_exchange_bool_acq (&__timer_compat_list[i],
163 newp, NULL))
164 {
165 *timerid = i;
166 break;
167 }
168
169 if (__glibc_unlikely (i == OLD_TIMER_MAX))
170 {
171 /* No free slot. */
172 __timer_delete (newp);
173 __set_errno (EINVAL);
174 res = -1;
175 }
176 }
177
178 return res;
179}
180compat_symbol (librt, __timer_create_old, timer_create, GLIBC_2_2);
181# endif /* OTHER_SHLIB_COMPAT */
182
183#else /* !TIMER_T_WAS_INT_COMPAT */
184# if OTHER_SHLIB_COMPAT (librt, GLIBC_2_2, GLIBC_2_34)
185compat_symbol (librt, ___timer_create, timer_create, GLIBC_2_2);
186# endif
187#endif /* !TIMER_T_WAS_INT_COMPAT */
188