diff options
Diffstat (limited to 'security/sandbox/chromium/base/threading/thread_restrictions.h')
-rw-r--r-- | security/sandbox/chromium/base/threading/thread_restrictions.h | 680 |
1 files changed, 680 insertions, 0 deletions
diff --git a/security/sandbox/chromium/base/threading/thread_restrictions.h b/security/sandbox/chromium/base/threading/thread_restrictions.h new file mode 100644 index 0000000000..55047c5b40 --- /dev/null +++ b/security/sandbox/chromium/base/threading/thread_restrictions.h @@ -0,0 +1,680 @@ +// 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" + +// ----------------------------------------------------------------------------- +// Usage documentation +// ----------------------------------------------------------------------------- +// +// Overview: +// This file exposes functions to ban and allow certain slow operations +// on a per-thread basis. To annotate *usage* of such slow operations, refer to +// scoped_blocking_call.h instead. +// +// Specific allowances that can be controlled in this file are: +// - 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. +// +// - Waiting on a //base sync primitive: Refers to calling one of these methods: +// - base::WaitableEvent::*Wait* +// - base::ConditionVariable::*Wait* +// - base::Process::WaitForExit* +// +// - Long CPU work: Refers to any code that takes more than 100 ms to +// run when there is no CPU contention and no hard page faults and therefore, +// is not suitable to run on a thread required to keep the browser responsive +// (where jank could be visible to the user). +// +// The following disallowance functions are offered: +// - DisallowBlocking(): Disallows blocking calls on the current thread. +// - DisallowBaseSyncPrimitives(): Disallows waiting on a //base sync primitive +// on the current thread. +// - DisallowUnresponsiveTasks() Disallows blocking calls, waiting on a //base +// sync primitive, and long cpu work on the current thread. +// +// In addition, scoped-allowance mechanisms are offered to make an exception +// within a scope for a behavior that is normally disallowed. +// - ScopedAllowBlocking(ForTesting): Allows blocking calls. +// - ScopedAllowBaseSyncPrimitives(ForTesting)(OutsideBlockingScope): Allow +// waiting on a //base sync primitive. The OutsideBlockingScope suffix allows +// uses in a scope where blocking is also disallowed. +// +// Avoid using allowances outside of unit tests. In unit tests, use allowances +// with the suffix "ForTesting". +// +// Prefer making blocking calls from tasks posted to base::ThreadPoolInstance +// with base::MayBlock(). +// +// Instead of waiting on a WaitableEvent or a ConditionVariable, prefer putting +// the work that should happen after the wait in a continuation callback and +// post it 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. +// +// Where unavoidable, put ScopedAllow* instances in the narrowest scope possible +// in the caller making the blocking call but no further down. For example: if a +// Cleanup() method needs to do a blocking call, document Cleanup() as blocking +// and add a ScopedAllowBlocking instance in callers that can't avoid making +// this call from a context where blocking is banned, as such: +// +// void Client::MyMethod() { +// (...) +// { +// // Blocking is okay here because XYZ. +// ScopedAllowBlocking allow_blocking; +// my_foo_->Cleanup(); +// } +// (...) +// } +// +// // This method can block. +// void Foo::Cleanup() { +// // Do NOT add the ScopedAllowBlocking in Cleanup() directly as that hides +// // its blocking nature from unknowing callers and defeats the purpose of +// // these checks. +// FlushStateToDisk(); +// } +// +// Note: In rare situations where the blocking call is an implementation detail +// (i.e. the impl makes a call that invokes AssertBlockingAllowed() but it +// somehow knows that in practice this will not block), it might be okay to hide +// the ScopedAllowBlocking instance in the impl with a comment explaining why +// that's okay. + +class BrowserProcessImpl; +class HistogramSynchronizer; +class KeyStorageLinux; +class NativeBackendKWallet; +class NativeDesktopMediaList; +class StartupTimeBomb; + +namespace android_webview { +class AwFormDatabaseService; +class CookieManager; +class ScopedAllowInitGLBindings; +class VizCompositorThreadRunnerWebView; +} +namespace audio { +class OutputDevice; +} +namespace blink { +class RTCVideoDecoderAdapter; +class RTCVideoEncoder; +class SourceStream; +class VideoFrameResourceProvider; +class WorkerThread; +namespace scheduler { +class WorkerThread; +} +} +namespace cc { +class CompletionEvent; +class TileTaskManagerImpl; +} +namespace chromeos { +class BlockingMethodCaller; +namespace system { +class StatisticsProviderImpl; +} +} +namespace chrome_browser_net { +class Predictor; +} +namespace chrome_cleaner { +class SystemReportComponent; +} +namespace content { +class BrowserGpuChannelHostFactory; +class BrowserMainLoop; +class BrowserProcessSubThread; +class BrowserShutdownProfileDumper; +class BrowserTestBase; +class CategorizedWorkerPool; +class DesktopCaptureDevice; +class InProcessUtilityThread; +class NestedMessagePumpAndroid; +class RenderProcessHostImpl; +class RenderWidgetHostViewMac; +class RTCVideoDecoder; +class SandboxHostLinux; +class ScopedAllowWaitForDebugURL; +class ServiceWorkerContextClient; +class SoftwareOutputDeviceMus; +class SynchronousCompositor; +class SynchronousCompositorHost; +class SynchronousCompositorSyncCallBridge; +class TextInputClientMac; +class WebContentsViewMac; +} // 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 history_report { +class HistoryReportJniBridge; +} +namespace gpu { +class GpuChannelHost; +} +namespace leveldb_env { +class DBTracker; +} +namespace media { +class AudioInputDevice; +class AudioOutputDevice; +class BlockingUrlProtocol; +class PaintCanvasVideoRenderer; +} +namespace memory_instrumentation { +class OSMetrics; +} +namespace midi { +class TaskService; // https://crbug.com/796830 +} +namespace module_installer { +class ScopedAllowModulePakLoad; +} +namespace mojo { +class CoreLibraryInitializer; +class SyncCallRestrictions; +namespace core { +class ScopedIPCSupport; +} +} +namespace printing { +class PrintJobWorker; +class PrinterQuery; +} +namespace rlz_lib { +class FinancialPing; +} +namespace syncer { +class GetLocalChangesRequest; +class HttpBridge; +class ModelSafeWorker; +} +namespace ui { +class CommandBufferClientImpl; +class CommandBufferLocal; +class GpuState; +class MaterialDesignController; +} +namespace weblayer { +class WebLayerPathProvider; +} +namespace net { +class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives; +class MultiThreadedProxyResolverScopedAllowJoinOnIO; +class NetworkChangeNotifierMac; +class NetworkConfigWatcherMacThread; +namespace internal { +class AddressTrackerLinux; +} +} + +namespace proxy_resolver { +class ScopedAllowThreadJoinForProxyResolverV8Tracing; +} + +namespace remoting { +class AutoThread; +namespace protocol { +class ScopedAllowThreadJoinForWebRtcTransport; +} +} + +namespace resource_coordinator { +class TabManagerDelegate; +} + +namespace service_manager { +class ServiceProcessLauncher; +} + +namespace shell_integration_linux { +class LaunchXdgUtilityScopedAllowBaseSyncPrimitives; +} + +namespace ui { +class WindowResizeHelperMac; +} + +namespace viz { +class HostGpuMemoryBufferManager; +} + +namespace vr { +class VrShell; +} + +namespace web { +class WebMainLoop; +class WebSubThread; +} + +namespace weblayer { +class ProfileImpl; +} + +namespace webrtc { +class DesktopConfigurationMonitor; +} + +namespace base { + +namespace sequence_manager { +namespace internal { +class TaskQueueImpl; +} +} // namespace sequence_manager + +namespace android { +class JavaHandlerThread; +} + +namespace internal { +class JobTaskSource; +class TaskTracker; +} + +class AdjustOOMScoreHelper; +class FileDescriptorWatcher; +class GetAppOutputScopedAllowBaseSyncPrimitives; +class ScopedAllowThreadRecallForStackSamplingProfiler; +class SimpleThread; +class StackSamplingProfiler; +class Thread; + +#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 + +// The static_assert() eats follow-on semicolons. `= default` would work +// too, but it makes clang realize that all the Scoped classes are no-ops in +// non-dcheck builds and it starts emitting many -Wunused-variable warnings. +#define EMPTY_BODY_IF_DCHECK_IS_OFF \ + {} \ + static_assert(true, "") +#endif + +namespace internal { + +// Asserts that blocking calls are allowed in the current scope. This is an +// internal call, external code should use ScopedBlockingCall instead, which +// serves as a precise annotation of the scope that may/will block. +INLINE_IF_DCHECK_IS_OFF void AssertBlockingAllowed() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +} // namespace internal + +// 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); +}; + +class BASE_EXPORT ScopedAllowBlocking { + private: + FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBlocking); + friend class ScopedAllowBlockingForTesting; + + // This can only be instantiated by friends. Use ScopedAllowBlockingForTesting + // in unit tests to avoid the friend requirement. + friend class AdjustOOMScoreHelper; + friend class android_webview::ScopedAllowInitGLBindings; + friend class content::BrowserProcessSubThread; + friend class content::RenderWidgetHostViewMac; // http://crbug.com/121917 + friend class content::WebContentsViewMac; + friend class cronet::CronetPrefsManager; + friend class cronet::CronetURLRequestContext; + friend class memory_instrumentation::OSMetrics; + friend class module_installer::ScopedAllowModulePakLoad; + friend class mojo::CoreLibraryInitializer; + friend class printing::PrintJobWorker; + friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703 + friend class ui::MaterialDesignController; + friend class web::WebSubThread; + friend class StackSamplingProfiler; + friend class weblayer::ProfileImpl; + friend class content::RenderProcessHostImpl; + friend class weblayer::WebLayerPathProvider; + + 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); +}; + +INLINE_IF_DCHECK_IS_OFF void DisallowBaseSyncPrimitives() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +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); + + // Allowed usage: + friend class SimpleThread; + friend class base::GetAppOutputScopedAllowBaseSyncPrimitives; + friend class blink::SourceStream; + friend class blink::WorkerThread; + friend class blink::scheduler::WorkerThread; + friend class chrome_cleaner::SystemReportComponent; + friend class content::BrowserMainLoop; + friend class content::BrowserProcessSubThread; + friend class content::ServiceWorkerContextClient; + friend class functions::ExecScriptScopedAllowBaseSyncPrimitives; + friend class history_report::HistoryReportJniBridge; + friend class internal::TaskTracker; + friend class leveldb_env::DBTracker; + friend class media::BlockingUrlProtocol; + friend class mojo::core::ScopedIPCSupport; + friend class net::MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives; + friend class rlz_lib::FinancialPing; + friend class shell_integration_linux:: + LaunchXdgUtilityScopedAllowBaseSyncPrimitives; + friend class syncer::HttpBridge; + friend class syncer::GetLocalChangesRequest; + friend class syncer::ModelSafeWorker; + friend class webrtc::DesktopConfigurationMonitor; + + // Usage that should be fixed: + friend class ::NativeBackendKWallet; // http://crbug.com/125331 + friend class ::chromeos::system:: + StatisticsProviderImpl; // http://crbug.com/125385 + friend class content::TextInputClientMac; // http://crbug.com/121917 + friend class blink::VideoFrameResourceProvider; // http://crbug.com/878070 + + 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); +}; + +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); + + // Allowed usage: + friend class ::BrowserProcessImpl; // http://crbug.com/125207 + friend class ::KeyStorageLinux; + friend class ::NativeDesktopMediaList; + friend class ::StartupTimeBomb; + friend class android::JavaHandlerThread; + friend class android_webview:: + AwFormDatabaseService; // http://crbug.com/904431 + friend class android_webview::CookieManager; + friend class android_webview::VizCompositorThreadRunnerWebView; + friend class audio::OutputDevice; + friend class base::sequence_manager::internal::TaskQueueImpl; + friend class base::FileDescriptorWatcher; + friend class base::internal::JobTaskSource; + friend class base::ScopedAllowThreadRecallForStackSamplingProfiler; + friend class base::StackSamplingProfiler; + friend class blink::RTCVideoDecoderAdapter; + friend class blink::RTCVideoEncoder; + friend class cc::TileTaskManagerImpl; + friend class content::CategorizedWorkerPool; + friend class content::DesktopCaptureDevice; + friend class content::InProcessUtilityThread; + friend class content::RTCVideoDecoder; + friend class content::SandboxHostLinux; + friend class content::ScopedAllowWaitForDebugURL; + friend class content::SynchronousCompositor; + friend class content::SynchronousCompositorHost; + friend class content::SynchronousCompositorSyncCallBridge; + friend class media::AudioInputDevice; + friend class media::AudioOutputDevice; + friend class media::PaintCanvasVideoRenderer; + friend class mojo::SyncCallRestrictions; + friend class net::NetworkConfigWatcherMacThread; + friend class viz::HostGpuMemoryBufferManager; + friend class vr::VrShell; + + // Usage that should be fixed: + friend class ::chromeos::BlockingMethodCaller; // http://crbug.com/125360 + friend class base::Thread; // http://crbug.com/918039 + friend class cc::CompletionEvent; // http://crbug.com/902653 + friend class content:: + BrowserGpuChannelHostFactory; // http://crbug.com/125248 + 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 remoting::protocol:: + ScopedAllowThreadJoinForWebRtcTransport; // http://crbug.com/660081 + friend class midi::TaskService; // https://crbug.com/796830 + friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097 + friend class net:: + MultiThreadedProxyResolverScopedAllowJoinOnIO; // http://crbug.com/69710 + friend class net::NetworkChangeNotifierMac; // http://crbug.com/125097 + friend class printing::PrinterQuery; // http://crbug.com/66082 + friend class proxy_resolver:: + ScopedAllowThreadJoinForProxyResolverV8Tracing; // http://crbug.com/69710 + friend class remoting::AutoThread; // https://crbug.com/944316 + // Not used in production yet, https://crbug.com/844078. + friend class service_manager::ServiceProcessLauncher; + friend class ui::WindowResizeHelperMac; // http://crbug.com/902829 + + 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); +}; + +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); +}; + +// Counterpart to base::DisallowUnresponsiveTasks() for tests to allow them to +// block their thread after it was banned. +class BASE_EXPORT ScopedAllowUnresponsiveTasksForTesting { + public: + ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF; + + private: +#if DCHECK_IS_ON() + const bool was_disallowed_base_sync_; + const bool was_disallowed_blocking_; + const bool was_disallowed_cpu_; +#endif + + DISALLOW_COPY_AND_ASSIGN(ScopedAllowUnresponsiveTasksForTesting); +}; + +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 + +// Asserts that running long CPU work is allowed in the current scope. +INLINE_IF_DCHECK_IS_OFF void AssertLongCPUWorkAllowed() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +INLINE_IF_DCHECK_IS_OFF void DisallowUnresponsiveTasks() + EMPTY_BODY_IF_DCHECK_IS_OFF; + +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. + // BEGIN ALLOWED USAGE. + friend class content::BrowserMainLoop; + friend class content::BrowserShutdownProfileDumper; + friend class content::BrowserTestBase; + friend class content::ScopedAllowWaitForDebugURL; + friend class ::HistogramSynchronizer; + friend class internal::TaskTracker; + friend class web::WebMainLoop; + friend class MessagePumpDefault; + friend class PlatformThread; + friend class ui::CommandBufferClientImpl; + friend class ui::CommandBufferLocal; + friend class ui::GpuState; + + // END ALLOWED USAGE. + // BEGIN USAGE THAT NEEDS TO BE FIXED. + friend class chrome_browser_net::Predictor; // http://crbug.com/78451 +#if !defined(OFFICIAL_BUILD) + friend class content::SoftwareOutputDeviceMus; // Interim non-production code +#endif +// 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 + + DISALLOW_IMPLICIT_CONSTRUCTORS(ThreadRestrictions); +}; + +#undef INLINE_IF_DCHECK_IS_OFF +#undef EMPTY_BODY_IF_DCHECK_IS_OFF + +} // namespace base + +#endif // BASE_THREADING_THREAD_RESTRICTIONS_H_ |