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

Unified Diff: net/proxy/proxy_config_service_linux.cc

Issue 10912132: Move ProxyConfigService construction onto the IO thread. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Adjust comments Created 8 years, 2 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/proxy/proxy_config_service_linux.cc
diff --git a/net/proxy/proxy_config_service_linux.cc b/net/proxy/proxy_config_service_linux.cc
index 1c8e5a367d84f5de9c47ea42f7d72cde7c0f5a6a..aee217d86619462e15020739397726ab13ffeaa9 100644
--- a/net/proxy/proxy_config_service_linux.cc
+++ b/net/proxy/proxy_config_service_linux.cc
@@ -207,10 +207,7 @@ class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
virtual ~SettingGetterImplGConf() {
// client_ should have been released before now, from
- // Delegate::OnDestroy(), while running on the UI thread. However
- // on exiting the process, it may happen that Delegate::OnDestroy()
- // task is left pending on the glib loop after the loop was quit,
- // and pending tasks may then be deleted without being run.
+ // Delegate::OnDestroy(), while running on the UI thread.
if (client_) {
// gconf client was not cleaned up.
if (task_runner_->BelongsToCurrentThread()) {
@@ -285,6 +282,7 @@ class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
client_ = NULL;
task_runner_ = NULL;
}
+ debounce_timer_.reset();
}
virtual bool SetUpNotifications(
@@ -293,6 +291,7 @@ class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
DCHECK(task_runner_->BelongsToCurrentThread());
GError* error = NULL;
notify_delegate_ = delegate;
+ debounce_timer_.reset(new base::OneShotTimer<SettingGetterImplGConf>());
// We have to keep track of the IDs returned by gconf_client_notify_add() so
// that we can remove them in ShutDown(). (Otherwise, notifications will be
// delivered to this object after it is deleted, which is bad, m'kay?)
@@ -474,8 +473,8 @@ class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
void OnChangeNotification() {
// We don't use Reset() because the timer may not yet be running.
// (In that case Stop() is a no-op.)
- debounce_timer_.Stop();
- debounce_timer_.Start(FROM_HERE,
+ debounce_timer_->Stop();
+ debounce_timer_->Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
this, &SettingGetterImplGConf::OnDebouncedNotification);
}
@@ -498,7 +497,7 @@ class SettingGetterImplGConf : public ProxyConfigServiceLinux::SettingGetter {
guint system_http_proxy_id_;
ProxyConfigServiceLinux::Delegate* notify_delegate_;
- base::OneShotTimer<SettingGetterImplGConf> debounce_timer_;
+ scoped_ptr<base::OneShotTimer<SettingGetterImplGConf> > debounce_timer_;
// Task runner for the thread that we make gconf calls on. It should
// be the UI thread and all our methods should be called on this
@@ -535,11 +534,7 @@ class SettingGetterImplGSettings
virtual ~SettingGetterImplGSettings() {
// client_ should have been released before now, from
- // Delegate::OnDestroy(), while running on the UI thread. However
- // on exiting the process, it may happen that
- // Delegate::OnDestroy() task is left pending on the glib loop
- // after the loop was quit, and pending tasks may then be deleted
- // without being run.
+ // Delegate::OnDestroy(), while running on the UI thread.
if (client_) {
// gconf client was not cleaned up.
if (task_runner_->BelongsToCurrentThread()) {
@@ -610,6 +605,7 @@ class SettingGetterImplGSettings
client_ = NULL;
task_runner_ = NULL;
}
+ debounce_timer_.reset();
}
virtual bool SetUpNotifications(
@@ -617,6 +613,8 @@ class SettingGetterImplGSettings
DCHECK(client_);
DCHECK(task_runner_->BelongsToCurrentThread());
notify_delegate_ = delegate;
+ debounce_timer_.reset(
+ new base::OneShotTimer<SettingGetterImplGSettings>());
// We could watch for the change-event signal instead of changed, but
// since we have to watch more than one object, we'd still have to
// debounce change notifications. This is conceptually simpler.
@@ -789,8 +787,8 @@ class SettingGetterImplGSettings
void OnChangeNotification() {
// We don't use Reset() because the timer may not yet be running.
// (In that case Stop() is a no-op.)
- debounce_timer_.Stop();
- debounce_timer_.Start(FROM_HERE,
+ debounce_timer_->Stop();
+ debounce_timer_->Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(kDebounceTimeoutMilliseconds),
this, &SettingGetterImplGSettings::OnDebouncedNotification);
}
@@ -811,7 +809,7 @@ class SettingGetterImplGSettings
GSettings* ftp_client_;
GSettings* socks_client_;
ProxyConfigServiceLinux::Delegate* notify_delegate_;
- base::OneShotTimer<SettingGetterImplGSettings> debounce_timer_;
+ scoped_ptr<base::OneShotTimer<SettingGetterImplGSettings> > debounce_timer_;
// Task runner for the thread that we make gsettings calls on. It should
// be the UI thread and all our methods should be called on this
@@ -969,12 +967,7 @@ class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
virtual ~SettingGetterImplKDE() {
// inotify_fd_ should have been closed before now, from
- // Delegate::OnDestroy(), while running on the file thread. However
- // on exiting the process, it may happen that Delegate::OnDestroy()
- // task is left pending on the file loop after the loop was quit,
- // and pending tasks may then be deleted without being run.
- // Here in the KDE version, we can safely close the file descriptor
- // anyway. (Not that it really matters; the process is exiting.)
+ // Delegate::OnDestroy(), while running on the file thread.
if (inotify_fd_ >= 0)
ShutDown();
DCHECK(inotify_fd_ < 0);
@@ -1011,12 +1004,14 @@ class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
close(inotify_fd_);
inotify_fd_ = -1;
}
+ debounce_timer_.reset();
}
virtual bool SetUpNotifications(
ProxyConfigServiceLinux::Delegate* delegate) OVERRIDE {
DCHECK(inotify_fd_ >= 0);
DCHECK(MessageLoop::current() == file_loop_);
+ debounce_timer_.reset(new base::OneShotTimer<SettingGetterImplKDE>());
// We can't just watch the kioslaverc file directly, since KDE will write
// a new copy of it and then rename it whenever settings are changed and
// inotify watches inodes (so we'll be watching the old deleted file after
@@ -1362,8 +1357,8 @@ class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
if (kioslaverc_touched) {
// We don't use Reset() because the timer may not yet be running.
// (In that case Stop() is a no-op.)
- debounce_timer_.Stop();
- debounce_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
+ debounce_timer_->Stop();
+ debounce_timer_->Start(FROM_HERE, base::TimeDelta::FromMilliseconds(
kDebounceTimeoutMilliseconds), this,
&SettingGetterImplKDE::OnDebouncedNotification);
}
@@ -1376,7 +1371,7 @@ class SettingGetterImplKDE : public ProxyConfigServiceLinux::SettingGetter,
int inotify_fd_;
base::MessagePumpLibevent::FileDescriptorWatcher inotify_watcher_;
ProxyConfigServiceLinux::Delegate* notify_delegate_;
- base::OneShotTimer<SettingGetterImplKDE> debounce_timer_;
+ scoped_ptr<base::OneShotTimer<SettingGetterImplKDE> > debounce_timer_;
FilePath kde_config_dir_;
bool indirect_manual_;
bool auto_no_pac_;
@@ -1604,19 +1599,31 @@ ProxyConfigServiceLinux::Delegate::Delegate(base::Environment* env_var_getter)
ProxyConfigServiceLinux::Delegate::Delegate(
base::Environment* env_var_getter, SettingGetter* setting_getter)
- : env_var_getter_(env_var_getter), setting_getter_(setting_getter) {
+ : env_var_getter_(env_var_getter), setting_getter_(setting_getter),
+ finished_initialization_(false) {
}
void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
base::SingleThreadTaskRunner* glib_thread_task_runner,
base::SingleThreadTaskRunner* io_thread_task_runner,
MessageLoopForIO* file_loop) {
- // We should be running on the default glib main loop thread right
- // now. gconf can only be accessed from this thread.
- DCHECK(glib_thread_task_runner->BelongsToCurrentThread());
+ DCHECK(io_thread_task_runner->BelongsToCurrentThread());
+
glib_thread_task_runner_ = glib_thread_task_runner;
io_thread_task_runner_ = io_thread_task_runner;
+ glib_thread_task_runner->PostTask(FROM_HERE, base::Bind(
+ &Delegate::SetUpAndFetchInitialConfigOnUIThread,
+ this,
+ file_loop));
+}
+
+void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfigOnUIThread(
+ MessageLoopForIO* file_loop) {
+ // We should be running on the default glib main loop thread right
+ // now. gconf can only be accessed from this thread.
+ DCHECK(glib_thread_task_runner_->BelongsToCurrentThread());
+
// If we are passed a NULL |io_thread_task_runner| or |file_loop|,
// then we don't set up proxy setting change notifications. This
// should not be the usual case but is intended to simplify test
@@ -1638,12 +1645,12 @@ void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
bool got_config = false;
if (setting_getter_.get() &&
- setting_getter_->Init(glib_thread_task_runner, file_loop) &&
- GetConfigFromSettings(&cached_config_)) {
- cached_config_.set_id(1); // Mark it as valid.
- cached_config_.set_source(setting_getter_->GetConfigSource());
+ setting_getter_->Init(glib_thread_task_runner_, file_loop) &&
+ GetConfigFromSettings(&reference_config_)) {
+ reference_config_.set_id(1); // Mark it as valid.
+ reference_config_.set_source(setting_getter_->GetConfigSource());
VLOG(1) << "Obtained proxy settings from "
- << ProxyConfigSourceToString(cached_config_.source());
+ << ProxyConfigSourceToString(reference_config_.source());
// If gconf proxy mode is "none", meaning direct, then we take
// that to be a valid config and will not check environment
@@ -1651,11 +1658,6 @@ void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
// whereever we can find one.
got_config = true;
- // Keep a copy of the config for use from this thread for
- // comparison with updated settings when we get notifications.
- reference_config_ = cached_config_;
- reference_config_.set_id(1); // Mark it as valid.
-
// We only set up notifications if we have IO and file loops available.
// We do this after getting the initial configuration so that we don't have
// to worry about cancelling it if the initial fetch above fails. Note that
@@ -1663,7 +1665,7 @@ void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
// that we won't lose any updates that may have happened after the initial
// fetch and before setting up notifications. We'll detect the common case
// of no changes in OnCheckProxyConfigSettings() (or sooner) and ignore it.
- if (io_thread_task_runner && file_loop) {
+ if (io_thread_task_runner_ && file_loop) {
scoped_refptr<base::SingleThreadTaskRunner> required_loop =
setting_getter_->GetNotificationTaskRunner();
if (!required_loop || required_loop->BelongsToCurrentThread()) {
@@ -1682,12 +1684,18 @@ void ProxyConfigServiceLinux::Delegate::SetUpAndFetchInitialConfig(
//
// Consulting environment variables doesn't need to be done from the
// default glib main loop, but it's a tiny enough amount of work.
- if (GetConfigFromEnv(&cached_config_)) {
- cached_config_.set_source(PROXY_CONFIG_SOURCE_ENV);
- cached_config_.set_id(1); // Mark it as valid.
+ if (GetConfigFromEnv(&reference_config_)) {
+ reference_config_.set_source(PROXY_CONFIG_SOURCE_ENV);
+ reference_config_.set_id(1); // Mark it as valid.
VLOG(1) << "Obtained proxy settings from environment variables";
}
}
+
+ // Post a task to the IO thread with the new configuration, so it can
+ // update |cached_config_|.
+ io_thread_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &ProxyConfigServiceLinux::Delegate::SetNewProxyConfig,
+ this, reference_config_));
}
// Depending on the SettingGetter in use, this method will be called
@@ -1715,6 +1723,9 @@ ProxyConfigService::ConfigAvailability
DCHECK(!io_thread_task_runner_ ||
io_thread_task_runner_->BelongsToCurrentThread());
+ if (!finished_initialization_)
+ return CONFIG_PENDING;
+
// Simply return the last proxy configuration that glib_default_loop
// notified us of.
if (cached_config_.is_valid()) {
@@ -1724,11 +1735,7 @@ ProxyConfigService::ConfigAvailability
config->set_source(PROXY_CONFIG_SOURCE_SYSTEM_FAILED);
}
- // We return CONFIG_VALID to indicate that *config was filled in. It is always
- // going to be available since we initialized eagerly on the UI thread.
- // TODO(eroman): do lazy initialization instead, so we no longer need
- // to construct ProxyConfigServiceLinux on the UI thread.
- // In which case, we may return false here.
+ // We return CONFIG_VALID to indicate that *config was filled in.
return CONFIG_VALID;
}
@@ -1763,6 +1770,7 @@ void ProxyConfigServiceLinux::Delegate::SetNewProxyConfig(
DCHECK(io_thread_task_runner_->BelongsToCurrentThread());
VLOG(1) << "Proxy configuration changed";
cached_config_ = new_config;
+ finished_initialization_ = true;
FOR_EACH_OBSERVER(
Observer, observers_,
OnProxyConfigChanged(new_config, ProxyConfigService::CONFIG_VALID));
@@ -1776,6 +1784,8 @@ void ProxyConfigServiceLinux::Delegate::PostDestroyTask() {
if (!shutdown_loop || shutdown_loop->BelongsToCurrentThread()) {
// Already on the right thread, call directly.
// This is the case for the unittests.
+ // The browser's UI thread will not execute any more tasks at this point
+ // so its necessary to call directly.
OnDestroy();
} else {
// Post to shutdown thread. Note that on browser shutdown, we may quit
@@ -1791,11 +1801,26 @@ void ProxyConfigServiceLinux::Delegate::OnDestroy() {
setting_getter_->ShutDown();
}
-ProxyConfigServiceLinux::ProxyConfigServiceLinux()
+ProxyConfigServiceLinux::ProxyConfigServiceLinux(
+ base::SingleThreadTaskRunner* glib_thread_task_runner,
+ base::SingleThreadTaskRunner* io_thread_task_runner,
+ MessageLoopForIO* file_loop)
: delegate_(new Delegate(base::Environment::Create())) {
+ SetupAndFetchInitialConfig(glib_thread_task_runner,
+ io_thread_task_runner,
+ file_loop);
}
ProxyConfigServiceLinux::~ProxyConfigServiceLinux() {
+}
+
+void ProxyConfigServiceLinux::StartTearDown() {
+ // This must be done prior to destruction. Destruction occurs as the threads
+ // are dying which is too late to post tasks. Previously this code relied on
+ // ~Delegate() being called when the unexecuted Delegate::OnDestroy() task
+ // was purged from the UI thread's MessageLoop as we shutdown. This didn't
+ // work very well as things like BelongsToCurrentThread() don't operate
+ // properly at this late a stage.
delegate_->PostDestroyTask();
}

Powered by Google App Engine
This is Rietveld 408576698