| 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();
|
| }
|
|
|
|
|