2 * Copyright 2013-2015 Samy Al Bahra.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * As RTM is currently only supported on TSO x86 architectures,
32 * fences have been omitted. They will be necessary for other
33 * non-TSO architectures with TM support.
38 #include <ck_string.h>
41 * skip_-prefixed counters represent the number of consecutive
42 * elisions to forfeit. retry_-prefixed counters represent the
43 * number of elision retries to attempt before forfeit.
45 * _busy: Lock was busy
46 * _other: Unknown explicit abort
47 * _conflict: Data conflict in elision section
49 struct ck_elide_config {
50 unsigned short skip_busy;
52 unsigned short skip_other;
54 unsigned short skip_conflict;
58 #define CK_ELIDE_CONFIG_DEFAULT_INITIALIZER { \
67 struct ck_elide_stat {
68 unsigned int n_fallback;
72 typedef struct ck_elide_stat ck_elide_stat_t;
74 #define CK_ELIDE_STAT_INITIALIZER { 0, 0, 0 }
76 CK_CC_INLINE static void
77 ck_elide_stat_init(ck_elide_stat_t *st)
80 memset(st, 0, sizeof(*st));
86 CK_ELIDE_HINT_RETRY = 0,
91 #define CK_ELIDE_LOCK_BUSY 0xFF
93 static enum _ck_elide_hint
94 _ck_elide_fallback(int *retry,
95 struct ck_elide_stat *st,
96 struct ck_elide_config *c,
102 return CK_ELIDE_HINT_RETRY;
105 return CK_ELIDE_HINT_STOP;
107 if (status & CK_PR_RTM_EXPLICIT) {
108 if (CK_PR_RTM_CODE(status) == CK_ELIDE_LOCK_BUSY) {
109 st->skip = c->skip_busy;
110 *retry = c->retry_busy;
111 return CK_ELIDE_HINT_SPIN;
114 st->skip = c->skip_other;
115 return CK_ELIDE_HINT_STOP;
118 if ((status & CK_PR_RTM_RETRY) &&
119 (status & CK_PR_RTM_CONFLICT)) {
120 st->skip = c->skip_conflict;
121 *retry = c->retry_conflict;
122 return CK_ELIDE_HINT_RETRY;
126 * Capacity, debug and nesting abortions are likely to be
127 * invariant conditions for the acquisition, execute regular
128 * path instead. If retry bit is not set, then take the hint.
130 st->skip = USHRT_MAX;
131 return CK_ELIDE_HINT_STOP;
135 * Defines an elision implementation according to the following variables:
136 * N - Namespace of elision implementation.
137 * T - Typename of mutex.
138 * L_P - Lock predicate, returns false if resource is available.
139 * L - Function to call if resource is unavailable of transaction aborts.
140 * U_P - Unlock predicate, returns false if elision failed.
141 * U - Function to call if transaction failed.
143 #define CK_ELIDE_PROTOTYPE(N, T, L_P, L, U_P, U) \
144 CK_CC_INLINE static void \
145 ck_elide_##N##_lock_adaptive(T *lock, \
146 struct ck_elide_stat *st, \
147 struct ck_elide_config *c) \
149 enum _ck_elide_hint hint; \
152 if (CK_CC_UNLIKELY(st->skip != 0)) { \
157 retry = c->retry_conflict; \
159 unsigned int status = ck_pr_rtm_begin(); \
160 if (status == CK_PR_RTM_STARTED) { \
161 if (L_P(lock) == true) \
162 ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \
167 hint = _ck_elide_fallback(&retry, st, c, status); \
168 if (hint == CK_ELIDE_HINT_RETRY) \
171 if (hint == CK_ELIDE_HINT_SPIN) { \
172 while (--retry != 0) { \
173 if (L_P(lock) == false) \
182 if (hint == CK_ELIDE_HINT_STOP) \
184 } while (CK_CC_LIKELY(--retry > 0)); \
190 CK_CC_INLINE static void \
191 ck_elide_##N##_unlock_adaptive(struct ck_elide_stat *st, T *lock) \
194 if (U_P(lock) == false) { \
204 CK_CC_INLINE static void \
205 ck_elide_##N##_lock(T *lock) \
208 if (ck_pr_rtm_begin() != CK_PR_RTM_STARTED) { \
213 if (L_P(lock) == true) \
214 ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \
218 CK_CC_INLINE static void \
219 ck_elide_##N##_unlock(T *lock) \
222 if (U_P(lock) == false) { \
231 #define CK_ELIDE_TRYLOCK_PROTOTYPE(N, T, TL_P, TL) \
232 CK_CC_INLINE static bool \
233 ck_elide_##N##_trylock(T *lock) \
236 if (ck_pr_rtm_begin() != CK_PR_RTM_STARTED) \
239 if (TL_P(lock) == true) \
240 ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \
246 * If RTM is not enabled on the target platform (CK_F_PR_RTM) then these
247 * elision wrappers directly calls into the user-specified lock operations.
248 * Unfortunately, the storage cost of both ck_elide_config and ck_elide_stat
249 * are paid (typically a storage cost that is a function of lock objects and
252 #define CK_ELIDE_PROTOTYPE(N, T, L_P, L, U_P, U) \
253 CK_CC_INLINE static void \
254 ck_elide_##N##_lock_adaptive(T *lock, \
255 struct ck_elide_stat *st, \
256 struct ck_elide_config *c) \
264 CK_CC_INLINE static void \
265 ck_elide_##N##_unlock_adaptive(struct ck_elide_stat *st, \
273 CK_CC_INLINE static void \
274 ck_elide_##N##_lock(T *lock) \
280 CK_CC_INLINE static void \
281 ck_elide_##N##_unlock(T *lock) \
288 #define CK_ELIDE_TRYLOCK_PROTOTYPE(N, T, TL_P, TL) \
289 CK_CC_INLINE static bool \
290 ck_elide_##N##_trylock(T *lock) \
295 #endif /* !CK_F_PR_RTM */
298 * Best-effort elision lock operations. First argument is name (N)
299 * associated with implementation and the second is a pointer to
300 * the type specified above (T).
302 * Unlike the adaptive variant, this interface does not have any retry
303 * semantics. In environments where jitter is low, this may yield a tighter
306 #define CK_ELIDE_LOCK(NAME, LOCK) ck_elide_##NAME##_lock(LOCK)
307 #define CK_ELIDE_UNLOCK(NAME, LOCK) ck_elide_##NAME##_unlock(LOCK)
308 #define CK_ELIDE_TRYLOCK(NAME, LOCK) ck_elide_##NAME##_trylock(LOCK)
311 * Adaptive elision lock operations. In addition to name and pointer
312 * to the lock, you must pass in a pointer to an initialized
313 * ck_elide_config structure along with a per-thread stat structure.
315 #define CK_ELIDE_LOCK_ADAPTIVE(NAME, STAT, CONFIG, LOCK) \
316 ck_elide_##NAME##_lock_adaptive(LOCK, STAT, CONFIG)
318 #define CK_ELIDE_UNLOCK_ADAPTIVE(NAME, STAT, LOCK) \
319 ck_elide_##NAME##_unlock_adaptive(STAT, LOCK)
321 #endif /* CK_ELIDE_H */