Back to index

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