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