/*
- * 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
#ifndef Watchpoint_h
#define Watchpoint_h
-#include <wtf/RefCounted.h>
+#include <wtf/Atomics.h>
#include <wtf/SentinelLinkedList.h>
+#include <wtf/ThreadSafeRefCounted.h>
namespace JSC {
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
// 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
class InlineWatchpointSet {
WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
public:
- InlineWatchpointSet(InitialWatchpointSetMode mode)
- : m_data((mode == InitializedWatching ? IsWatchedFlag : 0) | IsThinFlag)
+ InlineWatchpointSet(WatchpointState state)
+ : m_data(encodeState(state))
{
}
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();
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()