1/*
2 * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
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
29#include <wtf/Atomics.h>
30#include <wtf/SentinelLinkedList.h>
31#include <wtf/ThreadSafeRefCounted.h>
32
33namespace JSC {
34
35class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
36public:
37    Watchpoint()
38    {
39    }
40
41    virtual ~Watchpoint();
42
43    void fire() { fireInternal(); }
44
45protected:
46    virtual void fireInternal() = 0;
47};
48
49enum WatchpointState {
50    ClearWatchpoint,
51    IsWatched,
52    IsInvalidated
53};
54
55class InlineWatchpointSet;
56
57class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
58    friend class LLIntOffsetsExtractor;
59public:
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.
62
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(); }
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.
99    void startWatching()
100    {
101        ASSERT(state() != IsInvalidated);
102        m_state = IsWatched;
103    }
104
105    void fireAll()
106    {
107        if (state() != IsWatched)
108            return;
109        fireAllSlow();
110    }
111
112    void touch()
113    {
114        if (state() == ClearWatchpoint)
115            startWatching();
116        else
117            fireAll();
118    }
119
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.
131
132private:
133    void fireAllWatchpoints();
134
135    friend class InlineWatchpointSet;
136
137    int8_t m_state;
138    int8_t m_setIsNotEmpty;
139
140    SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
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
162class InlineWatchpointSet {
163    WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
164public:
165    InlineWatchpointSet(WatchpointState state)
166        : m_data(encodeState(state))
167    {
168    }
169
170    ~InlineWatchpointSet()
171    {
172        if (isThin())
173            return;
174        freeFat();
175    }
176
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
181    {
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;
189    }
190
191    // Like hasBeenInvalidated(), may be called from another thread.
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        }
205        ASSERT(decodeState(m_data) != IsInvalidated);
206        m_data = encodeState(IsWatched);
207    }
208
209    void fireAll()
210    {
211        if (isFat()) {
212            fat()->fireAll();
213            return;
214        }
215        if (decodeState(m_data) == ClearWatchpoint)
216            return;
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();
232    }
233
234private:
235    static const uintptr_t IsThinFlag        = 1;
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); }
241
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    }
260
261    WatchpointSet* fat()
262    {
263        ASSERT(isFat());
264        return fat(m_data);
265    }
266
267    const WatchpointSet* fat() const
268    {
269        ASSERT(isFat());
270        return fat(m_data);
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
290