diff --git a/libitm/beginend.cc b/libitm/beginend.cc index 4369946..8f5c4ef 100644 --- a/libitm/beginend.cc +++ b/libitm/beginend.cc @@ -166,18 +166,16 @@ GTM::gtm_thread::begin_transaction (uint32_t prop, const gtm_jmpbuf *jb) GTM_fatal("pr_undoLogCode not supported"); #if defined(USE_HTM_FASTPATH) && !defined(HTM_CUSTOM_FASTPATH) - // HTM fastpath. Only chosen in the absence of transaction_cancel to allow - // using an uninstrumented code path. - // The fastpath is enabled only by dispatch_htm's method group, which uses - // serial-mode methods as fallback. Serial-mode transactions cannot execute - // concurrently with HW transactions because the latter monitor the serial - // lock's writer flag and thus abort if another thread is or becomes a - // serial transaction. Therefore, if the fastpath is enabled, then a - // transaction is not executing as a HW transaction iff the serial lock is - // write-locked. This allows us to use htm_fastpath and the serial lock's - // writer flag to reliable determine whether the current thread runs a HW - // transaction, and thus we do not need to maintain this information in - // per-thread state. + // HTM fastpath. The fastpath is enabled only by dispatch_htm's method + // group, which uses serial-mode methods as fallback. Serial-mode + // transactions cannot execute concurrently with HW transactions because + // the latter monitor the serial lock's writer flag and thus abort if + // another thread is or becomes a serial transaction. Therefore, if the + // fastpath is enabled, then a transaction is not executing as a HW + // transaction iff the serial lock is write-locked. This allows us to + // use htm_fastpath and the serial lock's writer flag to reliable determine + // whether the current thread runs a HW transaction, and thus we do not + // need to maintain this information in per-thread state. // If an uninstrumented code path is not available, we can still run // instrumented code from a HW transaction because the HTM fastpath kicks // in early in both begin and commit, and the transaction is not canceled. @@ -187,7 +185,7 @@ GTM::gtm_thread::begin_transaction (uint32_t prop, const gtm_jmpbuf *jb) // indeed in serial mode, and HW transactions should never need serial mode // for any internal changes (e.g., they never abort visibly to the STM code // and thus do not trigger the standard retry handling). - if (likely(htm_fastpath && (prop & pr_hasNoAbort))) + if (likely(htm_fastpath)) { for (uint32_t t = htm_fastpath; t; t--) { @@ -198,33 +196,41 @@ GTM::gtm_thread::begin_transaction (uint32_t prop, const gtm_jmpbuf *jb) // Monitor the writer flag in the serial-mode lock, and abort // if there is an active or waiting serial-mode transaction. if (unlikely(serial_lock.is_write_locked())) - htm_abort(); + htm_abort_retry(); else // We do not need to set a_saveLiveVariables because of HTM. return (prop & pr_uninstrumentedCode) ? a_runUninstrumentedCode : a_runInstrumentedCode; } - // The transaction has aborted. Don't retry if it's unlikely that + // The transaction has aborted. Retry if it's likely that // retrying the transaction will be successful. - if (!htm_abort_should_retry(ret)) - break; - // Wait until any concurrent serial-mode transactions have finished. - // This is an empty critical section, but won't be elided. - if (serial_lock.is_write_locked()) + if (htm_abort_should_retry(ret)) { - tx = gtm_thr(); - if (unlikely(tx == NULL)) - { - // See below. - tx = new gtm_thread(); - set_gtm_thr(tx); - } - serial_lock.read_lock(tx); - serial_lock.read_unlock(tx); - // TODO We should probably reset the retry count t here, unless - // we have retried so often that we should go serial to avoid - // starvation. + // Wait until any concurrent serial-mode transactions have + // finished. This is an empty critical section, but won't + // be elided. + if (serial_lock.is_write_locked()) + { + tx = gtm_thr(); + if (unlikely(tx == NULL)) + { + // See below. + tx = new gtm_thread(); + set_gtm_thr(tx); + } + serial_lock.read_lock(tx); + serial_lock.read_unlock(tx); + // TODO We should probably reset the retry count t here, + // unless we have retried so often that we should go + // serial to avoid starvation. + } } + // Honor an abort from abortTransaction. + else if (htm_abort_is_cancel(ret)) + return a_abortTransaction | a_restoreLiveVariables; + // Otherwise quit using HTM and fall back to STM. + else + break; } } #endif @@ -438,6 +444,11 @@ GTM::gtm_thread::rollback (gtm_transaction_cp *cp, bool aborting) void ITM_REGPARM _ITM_abortTransaction (_ITM_abortReason reason) { +#if defined(USE_HTM_FASTPATH) + if (likely(htm_fastpath && !gtm_thread::serial_lock.is_write_locked())) + htm_abort_cancel(); +#endif + gtm_thread *tx = gtm_thr(); assert (reason == userAbort || reason == (userAbort | outerAbort)); diff --git a/libitm/config/x86/target.h b/libitm/config/x86/target.h index 41ae2eb..1d2e822 100644 --- a/libitm/config/x86/target.h +++ b/libitm/config/x86/target.h @@ -125,18 +125,32 @@ htm_commit () } static inline void -htm_abort () +htm_abort_retry () { // ??? According to a yet unpublished ABI rule, 0xff is reserved and // supposed to signal a busy lock. Source: andi.kleen@intel.com _xabort(0xff); } +static inline void +htm_abort_cancel () +{ + // ??? What's the unpublished ABI rule for this, Andi? + _xabort(0); +} + static inline bool htm_abort_should_retry (uint32_t begin_ret) { return begin_ret & _XABORT_RETRY; } + +static inline bool +htm_abort_is_cancel (uint32_t begin_ret) +{ + // return (begin_ret & _XABORT_EXPLICIT) && _XABORT_CODE(begin_ret) == 0; + return begin_ret == _XABORT_EXPLICIT; +} #endif