Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(910)

Unified Diff: Source/core/page/NetworkStateNotifier.cpp

Issue 289333003: Adds type information to the NetworkStateNotifier. Also allows for registration of observers. (Closed) Base URL: https://chromium.googlesource.com/chromium/blink.git@master
Patch Set: Now it's safe to add/remove observers during notification. Created 6 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: Source/core/page/NetworkStateNotifier.cpp
diff --git a/Source/core/page/NetworkStateNotifier.cpp b/Source/core/page/NetworkStateNotifier.cpp
index 02cc4615c96408f4abcfc60bcea128654b09758d..1de39f5224beba2addf9055c7e6d8afbd0053ce1 100644
--- a/Source/core/page/NetworkStateNotifier.cpp
+++ b/Source/core/page/NetworkStateNotifier.cpp
@@ -26,14 +26,21 @@
#include "config.h"
#include "core/page/NetworkStateNotifier.h"
+#include "core/dom/ExecutionContext.h"
#include "core/page/Page.h"
#include "wtf/Assertions.h"
+#include "wtf/Functional.h"
#include "wtf/MainThread.h"
#include "wtf/StdLibExtras.h"
#include "wtf/Threading.h"
namespace WebCore {
+NetworkStateNotifier::~NetworkStateNotifier()
+{
+ deleteAllValues(m_observers);
adamk 2014/05/21 09:21:31 This won't be needed if you use OwnPtrs as values.
jkarlin 2014/05/21 14:23:17 Done.
+}
+
NetworkStateNotifier& networkStateNotifier()
{
AtomicallyInitializedStatic(NetworkStateNotifier*, networkStateNotifier = new NetworkStateNotifier);
@@ -55,4 +62,106 @@ void NetworkStateNotifier::setOnLine(bool onLine)
Page::networkStateChanged(onLine);
}
+void NetworkStateNotifier::setWebConnectionType(blink::WebNetworkConnection::ConnectionType type)
+{
+ ASSERT(isMainThread());
+
+ MutexLocker locker(m_mutex);
+ if (m_type == type)
+ return;
+ m_type = type;
+
+ for (ObserverListMap::iterator it = m_observers.begin(); it != m_observers.end(); ++it) {
+ ExecutionContext* context = it->key;
+ context->postTask(bind(&NetworkStateNotifier::notifyObserversOnContext, this, context, type));
+ }
+}
+
+void NetworkStateNotifier::addObserver(NetworkStateObserver* observer, ExecutionContext* context)
+{
+ ASSERT(context->isContextThread());
+ ASSERT(observer);
+
+ MutexLocker locker(m_mutex);
+ ObserverListMap::iterator it = m_observers.find(context);
+ if (it == m_observers.end()) {
+ m_observers.set(context, new ObserverList());
+ it = m_observers.find(context);
adamk 2014/05/21 09:21:31 The idiomatic way to handle this song and dance in
jkarlin 2014/05/21 14:23:17 Done.
+ }
+
+ it->value->observers.append(observer);
adamk 2014/05/21 09:21:31 Maybe add an ASSERT that the observer isn't alread
jkarlin 2014/05/21 14:23:17 Done.
+}
+
+void NetworkStateNotifier::removeObserver(NetworkStateObserver* observer, ExecutionContext* context)
+{
+ ASSERT(context->isContextThread());
+ ASSERT(observer);
+
+ ObserverList* observerList = lockAndFindObserverList(context);
+ if (!observerList)
+ return;
+
+ Vector<NetworkStateObserver*>& observers = observerList->observers;
+ size_t index = observers.find(observer);
+ if (index != kNotFound) {
+ observers[index] = 0;
+ observerList->zeroedObservers.append(index);
+ }
+
+ if (!observerList->iterating && !observerList->zeroedObservers.isEmpty())
+ collectZeroedObservers(context);
adamk 2014/05/21 09:21:31 All this zeroed observers logic is pretty tricky.
jkarlin 2014/05/21 14:23:17 Done.
+}
+
+void NetworkStateNotifier::notifyObserversOnContext(ExecutionContext* context, blink::WebNetworkConnection::ConnectionType type)
+{
+ ASSERT(context->isContextThread());
+
+ ObserverList* observerList = lockAndFindObserverList(context);
+
+ // The context could have been removed before the notification task got to run.
adamk 2014/05/21 09:21:31 If this happens then the ASSERT above could crash.
jkarlin 2014/05/21 14:23:17 Done.
+ if (!observerList)
+ return;
+
+ observerList->iterating = true;
+
+ for (size_t i = 0; i < observerList->observers.size(); ++i) {
+ // Observers removed during iteration are zeroed out, skip them.
+ if (observerList->observers[i])
+ observerList->observers[i]->connectionTypeChange(type);
+ }
+
+ observerList->iterating = false;
+
+ if (!observerList->zeroedObservers.isEmpty())
+ collectZeroedObservers(context);
+}
+
+NetworkStateNotifier::ObserverList* NetworkStateNotifier::lockAndFindObserverList(ExecutionContext* context)
+{
+ MutexLocker locker(m_mutex);
+ ObserverListMap::iterator it = m_observers.find(context);
+ return it == m_observers.end() ? 0 : it->value;
}
+
+void NetworkStateNotifier::collectZeroedObservers(ExecutionContext* context)
adamk 2014/05/21 09:21:31 Please add a context thread assertion here.
jkarlin 2014/05/21 14:23:17 Done.
+{
+ // If any observers were removed during the iteration they will have
+ // 0 values, clean them up.
+ ObserverList* list = lockAndFindObserverList(context);
adamk 2014/05/21 09:21:31 Maybe the callers should just pass in the list?
jkarlin 2014/05/21 14:23:17 Done.
+ ASSERT(list);
adamk 2014/05/21 09:21:31 I think you also want to ASSERT(!list->iterating)
jkarlin 2014/05/21 14:23:17 Done.
+
+ for (size_t i = 0; i < list->zeroedObservers.size(); ++i)
+ list->observers.remove(list->zeroedObservers[i]);
+
+ list->zeroedObservers.clear();
+
+ {
+ MutexLocker locker(m_mutex);
+ if (list->observers.isEmpty()) {
+ m_observers.remove(context);
+ delete list;
adamk 2014/05/21 09:21:31 This delete can also go away if you use OwnPtrs as
jkarlin 2014/05/21 14:23:17 Done.
+ }
+ }
+}
+
+} // namespace WebCore

Powered by Google App Engine
This is Rietveld 408576698