@@ -27,6 +27,8 @@ template <typename ValueT>
27
27
class ThreadLocalCache {
28
28
struct PerInstanceState ;
29
29
30
+ using PointerAndFlag = std::pair<ValueT *, std::atomic<bool >>;
31
+
30
32
// / The "observer" is owned by a thread-local cache instance. It is
31
33
// / constructed the first time a `ThreadLocalCache` instance is accessed by a
32
34
// / thread, unless `perInstanceState` happens to get re-allocated to the same
@@ -41,7 +43,8 @@ class ThreadLocalCache {
41
43
// / This is the double pointer, explicitly allocated because we need to keep
42
44
// / the address stable if the TLC map re-allocates. It is owned by the
43
45
// / observer and shared with the value owner.
44
- std::shared_ptr<ValueT *> ptr = std::make_shared<ValueT *>(nullptr );
46
+ std::shared_ptr<PointerAndFlag> ptr =
47
+ std::make_shared<PointerAndFlag>(std::make_pair(nullptr , false ));
45
48
// / Because the `Owner` instance that lives inside `PerInstanceState`
46
49
// / contains a reference to the double pointer, and likewise this class
47
50
// / contains a reference to the value, we need to synchronize destruction of
@@ -62,18 +65,21 @@ class ThreadLocalCache {
62
65
// / Save a pointer to the reference and write it to the newly created entry.
63
66
Owner (Observer &observer)
64
67
: value(std::make_unique<ValueT>()), ptrRef(observer.ptr) {
65
- *observer.ptr = value.get ();
68
+ observer.ptr ->second = true ;
69
+ observer.ptr ->first = value.get ();
66
70
}
67
71
~Owner () {
68
- if (std::shared_ptr<ValueT *> ptr = ptrRef.lock ())
69
- *ptr = nullptr ;
72
+ if (std::shared_ptr<PointerAndFlag> ptr = ptrRef.lock ()) {
73
+ ptr->first = nullptr ;
74
+ ptr->second = false ;
75
+ }
70
76
}
71
77
72
78
Owner (Owner &&) = default ;
73
79
Owner &operator =(Owner &&) = default ;
74
80
75
81
std::unique_ptr<ValueT> value;
76
- std::weak_ptr<ValueT * > ptrRef;
82
+ std::weak_ptr<PointerAndFlag > ptrRef;
77
83
};
78
84
79
85
// Keep a separate shared_ptr protected state that can be acquired atomically
@@ -116,15 +122,15 @@ class ThreadLocalCache {
116
122
// back to the data here that is being destroyed.
117
123
for (auto &[instance, observer] : *this )
118
124
if (std::shared_ptr<PerInstanceState> state = observer.keepalive .lock ())
119
- state->remove (* observer.ptr );
125
+ state->remove (observer.ptr -> first );
120
126
}
121
127
122
128
// / Clear out any unused entries within the map. This method is not
123
129
// / thread-safe, and should only be called by the same thread as the cache.
124
130
void clearExpiredEntries () {
125
131
for (auto it = this ->begin (), e = this ->end (); it != e;) {
126
132
auto curIt = it++;
127
- if (!* curIt->second .ptr )
133
+ if (!curIt->second .ptr -> second )
128
134
this ->erase (curIt);
129
135
}
130
136
}
@@ -142,7 +148,7 @@ class ThreadLocalCache {
142
148
// Check for an already existing instance for this thread.
143
149
CacheType &staticCache = getStaticCache ();
144
150
Observer &threadInstance = staticCache[perInstanceState.get ()];
145
- if (ValueT *value = * threadInstance.ptr )
151
+ if (ValueT *value = threadInstance.ptr -> first )
146
152
return *value;
147
153
148
154
// Otherwise, create a new instance for this thread.
@@ -157,7 +163,7 @@ class ThreadLocalCache {
157
163
// entries in the static map. The cache is only cleared within the same
158
164
// thread to remove the need to lock the cache itself.
159
165
staticCache.clearExpiredEntries ();
160
- return ** threadInstance.ptr ;
166
+ return *threadInstance.ptr -> first ;
161
167
}
162
168
ValueT &operator *() { return get (); }
163
169
ValueT *operator ->() { return &get (); }
0 commit comments