]> git.saurik.com Git - apple/javascriptcore.git/blobdiff - bytecode/Watchpoint.h
JavaScriptCore-7601.1.46.3.tar.gz
[apple/javascriptcore.git] / bytecode / Watchpoint.h
index 7659c0db8d4c040caa8cc4f93b5b87fbf5e8b91a..88573177ae8649d8adee26bef9b39dc576d27a6f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #define Watchpoint_h
 
 #include <wtf/Atomics.h>
+#include <wtf/PrintStream.h>
 #include <wtf/SentinelLinkedList.h>
 #include <wtf/ThreadSafeRefCounted.h>
 
 namespace JSC {
 
+class FireDetail {
+    void* operator new(size_t) = delete;
+    
+public:
+    FireDetail()
+    {
+    }
+    
+    virtual ~FireDetail()
+    {
+    }
+    
+    virtual void dump(PrintStream&) const = 0;
+};
+
+class StringFireDetail : public FireDetail {
+public:
+    StringFireDetail(const char* string)
+        : m_string(string)
+    {
+    }
+    
+    virtual void dump(PrintStream& out) const override;
+
+private:
+    const char* m_string;
+};
+
 class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
 public:
     Watchpoint()
@@ -40,10 +69,10 @@ public:
     
     virtual ~Watchpoint();
 
-    void fire() { fireInternal(); }
+    void fire(const FireDetail& detail) { fireInternal(detail); }
     
 protected:
-    virtual void fireInternal() = 0;
+    virtual void fireInternal(const FireDetail&) = 0;
 };
 
 enum WatchpointState {
@@ -60,6 +89,12 @@ public:
     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.
     
+    // Fast way of getting the state, which only works from the main thread.
+    WatchpointState stateOnJSThread() const
+    {
+        return static_cast<WatchpointState>(m_state);
+    }
+    
     // 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
@@ -98,39 +133,61 @@ public:
     // set watchpoints that we believe will actually be fired.
     void startWatching()
     {
-        ASSERT(state() != IsInvalidated);
+        ASSERT(m_state != IsInvalidated);
+        if (m_state == IsWatched)
+            return;
+        WTF::storeStoreFence();
         m_state = IsWatched;
+        WTF::storeStoreFence();
     }
     
-    void fireAll()
+    void fireAll(const FireDetail& detail)
     {
-        if (state() != IsWatched)
+        if (LIKELY(m_state != IsWatched))
             return;
-        fireAllSlow();
+        fireAllSlow(detail);
     }
     
-    void touch()
+    void fireAll(const char* reason)
+    {
+        if (LIKELY(m_state != IsWatched))
+            return;
+        fireAllSlow(reason);
+    }
+    
+    void touch(const FireDetail& detail)
     {
         if (state() == ClearWatchpoint)
             startWatching();
         else
-            fireAll();
+            fireAll(detail);
     }
     
-    void invalidate()
+    void touch(const char* reason)
+    {
+        touch(StringFireDetail(reason));
+    }
+    
+    void invalidate(const FireDetail& detail)
     {
         if (state() == IsWatched)
-            fireAll();
+            fireAll(detail);
         m_state = IsInvalidated;
     }
-
+    
+    void invalidate(const char* reason)
+    {
+        invalidate(StringFireDetail(reason));
+    }
+    
     int8_t* addressOfState() { return &m_state; }
     int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
     
-    JS_EXPORT_PRIVATE void fireAllSlow(); // Call only if you've checked isWatched.
+    JS_EXPORT_PRIVATE void fireAllSlow(const FireDetail&); // Call only if you've checked isWatched.
+    JS_EXPORT_PRIVATE void fireAllSlow(const char* reason); // Ditto.
     
 private:
-    void fireAllWatchpoints();
+    void fireAllWatchpoints(const FireDetail&);
     
     friend class InlineWatchpointSet;
 
@@ -174,18 +231,34 @@ public:
         freeFat();
     }
     
+    // Fast way of getting the state, which only works from the main thread.
+    WatchpointState stateOnJSThread() const
+    {
+        uintptr_t data = m_data;
+        if (isFat(data))
+            return fat(data)->stateOnJSThread();
+        return decodeState(data);
+    }
+
+    // It is safe to call this from another thread. It may return a prior state,
+    // but that should be fine since you should only perform actions based on the
+    // state if you also add a watchpoint.
+    WatchpointState state() const
+    {
+        WTF::loadLoadFence();
+        uintptr_t data = m_data;
+        WTF::loadLoadFence();
+        if (isFat(data))
+            return fat(data)->state();
+        return decodeState(data);
+    }
+    
     // 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
     {
-        WTF::loadLoadFence();
-        uintptr_t data = m_data;
-        if (isFat(data)) {
-            WTF::loadLoadFence();
-            return fat(data)->hasBeenInvalidated();
-        }
-        return decodeState(data) == IsInvalidated;
+        return state() == IsInvalidated;
     }
     
     // Like hasBeenInvalidated(), may be called from another thread.
@@ -206,10 +279,10 @@ public:
         m_data = encodeState(IsWatched);
     }
     
-    void fireAll()
+    void fireAll(const FireDetail& detail)
     {
         if (isFat()) {
-            fat()->fireAll();
+            fat()->fireAll(detail);
             return;
         }
         if (decodeState(m_data) == ClearWatchpoint)
@@ -218,19 +291,38 @@ public:
         WTF::storeStoreFence();
     }
     
-    void touch()
+    void invalidate(const FireDetail& detail)
+    {
+        if (isFat())
+            fat()->invalidate(detail);
+        else
+            m_data = encodeState(IsInvalidated);
+    }
+    
+    JS_EXPORT_PRIVATE void fireAll(const char* reason);
+    
+    void touch(const FireDetail& detail)
     {
         if (isFat()) {
-            fat()->touch();
+            fat()->touch(detail);
             return;
         }
-        if (decodeState(m_data) == ClearWatchpoint)
+        uintptr_t data = m_data;
+        if (decodeState(data) == IsInvalidated)
+            return;
+        WTF::storeStoreFence();
+        if (decodeState(data) == ClearWatchpoint)
             m_data = encodeState(IsWatched);
         else
             m_data = encodeState(IsInvalidated);
         WTF::storeStoreFence();
     }
     
+    void touch(const char* reason)
+    {
+        touch(StringFireDetail(reason));
+    }
+    
 private:
     static const uintptr_t IsThinFlag        = 1;
     static const uintptr_t StateMask         = 6;
@@ -247,7 +339,7 @@ private:
     
     static uintptr_t encodeState(WatchpointState state)
     {
-        return (state << StateShift) | IsThinFlag;
+        return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag;
     }
     
     bool isThin() const { return isThin(m_data); }