// 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; class KeyStorageLinux; namespace android_webview { class AwFormDatabaseService; class CookieManager; class ScopedAllowInitGLBindings; } 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 BrowserProcessSubThread; class BrowserShutdownProfileDumper; class BrowserSurfaceViewManager; class BrowserTestBase; class CategorizedWorkerPool; class NestedMessagePumpAndroid; class ScopedAllowWaitForAndroidLayoutTests; class ScopedAllowWaitForDebugURL; class SessionStorageDatabase; class SoftwareOutputDeviceMus; class SynchronousCompositor; class SynchronousCompositorHost; class SynchronousCompositorSyncCallBridge; class TextInputClientMac; } // namespace content namespace cronet { class CronetPrefsManager; class CronetURLRequestContext; } // namespace cronet namespace dbus { class Bus; } namespace disk_cache { class BackendImpl; class InFlightIO; } namespace functions { class ExecScriptScopedAllowBaseSyncPrimitives; } namespace gpu { class GpuChannelHost; } namespace leveldb { class LevelDBMojoProxy; } namespace media { class AudioInputDevice; class BlockingUrlProtocol; } namespace midi { class TaskService; // https://crbug.com/796830 } namespace mojo { class CoreLibraryInitializer; class SyncCallRestrictions; namespace edk { class ScopedIPCSupport; } } namespace rlz_lib { class FinancialPing; } namespace ui { class CommandBufferClientImpl; class CommandBufferLocal; class GpuState; class MaterialDesignController; } namespace net { class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives; class NetworkChangeNotifierMac; namespace internal { class AddressTrackerLinux; } } namespace remoting { class AutoThread; } namespace resource_coordinator { class TabManagerDelegate; } namespace service_manager { class ServiceProcessLauncher; } namespace shell_integration { class LaunchXdgUtilityScopedAllowBaseSyncPrimitives; } namespace ui { class WindowResizeHelperMac; } namespace views { class ScreenMus; } namespace viz { class ServerGpuMemoryBufferManager; } namespace webrtc { class DesktopConfigurationMonitor; } namespace base { namespace android { class JavaHandlerThread; } namespace internal { class TaskTracker; } class GetAppOutputScopedAllowBaseSyncPrimitives; 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. // // Style tip: It's best if you put AssertBlockingAllowed() checks as close to // the blocking call as possible. For example: // // void ReadFile() { // PreWork(); // // base::AssertBlockingAllowed(); // fopen(...); // // PostWork(); // } // // void Bar() { // ReadFile(); // } // // void Foo() { // Bar(); // } 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 android_webview::ScopedAllowInitGLBindings; friend class content::BrowserProcessSubThread; friend class cronet::CronetPrefsManager; friend class cronet::CronetURLRequestContext; friend class media::AudioInputDevice; friend class mojo::CoreLibraryInitializer; friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703 friend class ui::MaterialDesignController; friend class ScopedAllowBlockingForTesting; friend class StackSamplingProfiler; 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 one of these methods: // - base::WaitableEvent::*Wait* // - base::ConditionVariable::*Wait* // - base::Process::WaitForExit* // 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. // // On Windows, join processes asynchronously using base::win::ObjectWatcher. // 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 base::GetAppOutputScopedAllowBaseSyncPrimitives; friend class content::BrowserProcessSubThread; friend class content::SessionStorageDatabase; friend class functions::ExecScriptScopedAllowBaseSyncPrimitives; friend class leveldb::LevelDBMojoProxy; friend class media::BlockingUrlProtocol; friend class net::MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives; friend class rlz_lib::FinancialPing; friend class shell_integration::LaunchXdgUtilityScopedAllowBaseSyncPrimitives; friend class webrtc::DesktopConfigurationMonitor; 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); friend class ::KeyStorageLinux; friend class content::SynchronousCompositor; friend class content::SynchronousCompositorHost; friend class content::SynchronousCompositorSyncCallBridge; friend class midi::TaskService; // https://crbug.com/796830 // Not used in production yet, https://crbug.com/844078. friend class service_manager::ServiceProcessLauncher; 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 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() EMPTY_BODY_IF_DCHECK_IS_OFF; ~ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF; private: #if DCHECK_IS_ON() const bool was_allowed_; #endif 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); // 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 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 ::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 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() EMPTY_BODY_IF_DCHECK_IS_OFF; ~ScopedAllowWait() EMPTY_BODY_IF_DCHECK_IS_OFF; private: #if DCHECK_IS_ON() const bool was_allowed_; #endif DISALLOW_COPY_AND_ASSIGN(ScopedAllowWait); }; DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions); }; } // namespace base #endif // BASE_THREADING_THREAD_RESTRICTIONS_H_