1 /*-
2  * Copyright (c) 2014-2018 MongoDB, Inc.
3  * Copyright (c) 2008-2014 WiredTiger, Inc.
4  *	All rights reserved.
5  *
6  * See the file LICENSE for redistribution information.
7  */
8 
9 #include "wt_internal.h"
10 
11 /*
12  * __wt_cond_alloc --
13  *	Allocate and initialize a condition variable.
14  */
15 int
__wt_cond_alloc(WT_SESSION_IMPL * session,const char * name,WT_CONDVAR ** condp)16 __wt_cond_alloc(WT_SESSION_IMPL *session, const char *name, WT_CONDVAR **condp)
17 {
18 	WT_CONDVAR *cond;
19 	WT_DECL_RET;
20 
21 	WT_RET(__wt_calloc_one(session, &cond));
22 	WT_ERR(pthread_mutex_init(&cond->mtx, NULL));
23 
24 #ifdef HAVE_PTHREAD_COND_MONOTONIC
25 	{
26 	pthread_condattr_t condattr;
27 
28 	WT_ERR(pthread_condattr_init(&condattr));
29 	ret = pthread_condattr_setclock(&condattr, CLOCK_MONOTONIC);
30 	if (ret == 0)
31 		ret = pthread_cond_init(&cond->cond, &condattr);
32 	WT_TRET(pthread_condattr_destroy(&condattr));
33 	WT_ERR(ret);
34 	}
35 #else
36 	WT_ERR(pthread_cond_init(&cond->cond, NULL));
37 #endif
38 
39 	cond->name = name;
40 	cond->waiters = 0;
41 
42 	*condp = cond;
43 	return (0);
44 
45 err:	__wt_free(session, cond);
46 	return (ret);
47 }
48 
49 /*
50  * __wt_cond_wait_signal --
51  *	Wait on a mutex, optionally timing out.  If we get it before the time
52  * out period expires, let the caller know.
53  */
54 void
__wt_cond_wait_signal(WT_SESSION_IMPL * session,WT_CONDVAR * cond,uint64_t usecs,bool (* run_func)(WT_SESSION_IMPL *),bool * signalled)55 __wt_cond_wait_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond,
56     uint64_t usecs, bool (*run_func)(WT_SESSION_IMPL *), bool *signalled)
57 {
58 	struct timespec ts;
59 	WT_DECL_RET;
60 	WT_TRACK_OP_DECL;
61 	bool locked;
62 
63 	WT_TRACK_OP_INIT(session);
64 
65 	locked = false;
66 
67 	/* Fast path if already signalled. */
68 	*signalled = true;
69 	if (__wt_atomic_addi32(&cond->waiters, 1) == 0) {
70 		WT_TRACK_OP_END(session);
71 		return;
72 	}
73 
74 	__wt_verbose(session, WT_VERB_MUTEX, "wait %s", cond->name);
75 	WT_STAT_CONN_INCR(session, cond_wait);
76 
77 	WT_ERR(pthread_mutex_lock(&cond->mtx));
78 	locked = true;
79 
80 	/*
81 	 * It's possible to race with threads waking us up. That's not a problem
82 	 * if there are multiple wakeups because the next wakeup will get us, or
83 	 * if we're only pausing for a short period. It's a problem if there's
84 	 * only a single wakeup, our waker is likely waiting for us to exit.
85 	 * After acquiring the mutex (so we're guaranteed to be awakened by any
86 	 * future wakeup call), optionally check if we're OK to keep running.
87 	 * This won't ensure our caller won't just loop and call us again, but
88 	 * at least it's not our fault.
89 	 *
90 	 * Assert we're not waiting longer than a second if not checking the
91 	 * run status.
92 	 */
93 	WT_ASSERT(session, run_func != NULL || usecs <= WT_MILLION);
94 	if (run_func != NULL && !run_func(session))
95 		goto skipping;
96 
97 	if (usecs > 0) {
98 		/*
99 		 * Get the current time as the basis for calculating when the
100 		 * wait should end.  Prefer a monotonic clock source to avoid
101 		 * unexpectedly long sleeps when the system clock is adjusted.
102 		 *
103 		 * Failing that, query the time directly and don't attempt to
104 		 * correct for the clock moving backwards, which would result
105 		 * in a sleep that is too long by however much the clock is
106 		 * updated.  This isn't as good as a monotonic clock source but
107 		 * makes the window of vulnerability smaller (i.e., the
108 		 * calculated time is only incorrect if the system clock
109 		 * changes in between us querying it and waiting).
110 		 */
111 #ifdef HAVE_PTHREAD_COND_MONOTONIC
112 		WT_SYSCALL_RETRY(clock_gettime(CLOCK_MONOTONIC, &ts), ret);
113 		if (ret != 0)
114 			WT_PANIC_MSG(session, ret, "clock_gettime");
115 #else
116 		__wt_epoch_raw(session, &ts);
117 #endif
118 		ts.tv_sec += (time_t)
119 		    (((uint64_t)ts.tv_nsec + WT_THOUSAND * usecs) / WT_BILLION);
120 		ts.tv_nsec = (long)
121 		    (((uint64_t)ts.tv_nsec + WT_THOUSAND * usecs) % WT_BILLION);
122 		ret = pthread_cond_timedwait(&cond->cond, &cond->mtx, &ts);
123 	} else
124 		ret = pthread_cond_wait(&cond->cond, &cond->mtx);
125 
126 	/*
127 	 * Check pthread_cond_wait() return for EINTR, ETIME and
128 	 * ETIMEDOUT, some systems return these errors.
129 	 */
130 	if (ret == EINTR ||
131 #ifdef ETIME
132 	    ret == ETIME ||
133 #endif
134 	    ret == ETIMEDOUT) {
135 skipping:	*signalled = false;
136 		ret = 0;
137 	}
138 
139 err:	(void)__wt_atomic_subi32(&cond->waiters, 1);
140 
141 	if (locked)
142 		WT_TRET(pthread_mutex_unlock(&cond->mtx));
143 
144 	WT_TRACK_OP_END(session);
145 	if (ret == 0)
146 		return;
147 
148 	WT_PANIC_MSG(session, ret, "pthread_cond_wait: %s", cond->name);
149 }
150 
151 /*
152  * __wt_cond_signal --
153  *	Signal a waiting thread.
154  */
155 void
__wt_cond_signal(WT_SESSION_IMPL * session,WT_CONDVAR * cond)156 __wt_cond_signal(WT_SESSION_IMPL *session, WT_CONDVAR *cond)
157 {
158 	WT_DECL_RET;
159 
160 	__wt_verbose(session, WT_VERB_MUTEX, "signal %s", cond->name);
161 
162 	/*
163 	 * Our callers often set flags to cause a thread to exit. Add a barrier
164 	 * to ensure exit flags are seen by the sleeping threads, otherwise we
165 	 * can wake up a thread, it immediately goes back to sleep, and we'll
166 	 * hang. Use a full barrier (we may not write before waiting on thread
167 	 * join).
168 	 */
169 	WT_FULL_BARRIER();
170 
171 	/*
172 	 * Fast path if we are in (or can enter), a state where the next waiter
173 	 * will return immediately as already signaled.
174 	 */
175 	if (cond->waiters == -1 ||
176 	    (cond->waiters == 0 && __wt_atomic_casi32(&cond->waiters, 0, -1)))
177 		return;
178 
179 	WT_ERR(pthread_mutex_lock(&cond->mtx));
180 	ret = pthread_cond_broadcast(&cond->cond);
181 	WT_TRET(pthread_mutex_unlock(&cond->mtx));
182 	if (ret == 0)
183 		return;
184 
185 err:
186 	WT_PANIC_MSG(session, ret, "pthread_cond_broadcast: %s", cond->name);
187 }
188 
189 /*
190  * __wt_cond_destroy --
191  *	Destroy a condition variable.
192  */
193 void
__wt_cond_destroy(WT_SESSION_IMPL * session,WT_CONDVAR ** condp)194 __wt_cond_destroy(WT_SESSION_IMPL *session, WT_CONDVAR **condp)
195 {
196 	WT_CONDVAR *cond;
197 	WT_DECL_RET;
198 
199 	cond = *condp;
200 	if (cond == NULL)
201 		return;
202 
203 	if ((ret = pthread_cond_destroy(&cond->cond)) != 0)
204 		WT_PANIC_MSG(
205 		    session, ret, "pthread_cond_destroy: %s", cond->name);
206 
207 	if ((ret = pthread_mutex_destroy(&cond->mtx)) != 0)
208 		WT_PANIC_MSG(
209 		    session, ret, "pthread_mutex_destroy: %s", cond->name);
210 
211 	__wt_free(session, *condp);
212 }
213