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

Unified Diff: net/android/network_change_notifier_android.cc

Issue 10979048: Fix potential threading issues in NetworkChangeNotifierAndroid. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 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: net/android/network_change_notifier_android.cc
diff --git a/net/android/network_change_notifier_android.cc b/net/android/network_change_notifier_android.cc
index f055af971db4a41bf54e6d386da5c2dc4a2d873e..6fd830c33a47dc3b14857946e8a518f8ab6260d2 100644
--- a/net/android/network_change_notifier_android.cc
+++ b/net/android/network_change_notifier_android.cc
@@ -5,7 +5,10 @@
#include "net/android/network_change_notifier_android.h"
#include "base/android/jni_android.h"
+#include "base/basictypes.h"
#include "base/logging.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
#include "jni/NetworkChangeNotifier_jni.h"
namespace net {
@@ -31,23 +34,124 @@ bool CheckConnectionType(int connection_type) {
} // namespace
-NetworkChangeNotifierAndroid::~NetworkChangeNotifierAndroid() {
- JNIEnv* env = base::android::AttachCurrentThread();
- Java_NetworkChangeNotifier_destroyInstance(env);
-}
+class NetworkChangeNotifierAndroid::DelegateImpl
+ : public NetworkChangeNotifierAndroid::Delegate {
+ public:
+ DelegateImpl(base::TaskRunner* ui_task_runner)
+ : ui_task_runner_(ui_task_runner) {
+ DCHECK(ui_task_runner);
+ SetDeletionFlag(false);
+ SetConnectionType(NetworkChangeNotifier::CONNECTION_UNKNOWN);
+ }
-void NetworkChangeNotifierAndroid::NotifyObserversOfConnectionTypeChange(
- JNIEnv* env,
- jobject obj,
- jint new_connection_type) {
- int connection_type = CheckConnectionType(new_connection_type) ?
- new_connection_type : CONNECTION_UNKNOWN;
- SetConnectionType(connection_type);
- NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
-}
+ void Init() {
+ if (IsRunningOnUIThread()) {
+ CreateJavaInstanceOnUIThread();
+ } else {
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DelegateImpl::CreateJavaInstanceOnUIThread,
+ base::Unretained(this)));
+ }
+ }
+
+ void SetConnectionType(ConnectionType connection_type) {
+ base::AutoLock auto_lock(lock_);
+ connection_type_ = connection_type;
+ }
+
+ void Destroy() {
+ if (IsRunningOnUIThread()) {
+ DestroyOnUIThread();
+ } else {
+ // Note that the deletion flag is only set on a non-UI thread.
+ SetDeletionFlag(true);
+ ui_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&DelegateImpl::DestroyOnUIThread, base::Unretained(this)));
+ }
+ }
+
+ virtual ConnectionType GetConnectionType() const {
+ base::AutoLock auto_lock(lock_);
+ return connection_type_;
+ }
+
+ // Delegate:
+ virtual void NotifyObserversOfConnectionTypeChange(
+ JNIEnv* env, jobject obj, jint new_connection_type) OVERRIDE {
+ DCHECK(IsRunningOnUIThread());
+ ConnectionType connection_type = CheckConnectionType(new_connection_type) ?
+ static_cast<ConnectionType>(new_connection_type) : CONNECTION_UNKNOWN;
+ SetConnectionType(connection_type);
+ base::AutoLock auto_lock(delete_lock_);
+ if (GetDeletionFlag())
+ // NetworkChangeNotifier's destruction has started on a non-UI thread.
+ // Omitting the return statement below could lead to a use after free in
+ // NotifyObservers() below.
+ return;
+ NetworkChangeNotifierAndroid::NotifyObservers();
+ }
+
+ virtual jint GetConnectionType(JNIEnv*, jobject) const OVERRIDE {
+ return GetConnectionType();
+ }
+
+ private:
+ virtual ~DelegateImpl() {}
-jint NetworkChangeNotifierAndroid::GetConnectionType(JNIEnv* env, jobject obj) {
- return GetCurrentConnectionType();
+ void CreateJavaInstanceOnUIThread() {
+ DCHECK(IsRunningOnUIThread());
+ JNIEnv* env = base::android::AttachCurrentThread();
+ java_network_change_notifier_.Reset(
+ Java_NetworkChangeNotifier_createInstance(
+ env,
+ base::android::GetApplicationContext(),
+ reinterpret_cast<jint>(this)));
+ }
+
+ void DestroyOnUIThread() {
+ DCHECK(IsRunningOnUIThread());
+ JNIEnv* env = base::android::AttachCurrentThread();
+ Java_NetworkChangeNotifier_destroyInstance(env);
+ delete this;
+ }
+
+ bool IsRunningOnUIThread() const {
+ return ui_task_runner_->RunsTasksOnCurrentThread();
+ }
+
+ void SetDeletionFlag(bool value) {
+ base::AutoLock auto_lock(delete_lock_);
+ delete_started_on_non_ui_thread_ = value;
+ }
+
+ bool GetDeletionFlag() {
+ base::AutoLock auto_lock(delete_lock_);
+ return delete_started_on_non_ui_thread_;
+ }
+
+ base::TaskRunner* const ui_task_runner_;
+ base::android::ScopedJavaGlobalRef<jobject> java_network_change_notifier_;
+
+ mutable base::Lock lock_; // Protects the state below.
+ // Written from the UI thread, read from any thread.
+ ConnectionType connection_type_;
+
+ base::Lock delete_lock_; // Protects the state below.
+ // Tells whether NetworkChangeNotifierAndroid's deletion has started on a
+ // thread other than the UI thread. This is used to handle the case where we
+ // receive a notification on the UI thread from the Java side after
+ // NetworkChangeNotifierAndroid was deleted on a non-UI thread (e.g. network
+ // thread).
+ // Written from a non-UI thread, read from the UI thread.
+ bool delete_started_on_non_ui_thread_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelegateImpl);
+};
+
+NetworkChangeNotifierAndroid::~NetworkChangeNotifierAndroid() {
+ delegate_->Destroy();
}
// static
@@ -55,19 +159,15 @@ bool NetworkChangeNotifierAndroid::Register(JNIEnv* env) {
return RegisterNativesImpl(env);
}
-NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid() {
- SetConnectionType(CONNECTION_UNKNOWN);
- JNIEnv* env = base::android::AttachCurrentThread();
- java_network_change_notifier_.Reset(
- Java_NetworkChangeNotifier_createInstance(
- env,
- base::android::GetApplicationContext(),
- reinterpret_cast<jint>(this)));
+NetworkChangeNotifierAndroid::NetworkChangeNotifierAndroid(
+ base::TaskRunner* ui_task_runner)
+ : delegate_(new DelegateImpl(ui_task_runner)) {
+ delegate_->Init();
}
-void NetworkChangeNotifierAndroid::SetConnectionType(int connection_type) {
- base::AutoLock auto_lock(lock_);
- connection_type_ = connection_type;
+// static
+void NetworkChangeNotifierAndroid::NotifyObservers() {
+ NetworkChangeNotifier::NotifyObserversOfConnectionTypeChange();
}
void NetworkChangeNotifierAndroid::ForceConnectivityState(bool state) {
@@ -77,8 +177,7 @@ void NetworkChangeNotifierAndroid::ForceConnectivityState(bool state) {
NetworkChangeNotifier::ConnectionType
NetworkChangeNotifierAndroid::GetCurrentConnectionType() const {
- base::AutoLock auto_lock(lock_);
- return static_cast<NetworkChangeNotifier::ConnectionType>(connection_type_);
+ return delegate_->GetConnectionType();
}
} // namespace net
« no previous file with comments | « net/android/network_change_notifier_android.h ('k') | net/android/network_change_notifier_android_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698