1 /* Copyright (C) 2000 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contributed by Kaz Kylheku <kaz@ashi.footprints.net>.
4 
5    The GNU C Library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Library General Public License as
7    published by the Free Software Foundation; either version 2 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    Library General Public License for more details.
14 
15    You should have received a copy of the GNU Library 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 <signal.h>
22 #include <pthread.h>
23 #include <time.h>
24 #include <unistd.h>
25 
26 #include "posix-timer.h"
27 
28 
29 /* Create new per-process timer using CLOCK.  */
30 int
timer_create(clock_id,evp,timerid)31 timer_create (clock_id, evp, timerid)
32      clockid_t clock_id;
33      struct sigevent *__restrict evp;
34      timer_t *__restrict timerid;
35 {
36   int retval = -1;
37   struct timer_node *newtimer = NULL;
38   struct thread_node *thread = NULL;
39 
40   if (clock_id != CLOCK_REALTIME
41 #ifdef _POSIX_CPUTIME
42       && clock_id != CLOCK_PROCESS_CPUTIME_ID
43 #endif
44 #ifdef _POSIX_THREAD_CPUTIME
45       && clock_id != CLOCK_THREAD_CPUTIME_ID
46 #endif
47       )
48     {
49       __set_errno (EINVAL);
50       return -1;
51     }
52 
53   pthread_once (&__timer_init_once_control, __timer_init_once);
54 
55   if (__timer_init_failed)
56     {
57       __set_errno (ENOMEM);
58       return -1;
59     }
60 
61   pthread_mutex_lock (&__timer_mutex);
62 
63   newtimer = __timer_alloc ();
64   if (__builtin_expect (newtimer == NULL, 0))
65     {
66       __set_errno (EAGAIN);
67       goto unlock_bail;
68     }
69 
70   if (evp != NULL)
71     newtimer->event = *evp;
72   else
73     {
74       newtimer->event.sigev_notify = SIGEV_SIGNAL;
75       newtimer->event.sigev_signo = SIGALRM;
76       newtimer->event.sigev_value.sival_int = timer_ptr2id (newtimer);
77       newtimer->event.sigev_notify_function = 0;
78     }
79 
80   newtimer->event.sigev_notify_attributes = &newtimer->attr;
81   newtimer->creator_pid = getpid ();
82 
83   switch (__builtin_expect (newtimer->event.sigev_notify, SIGEV_SIGNAL))
84     {
85     case SIGEV_NONE:
86       /* This is a strange choice!  */
87       break;
88 
89     case SIGEV_SIGNAL:
90       /* We have a global thread for delivering timed signals.
91 	 If it is not running, try to start it up.  */
92       switch (clock_id)
93 	{
94 	case CLOCK_REALTIME:
95 	default:
96 	  thread = &__timer_signal_thread_rclk;
97 	  break;
98 #ifdef _POSIX_CPUTIME
99 	case CLOCK_PROCESS_CPUTIME_ID:
100 	  thread = &__timer_signal_thread_pclk;
101 	  break;
102 #endif
103 #ifdef _POSIX_THREAD_CPUTIME
104 	case CLOCK_THREAD_CPUTIME_ID:
105 	  thread = &__timer_signal_thread_tclk;
106 	  break;
107 #endif
108 	}
109 
110       if (! thread->exists)
111 	{
112 	  if (__builtin_expect (__timer_thread_start (thread),
113 				1) < 0)
114 	    {
115 	      __set_errno (EAGAIN);
116 	      goto unlock_bail;
117             }
118         }
119       break;
120 
121     case SIGEV_THREAD:
122       /* Copy over thread attributes or set up default ones.  */
123       if (evp->sigev_notify_attributes)
124 	newtimer->attr = *(pthread_attr_t *) evp->sigev_notify_attributes;
125       else
126 	pthread_attr_init (&newtimer->attr);
127 
128       /* Ensure thread attributes call for deatched thread.  */
129       pthread_attr_setdetachstate (&newtimer->attr, PTHREAD_CREATE_DETACHED);
130 
131       /* Try to find existing thread having the right attributes.  */
132       thread = __timer_thread_find_matching (&newtimer->attr, clock_id);
133 
134       /* If no existing thread has these attributes, try to allocate one.  */
135       if (thread == NULL)
136 	thread = __timer_thread_alloc (&newtimer->attr, clock_id);
137 
138       /* Out of luck; no threads are available.  */
139       if (__builtin_expect (thread == NULL, 0))
140 	{
141 	  __set_errno (EAGAIN);
142 	  goto unlock_bail;
143 	}
144 
145       /* If the thread is not running already, try to start it.  */
146       if (! thread->exists
147 	  && __builtin_expect (! __timer_thread_start (thread), 0))
148 	{
149 	  __set_errno (EAGAIN);
150 	  goto unlock_bail;
151 	}
152       break;
153 
154     default:
155       __set_errno (EINVAL);
156       goto unlock_bail;
157     }
158 
159   newtimer->clock = clock_id;
160   newtimer->abstime = 0;
161   newtimer->armed = 0;
162   newtimer->thread = thread;
163 
164   *timerid = timer_ptr2id (newtimer);
165   retval = 0;
166 
167   if (__builtin_expect (retval, 0) == -1)
168     {
169     unlock_bail:
170       if (thread != NULL)
171 	__timer_thread_dealloc (thread);
172       if (newtimer != NULL)
173 	__timer_dealloc (newtimer);
174     }
175 
176   pthread_mutex_unlock (&__timer_mutex);
177 
178   return retval;
179 }
180