ca6ff6c7ef3722b431f4a68ef72b6e8f8ecfb6b4
[platform/upstream/glibc.git] / nptl / sysdeps / unix / sysv / linux / timer_create.c
1 /* Copyright (C) 2003,2004 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 not,
17    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18    Boston, MA 02111-1307, USA.  */
19
20 #include <errno.h>
21 #include <pthread.h>
22 #include <signal.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <time.h>
26 #include <sysdep.h>
27 #include <kernel-features.h>
28 #include <internaltypes.h>
29 #include <nptl/pthreadP.h>
30 #include "kernel-posix-timers.h"
31
32
33 #ifdef __NR_timer_create
34 # ifndef __ASSUME_POSIX_TIMERS
35 static int compat_timer_create (clockid_t clock_id, struct sigevent *evp,
36                                 timer_t *timerid);
37 #  define timer_create static compat_timer_create
38 #  include <nptl/sysdeps/pthread/timer_create.c>
39 #  undef timer_create
40
41 /* Nonzero if the system calls are not available.  */
42 int __no_posix_timers attribute_hidden;
43 # endif
44
45 # ifdef timer_create_alias
46 #  define timer_create timer_create_alias
47 # endif
48
49
50 int
51 timer_create (clock_id, evp, timerid)
52      clockid_t clock_id;
53      struct sigevent *evp;
54      timer_t *timerid;
55 {
56 # undef timer_create
57 # ifndef __ASSUME_POSIX_TIMERS
58   if  (__no_posix_timers >= 0)
59 # endif
60     {
61       /* If the user wants notification via a thread we need to handle
62          this special.  */
63       if (evp == NULL
64           || __builtin_expect (evp->sigev_notify != SIGEV_THREAD, 1))
65         {
66           struct sigevent local_evp;
67
68           /* We avoid allocating too much memory by basically
69              using struct timer as a derived class with the
70              first two elements being in the superclass.  We only
71              need these two elements here.  */
72           struct timer *newp = (struct timer *) malloc (offsetof (struct timer,
73                                                                   thrfunc));
74           if (newp == NULL)
75             /* No more memory.  */
76             return -1;
77
78           if (evp == NULL)
79             {
80               /* The kernel has to pass up the timer ID which is a
81                  userlevel object.  Therefore we cannot leave it up to
82                  the kernel to determine it.  */
83               local_evp.sigev_notify = SIGEV_SIGNAL;
84               local_evp.sigev_signo = SIGALRM;
85               local_evp.sigev_value.sival_ptr = newp;
86
87               evp = &local_evp;
88             }
89
90           kernel_timer_t ktimerid;
91           int retval = INLINE_SYSCALL (timer_create, 3, clock_id, evp,
92                                        &ktimerid);
93
94 # ifndef __ASSUME_POSIX_TIMERS
95           if (retval != -1 || errno != ENOSYS)
96 # endif
97             {
98 # ifndef __ASSUME_POSIX_TIMERS
99               __no_posix_timers = 1;
100 # endif
101
102               if (retval != -1)
103                 {
104                   newp->sigev_notify = (evp != NULL
105                                         ? evp->sigev_notify : SIGEV_SIGNAL);
106                   newp->ktimerid = ktimerid;
107
108                   *timerid = (timer_t) newp;
109                 }
110               else
111                 {
112                   /* Cannot allocate the timer, fail.  */
113                   free (newp);
114                   retval = -1;
115                 }
116
117               return retval;
118             }
119
120           free (newp);
121
122 # ifndef __ASSUME_POSIX_TIMERS
123           /* When we come here the syscall does not exist.  Make sure we
124              do not try to use it again.  */
125           __no_posix_timers = -1;
126 # endif
127         }
128       else
129         {
130 # ifndef __ASSUME_POSIX_TIMERS
131           /* Make sure we have the necessary kernel support.  */
132           if (__no_posix_timers == 0)
133             {
134               INTERNAL_SYSCALL_DECL (err);
135               struct timespec ts;
136               int res;
137               res = INTERNAL_SYSCALL (clock_getres, err, 2,
138                                       CLOCK_REALTIME, &ts);
139               __no_posix_timers = (INTERNAL_SYSCALL_ERROR_P (res, err)
140                                    ? -1 : 1);
141             }
142
143           if (__no_posix_timers > 0)
144 # endif
145             {
146               /* Create the helper thread.  */
147               pthread_once (&__helper_once, __start_helper_thread);
148               if (__helper_tid == 0)
149                 {
150                   /* No resources to start the helper thread.  */
151                   __set_errno (EAGAIN);
152                   return -1;
153                 }
154
155               struct timer *newp;
156               newp = (struct timer *) malloc (sizeof (struct timer));
157               if (newp == NULL)
158                 return -1;
159
160               /* Copy the thread parameters the user provided.  */
161               newp->sival = evp->sigev_value;
162               newp->thrfunc = evp->sigev_notify_function;
163
164               /* We cannot simply copy the thread attributes since the
165                  implementation might keep internal information for
166                  each instance.  */
167               (void) pthread_attr_init (&newp->attr);
168               if (evp->sigev_notify_attributes != NULL)
169                 {
170                   struct pthread_attr *nattr;
171                   struct pthread_attr *oattr;
172
173                   nattr = (struct pthread_attr *) &newp->attr;
174                   oattr = (struct pthread_attr *) evp->sigev_notify_attributes;
175
176                   nattr->schedparam = oattr->schedparam;
177                   nattr->schedpolicy = oattr->schedpolicy;
178                   nattr->flags = oattr->flags;
179                   nattr->guardsize = oattr->guardsize;
180                   nattr->stackaddr = oattr->stackaddr;
181                   nattr->stacksize = oattr->stacksize;
182                 }
183
184               /* In any case set the detach flag.  */
185               (void) pthread_attr_setdetachstate (&newp->attr,
186                                                   PTHREAD_CREATE_DETACHED);
187
188               /* Create the event structure for the kernel timer.  */
189               struct sigevent sev;
190               sev.sigev_value.sival_ptr = newp;
191               sev.sigev_signo = SIGTIMER;
192               sev.sigev_notify = SIGEV_SIGNAL | SIGEV_THREAD_ID;
193               /* This is the thread ID of the helper thread.  */
194               sev._sigev_un._pad[0] = __helper_tid;
195
196               /* Create the timer.  */
197               INTERNAL_SYSCALL_DECL (err);
198               int res;
199               res = INTERNAL_SYSCALL (timer_create, err, 3, clock_id, &sev,
200                                       &newp->ktimerid);
201               if (! INTERNAL_SYSCALL_ERROR_P (res, err))
202                 {
203                   *timerid = (timer_t) newp;
204                   return 0;
205                 }
206
207               /* Free the resources.  */
208               free (newp);
209
210               __set_errno (INTERNAL_SYSCALL_ERRNO (res, err));
211
212               return -1;
213             }
214         }
215     }
216
217 # ifndef __ASSUME_POSIX_TIMERS
218   /* Compatibility code.  */
219   return compat_timer_create (clock_id, evp, timerid);
220 # endif
221 }
222 #else
223 # ifdef timer_create_alias
224 #  define timer_create timer_create_alias
225 # endif
226 /* The new system calls are not available.  Use the userlevel
227    implementation.  */
228 # include <nptl/sysdeps/pthread/timer_create.c>
229 #endif