Chromium Code Reviews| Index: net/proxy/proxy_resolver_mac.cc |
| diff --git a/net/proxy/proxy_resolver_mac.cc b/net/proxy/proxy_resolver_mac.cc |
| index cd4873c3f1e55581f3a1cd5f29b2cb7691504c5e..22e6fcf8a67f9f6ddf42044125c35ff8a443c942 100644 |
| --- a/net/proxy/proxy_resolver_mac.cc |
| +++ b/net/proxy/proxy_resolver_mac.cc |
| @@ -11,6 +11,7 @@ |
| #include "base/mac/scoped_cftyperef.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| +#include "base/synchronization/lock.h" |
| #include "net/base/net_errors.h" |
| #include "net/proxy/proxy_info.h" |
| #include "net/proxy/proxy_resolver.h" |
| @@ -26,6 +27,12 @@ namespace net { |
| namespace { |
| +// Forward declaration of the callback function used by the |
| +// SynchronizedRunLoopObserver class. |
| +void RunLoopObserverCallBackFunc(CFRunLoopObserverRef observer, |
| + CFRunLoopActivity activity, |
| + void* info); |
| + |
| // Utility function to map a CFProxyType to a ProxyServer::Scheme. |
| // If the type is unknown, returns ProxyServer::SCHEME_INVALID. |
| ProxyServer::Scheme GetProxyServerScheme(CFStringRef proxy_type) { |
| @@ -63,6 +70,92 @@ void ResultCallback(void* client, CFArrayRef proxies, CFErrorRef error) { |
| CFRunLoopStop(CFRunLoopGetCurrent()); |
| } |
| +#pragma mark - SynchronizedRunLoopObserver |
| +// A run loop observer that guarantees that no any two run loop sources |
| +// protected by the same lock will be fired concurrently in different threads. |
| +// The observer does not prevent the parallel execution of the sources but only |
| +// synchronizes the run loop events associated with the sources. In the context |
| +// of proxy resolver, the observer is used to synchronize the execution of the |
| +// callbacks function that handles the result of |
| +// CFNetworkExecuteProxyAutoConfigurationURL execution. |
| +class SynchronizedRunLoopObserver { |
| + public: |
| + // Creates the instance of an observer that will synchronize the sources |
| + // using a given |lock|. |
| + SynchronizedRunLoopObserver(base::Lock& lock); |
|
mef
2016/06/28 19:41:06
add destructor with DCHECK(!lock_acquired_);
kapishnikov
2016/06/28 21:31:04
Done.
|
| + // Adds the observer to the current run loop for a given run loop mode. |
| + // This method should always be paired with |RemoveObserver|. |
| + void AddObserver(const CFStringRef mode); |
| + // Removes the observer from the current run loop for a given run loop mode. |
| + // This method should always be paired with |AddObserver|. |
| + void RemoveObserver(const CFStringRef mode); |
| + // Callback function that is called when an observable run loop event occurs. |
| + void RunLoopObserverCallBack(CFRunLoopObserverRef observer, |
| + CFRunLoopActivity activity); |
| + |
| + private: |
| + // Lock to use to synchronize the run loop sources. |
| + base::Lock& lock_; |
| + // Indicates whether the current observer holds the lock. It is used to |
| + // avoid double locking and releasing. |
| + bool lock_acquired_; |
| + // The underlying CFRunLoopObserverRef structure wrapped by this instance. |
| + base::ScopedCFTypeRef<CFRunLoopObserverRef> observer_; |
| +}; |
| + |
| +SynchronizedRunLoopObserver::SynchronizedRunLoopObserver(base::Lock& lock) |
| + : lock_(lock), lock_acquired_(false) { |
| + CFRunLoopObserverContext observer_context = {0, this, NULL, NULL, NULL}; |
| + observer_.reset(CFRunLoopObserverCreate( |
| + kCFAllocatorDefault, |
| + kCFRunLoopBeforeSources | kCFRunLoopBeforeWaiting | kCFRunLoopExit, true, |
| + 0, RunLoopObserverCallBackFunc, &observer_context)); |
| +} |
| + |
| +void SynchronizedRunLoopObserver::AddObserver(const CFStringRef mode) { |
|
mef
2016/06/28 19:41:06
Rename into AddToCurrentRunLoop().
kapishnikov
2016/06/28 21:31:04
Done.
|
| + CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer_.get(), mode); |
| +} |
| + |
| +void SynchronizedRunLoopObserver::RemoveObserver(const CFStringRef mode) { |
|
mef
2016/06/28 19:41:06
Rename into RemoveFromCurrentRunLoop().
kapishnikov
2016/06/28 21:31:04
Done.
|
| + CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer_.get(), mode); |
| +} |
| + |
| +void SynchronizedRunLoopObserver::RunLoopObserverCallBack( |
| + CFRunLoopObserverRef observer, |
| + CFRunLoopActivity activity) { |
| + // Acquire the lock when a source has been signaled and going to be fired. |
| + // In the context of the proxy resolver that happens when the proxy for a |
| + // given URL has been resolved and the callback function that handles the |
| + // result is going to be fired. |
| + // Release the lock when all source events have been handled. |
| + switch (activity) { |
| + case kCFRunLoopBeforeSources: |
| + if (!lock_acquired_) { |
|
mef
2016/06/28 16:59:51
Having |lock_acquired_| flag and checking it outsi
kapishnikov
2016/06/28 18:51:20
A SynchronizedRunLoopObservers instance is always
|
| + lock_.Acquire(); |
| + lock_acquired_ = true; |
| + } |
| + break; |
| + case kCFRunLoopBeforeWaiting: |
| + case kCFRunLoopExit: |
| + if (lock_acquired_) { |
| + lock_acquired_ = false; |
| + lock_.Release(); |
| + } |
| + break; |
| + } |
| +} |
| + |
| +void RunLoopObserverCallBackFunc(CFRunLoopObserverRef observer, |
| + CFRunLoopActivity activity, |
| + void* info) { |
| + // Forward the call to the instance of SynchronizedRunLoopObserver |
| + // that is associated with the current CF run loop observer. |
| + SynchronizedRunLoopObserver* observerInstance = |
| + (SynchronizedRunLoopObserver*)info; |
| + observerInstance->RunLoopObserverCallBack(observer, activity); |
| +} |
| + |
| +#pragma mark - ProxyResolverMac |
| class ProxyResolverMac : public ProxyResolver { |
| public: |
| explicit ProxyResolverMac( |
| @@ -98,6 +191,14 @@ int ProxyResolverMac::GetProxyForURL(const GURL& query_url, |
| const CompletionCallback& /*callback*/, |
| RequestHandle* /*request*/, |
| const BoundNetLog& net_log) { |
| + // A lock shared by all ProxyResolverMac instances. It is used to synchronize |
| + // the events of multiple CFNetworkExecuteProxyAutoConfigurationURL run loop |
| + // sources. These events are: |
| + // 1. Adding the source to the run loop. |
| + // 2. Handling the source result. |
| + // 3. Removing the source from the run loop. |
| + CR_DEFINE_STATIC_LOCAL(base::Lock, lock, ()); |
|
mef
2016/06/28 16:59:52
Would it make sense to make lock a static member o
kapishnikov
2016/06/28 18:51:20
The lock is used in this method to synchronize 'ad
mef
2016/06/28 19:41:07
Use base/lazy_instance.h instead, rename into 'cfn
kapishnikov
2016/06/28 21:31:04
Done & Done
|
| + |
| base::ScopedCFTypeRef<CFStringRef> query_ref( |
| base::SysUTF8ToCFStringRef(query_url.spec())); |
| base::ScopedCFTypeRef<CFURLRef> query_url_ref( |
| @@ -140,10 +241,29 @@ int ProxyResolverMac::GetProxyForURL(const GURL& query_url, |
| const CFStringRef private_runloop_mode = |
| CFSTR("org.chromium.ProxyResolverMac"); |
| + // Add the run loop observer to synchronize events of |
| + // CFNetworkExecuteProxyAutoConfigurationURL sources. See the definition of |
| + // |lock| above. |
| + std::unique_ptr<SynchronizedRunLoopObserver> observer( |
|
mef
2016/06/28 19:41:07
drop unique_ptr.
kapishnikov
2016/06/28 21:31:04
Done.
|
| + new SynchronizedRunLoopObserver(lock)); |
| + observer->AddObserver(private_runloop_mode); |
| + |
| + // Make sure that no CFNetworkExecuteProxyAutoConfigurationURL sources |
| + // are added to the run loop concurrently. |
| + lock.Acquire(); |
| CFRunLoopAddSource(CFRunLoopGetCurrent(), runloop_source.get(), |
| private_runloop_mode); |
| + lock.Release(); |
| CFRunLoopRunInMode(private_runloop_mode, DBL_MAX, false); |
| - CFRunLoopSourceInvalidate(runloop_source.get()); |
| + // Make sure that no CFNetworkExecuteProxyAutoConfigurationURL sources |
| + // are removed from the run loop concurrently. |
| + lock.Acquire(); |
|
mef
2016/06/28 19:41:06
Consider AutoLock
kapishnikov
2016/06/28 21:31:04
Done.
|
| + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), runloop_source.get(), |
| + private_runloop_mode); |
| + lock.Release(); |
| + |
| + observer->RemoveObserver(private_runloop_mode); |
| + |
| DCHECK(result != NULL); |
| if (CFGetTypeID(result) == CFErrorGetTypeID()) { |