]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - bytecode/Watchpoint.h
JavaScriptCore-7600.1.4.16.1.tar.gz
[apple/javascriptcore.git] / bytecode / Watchpoint.h
index e6fba93a9e043dfbb90cc0d2cc6cca13126c84ae..7659c0db8d4c040caa8cc4f93b5b87fbf5e8b91a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,8 +26,9 @@
 #ifndef Watchpoint_h
 #define Watchpoint_h
 
-#include <wtf/RefCounted.h>
+#include <wtf/Atomics.h>
 #include <wtf/SentinelLinkedList.h>
+#include <wtf/ThreadSafeRefCounted.h>
 
 namespace JSC {
 
@@ -45,17 +46,45 @@ protected:
     virtual void fireInternal() = 0;
 };
 
-enum InitialWatchpointSetMode { InitializedWatching, InitializedBlind };
+enum WatchpointState {
+    ClearWatchpoint,
+    IsWatched,
+    IsInvalidated
+};
 
 class InlineWatchpointSet;
 
-class WatchpointSet : public RefCounted<WatchpointSet> {
+class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
+    friend class LLIntOffsetsExtractor;
 public:
-    WatchpointSet(InitialWatchpointSetMode);
-    ~WatchpointSet();
+    JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
+    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.
     
-    bool isStillValid() const { return !m_isInvalidated; }
-    bool hasBeenInvalidated() const { return m_isInvalidated; }
+    // It is safe to call this from another thread. It may return an old
+    // state. Guarantees that if *first* read the state() of the thing being
+    // watched and it returned IsWatched and *second* you actually read its
+    // value then it's safe to assume that if the state being watched changes
+    // then also the watchpoint state() will change to IsInvalidated.
+    WatchpointState state() const
+    {
+        WTF::loadLoadFence();
+        WatchpointState result = static_cast<WatchpointState>(m_state);
+        WTF::loadLoadFence();
+        return result;
+    }
+    
+    // It is safe to call this from another thread.  It may return true
+    // even if the set actually had been invalidated, but that ought to happen
+    // only in the case of races, and should be rare. Guarantees that if you
+    // call this after observing something that must imply that the set is
+    // invalidated, then you will see this return false. This is ensured by
+    // issuing a load-load fence prior to querying the state.
+    bool isStillValid() const
+    {
+        return state() != IsInvalidated;
+    }
+    // Like isStillValid(), may be called from another thread.
+    bool hasBeenInvalidated() const { return !isStillValid(); }
     
     // As a convenience, this will ignore 0. That's because code paths in the DFG
     // that create speculation watchpoints may choose to bail out if speculation
@@ -67,27 +96,48 @@ public:
     // watchpoint would have fired. That's a pretty good indication that you
     // probably don't want to set watchpoints, since we typically don't want to
     // set watchpoints that we believe will actually be fired.
-    void startWatching() { m_isWatched = true; }
+    void startWatching()
+    {
+        ASSERT(state() != IsInvalidated);
+        m_state = IsWatched;
+    }
     
-    void notifyWrite()
+    void fireAll()
     {
-        if (!m_isWatched)
+        if (state() != IsWatched)
             return;
-        notifyWriteSlow();
+        fireAllSlow();
     }
     
-    bool* addressOfIsWatched() { return &m_isWatched; }
+    void touch()
+    {
+        if (state() == ClearWatchpoint)
+            startWatching();
+        else
+            fireAll();
+    }
     
-    JS_EXPORT_PRIVATE void notifyWriteSlow(); // Call only if you've checked isWatched.
+    void invalidate()
+    {
+        if (state() == IsWatched)
+            fireAll();
+        m_state = IsInvalidated;
+    }
+
+    int8_t* addressOfState() { return &m_state; }
+    int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
+    
+    JS_EXPORT_PRIVATE void fireAllSlow(); // Call only if you've checked isWatched.
     
 private:
     void fireAllWatchpoints();
     
     friend class InlineWatchpointSet;
-    
-    SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint> > m_set;
-    bool m_isWatched;
-    bool m_isInvalidated;
+
+    int8_t m_state;
+    int8_t m_setIsNotEmpty;
+
+    SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
 };
 
 // InlineWatchpointSet is a low-overhead, non-copyable watchpoint set in which
