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 */
31int	_lwp_cond_wait(lwp_cond_t *, lwp_mutex_t *);
32int	_lwp_cond_timedwait(lwp_cond_t *, lwp_mutex_t *, timespec_t *);
33int	_lwp_cond_reltimedwait(lwp_cond_t *, lwp_mutex_t *, timespec_t *);
34int	_lwp_cond_signal(lwp_cond_t *);
35int	_lwp_cond_broadcast(lwp_cond_t *);
36
37
38extern clock_t ddi_get_lbolt(void);
39
40static int cv__wait(kcondvar_t *, kmutex_t *, int);
41static clock_t cv__twait(kcondvar_t *, kmutex_t *, clock_t, int, int);
42
43static const lwp_cond_t  default_cv =
44	{{{0, 0, 0, 0}, USYNC_THREAD, _COND_MAGIC}, 0};
45
46
47/* ARGSUSED */
48void
49cv_init(kcondvar_t *cv, char *name, kcv_type_t typ, void *arg)
50{
51	*cv = default_cv;
52}
53
54/* ARGSUSED */
55void
56cv_destroy(kcondvar_t *cv)
57{
58}
59
60void
61cv_signal(kcondvar_t *cv)
62{
63	(void) _lwp_cond_signal(cv);
64}
65
66void
67cv_broadcast(kcondvar_t *cv)
68{
69	(void) _lwp_cond_broadcast(cv);
70}
71
72void
73cv_wait(kcondvar_t *cv, kmutex_t *mp)
74{
75	(void) cv__wait(cv, mp, 0);
76}
77
78int
79cv_wait_sig(kcondvar_t *cv, kmutex_t *mp)
80{
81	return (cv__wait(cv, mp, 1));
82}
83
84int
85cv__wait(kcondvar_t *cv, kmutex_t *mp, int sigok)
86{
87	int err;
88
89top:
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
105clock_t
106cv_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
114clock_t
115cv_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
123int
124cv_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*/
133clock_t
134cv_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
145clock_t
146cv_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
153clock_t
154cv_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 */
169static clock_t
170cv__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
186top:
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