2 * Copyright (C) 2012, 2013 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/SentinelLinkedList.h>
31 #include <wtf/ThreadSafeRefCounted.h>
35 class Watchpoint
: public BasicRawSentinelNode
<Watchpoint
> {
41 virtual ~Watchpoint();
43 void fire() { fireInternal(); }
46 virtual void fireInternal() = 0;
49 enum WatchpointState
{
55 class InlineWatchpointSet
;
57 class WatchpointSet
: public ThreadSafeRefCounted
<WatchpointSet
> {
58 friend class LLIntOffsetsExtractor
;
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.
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
71 WatchpointState result
= static_cast<WatchpointState
>(m_state
);
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
84 return state() != IsInvalidated
;
86 // Like isStillValid(), may be called from another thread.
87 bool hasBeenInvalidated() const { return !isStillValid(); }
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
*);
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.
101 ASSERT(state() != IsInvalidated
);
107 if (state() != IsWatched
)
114 if (state() == ClearWatchpoint
)
122 if (state() == IsWatched
)
124 m_state
= IsInvalidated
;
127 int8_t* addressOfState() { return &m_state
; }
128 int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty
; }
130 JS_EXPORT_PRIVATE
void fireAllSlow(); // Call only if you've checked isWatched.
133 void fireAllWatchpoints();
135 friend class InlineWatchpointSet
;
138 int8_t m_setIsNotEmpty
;
140 SentinelLinkedList
<Watchpoint
, BasicRawSentinelNode
<Watchpoint
>> m_set
;
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:
148 // Do you have to emit JIT code that rapidly tests whether the watchpoint set
149 // is being watched? If so, use WatchpointSet.
151 // Do you need multiple parties to have pointers to the same WatchpointSet?
152 // If so, use WatchpointSet.
154 // Do you have to allocate a lot of watchpoint sets? If so, use
155 // InlineWatchpointSet unless you answered "yes" to the previous questions.
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
162 class InlineWatchpointSet
{
163 WTF_MAKE_NONCOPYABLE(InlineWatchpointSet
);
165 InlineWatchpointSet(WatchpointState state
)
166 : m_data(encodeState(state
))
170 ~InlineWatchpointSet()
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.
180 bool hasBeenInvalidated() const
182 WTF::loadLoadFence();
183 uintptr_t data
= m_data
;
185 WTF::loadLoadFence();
186 return fat(data
)->hasBeenInvalidated();
188 return decodeState(data
) == IsInvalidated
;
191 // Like hasBeenInvalidated(), may be called from another thread.
192 bool isStillValid() const
194 return !hasBeenInvalidated();
197 void add(Watchpoint
*);
202 fat()->startWatching();
205 ASSERT(decodeState(m_data
) != IsInvalidated
);
206 m_data
= encodeState(IsWatched
);
215 if (decodeState(m_data
) == ClearWatchpoint
)
217 m_data
= encodeState(IsInvalidated
);
218 WTF::storeStoreFence();
227 if (decodeState(m_data
) == ClearWatchpoint
)
228 m_data
= encodeState(IsWatched
);
230 m_data
= encodeState(IsInvalidated
);
231 WTF::storeStoreFence();
235 static const uintptr_t IsThinFlag
= 1;
236 static const uintptr_t StateMask
= 6;
237 static const uintptr_t StateShift
= 1;
239 static bool isThin(uintptr_t data
) { return data
& IsThinFlag
; }
240 static bool isFat(uintptr_t data
) { return !isThin(data
); }
242 static WatchpointState
decodeState(uintptr_t data
)
244 ASSERT(isThin(data
));
245 return static_cast<WatchpointState
>((data
& StateMask
) >> StateShift
);
248 static uintptr_t encodeState(WatchpointState state
)
250 return (state
<< StateShift
) | IsThinFlag
;
253 bool isThin() const { return isThin(m_data
); }
254 bool isFat() const { return isFat(m_data
); };
256 static WatchpointSet
* fat(uintptr_t data
)
258 return bitwise_cast
<WatchpointSet
*>(data
);
267 const WatchpointSet
* fat() const
273 WatchpointSet
* inflate()
277 return inflateSlow();
280 JS_EXPORT_PRIVATE WatchpointSet
* inflateSlow();
281 JS_EXPORT_PRIVATE
void freeFat();
288 #endif // Watchpoint_h