Test for stack alignment.
[jlayton/glibc.git] / linuxthreads / sysdeps / unix / sysv / linux / mq_notify.c
1 /* Copyright (C) 2004 Free Software Foundation, Inc.
2    This file is part of the GNU C Library.
3    Contribute by Ulrich Drepper <drepper@redhat.com>, 2004.
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
7    License as published by the Free Software Foundation; either
8    version 2.1 of the 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; if not, write to the Free
17    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18    02111-1307 USA.  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <mqueue.h>
24 #include <pthread.h>
25 #include <signal.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sysdep.h>
29 #include <unistd.h>
30 #include <sys/socket.h>
31 #include <not-cancel.h>
32
33
34 #ifdef __NR_mq_notify
35
36 /* Defined in the kernel headers: */
37 #define NOTIFY_COOKIE_LEN       32      /* Length of the cookie used.  */
38 #define NOTIFY_WOKENUP          1       /* Code for notifcation.  */
39 #define NOTIFY_REMOVED          2       /* Code for closed message queue
40                                            of de-notifcation.  */
41
42
43 /* Data structure for the queued notification requests.  */
44 union notify_data
45 {
46   struct
47   {
48     void (*fct) (union sigval); /* The function to run.  */
49     union sigval param;         /* The parameter to pass.  */
50     pthread_attr_t *attr;       /* Attributes to create the thread with.  */
51     /* NB: on 64-bit machines the struct as a size of 24 bytes.  Which means
52        byte 31 can still be used for returning the status.  */
53   };
54   char raw[NOTIFY_COOKIE_LEN];
55 };
56
57
58 /* Keep track of the initialization.  */
59 static pthread_once_t once = PTHREAD_ONCE_INIT;
60
61
62 /* The netlink socket.  */
63 static int netlink_socket = -1;
64
65
66 /* Barrier used to make sure data passed to the new thread is not
67    resused by the parent.  */
68 static pthread_barrier_t notify_barrier;
69
70
71 /* Modify the signal mask.  We move this into a separate function so
72    that the stack space needed for sigset_t is not deducted from what
73    the thread can use.  */
74 static int
75 __attribute__ ((noinline))
76 change_sigmask (int how, sigset_t *oss)
77 {
78   sigset_t ss;
79   sigfillset (&ss);
80   return pthread_sigmask (how, &ss, oss);
81 }
82
83
84 /* The function used for the notification.  */
85 static void *
86 notification_function (void *arg)
87 {
88   /* Copy the function and parameter so that the parent thread can go
89      on with its life.  */
90   volatile union notify_data *data = (volatile union notify_data *) arg;
91   void (*fct) (union sigval) = data->fct;
92   union sigval param = data->param;
93
94   /* Let the parent go.  */
95   (void) pthread_barrier_wait (&notify_barrier);
96
97   /* Make the thread detached.  */
98   (void) pthread_detach (pthread_self ());
99
100   /* The parent thread has all signals blocked.  This is probably a
101      bit surprising for this thread.  So we unblock all of them.  */
102   (void) change_sigmask (SIG_UNBLOCK, NULL);
103
104   /* Now run the user code.  */
105   fct (param);
106
107   /* And we are done.  */
108   return NULL;
109 }
110
111
112 /* Helper thread.  */
113 static void *
114 helper_thread (void *arg)
115 {
116   while (1)
117     {
118       union notify_data data;
119
120       ssize_t n = recv (netlink_socket, &data, sizeof (data),
121                         MSG_NOSIGNAL | MSG_WAITALL);
122       if (n < NOTIFY_COOKIE_LEN)
123         continue;
124
125       if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_WOKENUP)
126         {
127           /* Just create the thread as instructed.  There is no way to
128              report a problem with creating a thread.  */
129           pthread_t th;
130           if (__builtin_expect (pthread_create (&th, data.attr,
131                                                 notification_function, &data)
132                                 == 0, 0))
133             /* Since we passed a pointer to DATA to the new thread we have
134                to wait until it is done with it.  */
135             (void) pthread_barrier_wait (&notify_barrier);
136         }
137       else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED)
138         /* The only state we keep is the copy of the thread attributes.  */
139         free (data.attr);
140     }
141   return NULL;
142 }
143
144
145 static void
146 reset_once (void)
147 {
148   once = PTHREAD_ONCE_INIT;
149 }
150
151
152 static void
153 init_mq_netlink (void)
154 {
155   /* This code might be called a second time after fork().  The file
156      descriptor is inherited from the parent.  */
157   if (netlink_socket == -1)
158     {
159       /* Just a normal netlink socket, not bound.  */
160       netlink_socket = socket (AF_NETLINK, SOCK_RAW, 0);
161       /* No need to do more if we have no socket.  */
162       if (netlink_socket == -1)
163         return;
164
165       /* Make sure the descriptor is closed on exec.  */
166       if (fcntl (netlink_socket, F_SETFD, FD_CLOEXEC) != 0)
167         goto errout;
168     }
169
170   int err = 1;
171
172   /* Initialize the barrier.  */
173   if (__builtin_expect (pthread_barrier_init (&notify_barrier, NULL, 2) == 0,
174                         0))
175     {
176       /* Create the helper thread.  */
177       pthread_attr_t attr;
178       (void) pthread_attr_init (&attr);
179       (void) pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
180       /* We do not need much stack space, the bare minimum will be enough.  */
181       (void) pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
182
183       /* Temporarily block all signals so that the newly created
184          thread inherits the mask.  */
185       sigset_t oss;
186       int have_no_oss = change_sigmask (SIG_BLOCK, &oss);
187
188       pthread_t th;
189       err = pthread_create (&th, &attr, helper_thread, NULL);
190
191       /* Reset the signal mask.  */
192       if (!have_no_oss)
193         pthread_sigmask (SIG_SETMASK, &oss, NULL);
194
195       (void) pthread_attr_destroy (&attr);
196
197       if (err == 0)
198         {
199           static int added_atfork;
200
201           if (added_atfork == 0
202               && pthread_atfork (NULL, NULL, reset_once) != 0)
203             {
204               /* The child thread will call recv() which is a
205                  cancellation point.  */
206               (void) pthread_cancel (th);
207               err = 1;
208             }
209           else
210             added_atfork = 1;
211         }
212     }
213
214   if (err != 0)
215     {
216     errout:
217       close_not_cancel_no_status (netlink_socket);
218       netlink_socket = -1;
219     }
220 }
221
222
223 /* Register notification upon message arrival to an empty message queue
224    MQDES.  */
225 int
226 mq_notify (mqd_t mqdes, const struct sigevent *notification)
227 {
228   /* Make sure the type is correctly defined.  */
229   assert (sizeof (union notify_data) == NOTIFY_COOKIE_LEN);
230
231   /* Special treatment needed for SIGEV_THREAD.  */
232   if (notification == NULL || notification->sigev_notify != SIGEV_THREAD)
233     return INLINE_SYSCALL (mq_notify, 2, mqdes, notification);
234
235   /* The kernel cannot directly start threads.  This will have to be
236      done at userlevel.  Since we cannot start threads from signal
237      handlers we have to create a dedicated thread which waits for
238      notifications for arriving messages and creates threads in
239      response.  */
240
241   /* Initialize only once.  */
242   pthread_once (&once, init_mq_netlink);
243
244   /* If we cannot create the netlink socket we cannot provide
245      SIGEV_THREAD support.  */
246   if (__builtin_expect (netlink_socket == -1, 0))
247     {
248       __set_errno (ENOSYS);
249       return -1;
250     }
251
252   /* Create the cookie.  It will hold almost all the state.  */
253   union notify_data data;
254   memset (&data, '\0', sizeof (data));
255   data.fct = notification->sigev_notify_function;
256   data.param = notification->sigev_value;
257
258   if (notification->sigev_notify_attributes != NULL)
259     {
260       /* The thread attribute has to be allocated separately.  */
261       data.attr = (pthread_attr_t *) malloc (sizeof (pthread_attr_t));
262       if (data.attr == NULL)
263         return -1;
264
265       memcpy (data.attr, notification->sigev_notify_attributes,
266               sizeof (pthread_attr_t));
267     }
268
269   /* Construct the new request.  */
270   struct sigevent se;
271   se.sigev_notify = SIGEV_THREAD;
272   se.sigev_signo = netlink_socket;
273   se.sigev_value.sival_ptr = &data;
274
275   /* Tell the kernel.  */
276   int retval = INLINE_SYSCALL (mq_notify, 2, mqdes, &se);
277
278   /* If it failed, free the allocated memory.  */
279   if (__builtin_expect (retval != 0, 0))
280     free (data.attr);
281
282   return retval;
283 }
284
285 #else
286 # include <sysdeps/generic/mq_notify.c>
287 #endif