summaryrefslogtreecommitdiffstats
path: root/compiler-rt/lib/tsan/rtl/tsan_clock.cc
diff options
context:
space:
mode:
Diffstat (limited to 'compiler-rt/lib/tsan/rtl/tsan_clock.cc')
-rw-r--r--compiler-rt/lib/tsan/rtl/tsan_clock.cc147
1 files changed, 71 insertions, 76 deletions
diff --git a/compiler-rt/lib/tsan/rtl/tsan_clock.cc b/compiler-rt/lib/tsan/rtl/tsan_clock.cc
index d234585bf27..d40f40f05a0 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_clock.cc
+++ b/compiler-rt/lib/tsan/rtl/tsan_clock.cc
@@ -69,13 +69,15 @@
//
// Description of SyncClock state:
// clk_ - variable size vector clock, low kClkBits hold timestamp,
-// the remaining bits hold "last_acq" counter;
-// if last_acq == release_seq_, then the respective thread has already
+// the remaining bits hold "acquired" flag (the actual value is thread's
+// reused counter);
+// if acquried == thr->reused_, then the respective thread has already
// acquired this clock (except possibly dirty_tids_).
// dirty_tids_ - holds up to two indeces in the vector clock that other threads
-// need to acquire regardless of last_acq value;
+// need to acquire regardless of "acquired" flag value;
// release_store_tid_ - denotes that the clock state is a result of
// release-store operation by the thread with release_store_tid_ index.
+// release_store_reused_ - reuse count of release_store_tid_.
// We don't have ThreadState in these methods, so this is an ugly hack that
// works only in C++.
@@ -89,11 +91,15 @@ namespace __tsan {
const unsigned kInvalidTid = (unsigned)-1;
-ThreadClock::ThreadClock(unsigned tid)
- : tid_(tid) {
- DCHECK_LT(tid, kMaxTidInClock);
+ThreadClock::ThreadClock(unsigned tid, unsigned reused)
+ : tid_(tid)
+ , reused_(reused + 1) { // 0 has special meaning
+ CHECK_LT(tid, kMaxTidInClock);
+ CHECK_EQ(reused_, ((u64)reused_ << kClkBits) >> kClkBits);
nclk_ = tid_ + 1;
+ last_acquire_ = 0;
internal_memset(clk_, 0, sizeof(clk_));
+ clk_[tid_].reused = reused_;
}
void ThreadClock::acquire(const SyncClock *src) {
@@ -108,37 +114,25 @@ void ThreadClock::acquire(const SyncClock *src) {
return;
}
- // If the clock is a result of release-store operation, and the current thread
- // has already acquired from that thread after or at that time,
- // don't need to do anything (src can't contain anything new for the
- // current thread).
- unsigned tid1 = src->release_store_tid_;
- if (tid1 != kInvalidTid && (src->clk_[tid1] & kClkMask) <= clk_[tid1]) {
- CPP_STAT_INC(StatClockAcquireFastRelease);
- return;
- }
-
// Check if we've already acquired src after the last release operation on src
bool acquired = false;
if (nclk > tid_) {
CPP_STAT_INC(StatClockAcquireLarge);
- u64 myepoch = src->clk_[tid_];
- u64 last_acq = myepoch >> kClkBits;
- if (last_acq == src->release_seq_) {
+ if (src->clk_[tid_].reused == reused_) {
CPP_STAT_INC(StatClockAcquireRepeat);
for (unsigned i = 0; i < kDirtyTids; i++) {
unsigned tid = src->dirty_tids_[i];
if (tid != kInvalidTid) {
- u64 epoch = src->clk_[tid] & kClkMask;
- if (clk_[tid] < epoch) {
- clk_[tid] = epoch;
+ u64 epoch = src->clk_[tid].epoch;
+ if (clk_[tid].epoch < epoch) {
+ clk_[tid].epoch = epoch;
acquired = true;
}
}
}
if (acquired) {
CPP_STAT_INC(StatClockAcquiredSomething);
- last_acquire_ = clk_[tid_];
+ last_acquire_ = clk_[tid_].epoch;
}
return;
}
@@ -148,28 +142,26 @@ void ThreadClock::acquire(const SyncClock *src) {
CPP_STAT_INC(StatClockAcquireFull);
nclk_ = max(nclk_, nclk);
for (uptr i = 0; i < nclk; i++) {
- u64 epoch = src->clk_[i] & kClkMask;
- if (clk_[i] < epoch) {
- clk_[i] = epoch;
+ u64 epoch = src->clk_[i].epoch;
+ if (clk_[i].epoch < epoch) {
+ clk_[i].epoch = epoch;
acquired = true;
}
}
// Remember that this thread has acquired this clock.
- if (nclk > tid_) {
- u64 myepoch = src->clk_[tid_];
- src->clk_[tid_] = (myepoch & kClkMask) | (src->release_seq_ << kClkBits);
- }
+ if (nclk > tid_)
+ src->clk_[tid_].reused = reused_;
if (acquired) {
CPP_STAT_INC(StatClockAcquiredSomething);
- last_acquire_ = clk_[tid_];
+ last_acquire_ = clk_[tid_].epoch;
}
}
void ThreadClock::release(SyncClock *dst) const {
- DCHECK(nclk_ <= kMaxTid);
- DCHECK(dst->clk_.Size() <= kMaxTid);
+ DCHECK_LE(nclk_, kMaxTid);
+ DCHECK_LE(dst->clk_.Size(), kMaxTid);
if (dst->clk_.Size() == 0) {
// ReleaseStore will correctly set release_store_tid_,
@@ -188,9 +180,10 @@ void ThreadClock::release(SyncClock *dst) const {
// Check if we had not acquired anything from other threads
// since the last release on dst. If so, we need to update
// only dst->clk_[tid_].
- if ((dst->clk_[tid_] & kClkMask) > last_acquire_) {
+ if (dst->clk_[tid_].epoch > last_acquire_) {
UpdateCurrentThread(dst);
- if (dst->release_store_tid_ != tid_)
+ if (dst->release_store_tid_ != tid_ ||
+ dst->release_store_reused_ != reused_)
dst->release_store_tid_ = kInvalidTid;
return;
}
@@ -202,22 +195,23 @@ void ThreadClock::release(SyncClock *dst) const {
if (acquired)
CPP_STAT_INC(StatClockReleaseAcquired);
// Update dst->clk_.
- for (uptr i = 0; i < nclk_; i++)
- dst->clk_[i] = max(dst->clk_[i] & kClkMask, clk_[i]);
- // Clear last_acq in the remaining elements.
+ for (uptr i = 0; i < nclk_; i++) {
+ dst->clk_[i].epoch = max(dst->clk_[i].epoch, clk_[i].epoch);
+ dst->clk_[i].reused = 0;
+ }
+ // Clear 'acquired' flag in the remaining elements.
if (nclk_ < dst->clk_.Size())
CPP_STAT_INC(StatClockReleaseClearTail);
for (uptr i = nclk_; i < dst->clk_.Size(); i++)
- dst->clk_[i] = dst->clk_[i] & kClkMask;
- // Since we've cleared all last_acq, we can reset release_seq_ as well.
- dst->release_seq_ = 1;
+ dst->clk_[i].reused = 0;
for (unsigned i = 0; i < kDirtyTids; i++)
dst->dirty_tids_[i] = kInvalidTid;
dst->release_store_tid_ = kInvalidTid;
+ dst->release_store_reused_ = 0;
// If we've acquired dst, remember this fact,
// so that we don't need to acquire it on next acquire.
if (acquired)
- dst->clk_[tid_] = dst->clk_[tid_] | (1ULL << kClkBits);
+ dst->clk_[tid_].reused = reused_;
}
void ThreadClock::ReleaseStore(SyncClock *dst) const {
@@ -232,7 +226,8 @@ void ThreadClock::ReleaseStore(SyncClock *dst) const {
}
if (dst->release_store_tid_ == tid_ &&
- (dst->clk_[tid_] & kClkMask) > last_acquire_) {
+ dst->release_store_reused_ == reused_ &&
+ dst->clk_[tid_].epoch > last_acquire_) {
CPP_STAT_INC(StatClockStoreFast);
UpdateCurrentThread(dst);
return;
@@ -240,21 +235,22 @@ void ThreadClock::ReleaseStore(SyncClock *dst) const {
// O(N) release-store.
CPP_STAT_INC(StatClockStoreFull);
- for (uptr i = 0; i < nclk_; i++)
- dst->clk_[i] = clk_[i];
+ for (uptr i = 0; i < nclk_; i++) {
+ dst->clk_[i].epoch = clk_[i].epoch;
+ dst->clk_[i].reused = 0;
+ }
// Clear the tail of dst->clk_.
if (nclk_ < dst->clk_.Size()) {
internal_memset(&dst->clk_[nclk_], 0,
(dst->clk_.Size() - nclk_) * sizeof(dst->clk_[0]));
CPP_STAT_INC(StatClockStoreTail);
}
- // Since we've cleared all last_acq, we can reset release_seq_ as well.
- dst->release_seq_ = 1;
for (unsigned i = 0; i < kDirtyTids; i++)
dst->dirty_tids_[i] = kInvalidTid;
dst->release_store_tid_ = tid_;
+ dst->release_store_reused_ = reused_;
// Rememeber that we don't need to acquire it in future.
- dst->clk_[tid_] = clk_[tid_] | (1ULL << kClkBits);
+ dst->clk_[tid_].reused = reused_;
}
void ThreadClock::acq_rel(SyncClock *dst) {
@@ -265,8 +261,8 @@ void ThreadClock::acq_rel(SyncClock *dst) {
// Updates only single element related to the current thread in dst->clk_.
void ThreadClock::UpdateCurrentThread(SyncClock *dst) const {
- // Update the threads time, but preserve last_acq.
- dst->clk_[tid_] = clk_[tid_] | (dst->clk_[tid_] & ~kClkMask);
+ // Update the threads time, but preserve 'acquired' flag.
+ dst->clk_[tid_].epoch = clk_[tid_].epoch;
for (unsigned i = 0; i < kDirtyTids; i++) {
if (dst->dirty_tids_[i] == tid_) {
@@ -279,29 +275,23 @@ void ThreadClock::UpdateCurrentThread(SyncClock *dst) const {
return;
}
}
- CPP_STAT_INC(StatClockReleaseFast3);
- dst->release_seq_++;
+ // Reset all 'acquired' flags, O(N).
+ CPP_STAT_INC(StatClockReleaseSlow);
+ for (uptr i = 0; i < dst->clk_.Size(); i++) {
+ dst->clk_[i].reused = 0;
+ }
for (unsigned i = 0; i < kDirtyTids; i++)
dst->dirty_tids_[i] = kInvalidTid;
- if ((dst->release_seq_ << kClkBits) == 0) {
- CPP_STAT_INC(StatClockReleaseLastOverflow);
- dst->release_seq_ = 1;
- for (uptr i = 0; i < dst->clk_.Size(); i++)
- dst->clk_[i] = dst->clk_[i] & kClkMask;
- }
}
// Checks whether the current threads has already acquired src.
bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const {
- u64 myepoch = src->clk_[tid_];
- u64 last_acq = myepoch >> kClkBits;
- if (last_acq != src->release_seq_)
+ if (src->clk_[tid_].reused != reused_)
return false;
for (unsigned i = 0; i < kDirtyTids; i++) {
unsigned tid = src->dirty_tids_[i];
if (tid != kInvalidTid) {
- u64 epoch = src->clk_[tid] & kClkMask;
- if (clk_[tid] < epoch)
+ if (clk_[tid].epoch < src->clk_[tid].epoch)
return false;
}
}
@@ -312,32 +302,36 @@ bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const {
// This function is called only from weird places like AcquireGlobal.
void ThreadClock::set(unsigned tid, u64 v) {
DCHECK_LT(tid, kMaxTid);
- DCHECK_GE(v, clk_[tid]);
- clk_[tid] = v;
+ DCHECK_GE(v, clk_[tid].epoch);
+ clk_[tid].epoch = v;
if (nclk_ <= tid)
nclk_ = tid + 1;
- last_acquire_ = clk_[tid_];
+ last_acquire_ = clk_[tid_].epoch;
}
void ThreadClock::DebugDump(int(*printf)(const char *s, ...)) {
printf("clock=[");
for (uptr i = 0; i < nclk_; i++)
- printf("%s%llu", i == 0 ? "" : ",", clk_[i]);
- printf("] tid=%u last_acq=%llu", tid_, last_acquire_);
+ printf("%s%llu", i == 0 ? "" : ",", clk_[i].epoch);
+ printf("] reused=[");
+ for (uptr i = 0; i < nclk_; i++)
+ printf("%s%llu", i == 0 ? "" : ",", clk_[i].reused);
+ printf("] tid=%u/%u last_acq=%llu",
+ tid_, reused_, last_acquire_);
}
SyncClock::SyncClock()
: clk_(MBlockClock) {
+ release_store_tid_ = kInvalidTid;
+ release_store_reused_ = 0;
for (uptr i = 0; i < kDirtyTids; i++)
dirty_tids_[i] = kInvalidTid;
- release_seq_ = 0;
- release_store_tid_ = kInvalidTid;
}
void SyncClock::Reset() {
clk_.Reset();
- release_seq_ = 0;
release_store_tid_ = kInvalidTid;
+ release_store_reused_ = 0;
for (uptr i = 0; i < kDirtyTids; i++)
dirty_tids_[i] = kInvalidTid;
}
@@ -345,11 +339,12 @@ void SyncClock::Reset() {
void SyncClock::DebugDump(int(*printf)(const char *s, ...)) {
printf("clock=[");
for (uptr i = 0; i < clk_.Size(); i++)
- printf("%s%llu", i == 0 ? "" : ",", clk_[i] & kClkMask);
- printf("] last_acq=[");
+ printf("%s%llu", i == 0 ? "" : ",", clk_[i].epoch);
+ printf("] reused=[");
for (uptr i = 0; i < clk_.Size(); i++)
- printf("%s%llu", i == 0 ? "" : ",", clk_[i] >> kClkBits);
- printf("] release_seq=%llu release_store_tid=%d dirty_tids=%d/%d",
- release_seq_, release_store_tid_, dirty_tids_[0], dirty_tids_[1]);
+ printf("%s%llu", i == 0 ? "" : ",", clk_[i].reused);
+ printf("] release_store_tid=%d/%d dirty_tids=%d/%d",
+ release_store_tid_, release_store_reused_,
+ dirty_tids_[0], dirty_tids_[1]);
}
} // namespace __tsan
OpenPOWER on IntegriCloud