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 #include <sys/systm.h>
28 
29 /* avoiding synch.h */
30 int	_lwp_cond_wait(lwp_cond_t *, lwp_mutex_t *);
31 int	_lwp_cond_timedwait(lwp_cond_t *, lwp_mutex_t *, timespec_t *);
32 int	_lwp_cond_reltimedwait(lwp_cond_t *, lwp_mutex_t *, timespec_t *);
33 int	_lwp_cond_signal(lwp_cond_t *);
34 int	_lwp_cond_broadcast(lwp_cond_t *);
35 
36 
37 extern clock_t ddi_get_lbolt(void);
38 
39 static int cv__wait(kcondvar_t *, kmutex_t *, int);
40 static clock_t cv__twait(kcondvar_t *, kmutex_t *, clock_t, int, 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, 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, 0));
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, 1));
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, 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, 0));
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, int hires)
161 {
162 	timestruc_t ts;
163 	int err;
164 
165 	if (delta <= 0)
166 		return (-1);
167 
168 	if (hires) {
169 		ts.tv_sec = delta / NANOSEC;
170 		ts.tv_nsec = delta % NANOSEC;
171 	} else {
172 		ts.tv_sec = delta / hz;
173 		ts.tv_nsec = (delta % hz) * (NANOSEC / hz);
174 	}
175 
176 top:
177 	if (ts.tv_sec == 0 && ts.tv_nsec == 0)
178 		return (-1);
179 
180 	ASSERT(mp->m_owner == _curthread());
181 	mp->m_owner = _KTHREAD_INVALID;
182 	err = _lwp_cond_reltimedwait(cv, &mp->m_lock, &ts);
183 	mp->m_owner = _curthread();
184 
185 	switch (err) {
186 	case 0:
187 		return (1);
188 	case EINTR:
189 		if (sigok)
190 			return (0);
191 		goto top;
192 	default:
193 		ASSERT(0);
194 		/* FALLTHROUGH */
195 	case ETIME:
196 		break;
197 	}
198 
199 	return (-1);
200 }
201