tsan_clock.h revision 1.2
1//===-- tsan_clock.h --------------------------------------------*- C++ -*-===//
2//
3// This file is distributed under the University of Illinois Open Source
4// License. See LICENSE.TXT for details.
5//
6//===----------------------------------------------------------------------===//
7//
8// This file is a part of ThreadSanitizer (TSan), a race detector.
9//
10//===----------------------------------------------------------------------===//
11#ifndef TSAN_CLOCK_H
12#define TSAN_CLOCK_H
13
14#include "tsan_defs.h"
15#include "tsan_dense_alloc.h"
16
17namespace __tsan {
18
19typedef DenseSlabAlloc<ClockBlock, 1<<16, 1<<10> ClockAlloc;
20typedef DenseSlabAllocCache ClockCache;
21
22// The clock that lives in sync variables (mutexes, atomics, etc).
23class SyncClock {
24 public:
25  SyncClock();
26  ~SyncClock();
27
28  uptr size() const;
29
30  // These are used only in tests.
31  u64 get(unsigned tid) const;
32  u64 get_clean(unsigned tid) const;
33
34  void Resize(ClockCache *c, uptr nclk);
35  void Reset(ClockCache *c);
36
37  void DebugDump(int(*printf)(const char *s, ...));
38
39  // Clock element iterator.
40  // Note: it iterates only over the table without regard to dirty entries.
41  class Iter {
42   public:
43    explicit Iter(SyncClock* parent);
44    Iter& operator++();
45    bool operator!=(const Iter& other);
46    ClockElem &operator*();
47
48   private:
49    SyncClock *parent_;
50    // [pos_, end_) is the current continuous range of clock elements.
51    ClockElem *pos_;
52    ClockElem *end_;
53    int block_;  // Current number of second level block.
54
55    NOINLINE void Next();
56  };
57
58  Iter begin();
59  Iter end();
60
61 private:
62  friend class ThreadClock;
63  friend class Iter;
64  static const uptr kDirtyTids = 2;
65
66  struct Dirty {
67    u64 epoch  : kClkBits;
68    u64 tid : 64 - kClkBits;  // kInvalidId if not active
69  };
70
71  unsigned release_store_tid_;
72  unsigned release_store_reused_;
73  Dirty dirty_[kDirtyTids];
74  // If size_ is 0, tab_ is nullptr.
75  // If size <= 64 (kClockCount), tab_ contains pointer to an array with
76  // 64 ClockElem's (ClockBlock::clock).
77  // Otherwise, tab_ points to an array with up to 127 u32 elements,
78  // each pointing to the second-level 512b block with 64 ClockElem's.
79  // Unused space in the first level ClockBlock is used to store additional
80  // clock elements.
81  // The last u32 element in the first level ClockBlock is always used as
82  // reference counter.
83  //
84  // See the following scheme for details.
85  // All memory blocks are 512 bytes (allocated from ClockAlloc).
86  // Clock (clk) elements are 64 bits.
87  // Idx and ref are 32 bits.
88  //
89  // tab_
90  //    |
91  //    \/
92  //    +----------------------------------------------------+
93  //    | clk128 | clk129 | ...unused... | idx1 | idx0 | ref |
94  //    +----------------------------------------------------+
95  //                                        |      |
96  //                                        |      \/
97  //                                        |      +----------------+
98  //                                        |      | clk0 ... clk63 |
99  //                                        |      +----------------+
100  //                                        \/
101  //                                        +------------------+
102  //                                        | clk64 ... clk127 |
103  //                                        +------------------+
104  //
105  // Note: dirty entries, if active, always override what's stored in the clock.
106  ClockBlock *tab_;
107  u32 tab_idx_;
108  u16 size_;
109  u16 blocks_;  // Number of second level blocks.
110
111  void Unshare(ClockCache *c);
112  bool IsShared() const;
113  bool Cachable() const;
114  void ResetImpl();
115  void FlushDirty();
116  uptr capacity() const;
117  u32 get_block(uptr bi) const;
118  void append_block(u32 idx);
119  ClockElem &elem(unsigned tid) const;
120};
121
122// The clock that lives in threads.
123class ThreadClock {
124 public:
125  typedef DenseSlabAllocCache Cache;
126
127  explicit ThreadClock(unsigned tid, unsigned reused = 0);
128
129  u64 get(unsigned tid) const;
130  void set(ClockCache *c, unsigned tid, u64 v);
131  void set(u64 v);
132  void tick();
133  uptr size() const;
134
135  void acquire(ClockCache *c, SyncClock *src);
136  void release(ClockCache *c, SyncClock *dst);
137  void acq_rel(ClockCache *c, SyncClock *dst);
138  void ReleaseStore(ClockCache *c, SyncClock *dst);
139  void ResetCached(ClockCache *c);
140
141  void DebugReset();
142  void DebugDump(int(*printf)(const char *s, ...));
143
144 private:
145  static const uptr kDirtyTids = SyncClock::kDirtyTids;
146  // Index of the thread associated with he clock ("current thread").
147  const unsigned tid_;
148  const unsigned reused_;  // tid_ reuse count.
149  // Current thread time when it acquired something from other threads.
150  u64 last_acquire_;
151
152  // Cached SyncClock (without dirty entries and release_store_tid_).
153  // We reuse it for subsequent store-release operations without intervening
154  // acquire operations. Since it is shared (and thus constant), clock value
155  // for the current thread is then stored in dirty entries in the SyncClock.
156  // We host a refernece to the table while it is cached here.
157  u32 cached_idx_;
158  u16 cached_size_;
159  u16 cached_blocks_;
160
161  // Number of active elements in the clk_ table (the rest is zeros).
162  uptr nclk_;
163  u64 clk_[kMaxTidInClock];  // Fixed size vector clock.
164
165  bool IsAlreadyAcquired(const SyncClock *src) const;
166  void UpdateCurrentThread(ClockCache *c, SyncClock *dst) const;
167};
168
169ALWAYS_INLINE u64 ThreadClock::get(unsigned tid) const {
170  DCHECK_LT(tid, kMaxTidInClock);
171  return clk_[tid];
172}
173
174ALWAYS_INLINE void ThreadClock::set(u64 v) {
175  DCHECK_GE(v, clk_[tid_]);
176  clk_[tid_] = v;
177}
178
179ALWAYS_INLINE void ThreadClock::tick() {
180  clk_[tid_]++;
181}
182
183ALWAYS_INLINE uptr ThreadClock::size() const {
184  return nclk_;
185}
186
187ALWAYS_INLINE SyncClock::Iter SyncClock::begin() {
188  return Iter(this);
189}
190
191ALWAYS_INLINE SyncClock::Iter SyncClock::end() {
192  return Iter(nullptr);
193}
194
195ALWAYS_INLINE uptr SyncClock::size() const {
196  return size_;
197}
198
199ALWAYS_INLINE SyncClock::Iter::Iter(SyncClock* parent)
200    : parent_(parent)
201    , pos_(nullptr)
202    , end_(nullptr)
203    , block_(-1) {
204  if (parent)
205    Next();
206}
207
208ALWAYS_INLINE SyncClock::Iter& SyncClock::Iter::operator++() {
209  pos_++;
210  if (UNLIKELY(pos_ >= end_))
211    Next();
212  return *this;
213}
214
215ALWAYS_INLINE bool SyncClock::Iter::operator!=(const SyncClock::Iter& other) {
216  return parent_ != other.parent_;
217}
218
219ALWAYS_INLINE ClockElem &SyncClock::Iter::operator*() {
220  return *pos_;
221}
222}  // namespace __tsan
223
224#endif  // TSAN_CLOCK_H
225