1 /*
   2  * This file and its contents are supplied under the terms of the
   3  * Common Development and Distribution License ("CDDL"), version 1.0.
   4  * You may only use this file in accordance with the terms of version
   5  * 1.0 of the CDDL.
   6  *
   7  * A full copy of the text of the CDDL should have accompanied this
   8  * source.  A copy of the CDDL is also available via the Internet at
   9  * http://www.illumos.org/license/CDDL.
  10  */
  11 
  12 /*
  13  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  14  */
  15 
  16 /*
  17  * Implement timeout(9f), untimeout(9f) on top of
  18  * libc timer_create, timer_settime, etc.
  19  */
  20 
  21 #include <sys/types.h>
  22 #include <sys/systm.h>
  23 #include <time.h>
  24 
  25 typedef void (*sigev_notify_func_t)(union sigval);
  26 
  27 /*
  28  * We never actually reference anything in this array, using it
  29  * just as a collection of addresses mapped from/to int values.
  30  * It would be fine to take addresses even beyond the end, but
  31  * to avoid confusion it's sized larger than _TIMER_MAX (32).
  32  */
  33 static char timeout_base[100];
  34 
  35 timeout_id_t
  36 timeout(void (*func)(void *), void *arg, clock_t delta)
  37 {
  38         struct sigevent sev;
  39         struct itimerspec its;
  40         timer_t tid;
  41         int err;
  42 
  43         if (delta <= 0)
  44                 return (NULL);
  45 
  46         bzero(&sev, sizeof (sev));
  47         sev.sigev_notify = SIGEV_THREAD;
  48         sev.sigev_value.sival_ptr = arg;
  49         sev.sigev_notify_function = (sigev_notify_func_t)func;
  50         err = timer_create(CLOCK_REALTIME, &sev, &tid);
  51         if (err != 0)
  52                 return (NULL);
  53 
  54         bzero(&its, sizeof (its));
  55         TICK_TO_TIMESTRUC(delta, &its.it_value);
  56         err = timer_settime(tid, 0, &its, NULL);
  57         if (err != 0) {
  58                 (void) timer_delete(tid);
  59                 return (NULL);
  60         }
  61 
  62         /* Convert return to a (sort of) pointer */
  63         return (timeout_base + tid);
  64 }
  65 
  66 clock_t
  67 untimeout(timeout_id_t id_arg)
  68 {
  69         struct itimerspec its;
  70         char *id_cp = id_arg;
  71         clock_t delta;
  72         timer_t tid;
  73         int rc;
  74 
  75         if (id_arg == NULL)
  76                 return (-1);
  77 
  78         /* Convert id_arg back to small integer. */
  79         tid = (int)(id_cp - timeout_base);
  80 
  81         bzero(&its, sizeof (its));
  82         rc = timer_settime(tid, 0, &its, &its);
  83         if (rc != 0) {
  84                 delta = 0;
  85         } else {
  86                 delta = TIMESTRUC_TO_TICK(&its.it_value);
  87                 if (delta < 0)
  88                         delta = 0;
  89         }
  90 
  91         rc = timer_delete(tid);
  92         if (rc != 0)
  93                 delta = -1;
  94 
  95         return (delta);
  96 }