]>
Commit | Line | Data |
---|---|---|
93a37866 | 1 | /* |
81345200 | 2 | * Copyright (C) 2012, 2013 Apple Inc. All rights reserved. |
93a37866 A |
3 | * |
4 | * Redistribution and use in source and binary forms, with or without | |
5 | * modification, are permitted provided that the following conditions | |
6 | * are met: | |
7 | * 1. Redistributions of source code must retain the above copyright | |
8 | * notice, this list of conditions and the following disclaimer. | |
9 | * 2. Redistributions in binary form must reproduce the above copyright | |
10 | * notice, this list of conditions and the following disclaimer in the | |
11 | * documentation and/or other materials provided with the distribution. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY | |
14 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
16 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR | |
17 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, | |
18 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
19 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR | |
20 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY | |
21 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
23 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
24 | */ | |
25 | ||
26 | #ifndef Watchpoint_h | |
27 | #define Watchpoint_h | |
28 | ||
81345200 | 29 | #include <wtf/Atomics.h> |
93a37866 | 30 | #include <wtf/SentinelLinkedList.h> |
81345200 | 31 | #include <wtf/ThreadSafeRefCounted.h> |
93a37866 A |
32 | |
33 | namespace JSC { | |
34 | ||
35 | class Watchpoint : public BasicRawSentinelNode<Watchpoint> { | |
36 | public: | |
37 | Watchpoint() | |
38 | { | |
39 | } | |
40 | ||
41 | virtual ~Watchpoint(); | |
42 | ||
43 | void fire() { fireInternal(); } | |
44 | ||
45 | protected: | |
46 | virtual void fireInternal() = 0; | |
47 | }; | |
48 | ||
81345200 A |
49 | enum WatchpointState { |
50 | ClearWatchpoint, | |
51 | IsWatched, | |
52 | IsInvalidated | |
53 | }; | |
93a37866 A |
54 | |
55 | class InlineWatchpointSet; | |
56 | ||
81345200 A |
57 | class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> { |
58 | friend class LLIntOffsetsExtractor; | |
93a37866 | 59 | public: |
81345200 A |
60 | JS_EXPORT_PRIVATE WatchpointSet(WatchpointState); |
61 | JS_EXPORT_PRIVATE ~WatchpointSet(); // Note that this will not fire any of the watchpoints; if you need to know when a WatchpointSet dies then you need a separate mechanism for this. | |
93a37866 | 62 | |
81345200 A |
63 | // It is safe to call this from another thread. It may return an old |
64 | // state. Guarantees that if *first* read the state() of the thing being | |
65 | // watched and it returned IsWatched and *second* you actually read its | |
66 | // value then it's safe to assume that if the state being watched changes | |
67 | // then also the watchpoint state() will change to IsInvalidated. | |
68 | WatchpointState state() const | |
69 | { | |
70 | WTF::loadLoadFence(); | |
71 | WatchpointState result = static_cast<WatchpointState>(m_state); | |
72 | WTF::loadLoadFence(); | |
73 | return result; | |
74 | } | |
75 | ||
76 | // It is safe to call this from another thread. It may return true | |
77 | // even if the set actually had been invalidated, but that ought to happen | |
78 | // only in the case of races, and should be rare. Guarantees that if you | |
79 | // call this after observing something that must imply that the set is | |
80 | // invalidated, then you will see this return false. This is ensured by | |
81 | // issuing a load-load fence prior to querying the state. | |
82 | bool isStillValid() const | |
83 | { | |
84 | return state() != IsInvalidated; | |
85 | } | |
86 | // Like isStillValid(), may be called from another thread. | |
87 | bool hasBeenInvalidated() const { return !isStillValid(); } | |
93a37866 A |
88 | |
89 | // As a convenience, this will ignore 0. That's because code paths in the DFG | |
90 | // that create speculation watchpoints may choose to bail out if speculation | |
91 | // had already been terminated. | |
92 | void add(Watchpoint*); | |
93 | ||
94 | // Force the watchpoint set to behave as if it was being watched even if no | |
95 | // watchpoints have been installed. This will result in invalidation if the | |
96 | // watchpoint would have fired. That's a pretty good indication that you | |
97 | // probably don't want to set watchpoints, since we typically don't want to | |
98 | // set watchpoints that we believe will actually be fired. | |
81345200 A |
99 | void startWatching() |
100 | { | |
101 | ASSERT(state() != IsInvalidated); | |
102 | m_state = IsWatched; | |
103 | } | |
93a37866 | 104 | |
81345200 | 105 | void fireAll() |
93a37866 | 106 | { |
81345200 | 107 | if (state() != IsWatched) |
93a37866 | 108 | return; |
81345200 | 109 | fireAllSlow(); |
93a37866 A |
110 | } |
111 | ||
81345200 A |
112 | void touch() |
113 | { | |
114 | if (state() == ClearWatchpoint) | |
115 | startWatching(); | |
116 | else | |
117 | fireAll(); | |
118 | } | |
93a37866 | 119 | |
81345200 A |
120 | void invalidate() |
121 | { | |
122 | if (state() == IsWatched) | |
123 | fireAll(); | |
124 | m_state = IsInvalidated; | |
125 | } | |
126 | ||
127 | int8_t* addressOfState() { return &m_state; } | |
128 | int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; } | |
129 | ||
130 | JS_EXPORT_PRIVATE void fireAllSlow(); // Call only if you've checked isWatched. | |
93a37866 A |
131 | |
132 | private: | |
133 | void fireAllWatchpoints(); | |
134 | ||
135 | friend class InlineWatchpointSet; | |
81345200 A |
136 | |
137 | int8_t m_state; | |
138 | int8_t m_setIsNotEmpty; | |
139 | ||
140 | SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set; | |
93a37866 A |
141 | }; |
142 | ||
143 | // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which | |
144 | // it is not possible to quickly query whether it is being watched in a single | |
145 | // branch. There is a fairly simple tradeoff between WatchpointSet and | |
146 | // InlineWatchpointSet: | |
147 | // | |
148 | // Do you have to emit JIT code that rapidly tests whether the watchpoint set | |
149 | // is being watched? If so, use WatchpointSet. | |
150 | // | |
151 | // Do you need multiple parties to have pointers to the same WatchpointSet? | |
152 | // If so, use WatchpointSet. | |
153 | // | |
154 | // Do you have to allocate a lot of watchpoint sets? If so, use | |
155 | // InlineWatchpointSet unless you answered "yes" to the previous questions. | |
156 | // | |
157 | // InlineWatchpointSet will use just one pointer-width word of memory unless | |
158 | // you actually add watchpoints to it, in which case it internally inflates | |
159 | // to a pointer to a WatchpointSet, and transfers its state to the | |
160 | // WatchpointSet. | |
161 | ||
162 | class InlineWatchpointSet { | |
163 | WTF_MAKE_NONCOPYABLE(InlineWatchpointSet); | |
164 | public: | |
81345200 A |
165 | InlineWatchpointSet(WatchpointState state) |
166 | : m_data(encodeState(state)) | |
93a37866 A |
167 | { |
168 | } | |
169 | ||
170 | ~InlineWatchpointSet() | |
171 | { | |
172 | if (isThin()) | |
173 | return; | |
174 | freeFat(); | |
175 | } | |
176 | ||
81345200 A |
177 | // It is safe to call this from another thread. It may return false |
178 | // even if the set actually had been invalidated, but that ought to happen | |
179 | // only in the case of races, and should be rare. | |
93a37866 A |
180 | bool hasBeenInvalidated() const |
181 | { | |
81345200 A |
182 | WTF::loadLoadFence(); |
183 | uintptr_t data = m_data; | |
184 | if (isFat(data)) { | |
185 | WTF::loadLoadFence(); | |
186 | return fat(data)->hasBeenInvalidated(); | |
187 | } | |
188 | return decodeState(data) == IsInvalidated; | |
93a37866 A |
189 | } |
190 | ||
81345200 | 191 | // Like hasBeenInvalidated(), may be called from another thread. |
93a37866 A |
192 | bool isStillValid() const |
193 | { | |
194 | return !hasBeenInvalidated(); | |
195 | } | |
196 | ||
197 | void add(Watchpoint*); | |
198 | ||
199 | void startWatching() | |
200 | { | |
201 | if (isFat()) { | |
202 | fat()->startWatching(); | |
203 | return; | |
204 | } | |
81345200 A |
205 | ASSERT(decodeState(m_data) != IsInvalidated); |
206 | m_data = encodeState(IsWatched); | |
93a37866 A |
207 | } |
208 | ||
81345200 | 209 | void fireAll() |
93a37866 A |
210 | { |
211 | if (isFat()) { | |
81345200 | 212 | fat()->fireAll(); |
93a37866 A |
213 | return; |
214 | } | |
81345200 | 215 | if (decodeState(m_data) == ClearWatchpoint) |
93a37866 | 216 | return; |
81345200 A |
217 | m_data = encodeState(IsInvalidated); |
218 | WTF::storeStoreFence(); | |
219 | } | |
220 | ||
221 | void touch() | |
222 | { | |
223 | if (isFat()) { | |
224 | fat()->touch(); | |
225 | return; | |
226 | } | |
227 | if (decodeState(m_data) == ClearWatchpoint) | |
228 | m_data = encodeState(IsWatched); | |
229 | else | |
230 | m_data = encodeState(IsInvalidated); | |
231 | WTF::storeStoreFence(); | |
93a37866 A |
232 | } |
233 | ||
234 | private: | |
235 | static const uintptr_t IsThinFlag = 1; | |
81345200 A |
236 | static const uintptr_t StateMask = 6; |
237 | static const uintptr_t StateShift = 1; | |
238 | ||
239 | static bool isThin(uintptr_t data) { return data & IsThinFlag; } | |
240 | static bool isFat(uintptr_t data) { return !isThin(data); } | |
93a37866 | 241 | |
81345200 A |
242 | static WatchpointState decodeState(uintptr_t data) |
243 | { | |
244 | ASSERT(isThin(data)); | |
245 | return static_cast<WatchpointState>((data & StateMask) >> StateShift); | |
246 | } | |
247 | ||
248 | static uintptr_t encodeState(WatchpointState state) | |
249 | { | |
250 | return (state << StateShift) | IsThinFlag; | |
251 | } | |
252 | ||
253 | bool isThin() const { return isThin(m_data); } | |
254 | bool isFat() const { return isFat(m_data); }; | |
255 | ||
256 | static WatchpointSet* fat(uintptr_t data) | |
257 | { | |
258 | return bitwise_cast<WatchpointSet*>(data); | |
259 | } | |
93a37866 A |
260 | |
261 | WatchpointSet* fat() | |
262 | { | |
263 | ASSERT(isFat()); | |
81345200 | 264 | return fat(m_data); |
93a37866 A |
265 | } |
266 | ||
267 | const WatchpointSet* fat() const | |
268 | { | |
269 | ASSERT(isFat()); | |
81345200 | 270 | return fat(m_data); |
93a37866 A |
271 | } |
272 | ||
273 | WatchpointSet* inflate() | |
274 | { | |
275 | if (LIKELY(isFat())) | |
276 | return fat(); | |
277 | return inflateSlow(); | |
278 | } | |
279 | ||
280 | JS_EXPORT_PRIVATE WatchpointSet* inflateSlow(); | |
281 | JS_EXPORT_PRIVATE void freeFat(); | |
282 | ||
283 | uintptr_t m_data; | |
284 | }; | |
285 | ||
286 | } // namespace JSC | |
287 | ||
288 | #endif // Watchpoint_h | |
289 |