Chromium Code Reviews| Index: net/base/dns_reload_timer.cc |
| =================================================================== |
| --- net/base/dns_reload_timer.cc (revision 70147) |
| +++ net/base/dns_reload_timer.cc (working copy) |
| @@ -4,12 +4,20 @@ |
| #include "net/base/dns_reload_timer.h" |
| -#if defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) |
| +#if WATCH_RESOLV_CONF |
| +#include <resolv.h> |
| + |
| +#include "base/file_path.h" |
| #include "base/lazy_instance.h" |
| +#include "base/scoped_ptr.h" |
| +#include "base/lock.h" |
| +#include "base/task.h" |
| +#include "base/thread.h" |
| #include "base/thread_local_storage.h" |
| #include "base/time.h" |
| +#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
|
| -namespace { |
| +namespace net { |
| // On Linux/BSD, changes to /etc/resolv.conf can go unnoticed thus resulting |
| // in DNS queries failing either because nameservers are unknown on startup |
| @@ -29,38 +37,75 @@ |
| // Keep a timer per calling thread to rate limit the calling of res_ninit. |
| class DnsReloadTimer { |
| public: |
| + |
| + bool ResolvConfUpdated() { |
| + struct DnsReloadTimer::LocalState* local_state = GetLocalState(); |
| + AutoLock l(lock_); |
| + if (local_state->resolv_conf_generation < resolv_conf_generation_ && |
| + Expired()) { |
| + local_state->resolv_conf_generation = resolv_conf_generation_; |
| + return true; |
| + } |
| + return false; |
| + } |
| + |
| // Check if the timer for the calling thread has expired. When no |
| // timer exists for the calling thread, create one. |
| bool Expired() { |
| const base::TimeDelta kRetryTime = base::TimeDelta::FromSeconds(1); |
| + struct DnsReloadTimer::LocalState* local_state = GetLocalState(); |
| base::TimeTicks now = base::TimeTicks::Now(); |
| - base::TimeTicks* timer_ptr = |
| - static_cast<base::TimeTicks*>(tls_index_.Get()); |
| - if (!timer_ptr) { |
| - timer_ptr = new base::TimeTicks(); |
| - *timer_ptr = base::TimeTicks::Now(); |
| - tls_index_.Set(timer_ptr); |
| - // Return true to reload dns info on the first call for each thread. |
| + if (now - local_state->timer > kRetryTime) { |
| + local_state->timer = now; |
| return true; |
| - } else if (now - *timer_ptr > kRetryTime) { |
| - *timer_ptr = now; |
| - return true; |
| } else { |
| return false; |
| } |
| } |
| - // Free the allocated timer. |
| + // Free the allocated LocalState. |
| static void SlotReturnFunction(void* data) { |
| - base::TimeTicks* tls_data = static_cast<base::TimeTicks*>(data); |
| + DnsReloadTimer::LocalState* tls_data = |
| + static_cast<DnsReloadTimer::LocalState*>(data); |
| delete tls_data; |
| } |
| + // Installs FileWatcher for _PATH_RESCONF (i.e. /etc/resolv.conf). |
| + // Must be run on file thread. |
| + void InitResolvConfWatcher(); |
| + |
| private: |
| friend struct base::DefaultLazyInstanceTraits<DnsReloadTimer>; |
| - DnsReloadTimer() { |
| + // One LocalState struct is kept in TLS. |
| + struct LocalState { |
| + base::TimeTicks timer; // Reload timer, used to rate-limit reloads. |
| + int resolv_conf_generation; // resolv.conf generation known to this thread. |
| + }; |
| + |
| + class ResolvConfWatcherDelegate : public FilePathWatcher::Delegate { |
| + public: |
| + explicit ResolvConfWatcherDelegate(int* conf_generation, Lock* lock) |
| + : conf_generation_(conf_generation), lock_(lock) { |
| + } |
| + |
| + virtual void OnFilePathChanged(const FilePath& path) { |
| + AutoLock l(*lock_); |
| + (*conf_generation_)++; |
| + LOG(INFO) << _PATH_RESCONF << " updated, new generation: " |
| + << *conf_generation_; |
| + } |
| + private: |
| + int* conf_generation_; |
| + Lock* lock_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(ResolvConfWatcherDelegate); |
| + }; |
| + |
| + // Generation starts at 1 so that all threads reload resolv.conf |
| + // on first invocation. |
| + DnsReloadTimer() : resolv_conf_generation_(1) { |
| // During testing the DnsReloadTimer Singleton may be created and destroyed |
| // multiple times. Initialize the ThreadLocalStorage slot only once. |
| if (!tls_index_.initialized()) |
| @@ -70,30 +115,94 @@ |
| ~DnsReloadTimer() { |
| } |
| + DnsReloadTimer::LocalState* GetLocalState(); |
| + |
| + Lock lock_; // Protects resolv_conf_generation_ |
| + int resolv_conf_generation_; |
| + |
| + scoped_ptr<FilePathWatcher> resolv_conf_watcher_; |
| + scoped_ptr<ResolvConfWatcherDelegate> resolv_conf_watcher_delegate_; |
| + |
| // We use thread local storage to identify which base::TimeTicks to |
| // interact with. |
| - static ThreadLocalStorage::Slot tls_index_ ; |
| + static ThreadLocalStorage::Slot tls_index_; |
| DISALLOW_COPY_AND_ASSIGN(DnsReloadTimer); |
| }; |
| +void DnsReloadTimer::InitResolvConfWatcher() { |
| + resolv_conf_watcher_.reset(new FilePathWatcher()); |
| + resolv_conf_watcher_delegate_.reset( |
| + new ResolvConfWatcherDelegate(&resolv_conf_generation_, &lock_)); |
| + if (!resolv_conf_watcher_->Watch( |
| + FilePath(FilePath::StringType(_PATH_RESCONF)), |
| + resolv_conf_watcher_delegate_.get())) { |
| + LOG(WARNING) << "Failed to install watcher for " << _PATH_RESCONF; |
| + // We failed to install watcher, for whatever reason. Free the watcher. |
| + resolv_conf_watcher_.reset(); |
| + resolv_conf_watcher_delegate_.reset(); |
| + } else { |
| + LOG(INFO) << "Installed watcher for " << _PATH_RESCONF; |
| + } |
| +} |
| + |
| +DnsReloadTimer::LocalState* DnsReloadTimer::GetLocalState() { |
| + DnsReloadTimer::LocalState* state = |
| + static_cast<DnsReloadTimer::LocalState*>(tls_index_.Get()); |
| + if (!state) { |
| + state = new DnsReloadTimer::LocalState(); |
| + state->timer = base::TimeTicks::Now(); |
| + state->resolv_conf_generation = 0; |
| + tls_index_.Set(state); |
| + } |
| + return state; |
| +} |
| + |
| // A TLS slot to the TimeTicks for the current thread. |
| // static |
| ThreadLocalStorage::Slot DnsReloadTimer::tls_index_(base::LINKER_INITIALIZED); |
| -base::LazyInstance<DnsReloadTimer, |
| - base::LeakyLazyInstanceTraits<DnsReloadTimer> > |
| - g_dns_reload_timer(base::LINKER_INITIALIZED); |
| +typedef base::LazyInstance<DnsReloadTimer, |
| + base::LeakyLazyInstanceTraits<DnsReloadTimer> > |
| + DnsReloadTimerInstance; |
| +DnsReloadTimerInstance g_dns_reload_timer(base::LINKER_INITIALIZED); |
| -} // namespace |
| +// This task is run on file thread |
| +// and calls DnsReloadTimer::InitResolvConfWatcher(). |
| +class InitResolvConfWatcherTask : public Task { |
| + public: |
| + InitResolvConfWatcherTask(DnsReloadTimerInstance* dns_reload_timer_instance) |
| + : dns_reload_timer_instance_(dns_reload_timer_instance) { |
| + } |
| -namespace net { |
| + virtual void Run() { |
| + dns_reload_timer_instance_->Pointer()->InitResolvConfWatcher(); |
| + } |
| + private: |
| + DnsReloadTimerInstance* dns_reload_timer_instance_; |
| + |
| + DISALLOW_COPY_AND_ASSIGN(InitResolvConfWatcherTask); |
| +}; |
| + |
| bool DnsReloadTimerHasExpired() { |
| DnsReloadTimer* dns_timer = g_dns_reload_timer.Pointer(); |
| return dns_timer->Expired(); |
| } |
| +bool ResolvConfUpdated() { |
| + DnsReloadTimer* dns_timer = g_dns_reload_timer.Pointer(); |
| + return dns_timer->ResolvConfUpdated(); |
| +} |
| + |
| +void InitResolvConfWatcher(base::Thread* file_thread) { |
| + // FileWatcher must be used on the file thread. |
| + // Additionally, we delay initialization - resolv.conf is read on first use |
| + // anyway, this watcher will most likely be useful later. |
| + file_thread->message_loop()->PostDelayedTask( |
| + FROM_HERE, new InitResolvConfWatcherTask(&g_dns_reload_timer), 5000); |
| +} |
| + |
| } // namespace net |
| -#endif // defined(OS_POSIX) && !defined(OS_MACOSX) && !defined(OS_OPENBSD) |
| +#endif // WATCH_RESOLV_CONF |