about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--linuxthreads/ChangeLog8
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h16
-rw-r--r--linuxthreads/sysdeps/unix/sysv/linux/mq_notify.c285
-rw-r--r--rt/Makefile3
-rw-r--r--rt/tst-timer3.c81
6 files changed, 396 insertions, 2 deletions
diff --git a/ChangeLog b/ChangeLog
index 1156766cf8..e864763861 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2004-04-12  Ulrich Drepper  <drepper@redhat.com>
+
+	* rt/Makefile (tests): Add tst-timer3.
+	* rt/tst-timer3.c: New file.  By Roland McGrath.
+
 2004-04-08  Ulrich Drepper  <drepper@redhat.com>
 
 	* rt/tst-mqueue3.c: New file.
diff --git a/linuxthreads/ChangeLog b/linuxthreads/ChangeLog
index ad881d4968..974c6faae9 100644
--- a/linuxthreads/ChangeLog
+++ b/linuxthreads/ChangeLog
@@ -1,3 +1,11 @@
+2004-04-12  Ulrich Drepper  <drepper@redhat.com>
+
+	* sysdeps/unix/sysv/linux/mq_notify.c: New file.
+
+	* sysdeps/unix/sysv/linux/i386/sysdep-cancel.h (CENABLE): Define
+	for librt.
+	(CDISABLE): Likewise.
+
 2004-04-08  Jakub Jelinek  <jakub@redhat.com>
 
 	* sysdeps/unix/sysv/linux/bits/local_lim.h (MQ_PRIO_MAX): Define.
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h b/linuxthreads/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
index baba152a77..ec21949a49 100644
--- a/linuxthreads/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
+++ b/linuxthreads/sysdeps/unix/sysv/linux/i386/sysdep-cancel.h
@@ -1,4 +1,4 @@
-/* Copyright (C) 2002, 2003 Free Software Foundation, Inc.
+/* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
    This file is part of the GNU C Library.
    Contributed by Jakub Jelinek <jakub@redhat.com>, 2002.
 
@@ -95,6 +95,20 @@
 # ifdef IS_IN_libpthread
 #  define CENABLE	call __pthread_enable_asynccancel;
 #  define CDISABLE	call __pthread_disable_asynccancel
+# elif defined IS_IN_librt
+#  ifdef __PIC__
+#   define CENABLE	pushl %ebx; \
+			SETUP_PIC_REG(bx); \
+			call __librt_enable_asynccancel@PLT; \
+			popl %ebx;
+#   define CDISABLE	pushl %ebx; \
+			SETUP_PIC_REG(bx); \
+			call __librt_disable_asynccancel@PLT; \
+			popl %ebx;
+#  else
+#   define CENABLE	call __librt_enable_asynccancel;
+#   define CDISABLE	call __librt_disable_asynccancel
+   #endif
 # else
 #  define CENABLE	call __libc_enable_asynccancel;
 #  define CDISABLE	call __libc_disable_asynccancel
diff --git a/linuxthreads/sysdeps/unix/sysv/linux/mq_notify.c b/linuxthreads/sysdeps/unix/sysv/linux/mq_notify.c
new file mode 100644
index 0000000000..e231e591d1
--- /dev/null
+++ b/linuxthreads/sysdeps/unix/sysv/linux/mq_notify.c
@@ -0,0 +1,285 @@
+/* Copyright (C) 2004 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+   Contribute by Ulrich Drepper <drepper@redhat.com>, 2004.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   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.  */
+
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <mqueue.h>
+#include <pthread.h>
+#include <signal.h>
+#include <string.h>
+#include <sysdep.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <not-cancel.h>
+
+
+#ifdef __NR_mq_notify
+
+/* Defined in the kernel headers: */
+#define NOTIFY_COOKIE_LEN	32	/* Length of the cookie used.  */
+#define NOTIFY_WOKENUP		1	/* Code for notifcation.  */
+#define NOTIFY_REMOVED		2	/* Code for closed message queue
+					   of de-notifcation.  */
+
+
+/* Data structure for the queued notification requests.  */
+union notify_data
+{
+  struct
+  {
+    void (*fct) (union sigval);	/* The function to run.  */
+    union sigval param;		/* The parameter to pass.  */
+    pthread_attr_t *attr;	/* Attributes to create the thread with.  */
+    /* NB: on 64-bit machines the struct as a size of 24 bytes.  Which means
+       byte 31 can still be used for returning the status.  */
+  };
+  char raw[NOTIFY_COOKIE_LEN];
+};
+
+
+/* Keep track of the initialization.  */
+static pthread_once_t once = PTHREAD_ONCE_INIT;
+
+
+/* The netlink socket.  */
+static int netlink_socket = -1;
+
+
+/* Barrier used to make sure data passed to the new thread is not
+   resused by the parent.  */
+static pthread_barrier_t notify_barrier;
+
+
+/* Modify the signal mask.  We move this into a separate function so
+   that the stack space needed for sigset_t is not deducted from what
+   the thread can use.  */
+static int
+__attribute__ ((noinline))
+change_sigmask (int how, sigset_t *oss)
+{
+  sigset_t ss;
+  sigfillset (&ss);
+  return pthread_sigmask (how, &ss, oss);
+}
+
+
+/* The function used for the notification.  */
+static void *
+notification_function (void *arg)
+{
+  /* Copy the function and parameter so that the parent thread can go
+     on with its life.  */
+  volatile union notify_data *data = (volatile union notify_data *) arg;
+  void (*fct) (union sigval) = data->fct;
+  union sigval param = data->param;
+
+  /* Let the parent go.  */
+  (void) pthread_barrier_wait (&notify_barrier);
+
+  /* Make the thread detached.  */
+  (void) pthread_detach (pthread_self ());
+
+  /* The parent thread has all signals blocked.  This is probably a
+     bit surprising for this thread.  So we unblock all of them.  */
+  (void) change_sigmask (SIG_UNBLOCK, NULL);
+
+  /* Now run the user code.  */
+  fct (param);
+
+  /* And we are done.  */
+  return NULL;
+}
+
+
+/* Helper thread.  */
+static void *
+helper_thread (void *arg)
+{
+  while (1)
+    {
+      union notify_data data;
+
+      ssize_t n = recv (netlink_socket, &data, sizeof (data),
+			MSG_NOSIGNAL | MSG_WAITALL);
+      if (n < NOTIFY_COOKIE_LEN)
+	continue;
+
+      if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_WOKENUP)
+	{
+	  /* Just create the thread as instructed.  There is no way to
+	     report a problem with creating a thread.  */
+	  pthread_t th;
+	  if (__builtin_expect (pthread_create (&th, data.attr,
+						notification_function, &data)
+				== 0, 0))
+	    /* Since we passed a pointer to DATA to the new thread we have
+	       to wait until it is done with it.  */
+	    (void) pthread_barrier_wait (&notify_barrier);
+	}
+      else if (data.raw[NOTIFY_COOKIE_LEN - 1] == NOTIFY_REMOVED)
+	/* The only state we keep is the copy of the thread attributes.  */
+	free (data.attr);
+    }
+}
+
+
+static void
+reset_once (void)
+{
+  once = PTHREAD_ONCE_INIT;
+}
+
+
+static void
+init_mq_netlink (void)
+{
+  /* This code might be called a second time after fork().  The file
+     descriptor is inherited from the parent.  */
+  if (netlink_socket == -1)
+    {
+      /* Just a normal netlink socket, not bound.  */
+      netlink_socket = socket (AF_NETLINK, SOCK_RAW, 0);
+      /* No need to do more if we have no socket.  */
+      if (netlink_socket == -1)
+	return;
+
+      /* Make sure the descriptor is closed on exec.  */
+      if (fcntl (netlink_socket, F_SETFD, FD_CLOEXEC) != 0)
+	goto errout;
+    }
+
+  int err = 1;
+
+  /* Initialize the barrier.  */
+  if (__builtin_expect (pthread_barrier_init (&notify_barrier, NULL, 2) == 0,
+			0))
+    {
+      /* Create the helper thread.  */
+      pthread_attr_t attr;
+      (void) pthread_attr_init (&attr);
+      (void) pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
+      /* We do not need much stack space, the bare minimum will be enough.  */
+      (void) pthread_attr_setstacksize (&attr, PTHREAD_STACK_MIN);
+
+      /* Temporarily block all signals so that the newly created
+	 thread inherits the mask.  */
+      sigset_t oss;
+      int have_no_oss = change_sigmask (SIG_BLOCK, &oss);
+
+      pthread_t th;
+      err = pthread_create (&th, &attr, helper_thread, NULL);
+
+      /* Reset the signal mask.  */
+      if (!have_no_oss)
+	pthread_sigmask (SIG_SETMASK, &oss, NULL);
+
+      (void) pthread_attr_destroy (&attr);
+
+      if (err == 0)
+	{
+	  static int added_atfork;
+
+	  if (added_atfork == 0
+	      && pthread_atfork (NULL, NULL, reset_once) != 0)
+	    {
+	      /* The child thread will call recv() which is a
+		 cancellation point.  */
+	      (void) pthread_cancel (th);
+	      err = 1;
+	    }
+	  else
+	    added_atfork = 1;
+	}
+    }
+
+  if (err != 0)
+    {
+    errout:
+      close_not_cancel_no_status (netlink_socket);
+      netlink_socket = -1;
+    }
+}
+
+
+/* Register notification upon message arrival to an empty message queue
+   MQDES.  */
+int
+mq_notify (mqd_t mqdes, const struct sigevent *notification)
+{
+  /* Make sure the type is correctly defined.  */
+  assert (sizeof (union notify_data) == NOTIFY_COOKIE_LEN);
+
+  /* Special treatment needed for SIGEV_THREAD.  */
+  if (notification == NULL || notification->sigev_notify != SIGEV_THREAD)
+    return INLINE_SYSCALL (mq_notify, 2, mqdes, notification);
+
+  /* The kernel cannot directly start threads.  This will have to be
+     done at userlevel.  Since we cannot start threads from signal
+     handlers we have to create a dedicated thread which waits for
+     notifications for arriving messages and creates threads in
+     response.  */
+
+  /* Initialize only once.  */
+  pthread_once (&once, init_mq_netlink);
+
+  /* If we cannot create the netlink socket we cannot provide
+     SIGEV_THREAD support.  */
+  if (__builtin_expect (netlink_socket == -1, 0))
+    {
+      __set_errno (ENOSYS);
+      return -1;
+    }
+
+  /* Create the cookie.  It will hold almost all the state.  */
+  union notify_data data;
+  memset (&data, '\0', sizeof (data));
+  data.fct = notification->sigev_notify_function;
+  data.param = notification->sigev_value;
+
+  if (notification->sigev_notify_attributes != NULL)
+    {
+      /* The thread attribute has to be allocated separately.  */
+      data.attr = (pthread_attr_t *) malloc (sizeof (pthread_attr_t));
+      if (data.attr == NULL)
+	return -1;
+
+      memcpy (data.attr, notification->sigev_notify_attributes,
+	      sizeof (pthread_attr_t));
+    }
+
+  /* Construct the new request.  */
+  struct sigevent se;
+  se.sigev_notify = SIGEV_THREAD;
+  se.sigev_signo = netlink_socket;
+  se.sigev_value.sival_ptr = &data;
+
+  /* Tell the kernel.  */
+  int retval = INLINE_SYSCALL (mq_notify, 2, mqdes, &se);
+
+  /* If it failed, free the allocated memory.  */
+  if (__builtin_expect (retval != 0, 0))
+    free (data.attr);
+
+  return retval;
+}
+
+#else
+# include <sysdeps/generic/mq_notify.c>
+#endif
diff --git a/rt/Makefile b/rt/Makefile
index fd5185292c..011bef357e 100644
--- a/rt/Makefile
+++ b/rt/Makefile
@@ -43,7 +43,8 @@ librt-routines = $(aio-routines) \
 
 tests := tst-shm tst-clock tst-clock_nanosleep tst-timer tst-timer2 \
 	 tst-aio tst-aio64 tst-aio2 tst-aio3 tst-aio4 tst-aio5 tst-aio6 \
