Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "net/base/dns_reload_timer.h" | 5 #include "net/base/dns_reload_timer.h" |
| 6 | 6 |
| 7 #if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) | 7 #if WATCH_RESOLV_CONF |
| 8 #include <resolv.h> | |
| 9 | |
| 10 #include "base/file_path.h" | |
| 8 #include "base/lazy_instance.h" | 11 #include "base/lazy_instance.h" |
| 12 #include "base/scoped_ptr.h" | |
| 13 #include "base/lock.h" | |
| 14 #include "base/task.h" | |
| 15 #include "base/thread.h" | |
| 9 #include "base/thread_local_storage.h" | 16 #include "base/thread_local_storage.h" |
| 10 #include "base/time.h" | 17 #include "base/time.h" |
| 18 #include "chrome/browser/file_path_watcher/file_path_watcher.h" | |
|
willchan no longer on Chromium
2011/01/04 23:50:41
net is a lower level module than chrome. chrome d
| |
| 11 | 19 |
| 12 namespace { | 20 namespace net { |
| 13 | 21 |
| 14 // On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting | 22 // On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting |
| 15 // in DNS queries failing either because nameservers are unknown on startup | 23 // in DNS queries failing either because nameservers are unknown on startup |
| 16 // or because nameserver info has changed as a result of e.g. connecting to | 24 // or because nameserver info has changed as a result of e.g. connecting to |
| 17 // a new network. Some distributions patch glibc to stat /etc/resolv.conf | 25 // a new network. Some distributions patch glibc to stat /etc/resolv.conf |
| 18 // to try to automatically detect such changes but these patches are not | 26 // to try to automatically detect such changes but these patches are not |
| 19 // universal and even patched systems such as Jaunty appear to need calls | 27 // universal and even patched systems such as Jaunty appear to need calls |
| 20 // to res_ninit to reload the nameserver information in different threads. | 28 // to res_ninit to reload the nameserver information in different threads. |
| 21 // | 29 // |
| 22 // We adopt the Mozilla solution here which is to call res_ninit when | 30 // We adopt the Mozilla solution here which is to call res_ninit when |
| 23 // lookups fail and to rate limit the reloading to once per second per | 31 // lookups fail and to rate limit the reloading to once per second per |
| 24 // thread. | 32 // thread. |
| 25 // | 33 // |
| 26 // OpenBSD does not have thread-safe res_ninit/res_nclose so we can't do | 34 // OpenBSD does not have thread-safe res_ninit/res_nclose so we can't do |
| 27 // the same trick there. | 35 // the same trick there. |
| 28 | 36 |
| 29 // Keep a timer per calling thread to rate limit the calling of res_ninit. | 37 // Keep a timer per calling thread to rate limit the calling of res_ninit. |
| 30 class DnsReloadTimer { | 38 class DnsReloadTimer { |
| 31 public: | 39 public: |
| 40 | |
| 41 bool ResolvConfUpdated() { | |
| 42 struct DnsReloadTimer::LocalState* local_state = GetLocalState(); | |
| 43 AutoLock l(lock_); | |
| 44 if (local_state->resolv_conf_generation < resolv_conf_generation_ && | |
| 45 Expired()) { | |
| 46 local_state->resolv_conf_generation = resolv_conf_generation_; | |
| 47 return true; | |
| 48 } | |
| 49 return false; | |
| 50 } | |
| 51 | |
| 32 // Check if the timer for the calling thread has expired. When no | 52 // Check if the timer for the calling thread has expired. When no |
| 33 // timer exists for the calling thread, create one. | 53 // timer exists for the calling thread, create one. |
| 34 bool Expired() { | 54 bool Expired() { |
| 35 const base::TimeDelta kRetryTime = base::TimeDelta::FromSeconds(1); | 55 const base::TimeDelta kRetryTime = base::TimeDelta::FromSeconds(1); |
| 56 struct DnsReloadTimer::LocalState* local_state = GetLocalState(); | |
| 36 base::TimeTicks now = base::TimeTicks::Now(); | 57 base::TimeTicks now = base::TimeTicks::Now(); |
| 37 base::TimeTicks* timer_ptr = | |
| 38 static_cast<base::TimeTicks*>(tls_index_.Get()); | |
| 39 | 58 |
| 40 if (!timer_ptr) { | 59 if (now - local_state->timer > kRetryTime) { |
| 41 timer_ptr = new base::TimeTicks(); | 60 local_state->timer = now; |
| 42 *timer_ptr = base::TimeTicks::Now(); | |
| 43 tls_index_.Set(timer_ptr); | |
| 44 // Return true to reload dns info on the first call for each thread. | |
| 45 return true; | |
| 46 } else if (now - *timer_ptr > kRetryTime) { | |
| 47 *timer_ptr = now; | |
| 48 return true; | 61 return true; |
| 49 } else { | 62 } else { |
| 50 return false; | 63 return false; |
| 51 } | 64 } |
| 52 } | 65 } |
| 53 | 66 |
| 54 // Free the allocated timer. | 67 // Free the allocated LocalState. |
| 55 static void SlotReturnFunction(void* data) { | 68 static void SlotReturnFunction(void* data) { |
| 56 base::TimeTicks* tls_data = static_cast<base::TimeTicks*>(data); | 69 DnsReloadTimer::LocalState* tls_data = |
| 70 static_cast<DnsReloadTimer::LocalState*>(data); | |
| 57 delete tls_data; | 71 delete tls_data; |
| 58 } | 72 } |
| 59 | 73 |
| 74 // Installs FileWatcher for _PATH_RESCONF (i.e. /etc/resolv.conf). | |
| 75 // Must be run on file thread. | |
| 76 void InitResolvConfWatcher(); | |
| 77 | |
| 60 private: | 78 private: |
| 61 friend struct base::DefaultLazyInstanceTraits<DnsReloadTimer>; | 79 friend struct base::DefaultLazyInstanceTraits<DnsReloadTimer>; |
| 62 | 80 |
| 63 DnsReloadTimer() { | 81 // One LocalState struct is kept in TLS. |
| 82 struct LocalState { | |
| 83 base::TimeTicks timer; // Reload timer, used to rate-limit reloads. | |
| 84 int resolv_conf_generation; // resolv.conf generation known to this thread. | |
| 85 }; | |
| 86 | |
| 87 class ResolvConfWatcherDelegate : public FilePathWatcher::Delegate { | |
| 88 public: | |
| 89 explicit ResolvConfWatcherDelegate(int* conf_generation, Lock* lock) | |
| 90 : conf_generation_(conf_generation), lock_(lock) { | |
| 91 } | |
| 92 | |
| 93 virtual void OnFilePathChanged(const FilePath& path) { | |
| 94 AutoLock l(*lock_); | |
| 95 (*conf_generation_)++; | |
| 96 LOG(INFO) << _PATH_RESCONF << " updated, new generation: " | |
| 97 << *conf_generation_; | |
| 98 } | |
| 99 private: | |
| 100 int* conf_generation_; | |
| 101 Lock* lock_; | |
| 102 | |
| 103 DISALLOW_COPY_AND_ASSIGN(ResolvConfWatcherDelegate); | |
| 104 }; | |
| 105 | |
| 106 // Generation starts at 1 so that all threads reload resolv.conf | |
| 107 // on first invocation. | |
| 108 DnsReloadTimer() : resolv_conf_generation_(1) { | |
| 64 // During testing the DnsReloadTimer Singleton may be created and destroyed | 109 // During testing the DnsReloadTimer Singleton may be created and destroyed |
| 65 // multiple times. Initialize the ThreadLocalStorage slot only once. | 110 // multiple times. Initialize the ThreadLocalStorage slot only once. |
| 66 if (!tls_index_.initialized()) | 111 if (!tls_index_.initialized()) |
| 67 tls_index_.Initialize(SlotReturnFunction); | 112 tls_index_.Initialize(SlotReturnFunction); |
| 68 } | 113 } |
| 69 | 114 |
| 70 ~DnsReloadTimer() { | 115 ~DnsReloadTimer() { |
| 71 } | 116 } |
| 72 | 117 |
| 118 DnsReloadTimer::LocalState* GetLocalState(); | |
| 119 | |
| 120 Lock lock_; // Protects resolv_conf_generation_ | |
| 121 int resolv_conf_generation_; | |
| 122 | |
| 123 scoped_ptr<FilePathWatcher> resolv_conf_watcher_; | |
| 124 scoped_ptr<ResolvConfWatcherDelegate> resolv_conf_watcher_delegate_; | |
| 125 | |
| 73 // We use thread local storage to identify which base::TimeTicks to | 126 // We use thread local storage to identify which base::TimeTicks to |
| 74 // interact with. | 127 // interact with. |
| 75 static ThreadLocalStorage::Slot tls_index_ ; | 128 static ThreadLocalStorage::Slot tls_index_; |
| 76 | 129 |
| 77 DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer); | 130 DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer); |
| 78 }; | 131 }; |
| 79 | 132 |
| 133 void DnsReloadTimer::InitResolvConfWatcher() { | |
| 134 resolv_conf_watcher_.reset(new FilePathWatcher()); | |
| 135 resolv_conf_watcher_delegate_.reset( | |
| 136 new ResolvConfWatcherDelegate(&resolv_conf_generation_, &lock_)); | |
| 137 if (!resolv_conf_watcher_->Watch( | |
| 138 FilePath(FilePath::StringType(_PATH_RESCONF)), | |
| 139 resolv_conf_watcher_delegate_.get())) { | |
| 140 LOG(WARNING) << "Failed to install watcher for " << _PATH_RESCONF; | |
| 141 // We failed to install watcher, for whatever reason. Free the watcher. | |
| 142 resolv_conf_watcher_.reset(); | |
| 143 resolv_conf_watcher_delegate_.reset(); | |
| 144 } else { | |
| 145 LOG(INFO) << "Installed watcher for " << _PATH_RESCONF; | |
| 146 } | |
| 147 } | |
| 148 | |
| 149 DnsReloadTimer::LocalState* DnsReloadTimer::GetLocalState() { | |
| 150 DnsReloadTimer::LocalState* state = | |
| 151 static_cast<DnsReloadTimer::LocalState*>(tls_index_.Get()); | |
| 152 if (!state) { | |
| 153 state = new DnsReloadTimer::LocalState(); | |
| 154 state->timer = base::TimeTicks::Now(); | |
| 155 state->resolv_conf_generation = 0; | |
| 156 tls_index_.Set(state); | |
| 157 } | |
| 158 return state; | |
| 159 } | |
| 160 | |
| 80 // A TLS slot to the TimeTicks for the current thread. | 161 // A TLS slot to the TimeTicks for the current thread. |
| 81 // static | 162 // static |
| 82 ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED); | 163 ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED); |
| 83 | 164 |
| 84 base::LazyInstance<DnsReloadTimer, | 165 typedef base::LazyInstance<DnsReloadTimer, |
| 85 base::LeakyLazyInstanceTraits<DnsReloadTimer> > | 166 base::LeakyLazyInstanceTraits<DnsReloadTimer> > |
| 86 g_dns_reload_timer(base::LINKER_INITIALIZED); | 167 DnsReloadTimerInstance; |
| 168 DnsReloadTimerInstance g_dns_reload_timer(base::LINKER_INITIALIZED); | |
| 87 | 169 |
| 88 } // namespace | 170 // This task is run on file thread |
| 171 // and calls DnsReloadTimer::InitResolvConfWatcher(). | |
| 172 class InitResolvConfWatcherTask : public Task { | |
| 173 public: | |
| 174 InitResolvConfWatcherTask(DnsReloadTimerInstance* dns_reload_timer_instance) | |
| 175 : dns_reload_timer_instance_(dns_reload_timer_instance) { | |
| 176 } | |
| 89 | 177 |
| 90 namespace net { | 178 virtual void Run() { |
| 179 dns_reload_timer_instance_->Pointer()->InitResolvConfWatcher(); | |
| 180 } | |
| 181 | |
| 182 private: | |
| 183 DnsReloadTimerInstance* dns_reload_timer_instance_; | |
| 184 | |
| 185 DISALLOW_COPY_AND_ASSIGN(InitResolvConfWatcherTask); | |
| 186 }; | |
| 91 | 187 |
| 92 bool DnsReloadTimerHasExpired() { | 188 bool DnsReloadTimerHasExpired() { |
| 93 DnsReloadTimer* dns_timer = g_dns_reload_timer.Pointer(); | 189 DnsReloadTimer* dns_timer = g_dns_reload_timer.Pointer(); |
| 94 return dns_timer->Expired(); | 190 return dns_timer->Expired(); |
| 95 } | 191 } |
| 96 | 192 |
| 193 bool ResolvConfUpdated() { | |
| 194 DnsReloadTimer* dns_timer = g_dns_reload_timer.Pointer(); | |
| 195 return dns_timer->ResolvConfUpdated(); | |
| 196 } | |
| 197 | |
| 198 void InitResolvConfWatcher(base::Thread* file_thread) { | |
| 199 // FileWatcher must be used on the file thread. | |
| 200 // Additionally, we delay initialization - resolv.conf is read on first use | |
| 201 // anyway, this watcher will most likely be useful later. | |
| 202 file_thread->message_loop()->PostDelayedTask( | |
| 203 FROM_HERE, new InitResolvConfWatcherTask(&g_dns_reload_timer), 5000); | |
| 204 } | |
| 205 | |
| 97 } // namespace net | 206 } // namespace net |
| 98 | 207 |
| 99 #endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) | 208 #endif // WATCH_RESOLV_CONF |
| OLD | NEW |