2 * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
29 #include <wtf/Atomics.h>
30 #include <wtf/PrintStream.h>
31 #include <wtf/SentinelLinkedList.h>
32 #include <wtf/ThreadSafeRefCounted.h>
37 void* operator new(size_t) = delete;
48 virtual void dump(PrintStream
&) const = 0;
51 class StringFireDetail
: public FireDetail
{
53 StringFireDetail(const char* string
)
58 virtual void dump(PrintStream
& out
) const override
;
64 class Watchpoint
: public BasicRawSentinelNode
<Watchpoint
> {
70 virtual ~Watchpoint();
72 void fire(const FireDetail
& detail
) { fireInternal(detail
); }
75 virtual void fireInternal(const FireDetail
&) = 0;
78 enum WatchpointState
{
84 class InlineWatchpointSet
;
86 class WatchpointSet
: public ThreadSafeRefCounted
<WatchpointSet
> {
87 friend class LLIntOffsetsExtractor
;
89 JS_EXPORT_PRIVATE
WatchpointSet(WatchpointState
);
90 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.
92 // Fast way of getting the state, which only works from the main thread.
93 WatchpointState
stateOnJSThread() const
95 return static_cast<WatchpointState
>(m_state
);
98 // It is safe to call this from another thread. It may return an old
99 // state. Guarantees that if *first* read the state() of the thing being
100 // watched and it returned IsWatched and *second* you actually read its
101 // value then it's safe to assume that if the state being watched changes
102 // then also the watchpoint state() will change to IsInvalidated.
103 WatchpointState
state() const
105 WTF::loadLoadFence();
106 WatchpointState result
= static_cast<WatchpointState
>(m_state
);
107 WTF::loadLoadFence();
111 // It is safe to call this from another thread. It may return true
112 // even if the set actually had been invalidated, but that ought to happen
113 // only in the case of races, and should be rare. Guarantees that if you
114 // call this after observing something that must imply that the set is
115 // invalidated, then you will see this return false. This is ensured by
116 // issuing a load-load fence prior to querying the state.
117 bool isStillValid() const
119 return state() != IsInvalidated
;
121 // Like isStillValid(), may be called from another thread.
122 bool hasBeenInvalidated() const { return !isStillValid(); }
124 // As a convenience, this will ignore 0. That's because code paths in the DFG
125 // that create speculation watchpoints may choose to bail out if speculation
126 // had already been terminated.
127 void add(Watchpoint
*);
129 // Force the watchpoint set to behave as if it was being watched even if no
130 // watchpoints have been installed. This will result in invalidation if the
131 // watchpoint would have fired. That's a pretty good indication that you
132 // probably don't want to set watchpoints, since we typically don't want to
133 // set watchpoints that we believe will actually be fired.
136 ASSERT(m_state
!= IsInvalidated
);
137 if (m_state
== IsWatched
)
139 WTF::storeStoreFence();
141 WTF::storeStoreFence();
144 void fireAll(const FireDetail
& detail
)
146 if (LIKELY(m_state
!= IsWatched
))
151 void fireAll(const char* reason
)
153 if (LIKELY(m_state
!= IsWatched
))
158 void touch(const FireDetail
& detail
)
160 if (state() == ClearWatchpoint
)
166 void touch(const char* reason
)
168 touch(StringFireDetail(reason
));
171 void invalidate(const FireDetail
& detail
)
173 if (state() == IsWatched
)
175 m_state
= IsInvalidated
;
178 void invalidate(const char* reason
)
180 invalidate(StringFireDetail(reason
));
183 int8_t* addressOfState() { return &m_state
; }
184 int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty
; }
186 JS_EXPORT_PRIVATE
void fireAllSlow(const FireDetail
&); // Call only if you've checked isWatched.
187 JS_EXPORT_PRIVATE
void fireAllSlow(const char* reason
); // Ditto.
190 void fireAllWatchpoints(const FireDetail
&);
192 friend class InlineWatchpointSet
;
195 int8_t m_setIsNotEmpty
;
197 SentinelLinkedList
<Watchpoint
, BasicRawSentinelNode
<Watchpoint
>> m_set
;
200 // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
201 // it is not possible to quickly query whether it is being watched in a single
202 // branch. There is a fairly simple tradeoff between WatchpointSet and
203 // InlineWatchpointSet:
205 // Do you have to emit JIT code that rapidly tests whether the watchpoint set
206 // is being watched? If so, use WatchpointSet.
208 // Do you need multiple parties to have pointers to the same WatchpointSet?
209 // If so, use WatchpointSet.
211 // Do you have to allocate a lot of watchpoint sets? If so, use
212 // InlineWatchpointSet unless you answered "yes" to the previous questions.
214 // InlineWatchpointSet will use just one pointer-width word of memory unless
215 // you actually add watchpoints to it, in which case it internally inflates
216 // to a pointer to a WatchpointSet, and transfers its state to the
219 class InlineWatchpointSet
{
220 WTF_MAKE_NONCOPYABLE(InlineWatchpointSet
);
222 InlineWatchpointSet(WatchpointState state
)
223 : m_data(encodeState(state
))
227 ~InlineWatchpointSet()
234 // Fast way of getting the state, which only works from the main thread.
235 WatchpointState
stateOnJSThread() const
237 uintptr_t data
= m_data
;
239 return fat(data
)->stateOnJSThread();
240 return decodeState(data
);
243 // It is safe to call this from another thread. It may return a prior state,
244 // but that should be fine since you should only perform actions based on the
245 // state if you also add a watchpoint.
246 WatchpointState
state() const
248 WTF::loadLoadFence();
249 uintptr_t data
= m_data
;
250 WTF::loadLoadFence();
252 return fat(data
)->state();
253 return decodeState(data
);
256 // It is safe to call this from another thread. It may return false
257 // even if the set actually had been invalidated, but that ought to happen
258 // only in the case of races, and should be rare.
259 bool hasBeenInvalidated() const
261 return state() == IsInvalidated
;
264 // Like hasBeenInvalidated(), may be called from another thread.
265 bool isStillValid() const
267 return !hasBeenInvalidated();
270 void add(Watchpoint
*);
275 fat()->startWatching();
278 ASSERT(decodeState(m_data
) != IsInvalidated
);
279 m_data
= encodeState(IsWatched
);
282 void fireAll(const FireDetail
& detail
)
285 fat()->fireAll(detail
);
288 if (decodeState(m_data
) == ClearWatchpoint
)
290 m_data
= encodeState(IsInvalidated
);
291 WTF::storeStoreFence();
294 void invalidate(const FireDetail
& detail
)
297 fat()->invalidate(detail
);
299 m_data
= encodeState(IsInvalidated
);
302 JS_EXPORT_PRIVATE
void fireAll(const char* reason
);
304 void touch(const FireDetail
& detail
)
307 fat()->touch(detail
);
310 uintptr_t data
= m_data
;
311 if (decodeState(data
) == IsInvalidated
)
313 WTF::storeStoreFence();
314 if (decodeState(data
) == ClearWatchpoint
)
315 m_data
= encodeState(IsWatched
);
317 m_data
= encodeState(IsInvalidated
);
318 WTF::storeStoreFence();
321 void touch(const char* reason
)
323 touch(StringFireDetail(reason
));
327 static const uintptr_t IsThinFlag
= 1;
328 static const uintptr_t StateMask
= 6;
329 static const uintptr_t StateShift
= 1;
331 static bool isThin(uintptr_t data
) { return data
& IsThinFlag
; }
332 static bool isFat(uintptr_t data
) { return !isThin(data
); }
334 static WatchpointState
decodeState(uintptr_t data
)
336 ASSERT(isThin(data
));
337 return static_cast<WatchpointState
>((data
& StateMask
) >> StateShift
);
340 static uintptr_t encodeState(WatchpointState state
)
342 return (static_cast<uintptr_t>(state
) << StateShift
) | IsThinFlag
;
345 bool isThin() const { return isThin(m_data
); }
346 bool isFat() const { return isFat(m_data
); };
348 static WatchpointSet
* fat(uintptr_t data
)
350 return bitwise_cast
<WatchpointSet
*>(data
);
359 const WatchpointSet
* fat() const
365 WatchpointSet
* inflate()
369 return inflateSlow();
372 JS_EXPORT_PRIVATE WatchpointSet
* inflateSlow();
373 JS_EXPORT_PRIVATE
void freeFat();
380 #endif // Watchpoint_h