-	 tst-aio7 tst-mqueue1 tst-mqueue2 tst-mqueue3 tst-mqueue4
+	 tst-aio7 tst-mqueue1 tst-mqueue2 tst-mqueue3 tst-mqueue4 \
+	 tst-timer3
 
 extra-libs := librt
 extra-libs-others := $(extra-libs)
diff --git a/rt/tst-timer3.c b/rt/tst-timer3.c
new file mode 100644
index 0000000000..10b6309179
--- /dev/null
+++ b/rt/tst-timer3.c
@@ -0,0 +1,81 @@
+/* Test for bogus per-thread deletion of timers.  */
+
+#include <stdio.h>
+#include <error.h>
+#include <time.h>
+#include <signal.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <pthread.h>
+
+
+/* Creating timers in another thread should work too.  */
+static void *
+do_timer_create (void *arg)
+{
+  struct sigevent *const sigev = arg;
+  timer_t *const timerId = sigev->sigev_value.sival_ptr;
+  if (timer_create (CLOCK_REALTIME, sigev, timerId) < 0)
+    {
+      printf ("timer_create: %m\n");
+      return NULL;
+    }
+  return timerId;
+}
+
+
+static int
+do_test (void)
+{
+  int i, res;
+  timer_t timerId;
+  struct itimerspec itval;
+  struct sigevent sigev;
+
+  itval.it_interval.tv_sec = 2;
+  itval.it_interval.tv_nsec = 0;
+  itval.it_value.tv_sec = 2;
+  itval.it_value.tv_nsec = 0;
+
+  sigev.sigev_notify = SIGEV_SIGNAL;
+  sigev.sigev_signo = SIGALRM;
+  sigev.sigev_value.sival_ptr = (void *) &timerId;
+
+  for (i = 0; i < 100; i++)
+    {
+      printf ("cnt = %d\n", i);
+
+      pthread_t thr;
+      res = pthread_create (&thr, NULL, &do_timer_create, &sigev);
+      if (res)
+	{
+	  printf ("pthread_create: %s\n", strerror (res));
+	  continue;
+	}
+      void *val;
+      res = pthread_join (thr, &val);
+      if (res)
+	{
+	  printf ("pthread_join: %s\n", strerror (res));
+	  continue;
+	}
+      if (val == NULL)
+	continue;
+
+      res = timer_settime (timerId, 0, &itval, NULL);
+      if (res < 0)
+	printf ("timer_settime: %m\n");
+
+      res = timer_delete (timerId);
+      if (res < 0)
+	printf ("timer_delete: %m\n");
+    }
+
+  return 0;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"