]> git.ipfire.org Git - thirdparty/glibc.git/blobdiff - resolv/gai_notify.c
manual: Adjust twalk_r documentation.
[thirdparty/glibc.git] / resolv / gai_notify.c
index 987a64c0e208cd95d0c6c1ea8028d97f745e4397..e13bd0bd1506f71c54032d596d4f5af1df3dc6fc 100644 (file)
@@ -1,4 +1,4 @@
-/* Copyright (C) 2001 Free Software Foundation, Inc.
+/* Copyright (C) 2001-2019 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Ulrich Drepper <drepper@redhat.com>, 2001.
 
    Lesser General Public License for more details.
 
    You should have received a copy of the GNU Lesser General Public
-   License along with the GNU C Library; if not, write to the Free
-   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
-   02111-1307 USA.  */
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
 
 #include <netdb.h>
 #include <pthread.h>
 #include <stdlib.h>
+#include <gai_misc.h>
 
-#include "gai_misc.h"
 
+struct notify_func
+  {
+    void (*func) (sigval_t);
+    sigval_t value;
+  };
 
 static void *
 notify_func_wrapper (void *arg)
 {
-  struct sigevent *sigev = arg;
-  sigev->sigev_notify_function (sigev->sigev_value);
+  gai_start_notify_thread ();
+  struct notify_func *const n = arg;
+  void (*func) (sigval_t) = n->func;
+  sigval_t value = n->value;
+  free (n);
+  (*func) (value);
   return NULL;
 }
 
 
 int
-internal_function
 __gai_notify_only (struct sigevent *sigev, pid_t caller_pid)
 {
   int result = 0;
@@ -54,8 +61,26 @@ __gai_notify_only (struct sigevent *sigev, pid_t caller_pid)
          pattr = &attr;
        }
 
-      if (pthread_create (&tid, pattr, notify_func_wrapper, sigev) < 0)
+      /* SIGEV may be freed as soon as we return, so we cannot let the
+        notification thread use that pointer.  Even though a sigval_t is
+        only one word and the same size as a void *, we cannot just pass
+        the value through pthread_create as the argument and have the new
+        thread run the user's function directly, because on some machines
+        the calling convention for a union like sigval_t is different from
+        that for a pointer type like void *.  */
+      struct notify_func *nf = malloc (sizeof *nf);
+      if (nf == NULL)
        result = -1;
+      else
+       {
+         nf->func = sigev->sigev_notify_function;
+         nf->value = sigev->sigev_value;
+         if (pthread_create (&tid, pattr, notify_func_wrapper, nf) < 0)
+           {
+             free (nf);
+             result = -1;
+           }
+       }
     }
   else if (sigev->sigev_notify == SIGEV_SIGNAL)
     /* We have to send a signal.  */
@@ -68,7 +93,6 @@ __gai_notify_only (struct sigevent *sigev, pid_t caller_pid)
 
 
 void
-internal_function
 __gai_notify (struct requestlist *req)
 {
   struct waitlist *waitlist;
@@ -79,15 +103,21 @@ __gai_notify (struct requestlist *req)
     {
       struct waitlist *next = waitlist->next;
 
-      /* Decrement the counter.  This is used in both cases.  */
-      --*waitlist->counterp;
-
       if (waitlist->sigevp == NULL)
-       pthread_cond_signal (waitlist->cond);
+       {
+#ifdef DONT_NEED_GAI_MISC_COND
+         GAI_MISC_NOTIFY (waitlist);
+#else
+         /* Decrement the counter.  */
+         --*waitlist->counterp;
+
+         pthread_cond_signal (waitlist->cond);
+#endif
+       }
       else
-       /* This is part of a asynchronous `getaddrinfo_a' operation.  If
+       /* This is part of an asynchronous `getaddrinfo_a' operation.  If
           this request is the last one, send the signal.  */
-       if (*waitlist->counterp == 0)
+       if (--*waitlist->counterp == 0)
          {
            __gai_notify_only (waitlist->sigevp, waitlist->caller_pid);
            /* This is tricky.  See getaddrinfo_a.c for the reason why