// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef BASE_THREADING_THREAD_RESTRICTIONS_H_ #define BASE_THREADING_THREAD_RESTRICTIONS_H_ #include "base/base_export.h" #include "base/gtest_prod_util.h" #include "base/logging.h" #include "base/macros.h" class BrowserProcessImpl; class HistogramSynchronizer; class NativeBackendKWallet; namespace android_webview { class AwFormDatabaseService; class CookieManager; } namespace cc { class CompletionEvent; class SingleThreadTaskGraphRunner; } namespace chromeos { class BlockingMethodCaller; namespace system { class StatisticsProviderImpl; } } namespace chrome_browser_net { class Predictor; } namespace content { class BrowserGpuChannelHostFactory; class BrowserGpuMemoryBufferManager; class BrowserMainLoop; class BrowserShutdownProfileDumper; class BrowserSurfaceViewManager; class BrowserTestBase; class CategorizedWorkerPool; class NestedMessagePumpAndroid; class ScopedAllowWaitForAndroidLayoutTests; class ScopedAllowWaitForDebugURL; class SoftwareOutputDeviceMus; class SynchronousCompositor; class SynchronousCompositorBrowserFilter; class SynchronousCompositorHost; class TextInputClientMac; } // namespace content namespace cronet { class CronetPrefsManager; class CronetURLRequestContextAdapter; } // namespace cronet namespace dbus { class Bus; } namespace disk_cache { class BackendImpl; class InFlightIO; } namespace gpu { class GpuChannelHost; } namespace leveldb { class LevelDBMojoProxy; } namespace mojo { class SyncCallRestrictions; namespace edk { class ScopedIPCSupport; } } namespace ui { class CommandBufferClientImpl; class CommandBufferLocal; class GpuState; } namespace net { class NetworkChangeNotifierMac; class OCSPScopedAllowBaseSyncPrimitives; namespace internal { class AddressTrackerLinux; } } namespace remoting { class AutoThread; } namespace ui { class WindowResizeHelperMac; } namespace views { class ScreenMus; } namespace viz { class ServerGpuMemoryBufferManager; } namespace base { namespace android { class JavaHandlerThread; } namespace internal { class TaskTracker; } class SequencedWorkerPool; class SimpleThread; class StackSamplingProfiler; class Thread; class ThreadTestHelper; #if DCHECK_IS_ON() #define INLINE_IF_DCHECK_IS_OFF BASE_EXPORT #define EMPTY_BODY_IF_DCHECK_IS_OFF #else #define INLINE_IF_DCHECK_IS_OFF inline #define EMPTY_BODY_IF_DCHECK_IS_OFF \ {} #endif // A "blocking call" refers to any call that causes the calling thread to wait // off-CPU. It includes but is not limited to calls that wait on synchronous // file I/O operations: read or write a file from disk, interact with a pipe or // a socket, rename or delete a file, enumerate files in a directory, etc. // Acquiring a low contention lock is not considered a blocking call. // Asserts that blocking calls are allowed in the current scope. INLINE_IF_DCHECK_IS_OFF void AssertBlockingAllowed() EMPTY_BODY_IF_DCHECK_IS_OFF; // Disallows blocking on the current thread. INLINE_IF_DCHECK_IS_OFF void DisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; // Disallows blocking calls within its scope. class BASE_EXPORT ScopedDisallowBlocking { public: ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; ~ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; private: #if DCHECK_IS_ON() const bool was_disallowed_; #endif DISALLOW_COPY_AND_ASSIGN(ScopedDisallowBlocking); }; // ScopedAllowBlocking(ForTesting) allow blocking calls within a scope where // they are normally disallowed. // // Avoid using this. Prefer making blocking calls from tasks posted to // base::TaskScheduler with base::MayBlock(). class BASE_EXPORT ScopedAllowBlocking { private: // This can only be instantiated by friends. Use ScopedAllowBlockingForTesting // in unit tests to avoid the friend requirement. FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBlocking); friend class cronet::CronetPrefsManager; friend class cronet::CronetURLRequestContextAdapter; friend class ScopedAllowBlockingForTesting; ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; ~ScopedAllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF; #if DCHECK_IS_ON() const bool was_disallowed_; #endif DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlocking); }; class ScopedAllowBlockingForTesting { public: ScopedAllowBlockingForTesting() {} ~ScopedAllowBlockingForTesting() {} private: #if DCHECK_IS_ON() ScopedAllowBlocking scoped_allow_blocking_; #endif DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlockingForTesting); }; // "Waiting on a //base sync primitive" refers to calling // base::WaitableEvent::*Wait* or base::ConditionVariable::*Wait*. // Disallows waiting on a //base sync primitive on the current thread. INLINE_IF_DCHECK_IS_OFF void DisallowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF; // ScopedAllowBaseSyncPrimitives(ForTesting)(OutsideBlockingScope) allow waiting // on a //base sync primitive within a scope where this is normally disallowed. // // Avoid using this. Instead of waiting on a WaitableEvent or a // ConditionVariable, put the work that should happen after the wait in a // callback and post that callback from where the WaitableEvent or // ConditionVariable would have been signaled. If something needs to be // scheduled after many tasks have executed, use base::BarrierClosure. // This can only be used in a scope where blocking is allowed. class BASE_EXPORT ScopedAllowBaseSyncPrimitives { private: // This can only be instantiated by friends. Use // ScopedAllowBaseSyncPrimitivesForTesting in unit tests to avoid the friend // requirement. FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBaseSyncPrimitives); FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBaseSyncPrimitivesResetsState); FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBaseSyncPrimitivesWithBlockingDisallowed); friend class leveldb::LevelDBMojoProxy; friend class net::OCSPScopedAllowBaseSyncPrimitives; ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF; ~ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF; #if DCHECK_IS_ON() const bool was_disallowed_; #endif DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitives); }; // This can be used in a scope where blocking is disallowed. class BASE_EXPORT ScopedAllowBaseSyncPrimitivesOutsideBlockingScope { private: // This can only be instantiated by friends. Use // ScopedAllowBaseSyncPrimitivesForTesting in unit tests to avoid the friend // requirement. FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBaseSyncPrimitivesOutsideBlockingScope); FRIEND_TEST_ALL_PREFIXES( ThreadRestrictionsTest, ScopedAllowBaseSyncPrimitivesOutsideBlockingScopeResetsState); ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() EMPTY_BODY_IF_DCHECK_IS_OFF; ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope() EMPTY_BODY_IF_DCHECK_IS_OFF; #if DCHECK_IS_ON() const bool was_disallowed_; #endif DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesOutsideBlockingScope); }; // This can be used in tests without being a friend of // ScopedAllowBaseSyncPrimitives(OutsideBlockingScope). class BASE_EXPORT ScopedAllowBaseSyncPrimitivesForTesting { public: ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; ~ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; private: #if DCHECK_IS_ON() const bool was_disallowed_; #endif DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesForTesting); }; namespace internal { // Asserts that waiting on a //base sync primitive is allowed in the current // scope. INLINE_IF_DCHECK_IS_OFF void AssertBaseSyncPrimitivesAllowed() EMPTY_BODY_IF_DCHECK_IS_OFF; // Resets all thread restrictions on the current thread. INLINE_IF_DCHECK_IS_OFF void ResetThreadRestrictionsForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; } // namespace internal // Certain behavior is disallowed on certain threads. ThreadRestrictions helps // enforce these rules. Examples of such rules: // // * Do not do blocking IO (makes the thread janky) // * Do not access Singleton/LazyInstance (may lead to shutdown crashes) // // Here's more about how the protection works: // // 1) If a thread should not be allowed to make IO calls, mark it: // base::ThreadRestrictions::SetIOAllowed(false); // By default, threads *are* allowed to make IO calls. // In Chrome browser code, IO calls should be proxied to a TaskRunner with // the base::MayBlock() trait. // // 2) If a function makes a call that will go out to disk, check whether the // current thread is allowed: // base::ThreadRestrictions::AssertIOAllowed(); // // // Style tip: where should you put AssertIOAllowed checks? It's best // if you put them as close to the disk access as possible, at the // lowest level. This rule is simple to follow and helps catch all // callers. For example, if your function GoDoSomeBlockingDiskCall() // only calls other functions in Chrome and not fopen(), you should go // add the AssertIOAllowed checks in the helper functions. class BASE_EXPORT ThreadRestrictions { public: // Constructing a ScopedAllowIO temporarily allows IO for the current // thread. Doing this is almost certainly always incorrect. // // DEPRECATED. Use ScopedAllowBlocking(ForTesting). class BASE_EXPORT ScopedAllowIO { public: ScopedAllowIO() { previous_value_ = SetIOAllowed(true); } ~ScopedAllowIO() { SetIOAllowed(previous_value_); } private: // Whether IO is allowed when the ScopedAllowIO was constructed. bool previous_value_; DISALLOW_COPY_AND_ASSIGN(ScopedAllowIO); }; #if DCHECK_IS_ON() // Set whether the current thread to make IO calls. // Threads start out in the *allowed* state. // Returns the previous value. // // DEPRECATED. Use ScopedAllowBlocking(ForTesting) or ScopedDisallowBlocking. static bool SetIOAllowed(bool allowed); // Check whether the current thread is allowed to make IO calls, // and DCHECK if not. See the block comment above the class for // a discussion of where to add these checks. // // DEPRECATED. Use AssertBlockingAllowed. static void AssertIOAllowed(); // Set whether the current thread can use singletons. Returns the previous // value. static bool SetSingletonAllowed(bool allowed); // Check whether the current thread is allowed to use singletons (Singleton / // LazyInstance). DCHECKs if not. static void AssertSingletonAllowed(); // Disable waiting on the current thread. Threads start out in the *allowed* // state. Returns the previous value. // // DEPRECATED. Use DisallowBaseSyncPrimitives. static void DisallowWaiting(); #else // Inline the empty definitions of these functions so that they can be // compiled out. static bool SetIOAllowed(bool allowed) { return true; } static void AssertIOAllowed() {} static bool SetSingletonAllowed(bool allowed) { return true; } static void AssertSingletonAllowed() {} static void DisallowWaiting() {} #endif private: // DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to jam or brettw first. // BEGIN ALLOWED USAGE. friend class android_webview::AwFormDatabaseService; friend class android_webview::CookieManager; friend class base::StackSamplingProfiler; friend class content::BrowserMainLoop; friend class content::BrowserShutdownProfileDumper; friend class content::BrowserSurfaceViewManager; friend class content::BrowserTestBase; friend class content::NestedMessagePumpAndroid; friend class content::ScopedAllowWaitForAndroidLayoutTests; friend class content::ScopedAllowWaitForDebugURL; friend class content::SynchronousCompositor; friend class content::SynchronousCompositorBrowserFilter; friend class content::SynchronousCompositorHost; friend class ::HistogramSynchronizer; friend class internal::TaskTracker; friend class cc::CompletionEvent; friend class cc::SingleThreadTaskGraphRunner; friend class content::CategorizedWorkerPool; friend class remoting::AutoThread; friend class ui::WindowResizeHelperMac; friend class MessagePumpDefault; friend class SequencedWorkerPool; friend class SimpleThread; friend class Thread; friend class ThreadTestHelper; friend class PlatformThread; friend class android::JavaHandlerThread; friend class mojo::SyncCallRestrictions; friend class mojo::edk::ScopedIPCSupport; friend class ui::CommandBufferClientImpl; friend class ui::CommandBufferLocal; friend class ui::GpuState; // END ALLOWED USAGE. // BEGIN USAGE THAT NEEDS TO BE FIXED. friend class ::chromeos::BlockingMethodCaller; // http://crbug.com/125360 friend class ::chromeos::system::StatisticsProviderImpl; // http://crbug.com/125385 friend class chrome_browser_net::Predictor; // http://crbug.com/78451 friend class content::BrowserGpuChannelHostFactory; // http://crbug.com/125248 friend class content::BrowserGpuMemoryBufferManager; // http://crbug.com/420368 friend class content::TextInputClientMac; // http://crbug.com/121917 friend class dbus::Bus; // http://crbug.com/125222 friend class disk_cache::BackendImpl; // http://crbug.com/74623 friend class disk_cache::InFlightIO; // http://crbug.com/74623 friend class gpu::GpuChannelHost; // http://crbug.com/125264 friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097 friend class net::NetworkChangeNotifierMac; // http://crbug.com/125097 friend class ::BrowserProcessImpl; // http://crbug.com/125207 friend class ::NativeBackendKWallet; // http://crbug.com/125331 #if !defined(OFFICIAL_BUILD) friend class content::SoftwareOutputDeviceMus; // Interim non-production code #endif friend class views::ScreenMus; friend class viz::ServerGpuMemoryBufferManager; // END USAGE THAT NEEDS TO BE FIXED. #if DCHECK_IS_ON() // DEPRECATED. Use ScopedAllowBaseSyncPrimitives. static bool SetWaitAllowed(bool allowed); #else static bool SetWaitAllowed(bool allowed) { return true; } #endif // Constructing a ScopedAllowWait temporarily allows waiting on the current // thread. Doing this is almost always incorrect, which is why we limit who // can use this through friend. If you find yourself needing to use this, find // another way. Talk to jam or brettw. // // DEPRECATED. Use ScopedAllowBaseSyncPrimitives. class BASE_EXPORT ScopedAllowWait { public: ScopedAllowWait() { previous_value_ = SetWaitAllowed(true); } ~ScopedAllowWait() { SetWaitAllowed(previous_value_); } private: // Whether singleton use is allowed when the ScopedAllowWait was // constructed. bool previous_value_; DISALLOW_COPY_AND_ASSIGN(ScopedAllowWait); }; DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions); }; } // namespace base #endif // BASE_THREADING_THREAD_RESTRICTIONS_H_