static void fe_mt_tsx_lock_x86rtm(fe_mt_tsx *tsx) { static const char *TAG = "fe_mt"; /* Currently, we prevent nested transactions. * FIXME Prove it is actually required. */ if(_xtest()) return; retry: unsigned status = _xbegin(); if(status == _XBEGIN_STARTED) return; if(status & _XABORT_RETRY) goto retry; fe_logv("x86 RTM transaction aborted." "Falling back to regular lock. Reason :\n\t"); if(status & _XABORT_EXPLICIT) fe_logv(TAG, "Explicit abort. Code : %d\n", _XABORT_CODE(status)); if(status & _XABORT_CONFLICT) fe_logv(TAG, "Memory conflict with another thread.\n"); if(status & _XABORT_CAPACITY) fe_logv(TAG, "Too much memory used by the transaction.\n"); if(status & _XABORT_DEBUG) fe_logv(TAG, "Debug trap.\n"); if(status & _XABORT_NESTED) fe_logv(TAG, "Abort in an inner nested transaction.\n"); fe_mt_tsx_lock_fallback(tsx); }
TransactionalScope::TransactionalScope(spinlock_t &fallback_mutex, bool writeAccess) : // initializaer list spinlock(fallback_mutex) { unsigned int xact_status; threadstate_t &ts = tstate; ts.txCount++; // we are already executing transactionally, continue. if (_xtest()) return; do { xact_status = _xbegin(); if (xact_status == _XBEGIN_STARTED) { if ( *(reinterpret_cast<int*>(&fallback_mutex)) == 0 ) { return; } else { _xabort(0xFF); } } else { /** We have aborted. */ ++ts.totalAborts; ++ts.successiveAborts; // if we xaborted because the lock was held, acquire the lock if ((xact_status & _XABORT_EXPLICIT) && _XABORT_CODE(xact_status) == 0xFF) { ts.maxAborts = 1; ts.maxTxLen = 1; break; } //if xabort:retry or xabort:conflict is set retry if (xact_status & (_XABORT_RETRY | _XABORT_CONFLICT)) { ts.maxTxLen = 1; } // // if we used too much buffer space inside the transaction half the max transaction length if ((xact_status & _XABORT_CAPACITY)) { ts.maxTxLen = 1; } _mm_pause(); } } while (ts.successiveAborts < ts.maxAborts); ts.fallbackTaken++; // Fallback to lock if (writeAccess) { spinlock.lock(); } else { spinlock.lock_read(); } }
int main(void) { int status; if ((status = _xbegin()) == _XBEGIN_STARTED) { if (_xtest()) _xabort(1); _xend(); } else printf("aborted %x, %d", status, _XABORT_CODE(status)); return 0; }
void spin_lock_rtm(int *lock) { int i; unsigned status; unsigned retry = RETRY_OTHER; for (i = 0; i < retry; i++) { if ((status = _xbegin()) == _XBEGIN_STARTED) { if (lock_is_free(lock)) return; _xabort(0xff); } trace_abort(status); if ((status & _XABORT_EXPLICIT) && _XABORT_CODE(status) == 0xff) { while (!lock_is_free(lock)) pause(); } else if (!(status & _XABORT_RETRY) && !(status & _XABORT_CAPACITY)) break; if (status & _XABORT_CONFLICT) { retry = RETRY_CON; while (!lock_is_free(lock)) pause(); /* Could do various kinds of backoff here. */ } else if (status & _XABORT_CAPACITY) { retry = RETRY_CAP; } else { retry = RETRY_OTHER; } } /* Could do adaptation here */ while (__sync_sub_and_fetch(lock, 1) < 0) { do pause(); while (!lock_is_free(lock)); /* Could do respeculation here */ } }
uint32_t mem_read(tid_t tid, uint32_t *addr) { version_t version; uint32_t val; objid_t objid = calc_objid(addr); struct objinfo *info = &g_objinfo[objid]; if ((g_sim_bbcnt % RTM_BATCH_N) == 0) { // Simulate basic block begin. assert(!_xtest()); int ret = _xbegin(); (void)ret; #ifdef RTM_STAT if (ret != _XBEGIN_STARTED) { fprintf(stderr, "T%d R%ld aborted %x, %d\n", g_tid, memop, ret, _XABORT_CODE(ret)); g_rtm_abort_cnt++; } #endif } int in_rtm = _xtest(); if (in_rtm) { version = info->version; // XXX It's possible the transaction commits while another write is in // fallback handler and has increased version by 1, thus we would get an // odd version here. Also refer to read tx in rtmseq. if (version & 1) { _xabort(1); } val = *addr; } else { do { version = info->version; while (unlikely(version & 1)) { cpu_relax(); version = info->version; } barrier(); val = *addr; barrier(); } while (version != info->version); } if (in_rtm && (g_sim_bbcnt % RTM_BATCH_N == RTM_BATCH_N - 1)) { // Simulate basic block end. // XXX Update: since we have checked odd version in tx region, we // will abort for parallel execution of read tx and write fallback. // So no need to check for lock here. // XXX A transaction is accessing different shared objects. Here we only // check for a single object's write lock, it's not enough. That's why // we need the odd version check in the transaction region. /* *if (info->write_lock) { * _xabort(2); *} */ _xend(); // Avoid taking log inside transaction. batch_read_log(objid, version); batch_process_log(); } else { batch_read_log(objid, version); } g_sim_bbcnt++; return val; }
void mem_write(tid_t tid, uint32_t *addr, uint32_t val) { version_t version; objid_t objid = calc_objid(addr); struct objinfo *info = &g_objinfo[objid]; if ((g_sim_bbcnt % RTM_BATCH_N) == 0) { assert(!_xtest()); int ret = _xbegin(); (void)ret; #ifdef RTM_STAT if (ret != _XBEGIN_STARTED) { fprintf(stderr, "T%d W%ld aborted %x, %d\n", g_tid, memop, ret, _XABORT_CODE(ret)); g_rtm_abort_cnt++; } #endif } int in_rtm = _xtest(); if (in_rtm) { version = info->version; // XXX To ensure exclusion of write tx and fallback. Same as in read // transaction. if (info->write_lock) { _xabort(3); } barrier(); *addr = val; barrier(); info->version += 2; // XXX The barrier is necessary, because there are reordering inside a // transaction. The reason is the same as in seqlock implementation. __sync_synchronize(); } else { spin_lock(&info->write_lock); version = info->version; barrier(); // Odd version means that there's writer trying to update value. info->version++; barrier(); *addr = val; // This barrier disallows read to happen before the write. // The explicit barrier here may also make the compiler unnecessary here. __sync_synchronize(); info->version++; spin_unlock(&info->write_lock); } if (in_rtm && (g_sim_bbcnt % RTM_BATCH_N == RTM_BATCH_N - 1)) { // XXX Update: since we have checked lock in tx region, we // will abort for parallel execution of write tx and write fallback. // So no need to check for lock here. /* *if (info->write_lock) { * _xabort(4); *} */ _xend(); // Avoid taking log inside transaction. batch_write_log(objid, version); batch_process_log(); } else { batch_write_log(objid, version); } g_sim_bbcnt++; }