Back to index

glibc  2.9
mq_notify.c
Go to the documentation of this file.
00001 /* Copyright (C) 2004, 2005, 2008 Free Software Foundation, Inc.
00002    This file is part of the GNU C Library.
00003    Contribute by Ulrich Drepper <drepper@redhat.com>, 2004.
00004 
00005    The GNU C Library is free software; you can redistribute it and/or
00006    modify it under the terms of the GNU Lesser General Public
00007    License as published by the Free Software Foundation; either
00008    version 2.1 of the License, or (at your option) any later version.
00009 
00010    The GNU C Library is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY; without even the implied warranty of
00012    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013    Lesser General Public License for more details.
00014 
00015    You should have received a copy of the GNU Lesser General Public
00016    License along with the GNU C Library; if not, write to the Free
00017    Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
00018    02111-1307 USA.  */
00019 
00020 #include <assert.h>
00021 #include <errno.h>
00022 #include <fcntl.h>
00023 #include <mqueue.h>
00024 #include <pthread.h>
00025 #include <signal.h>
00026 #include <stdlib.h>
00027 #include <string.h>
00028 #include <sysdep.h>
00029 #include <unistd.h>
00030 #include <sys/socket.h>
00031 #include <not-cancel.h>
00032 #include <kernel-features.h>
00033 
00034 
00035 #ifdef __NR_mq_notify
00036 
00037 /* Defined in the kernel headers: */
00038 #define NOTIFY_COOKIE_LEN   32     /* Length of the cookie used.  */
00039 #define NOTIFY_WOKENUP             1      /* Code for notifcation.  */
00040 #define NOTIFY_REMOVED             2      /* Code for closed message queue
00041                                       of de-notifcation.  */
00042 
00043 
00044 /* Data structure for the queued notification requests.  */
00045 union notify_data
00046 {
00047   struct
00048   {
00049     void (*fct) (union sigval);    /* The function to run.  */
00050     union sigval param;            /* The parameter to pass.  */
00051     pthread_attr_t *attr;   /* Attributes to create the thread with.  */
00052     /* NB: on 64-bit machines the struct as a size of 24 bytes.  Which means
00053        byte 31 can still be used for returning the status.  */
00054   };
00055   char raw[NOTIFY_COOKIE_LEN];
00056 };
00057 
00058 
00059 /* Keep track of the initialization.  */
00060 static pthread_once_t once = PTHREAD_ONCE_INIT;
00061 
00062 
00063 /* The netlink socket.  */
00064 static int netlink_socket = -1;
00065 
00066 
00067 /* Barrier used to make sure data passed to the new thread is not
00068    resused by the parent.  */
00069 static pthread_barrier_t notify_barrier;
00070 
00071 
00072 /* Modify the signal mask.  We move this into a separate function so
00073    that the stack space needed for sigset_t is not deducted from what
00074    the thread can use.  */
00075 static int
00076 __attribute__ ((noinline))
00077 change_sigmask (int how, sigset_t *oss)
00078 {
00079   sigset_t ss;
00080   sigfillset (&ss);
00081   return pthread_sigmask (how, &ss, oss);
00082 }
00083 
00084 
00085 /* The function used for the notification.  */
00086 static void *
00087 notification_function (void *arg)
00088 {
00089   /* Copy the function and parameter so that the parent thread can go
00090      on with its life.  */
00091   volatile union notify_data *data = (volatile union notify_data *) arg;
00092   void (*fct) (union sigval) = data->fct;
00093   union sigval param = data->param;
00094 
00095   /* Let the parent go.  */
00096   (void) pthread_barrier_wait (&notify_barrier);
00097 
00098   /* Make the thread detached.  */
00099   (void) pthread_detach (pthread_self ());
00100 
00101   /* The parent thread has all signals blocked.  This is probably a
00102      bit surprising for this thread.  So we unblock all of them.  */
00103   (void) change_sigmask (SIG_UNBLOCK, NULL);
00104 
00105   /* Now run the user code.  */
00106   fct (param);
00107 
00108   /* And we are done.  */
00109   return NULL;
00110 }
00111 
00112 
00113 /* Helper thread.  */
00114 static void *
00115 helper_thread (void *arg)
00116 {
00117   while (1)
00118     {
00119       union notify_data data;
00120 
00121       ssize_t n = recv (netlink_socket, &data, sizeof (data),
00122                      MSG_NOSIGNAL | MSG_WAITALL);
00123       if (n < NOTIFY_COOKIE_LEN)
00124        continue;
00125 
00126       if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_WOKENUP)
00127        {
00128          /* Just create the thread as instructed.  There is no way to
00129             report a problem with creating a thread.  */
00130          pthread_t th;
00131          if (__builtin_expect (pthread_create (&th, data.attr,
00132                                           notification_function, &data)
00133                             == 0, 0))
00134            /* Since we passed a pointer to DATA to the new thread we have
00135               to wait until it is done with it.  */
00136            (void) pthread_barrier_wait (&notify_barrier);
00137        }
00138       else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED)
00139        /* The only state we keep is the copy of the thread attributes.  */
00140        free (data.attr);
00141     }
00142   return NULL;
00143 }
00144 
00145 
00146 static void
00147 reset_once (void)
00148 {
00149   once = PTHREAD_ONCE_INIT;
00150 }
00151 
00152 
00153 static void
00154 init_mq_netlink (void)
00155 {
00156 #ifdef SOCK_CLOEXEC
00157 # ifndef __ASSUME_SOCK_CLOEXEC
00158   static int have_sock_cloexec;
00159 # else
00160 #  define have_sock_cloexec 1
00161 # endif
00162 #else
00163 # define have_sock_cloexec -1
00164 # define SOCK_CLOEXEC 0
00165 #endif
00166 
00167   /* This code might be called a second time after fork().  The file
00168      descriptor is inherited from the parent.  */
00169   if (netlink_socket == -1)
00170     {
00171       /* Just a normal netlink socket, not bound.  */
00172       if (have_sock_cloexec >= 0)
00173        {
00174          netlink_socket = socket (AF_NETLINK, SOCK_RAW | SOCK_CLOEXEC, 0);
00175 #if defined SOCK_CLOEXEC && !defined __ASSUME_SOCK_CLOEXEC
00176          if (have_sock_cloexec == 0)
00177            have_sock_cloexec = (netlink_socket != -1 || errno != EINVAL
00178                              ? 1 : -1);
00179 #endif
00180        }
00181       if (have_sock_cloexec < 0)
00182        netlink_socket = socket (AF_NETLINK, SOCK_RAW, 0);
00183       /* No need to do more if we have no socket.  */
00184       if (netlink_socket == -1)
00185        return;
00186 
00187       /* Make sure the descriptor is closed on exec.  */
00188       if (have_sock_cloexec < 0
00189          && fcntl (netlink_socket, F_SETFD, FD_CLOEXEC) != 0)
00190        goto errout;
00191     }
00192 
00193   int err = 1;
00194 
00195   /* Initialize the barrier.  */
00196   if (__builtin_expect (pthread_barrier_init (&notify_barrier, NULL, 2) == 0,
00197                      0))
00198     {
00199       /* Create the helper thread.  */
00200       pthread_attr_t attr;
00201       (void) pthread_attr_init (&attr);
00202       (void) pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
00203       /* We do not need much stack space, the bare minimum will be enough.  */
00204       (void) pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
00205 
00206       /* Temporarily block all signals so that the newly created
00207         thread inherits the mask.  */
00208       sigset_t oss;
00209       int have_no_oss = change_sigmask (SIG_BLOCK, &oss);
00210 
00211       pthread_t th;
00212       err = pthread_create (&th, &attr, helper_thread, NULL);
00213 
00214       /* Reset the signal mask.  */
00215       if (!have_no_oss)
00216        pthread_sigmask (SIG_SETMASK, &oss, NULL);
00217 
00218       (void) pthread_attr_destroy (&attr);
00219 
00220       if (err == 0)
00221        {
00222          static int added_atfork;
00223 
00224          if (added_atfork == 0
00225              && pthread_atfork (NULL, NULL, reset_once) != 0)
00226            {
00227              /* The child thread will call recv() which is a
00228                cancellation point.  */
00229              (void) pthread_cancel (th);
00230              err = 1;
00231            }
00232          else
00233            added_atfork = 1;
00234        }
00235     }
00236 
00237   if (err != 0)
00238     {
00239     errout:
00240       close_not_cancel_no_status (netlink_socket);
00241       netlink_socket = -1;
00242     }
00243 }
00244 
00245 
00246 /* Register notification upon message arrival to an empty message queue
00247    MQDES.  */
00248 int
00249 mq_notify (mqd_t mqdes, const struct sigevent *notification)
00250 {
00251   /* Make sure the type is correctly defined.  */
00252   assert (sizeof (union notify_data) == NOTIFY_COOKIE_LEN);
00253 
00254   /* Special treatment needed for SIGEV_THREAD.  */
00255   if (notification == NULL || notification->sigev_notify != SIGEV_THREAD)
00256     return INLINE_SYSCALL (mq_notify, 2, mqdes, notification);
00257 
00258   /* The kernel cannot directly start threads.  This will have to be
00259      done at userlevel.  Since we cannot start threads from signal
00260      handlers we have to create a dedicated thread which waits for
00261      notifications for arriving messages and creates threads in
00262      response.  */
00263 
00264   /* Initialize only once.  */
00265   pthread_once (&once, init_mq_netlink);
00266 
00267   /* If we cannot create the netlink socket we cannot provide
00268      SIGEV_THREAD support.  */
00269   if (__builtin_expect (netlink_socket == -1, 0))
00270     {
00271       __set_errno (ENOSYS);
00272       return -1;
00273     }
00274 
00275   /* Create the cookie.  It will hold almost all the state.  */
00276   union notify_data data;
00277   memset (&data, '\0', sizeof (data));
00278   data.fct = notification->sigev_notify_function;
00279   data.param = notification->sigev_value;
00280 
00281   if (notification->sigev_notify_attributes != NULL)
00282     {
00283       /* The thread attribute has to be allocated separately.  */
00284       data.attr = (pthread_attr_t *) malloc (sizeof (pthread_attr_t));
00285       if (data.attr == NULL)
00286        return -1;
00287 
00288       memcpy (data.attr, notification->sigev_notify_attributes,
00289              sizeof (pthread_attr_t));
00290     }
00291 
00292   /* Construct the new request.  */
00293   struct sigevent se;
00294   se.sigev_notify = SIGEV_THREAD;
00295   se.sigev_signo = netlink_socket;
00296   se.sigev_value.sival_ptr = &data;
00297 
00298   /* Tell the kernel.  */
00299   int retval = INLINE_SYSCALL (mq_notify, 2, mqdes, &se);
00300 
00301   /* If it failed, free the allocated memory.  */
00302   if (__builtin_expect (retval != 0, 0))
00303     free (data.attr);
00304 
00305   return retval;
00306 }
00307 
00308 #else
00309 # include <rt/mq_notify.c>
00310 #endif