@@ -112,8 +162,8 @@ private:
 class InlineWatchpointSet {
     WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
 public:
-    InlineWatchpointSet(InitialWatchpointSetMode mode)
-        : m_data((mode == InitializedWatching ? IsWatchedFlag : 0) | IsThinFlag)
+    InlineWatchpointSet(WatchpointState state)
+        : m_data(encodeState(state))
     {
     }
     
@@ -124,13 +174,21 @@ public:
         freeFat();
     }
     
+    // It is safe to call this from another thread.  It may return false
+    // even if the set actually had been invalidated, but that ought to happen
+    // only in the case of races, and should be rare.
     bool hasBeenInvalidated() const
     {
-        if (isFat())
-            return fat()->hasBeenInvalidated();
-        return m_data & IsInvalidatedFlag;
+        WTF::loadLoadFence();
+        uintptr_t data = m_data;
+        if (isFat(data)) {
+            WTF::loadLoadFence();
+            return fat(data)->hasBeenInvalidated();
+        }
+        return decodeState(data) == IsInvalidated;
     }
     
+    // Like hasBeenInvalidated(), may be called from another thread.
     bool isStillValid() const
     {
         return !hasBeenInvalidated();
@@ -144,38 +202,72 @@ public:
             fat()->startWatching();
             return;
         }
-        m_data |= IsWatchedFlag;
+        ASSERT(decodeState(m_data) != IsInvalidated);
+        m_data = encodeState(IsWatched);
     }
     
-    void notifyWrite()
+    void fireAll()
     {
         if (isFat()) {
-            fat()->notifyWrite();
+            fat()->fireAll();
             return;
         }
-        if (!(m_data & IsWatchedFlag))
+        if (decodeState(m_data) == ClearWatchpoint)
             return;
-        m_data |= IsInvalidatedFlag;
+        m_data = encodeState(IsInvalidated);
+        WTF::storeStoreFence();
+    }
+    
+    void touch()
+    {
+        if (isFat()) {
+            fat()->touch();
+            return;
+        }
+        if (decodeState(m_data) == ClearWatchpoint)
+            m_data = encodeState(IsWatched);
+        else
+            m_data = encodeState(IsInvalidated);
+        WTF::storeStoreFence();
     }
     
 private:
     static const uintptr_t IsThinFlag        = 1;
-    static const uintptr_t IsInvalidatedFlag = 2;
-    static const uintptr_t IsWatchedFlag     = 4;
+    static const uintptr_t StateMask         = 6;
+    static const uintptr_t StateShift        = 1;
+    
+    static bool isThin(uintptr_t data) { return data & IsThinFlag; }
+    static bool isFat(uintptr_t data) { return !isThin(data); }
     
-    bool isThin() const { return m_data & IsThinFlag; }
-    bool isFat() const { return !isThin(); };
+    static WatchpointState decodeState(uintptr_t data)
+    {
+        ASSERT(isThin(data));
+        return static_cast<WatchpointState>((data & StateMask) >> StateShift);
+    }
+    
+    static uintptr_t encodeState(WatchpointState state)
+    {
+        return (state << StateShift) | IsThinFlag;
+    }
+    
+    bool isThin() const { return isThin(m_data); }
+    bool isFat() const { return isFat(m_data); };
+    
+    static WatchpointSet* fat(uintptr_t data)
+    {
+        return bitwise_cast<WatchpointSet*>(data);
+    }
     
     WatchpointSet* fat()
     {
         ASSERT(isFat());
-        return bitwise_cast<WatchpointSet*>(m_data);
+        return fat(m_data);
     }
     
     const WatchpointSet* fat() const
     {
         ASSERT(isFat());
-        return bitwise_cast<WatchpointSet*>(m_data);
+        return fat(m_data);
     }
     
     WatchpointSet* inflate()