Index: third_party/WebKit/Source/platform/LifecycleNotifier.h |
diff --git a/third_party/WebKit/Source/platform/LifecycleNotifier.h b/third_party/WebKit/Source/platform/LifecycleNotifier.h |
index 27cbdc52b1a7c276c92614f318008bda2dd8de17..568d89160b83852ea802cc5bc556af3a86400bd5 100644 |
--- a/third_party/WebKit/Source/platform/LifecycleNotifier.h |
+++ b/third_party/WebKit/Source/platform/LifecycleNotifier.h |
@@ -53,29 +53,64 @@ public: |
visitor->trace(m_observers); |
} |
- bool isIteratingOverObservers() const { return m_iterating != IteratingNone; } |
+ bool isIteratingOverObservers() const { return m_iterationState != NotIterating; } |
protected: |
LifecycleNotifier() |
- : m_iterating(IteratingNone) |
+ : m_iterationState(NotIterating) |
{ |
} |
- enum IterationType { |
- IteratingNone, |
- IteratingOverAll, |
+#if DCHECK_IS_ON() |
+ T* context() { return static_cast<T*>(this); } |
+#endif |
+ |
+ using ObserverSet = HeapHashSet<WeakMember<Observer>>; |
+ |
+ enum IterationState { |
+ AllowingNone = 0, |
+ AllowingAddition, |
+ AllowingRemoval, |
+ NotIterating = AllowingAddition | AllowingRemoval, |
}; |
- IterationType m_iterating; |
+ class IterationScope { |
+ STACK_ALLOCATED(); |
+ public: |
+ explicit IterationScope(LifecycleNotifier* notifier, IterationState allowable = AllowingNone) |
haraken
2016/06/27 01:06:34
Is the second parameter used somewhere?
sof
2016/06/27 05:14:14
Line 85
|
+ : m_notifier(notifier) |
+ { |
+ // Don't support nested iterations. |
+ RELEASE_ASSERT(m_notifier->m_iterationState == NotIterating); |
+ m_notifier->m_iterationState = allowable; |
+ |
+ // Swap out the notifier's weak collection to this stack allocated |
+ // object, ensuring that collection references are strongified across |
+ // conservative GCs. |
+ // |
+ // If not done, weak processing could mutate the underlying table |
+ // while it is being iterated over. |
+ m_observers.swap(m_notifier->m_observers); |
haraken
2016/06/27 01:06:34
I don't fully understand why we need to explicitly
sof
2016/06/27 05:14:14
I was thinking about that overnight, this being to
sof
2016/06/27 06:36:54
Now done.
|
+ } |
+ ~IterationScope() |
+ { |
+ m_notifier->m_iterationState = NotIterating; |
+ m_observers.swap(m_notifier->m_observers); |
+ } |
+ |
+ const typename LifecycleNotifier::ObserverSet& observers() const { return m_observers; } |
+ |
+ private: |
+ Member<LifecycleNotifier> m_notifier; |
+ typename LifecycleNotifier::ObserverSet m_observers; |
+ }; |
-protected: |
- using ObserverSet = HeapHashSet<WeakMember<Observer>>; |
+ friend class IterationScope; |
+ // Iteration state is recorded while iterating the observer set, |
+ // optionally barring add or remove mutations while it runs. |
+ IterationState m_iterationState; |
ObserverSet m_observers; |
- |
-#if DCHECK_IS_ON() |
- T* context() { return static_cast<T*>(this); } |
-#endif |
}; |
template<typename T, typename Observer> |
@@ -88,7 +123,8 @@ inline LifecycleNotifier<T, Observer>::~LifecycleNotifier() |
template<typename T, typename Observer> |
inline void LifecycleNotifier<T, Observer>::notifyContextDestroyed() |
{ |
- TemporaryChange<IterationType> scope(m_iterating, IteratingOverAll); |
+ // Observer unregistration is allowed, but effectively a no-op. |
+ TemporaryChange<IterationState> scope(m_iterationState, AllowingRemoval); |
ObserverSet observers; |
m_observers.swap(observers); |
for (Observer* observer : observers) { |
@@ -100,13 +136,14 @@ inline void LifecycleNotifier<T, Observer>::notifyContextDestroyed() |
template<typename T, typename Observer> |
inline void LifecycleNotifier<T, Observer>::addObserver(Observer* observer) |
{ |
- RELEASE_ASSERT(m_iterating != IteratingOverAll); |
+ RELEASE_ASSERT(m_iterationState & AllowingAddition); |
m_observers.add(observer); |
} |
template<typename T, typename Observer> |
inline void LifecycleNotifier<T, Observer>::removeObserver(Observer* observer) |
{ |
+ RELEASE_ASSERT(m_iterationState & AllowingRemoval); |
m_observers.remove(observer); |
} |