X-Git-Url: https://git.saurik.com/apple/javascriptcore.git/blobdiff_plain/93a3786624b2768d89bfa27e46598dc64e2fb70a..2656c66b5b30d5597e842a751c7f19ad6c2fe31a:/bytecode/Watchpoint.h diff --git a/bytecode/Watchpoint.h b/bytecode/Watchpoint.h index e6fba93..7659c0d 100644 --- a/bytecode/Watchpoint.h +++ b/bytecode/Watchpoint.h @@ -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 +#include #include +#include 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 { +class WatchpointSet : public ThreadSafeRefCounted { + 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(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 > m_set; - bool m_isWatched; - bool m_isInvalidated; + + int8_t m_state; + int8_t m_setIsNotEmpty; + + SentinelLinkedList> 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((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(data); + } WatchpointSet* fat() { ASSERT(isFat()); - return bitwise_cast(m_data); + return fat(m_data); } const WatchpointSet* fat() const { ASSERT(isFat()); - return bitwise_cast(m_data); + return fat(m_data); } WatchpointSet* inflate()