1*74e9b5f2SOlivier Houchard #include <ck_ec.h>
2*74e9b5f2SOlivier Houchard #include <ck_limits.h>
3*74e9b5f2SOlivier Houchard
4*74e9b5f2SOlivier Houchard #include "ck_ec_timeutil.h"
5*74e9b5f2SOlivier Houchard
6*74e9b5f2SOlivier Houchard #define DEFAULT_BUSY_LOOP_ITER 100U
7*74e9b5f2SOlivier Houchard
8*74e9b5f2SOlivier Houchard /*
9*74e9b5f2SOlivier Houchard * The 2ms, 8x/iter default parameter hit 1.024 seconds after 3
10*74e9b5f2SOlivier Houchard * iterations.
11*74e9b5f2SOlivier Houchard */
12*74e9b5f2SOlivier Houchard #define DEFAULT_INITIAL_WAIT_NS 2000000L /* Start at 2 ms */
13*74e9b5f2SOlivier Houchard /* Grow the wait time 8x/iteration. */
14*74e9b5f2SOlivier Houchard #define DEFAULT_WAIT_SCALE_FACTOR 8
15*74e9b5f2SOlivier Houchard #define DEFAULT_WAIT_SHIFT_COUNT 0
16*74e9b5f2SOlivier Houchard
17*74e9b5f2SOlivier Houchard struct ck_ec32_slow_path_state {
18*74e9b5f2SOlivier Houchard struct ck_ec32 *ec;
19*74e9b5f2SOlivier Houchard uint32_t flagged_word;
20*74e9b5f2SOlivier Houchard };
21*74e9b5f2SOlivier Houchard
22*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
23*74e9b5f2SOlivier Houchard struct ck_ec64_slow_path_state {
24*74e9b5f2SOlivier Houchard struct ck_ec64 *ec;
25*74e9b5f2SOlivier Houchard uint64_t flagged_word;
26*74e9b5f2SOlivier Houchard };
27*74e9b5f2SOlivier Houchard #endif
28*74e9b5f2SOlivier Houchard
29*74e9b5f2SOlivier Houchard /* Once we've waited for >= 1 sec, go for the full deadline. */
30*74e9b5f2SOlivier Houchard static const struct timespec final_wait_time = {
31*74e9b5f2SOlivier Houchard .tv_sec = 1
32*74e9b5f2SOlivier Houchard };
33*74e9b5f2SOlivier Houchard
34*74e9b5f2SOlivier Houchard void
ck_ec32_wake(struct ck_ec32 * ec,const struct ck_ec_ops * ops)35*74e9b5f2SOlivier Houchard ck_ec32_wake(struct ck_ec32 *ec, const struct ck_ec_ops *ops)
36*74e9b5f2SOlivier Houchard {
37*74e9b5f2SOlivier Houchard /* Spurious wake-ups are OK. Clear the flag before futexing. */
38*74e9b5f2SOlivier Houchard ck_pr_and_32(&ec->counter, (1U << 31) - 1);
39*74e9b5f2SOlivier Houchard ops->wake32(ops, &ec->counter);
40*74e9b5f2SOlivier Houchard return;
41*74e9b5f2SOlivier Houchard }
42*74e9b5f2SOlivier Houchard
43*74e9b5f2SOlivier Houchard int
ck_ec32_wait_slow(struct ck_ec32 * ec,const struct ck_ec_ops * ops,uint32_t old_value,const struct timespec * deadline)44*74e9b5f2SOlivier Houchard ck_ec32_wait_slow(struct ck_ec32 *ec,
45*74e9b5f2SOlivier Houchard const struct ck_ec_ops *ops,
46*74e9b5f2SOlivier Houchard uint32_t old_value,
47*74e9b5f2SOlivier Houchard const struct timespec *deadline)
48*74e9b5f2SOlivier Houchard {
49*74e9b5f2SOlivier Houchard return ck_ec32_wait_pred_slow(ec, ops, old_value,
50*74e9b5f2SOlivier Houchard NULL, NULL, deadline);
51*74e9b5f2SOlivier Houchard }
52*74e9b5f2SOlivier Houchard
53*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
54*74e9b5f2SOlivier Houchard void
ck_ec64_wake(struct ck_ec64 * ec,const struct ck_ec_ops * ops)55*74e9b5f2SOlivier Houchard ck_ec64_wake(struct ck_ec64 *ec, const struct ck_ec_ops *ops)
56*74e9b5f2SOlivier Houchard {
57*74e9b5f2SOlivier Houchard ck_pr_and_64(&ec->counter, ~1);
58*74e9b5f2SOlivier Houchard ops->wake64(ops, &ec->counter);
59*74e9b5f2SOlivier Houchard return;
60*74e9b5f2SOlivier Houchard }
61*74e9b5f2SOlivier Houchard
62*74e9b5f2SOlivier Houchard int
ck_ec64_wait_slow(struct ck_ec64 * ec,const struct ck_ec_ops * ops,uint64_t old_value,const struct timespec * deadline)63*74e9b5f2SOlivier Houchard ck_ec64_wait_slow(struct ck_ec64 *ec,
64*74e9b5f2SOlivier Houchard const struct ck_ec_ops *ops,
65*74e9b5f2SOlivier Houchard uint64_t old_value,
66*74e9b5f2SOlivier Houchard const struct timespec *deadline)
67*74e9b5f2SOlivier Houchard {
68*74e9b5f2SOlivier Houchard return ck_ec64_wait_pred_slow(ec, ops, old_value,
69*74e9b5f2SOlivier Houchard NULL, NULL, deadline);
70*74e9b5f2SOlivier Houchard }
71*74e9b5f2SOlivier Houchard #endif
72*74e9b5f2SOlivier Houchard
73*74e9b5f2SOlivier Houchard int
ck_ec_deadline_impl(struct timespec * new_deadline,const struct ck_ec_ops * ops,const struct timespec * timeout)74*74e9b5f2SOlivier Houchard ck_ec_deadline_impl(struct timespec *new_deadline,
75*74e9b5f2SOlivier Houchard const struct ck_ec_ops *ops,
76*74e9b5f2SOlivier Houchard const struct timespec *timeout)
77*74e9b5f2SOlivier Houchard {
78*74e9b5f2SOlivier Houchard struct timespec now;
79*74e9b5f2SOlivier Houchard int r;
80*74e9b5f2SOlivier Houchard
81*74e9b5f2SOlivier Houchard if (timeout == NULL) {
82*74e9b5f2SOlivier Houchard new_deadline->tv_sec = TIME_MAX;
83*74e9b5f2SOlivier Houchard new_deadline->tv_nsec = NSEC_MAX;
84*74e9b5f2SOlivier Houchard return 0;
85*74e9b5f2SOlivier Houchard }
86*74e9b5f2SOlivier Houchard
87*74e9b5f2SOlivier Houchard r = ops->gettime(ops, &now);
88*74e9b5f2SOlivier Houchard if (r != 0) {
89*74e9b5f2SOlivier Houchard return -1;
90*74e9b5f2SOlivier Houchard }
91*74e9b5f2SOlivier Houchard
92*74e9b5f2SOlivier Houchard *new_deadline = timespec_add(now, *timeout);
93*74e9b5f2SOlivier Houchard return 0;
94*74e9b5f2SOlivier Houchard }
95*74e9b5f2SOlivier Houchard
96*74e9b5f2SOlivier Houchard /* The rest of the file implements wait_pred_slow. */
97*74e9b5f2SOlivier Houchard
98*74e9b5f2SOlivier Houchard /*
99*74e9b5f2SOlivier Houchard * Returns a timespec value for deadline_ptr. If deadline_ptr is NULL,
100*74e9b5f2SOlivier Houchard * returns a timespec far in the future.
101*74e9b5f2SOlivier Houchard */
102*74e9b5f2SOlivier Houchard static struct timespec
canonical_deadline(const struct timespec * deadline_ptr)103*74e9b5f2SOlivier Houchard canonical_deadline(const struct timespec *deadline_ptr)
104*74e9b5f2SOlivier Houchard {
105*74e9b5f2SOlivier Houchard
106*74e9b5f2SOlivier Houchard if (deadline_ptr == NULL) {
107*74e9b5f2SOlivier Houchard return (struct timespec) { .tv_sec = TIME_MAX };
108*74e9b5f2SOlivier Houchard }
109*74e9b5f2SOlivier Houchard
110*74e9b5f2SOlivier Houchard return *deadline_ptr;
111*74e9b5f2SOlivier Houchard }
112*74e9b5f2SOlivier Houchard
113*74e9b5f2SOlivier Houchard /*
114*74e9b5f2SOlivier Houchard * Really slow (sleeping) path for ck_ec_wait. Drives the exponential
115*74e9b5f2SOlivier Houchard * backoff scheme to sleep for longer and longer periods of time,
116*74e9b5f2SOlivier Houchard * until either the sleep function returns true (the eventcount's
117*74e9b5f2SOlivier Houchard * value has changed), or the predicate returns non-0 (something else
118*74e9b5f2SOlivier Houchard * has changed).
119*74e9b5f2SOlivier Houchard *
120*74e9b5f2SOlivier Houchard * If deadline is ever reached, returns -1 (timeout).
121*74e9b5f2SOlivier Houchard *
122*74e9b5f2SOlivier Houchard * TODO: add some form of randomisation to the intermediate timeout
123*74e9b5f2SOlivier Houchard * values.
124*74e9b5f2SOlivier Houchard */
125*74e9b5f2SOlivier Houchard static int
exponential_backoff(struct ck_ec_wait_state * wait_state,bool (* sleep)(const void * sleep_state,const struct ck_ec_wait_state * wait_state,const struct timespec * partial_deadline),const void * sleep_state,int (* pred)(const struct ck_ec_wait_state * state,struct timespec * deadline),const struct timespec * deadline)126*74e9b5f2SOlivier Houchard exponential_backoff(struct ck_ec_wait_state *wait_state,
127*74e9b5f2SOlivier Houchard bool (*sleep)(const void *sleep_state,
128*74e9b5f2SOlivier Houchard const struct ck_ec_wait_state *wait_state,
129*74e9b5f2SOlivier Houchard const struct timespec *partial_deadline),
130*74e9b5f2SOlivier Houchard const void *sleep_state,
131*74e9b5f2SOlivier Houchard int (*pred)(const struct ck_ec_wait_state *state,
132*74e9b5f2SOlivier Houchard struct timespec *deadline),
133*74e9b5f2SOlivier Houchard const struct timespec *deadline)
134*74e9b5f2SOlivier Houchard {
135*74e9b5f2SOlivier Houchard struct timespec begin;
136*74e9b5f2SOlivier Houchard struct timespec stop_backoff;
137*74e9b5f2SOlivier Houchard const struct ck_ec_ops *ops = wait_state->ops;
138*74e9b5f2SOlivier Houchard const uint32_t scale_factor = (ops->wait_scale_factor != 0)
139*74e9b5f2SOlivier Houchard ? ops->wait_scale_factor
140*74e9b5f2SOlivier Houchard : DEFAULT_WAIT_SCALE_FACTOR;
141*74e9b5f2SOlivier Houchard const uint32_t shift_count = (ops->wait_shift_count != 0)
142*74e9b5f2SOlivier Houchard ? ops->wait_shift_count
143*74e9b5f2SOlivier Houchard : DEFAULT_WAIT_SHIFT_COUNT;
144*74e9b5f2SOlivier Houchard uint32_t wait_ns = (ops->initial_wait_ns != 0)
145*74e9b5f2SOlivier Houchard ? ops->initial_wait_ns
146*74e9b5f2SOlivier Houchard : DEFAULT_INITIAL_WAIT_NS;
147*74e9b5f2SOlivier Houchard bool first = true;
148*74e9b5f2SOlivier Houchard
149*74e9b5f2SOlivier Houchard for (;;) {
150*74e9b5f2SOlivier Houchard struct timespec now;
151*74e9b5f2SOlivier Houchard struct timespec partial_deadline;
152*74e9b5f2SOlivier Houchard
153*74e9b5f2SOlivier Houchard if (check_deadline(&now, ops, *deadline) == true) {
154*74e9b5f2SOlivier Houchard /* Timeout. Bail out. */
155*74e9b5f2SOlivier Houchard return -1;
156*74e9b5f2SOlivier Houchard }
157*74e9b5f2SOlivier Houchard
158*74e9b5f2SOlivier Houchard if (first) {
159*74e9b5f2SOlivier Houchard begin = now;
160*74e9b5f2SOlivier Houchard wait_state->start = begin;
161*74e9b5f2SOlivier Houchard stop_backoff = timespec_add(begin, final_wait_time);
162*74e9b5f2SOlivier Houchard first = false;
163*74e9b5f2SOlivier Houchard }
164*74e9b5f2SOlivier Houchard
165*74e9b5f2SOlivier Houchard wait_state->now = now;
166*74e9b5f2SOlivier Houchard if (timespec_cmp(now, stop_backoff) >= 0) {
167*74e9b5f2SOlivier Houchard partial_deadline = *deadline;
168*74e9b5f2SOlivier Houchard } else {
169*74e9b5f2SOlivier Houchard do {
170*74e9b5f2SOlivier Houchard partial_deadline =
171*74e9b5f2SOlivier Houchard timespec_add_ns(begin, wait_ns);
172*74e9b5f2SOlivier Houchard wait_ns =
173*74e9b5f2SOlivier Houchard wait_time_scale(wait_ns,
174*74e9b5f2SOlivier Houchard scale_factor,
175*74e9b5f2SOlivier Houchard shift_count);
176*74e9b5f2SOlivier Houchard } while (timespec_cmp(partial_deadline, now) <= 0);
177*74e9b5f2SOlivier Houchard }
178*74e9b5f2SOlivier Houchard
179*74e9b5f2SOlivier Houchard if (pred != NULL) {
180*74e9b5f2SOlivier Houchard int r = pred(wait_state, &partial_deadline);
181*74e9b5f2SOlivier Houchard if (r != 0) {
182*74e9b5f2SOlivier Houchard return r;
183*74e9b5f2SOlivier Houchard }
184*74e9b5f2SOlivier Houchard }
185*74e9b5f2SOlivier Houchard
186*74e9b5f2SOlivier Houchard /* Canonicalize deadlines in the far future to NULL. */
187*74e9b5f2SOlivier Houchard if (sleep(sleep_state, wait_state,
188*74e9b5f2SOlivier Houchard ((partial_deadline.tv_sec == TIME_MAX)
189*74e9b5f2SOlivier Houchard ? NULL : &partial_deadline)) == true) {
190*74e9b5f2SOlivier Houchard return 0;
191*74e9b5f2SOlivier Houchard }
192*74e9b5f2SOlivier Houchard }
193*74e9b5f2SOlivier Houchard }
194*74e9b5f2SOlivier Houchard
195*74e9b5f2SOlivier Houchard /*
196*74e9b5f2SOlivier Houchard * Loops up to BUSY_LOOP_ITER times, or until ec's counter value
197*74e9b5f2SOlivier Houchard * (including the flag) differs from old_value.
198*74e9b5f2SOlivier Houchard *
199*74e9b5f2SOlivier Houchard * Returns the new value in ec.
200*74e9b5f2SOlivier Houchard */
201*74e9b5f2SOlivier Houchard #define DEF_WAIT_EASY(W) \
202*74e9b5f2SOlivier Houchard static uint##W##_t ck_ec##W##_wait_easy(struct ck_ec##W* ec, \
203*74e9b5f2SOlivier Houchard const struct ck_ec_ops *ops, \
204*74e9b5f2SOlivier Houchard uint##W##_t expected) \
205*74e9b5f2SOlivier Houchard { \
206*74e9b5f2SOlivier Houchard uint##W##_t current = ck_pr_load_##W(&ec->counter); \
207*74e9b5f2SOlivier Houchard size_t n = (ops->busy_loop_iter != 0) \
208*74e9b5f2SOlivier Houchard ? ops->busy_loop_iter \
209*74e9b5f2SOlivier Houchard : DEFAULT_BUSY_LOOP_ITER; \
210*74e9b5f2SOlivier Houchard \
211*74e9b5f2SOlivier Houchard for (size_t i = 0; \
212*74e9b5f2SOlivier Houchard i < n && current == expected; \
213*74e9b5f2SOlivier Houchard i++) { \
214*74e9b5f2SOlivier Houchard ck_pr_stall(); \
215*74e9b5f2SOlivier Houchard current = ck_pr_load_##W(&ec->counter); \
216*74e9b5f2SOlivier Houchard } \
217*74e9b5f2SOlivier Houchard \
218*74e9b5f2SOlivier Houchard return current; \
219*74e9b5f2SOlivier Houchard }
220*74e9b5f2SOlivier Houchard
221*74e9b5f2SOlivier Houchard DEF_WAIT_EASY(32)
222*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
223*74e9b5f2SOlivier Houchard DEF_WAIT_EASY(64)
224*74e9b5f2SOlivier Houchard #endif
225*74e9b5f2SOlivier Houchard #undef DEF_WAIT_EASY
226*74e9b5f2SOlivier Houchard /*
227*74e9b5f2SOlivier Houchard * Attempts to upgrade ec->counter from unflagged to flagged.
228*74e9b5f2SOlivier Houchard *
229*74e9b5f2SOlivier Houchard * Returns true if the event count has changed. Otherwise, ec's
230*74e9b5f2SOlivier Houchard * counter word is equal to flagged on return, or has been at some
231*74e9b5f2SOlivier Houchard * time before the return.
232*74e9b5f2SOlivier Houchard */
233*74e9b5f2SOlivier Houchard #define DEF_UPGRADE(W) \
234*74e9b5f2SOlivier Houchard static bool ck_ec##W##_upgrade(struct ck_ec##W* ec, \
235*74e9b5f2SOlivier Houchard uint##W##_t current, \
236*74e9b5f2SOlivier Houchard uint##W##_t unflagged, \
237*74e9b5f2SOlivier Houchard uint##W##_t flagged) \
238*74e9b5f2SOlivier Houchard { \
239*74e9b5f2SOlivier Houchard uint##W##_t old_word; \
240*74e9b5f2SOlivier Houchard \
241*74e9b5f2SOlivier Houchard if (current == flagged) { \
242*74e9b5f2SOlivier Houchard /* Nothing to do, no change. */ \
243*74e9b5f2SOlivier Houchard return false; \
244*74e9b5f2SOlivier Houchard } \
245*74e9b5f2SOlivier Houchard \
246*74e9b5f2SOlivier Houchard if (current != unflagged) { \
247*74e9b5f2SOlivier Houchard /* We have a different counter value! */ \
248*74e9b5f2SOlivier Houchard return true; \
249*74e9b5f2SOlivier Houchard } \
250*74e9b5f2SOlivier Houchard \
251*74e9b5f2SOlivier Houchard /* \
252*74e9b5f2SOlivier Houchard * Flag the counter value. The CAS only fails if the \
253*74e9b5f2SOlivier Houchard * counter is already flagged, or has a new value. \
254*74e9b5f2SOlivier Houchard */ \
255*74e9b5f2SOlivier Houchard return (ck_pr_cas_##W##_value(&ec->counter, \
256*74e9b5f2SOlivier Houchard unflagged, flagged, \
257*74e9b5f2SOlivier Houchard &old_word) == false && \
258*74e9b5f2SOlivier Houchard old_word != flagged); \
259*74e9b5f2SOlivier Houchard }
260*74e9b5f2SOlivier Houchard
261*74e9b5f2SOlivier Houchard DEF_UPGRADE(32)
262*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
263*74e9b5f2SOlivier Houchard DEF_UPGRADE(64)
264*74e9b5f2SOlivier Houchard #endif
265*74e9b5f2SOlivier Houchard #undef DEF_UPGRADE
266*74e9b5f2SOlivier Houchard
267*74e9b5f2SOlivier Houchard /*
268*74e9b5f2SOlivier Houchard * Blocks until partial_deadline on the ck_ec. Returns true if the
269*74e9b5f2SOlivier Houchard * eventcount's value has changed. If partial_deadline is NULL, wait
270*74e9b5f2SOlivier Houchard * forever.
271*74e9b5f2SOlivier Houchard */
272*74e9b5f2SOlivier Houchard static bool
ck_ec32_wait_slow_once(const void * vstate,const struct ck_ec_wait_state * wait_state,const struct timespec * partial_deadline)273*74e9b5f2SOlivier Houchard ck_ec32_wait_slow_once(const void *vstate,
274*74e9b5f2SOlivier Houchard const struct ck_ec_wait_state *wait_state,
275*74e9b5f2SOlivier Houchard const struct timespec *partial_deadline)
276*74e9b5f2SOlivier Houchard {
277*74e9b5f2SOlivier Houchard const struct ck_ec32_slow_path_state *state = vstate;
278*74e9b5f2SOlivier Houchard const struct ck_ec32 *ec = state->ec;
279*74e9b5f2SOlivier Houchard const uint32_t flagged_word = state->flagged_word;
280*74e9b5f2SOlivier Houchard
281*74e9b5f2SOlivier Houchard wait_state->ops->wait32(wait_state, &ec->counter,
282*74e9b5f2SOlivier Houchard flagged_word, partial_deadline);
283*74e9b5f2SOlivier Houchard return ck_pr_load_32(&ec->counter) != flagged_word;
284*74e9b5f2SOlivier Houchard }
285*74e9b5f2SOlivier Houchard
286*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
287*74e9b5f2SOlivier Houchard static bool
ck_ec64_wait_slow_once(const void * vstate,const struct ck_ec_wait_state * wait_state,const struct timespec * partial_deadline)288*74e9b5f2SOlivier Houchard ck_ec64_wait_slow_once(const void *vstate,
289*74e9b5f2SOlivier Houchard const struct ck_ec_wait_state *wait_state,
290*74e9b5f2SOlivier Houchard const struct timespec *partial_deadline)
291*74e9b5f2SOlivier Houchard {
292*74e9b5f2SOlivier Houchard const struct ck_ec64_slow_path_state *state = vstate;
293*74e9b5f2SOlivier Houchard const struct ck_ec64 *ec = state->ec;
294*74e9b5f2SOlivier Houchard const uint64_t flagged_word = state->flagged_word;
295*74e9b5f2SOlivier Houchard
296*74e9b5f2SOlivier Houchard /* futex_wait will only compare the low 32 bits. Perform a
297*74e9b5f2SOlivier Houchard * full comparison here to maximise the changes of catching an
298*74e9b5f2SOlivier Houchard * ABA in the low 32 bits.
299*74e9b5f2SOlivier Houchard */
300*74e9b5f2SOlivier Houchard if (ck_pr_load_64(&ec->counter) != flagged_word) {
301*74e9b5f2SOlivier Houchard return true;
302*74e9b5f2SOlivier Houchard }
303*74e9b5f2SOlivier Houchard
304*74e9b5f2SOlivier Houchard wait_state->ops->wait64(wait_state, &ec->counter,
305*74e9b5f2SOlivier Houchard flagged_word, partial_deadline);
306*74e9b5f2SOlivier Houchard return ck_pr_load_64(&ec->counter) != flagged_word;
307*74e9b5f2SOlivier Houchard }
308*74e9b5f2SOlivier Houchard #endif
309*74e9b5f2SOlivier Houchard
310*74e9b5f2SOlivier Houchard /*
311*74e9b5f2SOlivier Houchard * The full wait logic is a lot of code (> 1KB). Encourage the
312*74e9b5f2SOlivier Houchard * compiler to lay this all out linearly with LIKELY annotations on
313*74e9b5f2SOlivier Houchard * every early exit.
314*74e9b5f2SOlivier Houchard */
315*74e9b5f2SOlivier Houchard #define WAIT_SLOW_BODY(W, ec, ops, pred, data, deadline_ptr, \
316*74e9b5f2SOlivier Houchard old_value, unflagged, flagged) \
317*74e9b5f2SOlivier Houchard do { \
318*74e9b5f2SOlivier Houchard struct ck_ec_wait_state wait_state = { \
319*74e9b5f2SOlivier Houchard .ops = ops, \
320*74e9b5f2SOlivier Houchard .data = data \
321*74e9b5f2SOlivier Houchard }; \
322*74e9b5f2SOlivier Houchard const struct ck_ec##W##_slow_path_state state = { \
323*74e9b5f2SOlivier Houchard .ec = ec, \
324*74e9b5f2SOlivier Houchard .flagged_word = flagged \
325*74e9b5f2SOlivier Houchard }; \
326*74e9b5f2SOlivier Houchard const struct timespec deadline = \
327*74e9b5f2SOlivier Houchard canonical_deadline(deadline_ptr); \
328*74e9b5f2SOlivier Houchard \
329*74e9b5f2SOlivier Houchard /* Detect infinite past deadlines. */ \
330*74e9b5f2SOlivier Houchard if (CK_CC_LIKELY(deadline.tv_sec <= 0)) { \
331*74e9b5f2SOlivier Houchard return -1; \
332*74e9b5f2SOlivier Houchard } \
333*74e9b5f2SOlivier Houchard \
334*74e9b5f2SOlivier Houchard for (;;) { \
335*74e9b5f2SOlivier Houchard uint##W##_t current; \
336*74e9b5f2SOlivier Houchard int r; \
337*74e9b5f2SOlivier Houchard \
338*74e9b5f2SOlivier Houchard current = ck_ec##W##_wait_easy(ec, ops, unflagged); \
339*74e9b5f2SOlivier Houchard \
340*74e9b5f2SOlivier Houchard /* \
341*74e9b5f2SOlivier Houchard * We're about to wait harder (i.e., \
342*74e9b5f2SOlivier Houchard * potentially with futex). Make sure the \
343*74e9b5f2SOlivier Houchard * counter word is flagged. \
344*74e9b5f2SOlivier Houchard */ \
345*74e9b5f2SOlivier Houchard if (CK_CC_LIKELY( \
346*74e9b5f2SOlivier Houchard ck_ec##W##_upgrade(ec, current, \
347*74e9b5f2SOlivier Houchard unflagged, flagged) == true)) { \
348*74e9b5f2SOlivier Houchard ck_pr_fence_acquire(); \
349*74e9b5f2SOlivier Houchard return 0; \
350*74e9b5f2SOlivier Houchard } \
351*74e9b5f2SOlivier Houchard \
352*74e9b5f2SOlivier Houchard /* \
353*74e9b5f2SOlivier Houchard * By now, ec->counter == flagged_word (at \
354*74e9b5f2SOlivier Houchard * some point in the past). Spin some more to \
355*74e9b5f2SOlivier Houchard * heuristically let any in-flight SP inc/add \
356*74e9b5f2SOlivier Houchard * to retire. This does not affect \
357*74e9b5f2SOlivier Houchard * correctness, but practically eliminates \
358*74e9b5f2SOlivier Houchard * lost wake-ups. \
359*74e9b5f2SOlivier Houchard */ \
360*74e9b5f2SOlivier Houchard current = ck_ec##W##_wait_easy(ec, ops, flagged); \
361*74e9b5f2SOlivier Houchard if (CK_CC_LIKELY(current != flagged_word)) { \
362*74e9b5f2SOlivier Houchard ck_pr_fence_acquire(); \
363*74e9b5f2SOlivier Houchard return 0; \
364*74e9b5f2SOlivier Houchard } \
365*74e9b5f2SOlivier Houchard \
366*74e9b5f2SOlivier Houchard r = exponential_backoff(&wait_state, \
367*74e9b5f2SOlivier Houchard ck_ec##W##_wait_slow_once, \
368*74e9b5f2SOlivier Houchard &state, \
369*74e9b5f2SOlivier Houchard pred, &deadline); \
370*74e9b5f2SOlivier Houchard if (r != 0) { \
371*74e9b5f2SOlivier Houchard return r; \
372*74e9b5f2SOlivier Houchard } \
373*74e9b5f2SOlivier Houchard \
374*74e9b5f2SOlivier Houchard if (ck_ec##W##_value(ec) != old_value) { \
375*74e9b5f2SOlivier Houchard ck_pr_fence_acquire(); \
376*74e9b5f2SOlivier Houchard return 0; \
377*74e9b5f2SOlivier Houchard } \
378*74e9b5f2SOlivier Houchard \
379*74e9b5f2SOlivier Houchard /* Spurious wake-up. Redo the slow path. */ \
380*74e9b5f2SOlivier Houchard } \
381*74e9b5f2SOlivier Houchard } while (0)
382*74e9b5f2SOlivier Houchard
383*74e9b5f2SOlivier Houchard int
ck_ec32_wait_pred_slow(struct ck_ec32 * ec,const struct ck_ec_ops * ops,uint32_t old_value,int (* pred)(const struct ck_ec_wait_state * state,struct timespec * deadline),void * data,const struct timespec * deadline_ptr)384*74e9b5f2SOlivier Houchard ck_ec32_wait_pred_slow(struct ck_ec32 *ec,
385*74e9b5f2SOlivier Houchard const struct ck_ec_ops *ops,
386*74e9b5f2SOlivier Houchard uint32_t old_value,
387*74e9b5f2SOlivier Houchard int (*pred)(const struct ck_ec_wait_state *state,
388*74e9b5f2SOlivier Houchard struct timespec *deadline),
389*74e9b5f2SOlivier Houchard void *data,
390*74e9b5f2SOlivier Houchard const struct timespec *deadline_ptr)
391*74e9b5f2SOlivier Houchard {
392*74e9b5f2SOlivier Houchard const uint32_t unflagged_word = old_value;
393*74e9b5f2SOlivier Houchard const uint32_t flagged_word = old_value | (1UL << 31);
394*74e9b5f2SOlivier Houchard
395*74e9b5f2SOlivier Houchard if (CK_CC_UNLIKELY(ck_ec32_value(ec) != old_value)) {
396*74e9b5f2SOlivier Houchard return 0;
397*74e9b5f2SOlivier Houchard }
398*74e9b5f2SOlivier Houchard
399*74e9b5f2SOlivier Houchard WAIT_SLOW_BODY(32, ec, ops, pred, data, deadline_ptr,
400*74e9b5f2SOlivier Houchard old_value, unflagged_word, flagged_word);
401*74e9b5f2SOlivier Houchard }
402*74e9b5f2SOlivier Houchard
403*74e9b5f2SOlivier Houchard #ifdef CK_F_EC64
404*74e9b5f2SOlivier Houchard int
ck_ec64_wait_pred_slow(struct ck_ec64 * ec,const struct ck_ec_ops * ops,uint64_t old_value,int (* pred)(const struct ck_ec_wait_state * state,struct timespec * deadline),void * data,const struct timespec * deadline_ptr)405*74e9b5f2SOlivier Houchard ck_ec64_wait_pred_slow(struct ck_ec64 *ec,
406*74e9b5f2SOlivier Houchard const struct ck_ec_ops *ops,
407*74e9b5f2SOlivier Houchard uint64_t old_value,
408*74e9b5f2SOlivier Houchard int (*pred)(const struct ck_ec_wait_state *state,
409*74e9b5f2SOlivier Houchard struct timespec *deadline),
410*74e9b5f2SOlivier Houchard void *data,
411*74e9b5f2SOlivier Houchard const struct timespec *deadline_ptr)
412*74e9b5f2SOlivier Houchard {
413*74e9b5f2SOlivier Houchard const uint64_t unflagged_word = old_value << 1;
414*74e9b5f2SOlivier Houchard const uint64_t flagged_word = unflagged_word | 1;
415*74e9b5f2SOlivier Houchard
416*74e9b5f2SOlivier Houchard if (CK_CC_UNLIKELY(ck_ec64_value(ec) != old_value)) {
417*74e9b5f2SOlivier Houchard return 0;
418*74e9b5f2SOlivier Houchard }
419*74e9b5f2SOlivier Houchard
420*74e9b5f2SOlivier Houchard WAIT_SLOW_BODY(64, ec, ops, pred, data, deadline_ptr,
421*74e9b5f2SOlivier Houchard old_value, unflagged_word, flagged_word);
422*74e9b5f2SOlivier Houchard }
423*74e9b5f2SOlivier Houchard #endif
424*74e9b5f2SOlivier Houchard
425*74e9b5f2SOlivier Houchard #undef WAIT_SLOW_BODY
426