summaryrefslogtreecommitdiff
path: root/lib/tsan/rtl/tsan_clock.h
blob: 90eaca28a9cba1be428dfa62d5f408911dd7949a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//===-- tsan_clock.h --------------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
#ifndef TSAN_CLOCK_H
#define TSAN_CLOCK_H

#include "tsan_defs.h"
#include "tsan_dense_alloc.h"

namespace __tsan {

struct ClockElem {
  u64 epoch  : kClkBits;
  u64 reused : 64 - kClkBits;
};

struct ClockBlock {
  static const uptr kSize = 512;
  static const uptr kTableSize = kSize / sizeof(u32);
  static const uptr kClockCount = kSize / sizeof(ClockElem);

  union {
    u32       table[kTableSize];
    ClockElem clock[kClockCount];
  };

  ClockBlock() {
  }
};

typedef DenseSlabAlloc<ClockBlock, 1<<16, 1<<10> ClockAlloc;
typedef DenseSlabAllocCache ClockCache;

// The clock that lives in sync variables (mutexes, atomics, etc).
class SyncClock {
 public:
  SyncClock();
  ~SyncClock();

  uptr size() const {
    return size_;
  }

  u64 get(unsigned tid) const {
    return elem(tid).epoch;
  }

  void Resize(ClockCache *c, uptr nclk);
  void Reset(ClockCache *c);

  void DebugDump(int(*printf)(const char *s, ...));

 private:
  friend struct ThreadClock;
  static const uptr kDirtyTids = 2;

  unsigned release_store_tid_;
  unsigned release_store_reused_;
  unsigned dirty_tids_[kDirtyTids];
  // tab_ contains indirect pointer to a 512b block using DenseSlabAlloc.
  // If size_ <= 64, then tab_ points to an array with 64 ClockElem's.
  // Otherwise, tab_ points to an array with 128 u32 elements,
  // each pointing to the second-level 512b block with 64 ClockElem's.
  ClockBlock *tab_;
  u32 tab_idx_;
  u32 size_;

  ClockElem &elem(unsigned tid) const;
};

// The clock that lives in threads.
struct ThreadClock {
 public:
  typedef DenseSlabAllocCache Cache;

  explicit ThreadClock(unsigned tid, unsigned reused = 0);

  u64 get(unsigned tid) const {
    DCHECK_LT(tid, kMaxTidInClock);
    return clk_[tid].epoch;
  }

  void set(ClockCache *c, unsigned tid, u64 v);

  void set(u64 v) {
    DCHECK_GE(v, clk_[tid_].epoch);
    clk_[tid_].epoch = v;
  }

  void tick() {
    clk_[tid_].epoch++;
  }

  uptr size() const {
    return nclk_;
  }

  void acquire(ClockCache *c, const SyncClock *src);
  void release(ClockCache *c, SyncClock *dst) const;
  void acq_rel(ClockCache *c, SyncClock *dst);
  void ReleaseStore(ClockCache *c, SyncClock *dst) const;
  void ResetCached(ClockCache *c);

  void DebugReset();
  void DebugDump(int(*printf)(const char *s, ...));

 private:
  static const uptr kDirtyTids = SyncClock::kDirtyTids;
  const unsigned tid_;
  const unsigned reused_;
  u64 last_acquire_;
  uptr nclk_;
  ClockElem clk_[kMaxTidInClock];

  bool IsAlreadyAcquired(const SyncClock *src) const;
  void UpdateCurrentThread(SyncClock *dst) const;
};

}  // namespace __tsan

#endif  // TSAN_CLOCK_H