/*1* Copyright 2013-2015 Samy Al Bahra.2* All rights reserved.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12*13* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND14* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE15* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE16* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE17* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL18* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS19* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)20* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT21* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY22* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF23* SUCH DAMAGE.24*/2526#ifndef CK_ELIDE_H27#define CK_ELIDE_H2829/*30* As RTM is currently only supported on TSO x86 architectures,31* fences have been omitted. They will be necessary for other32* non-TSO architectures with TM support.33*/3435#include <ck_cc.h>36#include <ck_pr.h>37#include <ck_string.h>3839/*40* skip_-prefixed counters represent the number of consecutive41* elisions to forfeit. retry_-prefixed counters represent the42* number of elision retries to attempt before forfeit.43*44* _busy: Lock was busy45* _other: Unknown explicit abort46* _conflict: Data conflict in elision section47*/48struct ck_elide_config {49unsigned short skip_busy;50short retry_busy;51unsigned short skip_other;52short retry_other;53unsigned short skip_conflict;54short retry_conflict;55};5657#define CK_ELIDE_CONFIG_DEFAULT_INITIALIZER { \58.skip_busy = 5, \59.retry_busy = 256, \60.skip_other = 3, \61.retry_other = 3, \62.skip_conflict = 2, \63.retry_conflict = 5 \64}6566struct ck_elide_stat {67unsigned int n_fallback;68unsigned int n_elide;69unsigned short skip;70};71typedef struct ck_elide_stat ck_elide_stat_t;7273#define CK_ELIDE_STAT_INITIALIZER { 0, 0, 0 }7475CK_CC_INLINE static void76ck_elide_stat_init(ck_elide_stat_t *st)77{7879memset(st, 0, sizeof(*st));80return;81}8283#ifdef CK_F_PR_RTM84enum _ck_elide_hint {85CK_ELIDE_HINT_RETRY = 0,86CK_ELIDE_HINT_SPIN,87CK_ELIDE_HINT_STOP88};8990#define CK_ELIDE_LOCK_BUSY 0xFF9192static enum _ck_elide_hint93_ck_elide_fallback(int *retry,94struct ck_elide_stat *st,95struct ck_elide_config *c,96unsigned int status)97{9899st->n_fallback++;100if (*retry > 0)101return CK_ELIDE_HINT_RETRY;102103if (st->skip != 0)104return CK_ELIDE_HINT_STOP;105106if (status & CK_PR_RTM_EXPLICIT) {107if (CK_PR_RTM_CODE(status) == CK_ELIDE_LOCK_BUSY) {108st->skip = c->skip_busy;109*retry = c->retry_busy;110return CK_ELIDE_HINT_SPIN;111}112113st->skip = c->skip_other;114return CK_ELIDE_HINT_STOP;115}116117if ((status & CK_PR_RTM_RETRY) &&118(status & CK_PR_RTM_CONFLICT)) {119st->skip = c->skip_conflict;120*retry = c->retry_conflict;121return CK_ELIDE_HINT_RETRY;122}123124/*125* Capacity, debug and nesting abortions are likely to be126* invariant conditions for the acquisition, execute regular127* path instead. If retry bit is not set, then take the hint.128*/129st->skip = USHRT_MAX;130return CK_ELIDE_HINT_STOP;131}132133/*134* Defines an elision implementation according to the following variables:135* N - Namespace of elision implementation.136* T - Typename of mutex.137* L_P - Lock predicate, returns false if resource is available.138* L - Function to call if resource is unavailable of transaction aborts.139* U_P - Unlock predicate, returns false if elision failed.140* U - Function to call if transaction failed.141*/142#define CK_ELIDE_PROTOTYPE(N, T, L_P, L, U_P, U) \143CK_CC_INLINE static void \144ck_elide_##N##_lock_adaptive(T *lock, \145struct ck_elide_stat *st, \146struct ck_elide_config *c) \147{ \148enum _ck_elide_hint hint; \149int retry; \150\151if (CK_CC_UNLIKELY(st->skip != 0)) { \152st->skip--; \153goto acquire; \154} \155\156retry = c->retry_conflict; \157do { \158unsigned int status = ck_pr_rtm_begin(); \159if (status == CK_PR_RTM_STARTED) { \160if (L_P(lock) == true) \161ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \162\163return; \164} \165\166hint = _ck_elide_fallback(&retry, st, c, status); \167if (hint == CK_ELIDE_HINT_RETRY) \168continue; \169\170if (hint == CK_ELIDE_HINT_SPIN) { \171while (--retry != 0) { \172if (L_P(lock) == false) \173break; \174\175ck_pr_stall(); \176} \177\178continue; \179} \180\181if (hint == CK_ELIDE_HINT_STOP) \182break; \183} while (CK_CC_LIKELY(--retry > 0)); \184\185acquire: \186L(lock); \187return; \188} \189CK_CC_INLINE static void \190ck_elide_##N##_unlock_adaptive(struct ck_elide_stat *st, T *lock) \191{ \192\193if (U_P(lock) == false) { \194ck_pr_rtm_end(); \195st->skip = 0; \196st->n_elide++; \197} else { \198U(lock); \199} \200\201return; \202} \203CK_CC_INLINE static void \204ck_elide_##N##_lock(T *lock) \205{ \206\207if (ck_pr_rtm_begin() != CK_PR_RTM_STARTED) { \208L(lock); \209return; \210} \211\212if (L_P(lock) == true) \213ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \214\215return; \216} \217CK_CC_INLINE static void \218ck_elide_##N##_unlock(T *lock) \219{ \220\221if (U_P(lock) == false) { \222ck_pr_rtm_end(); \223} else { \224U(lock); \225} \226\227return; \228}229230#define CK_ELIDE_TRYLOCK_PROTOTYPE(N, T, TL_P, TL) \231CK_CC_INLINE static bool \232ck_elide_##N##_trylock(T *lock) \233{ \234\235if (ck_pr_rtm_begin() != CK_PR_RTM_STARTED) \236return false; \237\238if (TL_P(lock) == true) \239ck_pr_rtm_abort(CK_ELIDE_LOCK_BUSY); \240\241return true; \242}243#else244/*245* If RTM is not enabled on the target platform (CK_F_PR_RTM) then these246* elision wrappers directly calls into the user-specified lock operations.247* Unfortunately, the storage cost of both ck_elide_config and ck_elide_stat248* are paid (typically a storage cost that is a function of lock objects and249* thread count).250*/251#define CK_ELIDE_PROTOTYPE(N, T, L_P, L, U_P, U) \252CK_CC_INLINE static void \253ck_elide_##N##_lock_adaptive(T *lock, \254struct ck_elide_stat *st, \255struct ck_elide_config *c) \256{ \257\258(void)st; \259(void)c; \260L(lock); \261return; \262} \263CK_CC_INLINE static void \264ck_elide_##N##_unlock_adaptive(struct ck_elide_stat *st, \265T *lock) \266{ \267\268(void)st; \269U(lock); \270return; \271} \272CK_CC_INLINE static void \273ck_elide_##N##_lock(T *lock) \274{ \275\276L(lock); \277return; \278} \279CK_CC_INLINE static void \280ck_elide_##N##_unlock(T *lock) \281{ \282\283U(lock); \284return; \285}286287#define CK_ELIDE_TRYLOCK_PROTOTYPE(N, T, TL_P, TL) \288CK_CC_INLINE static bool \289ck_elide_##N##_trylock(T *lock) \290{ \291\292return TL_P(lock); \293}294#endif /* !CK_F_PR_RTM */295296/*297* Best-effort elision lock operations. First argument is name (N)298* associated with implementation and the second is a pointer to299* the type specified above (T).300*301* Unlike the adaptive variant, this interface does not have any retry302* semantics. In environments where jitter is low, this may yield a tighter303* fast path.304*/305#define CK_ELIDE_LOCK(NAME, LOCK) ck_elide_##NAME##_lock(LOCK)306#define CK_ELIDE_UNLOCK(NAME, LOCK) ck_elide_##NAME##_unlock(LOCK)307#define CK_ELIDE_TRYLOCK(NAME, LOCK) ck_elide_##NAME##_trylock(LOCK)308309/*310* Adaptive elision lock operations. In addition to name and pointer311* to the lock, you must pass in a pointer to an initialized312* ck_elide_config structure along with a per-thread stat structure.313*/314#define CK_ELIDE_LOCK_ADAPTIVE(NAME, STAT, CONFIG, LOCK) \315ck_elide_##NAME##_lock_adaptive(LOCK, STAT, CONFIG)316317#define CK_ELIDE_UNLOCK_ADAPTIVE(NAME, STAT, LOCK) \318ck_elide_##NAME##_unlock_adaptive(STAT, LOCK)319320#endif /* CK_ELIDE_H */321322323