Print this page
OS-???? [lx] SIGEV_THREAD_ID emulation needed

@@ -23,13 +23,17 @@
  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
  * Use is subject to license terms.
  * Copyright 2015 Joyent, Inc.
  */
 
+#include <sys/syscall.h>
+
 #include <errno.h>
+#include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <unistd.h>
 #include <sys/resource.h>
 #include <sys/lx_misc.h>
 #include <sys/lx_syscall.h>
 #include <lx_signum.h>
 

@@ -111,14 +115,15 @@
 static int ltos_sigev[] = {
         SIGEV_SIGNAL,
         SIGEV_NONE,
         SIGEV_THREAD,
         0,              /* Linux skips event 3 */
-        SIGEV_THREAD    /* the Linux SIGEV_THREAD_ID */
+        SIGEV_THREAD    /* Linux SIGEV_THREAD_ID -- see lx_sigev_thread_id() */
 };
 
 #define LX_SIGEV_MAX    (sizeof (ltos_sigev) / sizeof (ltos_sigev[0]))
+#define LX_SIGEV_THREAD_ID      4
 
 long
 lx_clock_nanosleep(int clock, int flags, struct timespec *rqtp,
     struct timespec *rmtp)
 {

@@ -161,10 +166,26 @@
 {
         return (-EPERM);
 }
 
 /*
+ * Notification function for use with native SIGEV_THREAD in order to
+ * emulate Linux SIGEV_THREAD_ID. Native SIGEV_THREAD is used as the
+ * timer mechanism and B_SIGEV_THREAD_ID performs the actual event
+ * delivery to the appropriate lx tid.
+ */
+static void
+lx_sigev_thread_id(union sigval sival)
+{
+        lx_sigevent_t *lev = (lx_sigevent_t *)sival.sival_ptr;
+        syscall(SYS_brand, B_SIGEV_THREAD_ID, lev->lx_sigev_un.lx_tid,
+            lev->lx_sigev_signo, lev->lx_sigev_value);
+        free(lev);
+}
+
+
+/*
  * The Illumos timer_create man page says it accepts the following clocks:
  *   CLOCK_REALTIME (3) wall clock
  *   CLOCK_VIRTUAL (1)  user CPU usage clock - No Backend
  *   CLOCK_PROF (2)     user and system CPU usage clock - No Backend
  *   CLOCK_HIGHRES (4)  non-adjustable, high-resolution clock

@@ -191,14 +212,60 @@
         sev.sigev_notify = ltos_sigev[lev.lx_sigev_notify];
         sev.sigev_signo = ltos_signo[lev.lx_sigev_signo];
         sev.sigev_value = lev.lx_sigev_value;
 
         /*
-         * The sigevent sigev_notify_function and sigev_notify_attributes
-         * members are not used by timer_create, so no conversion is needed.
+         * Assume all Linux libc implementations map SIGEV_THREAD to
+         * SIGEV_THREAD_ID and ignore passed-in attributes.
          */
+        sev.sigev_notify_attributes = NULL;
 
+        if (lev.lx_sigev_notify == LX_SIGEV_THREAD_ID) {
+                pid_t caller_pid = getpid();
+                pid_t target_pid;
+                int rval;
+
+                if ((rval = lx_lpid_to_spair(lev.lx_sigev_un.lx_tid,
+                            &target_pid, NULL)) != 0) {
+
+                        /*
+                         * Attempt to stick to the defined ERRORS in
+                         * timer_create(2).
+                         */
+                        if (rval == -ESRCH)
+                                return (-EINVAL);
+
+                        return (rval);
+                }
+
+                /*
+                 * The caller of SIGEV_THREAD_ID must be in the same
+                 * process as the target thread.
+                 */
+                if (caller_pid != target_pid)
+                        return (-EINVAL);
+
+                /*
+                 * Pass the original lx sigevent_t to the native
+                 * notify function so that it may pass it to the lx
+                 * helper thread. It is the responsibility of
+                 * lx_sigev_thread_id() to free lev_copy after the
+                 * information is relayed to lx.
+                 */
+                lx_sigevent_t *lev_copy = malloc(sizeof (lx_sigevent_t));
+                if (lev_copy == NULL)
+                        return (-ENOMEM);
+
+                if (uucopy(&lev, lev_copy, sizeof (lx_sigevent_t)) < 0) {
+                        free(lev_copy);
+                        return (-EFAULT);
+                }
+
+                sev.sigev_notify_function = lx_sigev_thread_id;
+                sev.sigev_value.sival_ptr = lev_copy;
+        }
+
         return ((timer_create(ltos_timer[clock], &sev, tid) < 0) ? -errno : 0);
 }
 
 long
 lx_timer_settime(timer_t tid, int flags, struct itimerspec *new_val,