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