summaryrefslogtreecommitdiff
path: root/Source/JavaScriptCore/bytecode/Watchpoint.h
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/bytecode/Watchpoint.h
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/JavaScriptCore/bytecode/Watchpoint.h')
-rw-r--r--Source/JavaScriptCore/bytecode/Watchpoint.h215
1 files changed, 180 insertions, 35 deletions
diff --git a/Source/JavaScriptCore/bytecode/Watchpoint.h b/Source/JavaScriptCore/bytecode/Watchpoint.h
index 8790f4e62..69e393de4 100644
--- a/Source/JavaScriptCore/bytecode/Watchpoint.h
+++ b/Source/JavaScriptCore/bytecode/Watchpoint.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012, 2013 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -23,16 +23,50 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef Watchpoint_h
-#define Watchpoint_h
+#pragma once
#include <wtf/Atomics.h>
+#include <wtf/FastMalloc.h>
+#include <wtf/Noncopyable.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)
+ {
+ }
+
+ void dump(PrintStream& out) const override;
+
+private:
+ const char* m_string;
+};
+
+class WatchpointSet;
+
class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
+ WTF_MAKE_NONCOPYABLE(Watchpoint);
+ WTF_MAKE_FAST_ALLOCATED;
public:
Watchpoint()
{
@@ -40,10 +74,12 @@ public:
virtual ~Watchpoint();
- void fire() { fireInternal(); }
-
protected:
- virtual void fireInternal() = 0;
+ virtual void fireInternal(const FireDetail&) = 0;
+
+private:
+ friend class WatchpointSet;
+ void fire(const FireDetail&);
};
enum WatchpointState {
@@ -53,12 +89,22 @@ enum WatchpointState {
};
class InlineWatchpointSet;
+class VM;
class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
friend class LLIntOffsetsExtractor;
public:
- WatchpointSet(WatchpointState);
- ~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.
+ JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
+
+ // FIXME: In many cases, it would be amazing if this *did* fire the watchpoints. I suspect that
+ // this might be hard to get right, but still, it might be awesome.
+ 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
@@ -98,39 +144,67 @@ 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(VM& vm, const FireDetail& detail)
{
- if (state() != IsWatched)
+ if (LIKELY(m_state != IsWatched))
return;
- fireAllSlow();
+ fireAllSlow(vm, detail);
}
- void touch()
+ void fireAll(VM& vm, const char* reason)
+ {
+ if (LIKELY(m_state != IsWatched))
+ return;
+ fireAllSlow(vm, reason);
+ }
+
+ void touch(VM& vm, const FireDetail& detail)
{
if (state() == ClearWatchpoint)
startWatching();
else
- fireAll();
+ fireAll(vm, detail);
}
- void invalidate()
+ void touch(VM& vm, const char* reason)
+ {
+ touch(vm, StringFireDetail(reason));
+ }
+
+ void invalidate(VM& vm, const FireDetail& detail)
{
if (state() == IsWatched)
- fireAll();
+ fireAll(vm, detail);
m_state = IsInvalidated;
}
-
+
+ void invalidate(VM& vm, const char* reason)
+ {
+ invalidate(vm, StringFireDetail(reason));
+ }
+
+ bool isBeingWatched() const
+ {
+ return m_setIsNotEmpty;
+ }
+
int8_t* addressOfState() { return &m_state; }
+ static ptrdiff_t offsetOfState() { return OBJECT_OFFSETOF(WatchpointSet, 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(VM&, const FireDetail&); // Call only if you've checked isWatched.
+ JS_EXPORT_PRIVATE void fireAllSlow(VM&, const char* reason); // Ditto.
private:
- void fireAllWatchpoints();
+ void fireAllWatchpoints(VM&, const FireDetail&);
friend class InlineWatchpointSet;
@@ -174,18 +248,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 +296,10 @@ public:
m_data = encodeState(IsWatched);
}
- void fireAll()
+ void fireAll(VM& vm, const FireDetail& detail)
{
if (isFat()) {
- fat()->fireAll();
+ fat()->fireAll(vm, detail);
return;
}
if (decodeState(m_data) == ClearWatchpoint)
@@ -218,19 +308,77 @@ public:
WTF::storeStoreFence();
}
- void touch()
+ void invalidate(VM& vm, const FireDetail& detail)
+ {
+ if (isFat())
+ fat()->invalidate(vm, detail);
+ else
+ m_data = encodeState(IsInvalidated);
+ }
+
+ JS_EXPORT_PRIVATE void fireAll(VM&, const char* reason);
+
+ void touch(VM& vm, const FireDetail& detail)
{
if (isFat()) {
- fat()->touch();
+ fat()->touch(vm, 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(VM& vm, const char* reason)
+ {
+ touch(vm, StringFireDetail(reason));
+ }
+
+ // Note that for any watchpoint that is visible from the DFG, it would be incorrect to write code like:
+ //
+ // if (w.isBeingWatched())
+ // w.fireAll()
+ //
+ // Concurrently to this, the DFG could do:
+ //
+ // if (w.isStillValid())
+ // perform optimizations;
+ // if (!w.isStillValid())
+ // retry compilation;
+ //
+ // Note that the DFG algorithm is widespread, and sound, because fireAll() and invalidate() will leave
+ // the watchpoint in a !isStillValid() state. Hence, if fireAll() or invalidate() interleaved between
+ // the first isStillValid() check and the second one, then it would simply cause the DFG to retry
+ // compilation later.
+ //
+ // But, if you change some piece of state that the DFG might optimize for, but invalidate the
+ // watchpoint by doing:
+ //
+ // if (w.isBeingWatched())
+ // w.fireAll()
+ //
+ // then the DFG would never know that you invalidated state between the two checks.
+ //
+ // There are two ways to work around this:
+ //
+ // - Call fireAll() without a isBeingWatched() check. Then, the DFG will know that the watchpoint has
+ // been invalidated when it does its second check.
+ //
+ // - Do not expose the watchpoint set to the DFG directly, and have your own way of validating whether
+ // the assumptions that the DFG thread used are still valid when the DFG code is installed.
+ bool isBeingWatched() const
+ {
+ if (isFat())
+ return fat()->isBeingWatched();
+ return false;
+ }
+
private:
static const uintptr_t IsThinFlag = 1;
static const uintptr_t StateMask = 6;
@@ -247,7 +395,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); }
@@ -284,6 +432,3 @@ private:
};
} // namespace JSC
-
-#endif // Watchpoint_h
-