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 2013 Nexenta Systems, Inc.  All rights reserved.
  14  * Copyright 2017 RackTop Systems.
  15  */
  16 
  17 /*
  18  * condvar(9f)
  19  */
  20 
  21 /* This is the API we're emulating */
  22 #include <sys/condvar.h>
  23 
  24 #include <sys/errno.h>
  25 #include <sys/debug.h>
  26 #include <sys/thread.h>
  27 
  28 /* avoiding synch.h */
  29 int     _lwp_cond_wait(lwp_cond_t *, lwp_mutex_t *);
  30 int     _lwp_cond_timedwait(lwp_cond_t *, lwp_mutex_t *, timespec_t *);
  31 int     _lwp_cond_reltimedwait(lwp_cond_t *, lwp_mutex_t *, timespec_t *);
  32 int     _lwp_cond_signal(lwp_cond_t *);
  33 int     _lwp_cond_broadcast(lwp_cond_t *);
  34 
  35 
  36 extern clock_t ddi_get_lbolt(void);
  37 extern void clock2ts(clock_t, timespec_t *);
  38 
  39 static int cv__wait(kcondvar_t *, kmutex_t *, int);
  40 static clock_t cv__twait(kcondvar_t *, kmutex_t *, clock_t, int);
  41 
  42 static const lwp_cond_t  default_cv =
  43         {{{0, 0, 0, 0}, USYNC_THREAD, _COND_MAGIC}, 0};
  44 
  45 
  46 /* ARGSUSED */
  47 void
  48 cv_init(kcondvar_t *cv, char *name, kcv_type_t typ, void *arg)
  49 {
  50         *cv = default_cv;
  51 }
  52 
  53 /* ARGSUSED */
  54 void
  55 cv_destroy(kcondvar_t *cv)
  56 {
  57 }
  58 
  59 void
  60 cv_signal(kcondvar_t *cv)
  61 {
  62         (void) _lwp_cond_signal(cv);
  63 }
  64 
  65 void
  66 cv_broadcast(kcondvar_t *cv)
  67 {
  68         (void) _lwp_cond_broadcast(cv);
  69 }
  70 
  71 void
  72 cv_wait(kcondvar_t *cv, kmutex_t *mp)
  73 {
  74         (void) cv__wait(cv, mp, 0);
  75 }
  76 
  77 int
  78 cv_wait_sig(kcondvar_t *cv, kmutex_t *mp)
  79 {
  80         return (cv__wait(cv, mp, 1));
  81 }
  82 
  83 int
  84 cv__wait(kcondvar_t *cv, kmutex_t *mp, int sigok)
  85 {
  86         int err;
  87 
  88 top:
  89         ASSERT(mp->m_owner == _curthread());
  90         mp->m_owner = _KTHREAD_INVALID;
  91         err = _lwp_cond_wait(cv, &mp->m_lock);
  92         mp->m_owner = _curthread();
  93 
  94         if (err == 0)
  95                 return (1);
  96         if (err == EINTR) {
  97                 if (sigok)
  98                         return (0);
  99                 goto top;
 100         }
 101         return (-1);
 102 }
 103 
 104 clock_t
 105 cv_timedwait(kcondvar_t *cv, kmutex_t *mp, clock_t abstime)
 106 {
 107         clock_t delta;
 108 
 109         delta = abstime - ddi_get_lbolt();
 110         return (cv__twait(cv, mp, delta, 0));
 111 }
 112 
 113 clock_t
 114 cv_timedwait_sig(kcondvar_t *cv, kmutex_t *mp, clock_t abstime)
 115 {
 116         clock_t delta;
 117 
 118         delta = abstime - ddi_get_lbolt();
 119         return (cv__twait(cv, mp, delta, 1));
 120 }
 121 
 122 /*ARGSUSED*/
 123 clock_t
 124 cv_timedwait_hires(kcondvar_t *cv, kmutex_t *mp, hrtime_t tim, hrtime_t res,
 125         int flag)
 126 {
 127         clock_t delta;
 128 
 129         delta = tim;
 130         if (flag & CALLOUT_FLAG_ABSOLUTE)
 131                 delta -= gethrtime();
 132         return (cv__twait(cv, mp, delta, 0));
 133 }
 134 
 135 clock_t
 136 cv_reltimedwait(kcondvar_t *cv, kmutex_t *mp, clock_t delta, time_res_t res)
 137 {
 138         _NOTE(ARGUNUSED(res))
 139 
 140         return (cv__twait(cv, mp, delta, 0));
 141 }
 142 
 143 clock_t
 144 cv_reltimedwait_sig(kcondvar_t *cv, kmutex_t *mp, clock_t delta,
 145     time_res_t res)
 146 {
 147         _NOTE(ARGUNUSED(res))
 148 
 149         return (cv__twait(cv, mp, delta, 1));
 150 }
 151 
 152 /*
 153  * Factored out implementation of all the cv_*timedwait* functions.
 154  * Note that the delta passed in is relative to the (simulated)
 155  * current time reported by ddi_get_lbolt().  Convert that to
 156  * timespec format and keep calling _lwp_cond_reltimedwait,
 157  * which (NB!) decrements that delta in-place!
 158  */
 159 static clock_t
 160 cv__twait(kcondvar_t *cv, kmutex_t *mp, clock_t delta, int sigok)
 161 {
 162         timestruc_t ts;
 163         int err;
 164 
 165         if (delta <= 0)
 166                 return (-1);
 167 
 168         clock2ts(delta, &ts);
 169 
 170 top:
 171         if (ts.tv_sec == 0 && ts.tv_nsec == 0)
 172                 return (-1);
 173 
 174         ASSERT(mp->m_owner == _curthread());
 175         mp->m_owner = _KTHREAD_INVALID;
 176         err = _lwp_cond_reltimedwait(cv, &mp->m_lock, &ts);
 177         mp->m_owner = _curthread();
 178 
 179         switch (err) {
 180         case 0:
 181                 return (1);
 182         case EINTR:
 183                 if (sigok)
 184                         return (0);
 185                 goto top;
 186         default:
 187                 ASSERT(0);
 188                 /* FALLTHROUGH */
 189         case ETIME:
 190                 break;
 191         }
 192 
 193         return (-1);
 194 }