diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/JavaScriptCore/bytecode/Watchpoint.h | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/JavaScriptCore/bytecode/Watchpoint.h')
-rw-r--r-- | Source/JavaScriptCore/bytecode/Watchpoint.h | 215 |
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 - |