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