// 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 <memory>

#include "base/base_export.h"
#include "base/check_op.h"
#include "base/compiler_specific.h"
#include "base/dcheck_is_on.h"
#include "base/gtest_prod_util.h"
#include "base/location.h"
#include "base/macros.h"
#include "build/build_config.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*
//
// - Accessing singletons: Accessing global state (Singleton / LazyInstance) is
//   problematic on threads whom aren't joined on shutdown as they can be using
//   the state as it becomes invalid during tear down. base::NoDestructor is the
//   preferred alternative for global state and doesn't have this restriction.
//
// - 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.
//  - DisallowSingleton(): Disallows using singletons 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 ChromeNSSCryptoModuleDelegate;
class KeyStorageLinux;
class NativeBackendKWallet;
class NativeDesktopMediaList;
class Profile;
class StartupTabProviderImpl;
class GaiaConfig;
class WebEngineBrowserMainParts;

Profile* GetLastProfileMac();

namespace android_webview {
class AwFormDatabaseService;
class CookieManager;
class ScopedAllowInitGLBindings;
class VizCompositorThreadRunnerWebView;
}
namespace ash {
class MojoUtils;
class BrowserDataMigrator;
}
namespace audio {
class OutputDevice;
}
namespace blink {
class DiskDataAllocator;
class RTCVideoDecoderAdapter;
class RTCVideoEncoder;
class SourceStream;
class VideoFrameResourceProvider;
class WebRtcVideoFrameAdapter;
class LegacyWebRtcVideoFrameAdapter;
class WorkerThread;
namespace scheduler {
class WorkerThread;
}
}  // namespace blink
namespace cc {
class CompletionEvent;
class TileTaskManagerImpl;
}
namespace chromecast {
class CrashUtil;
}
namespace chromeos {
class BlockingMethodCaller;
namespace system {
class StatisticsProviderImpl;
bool IsCoreSchedulingAvailable();
int NumberOfProcessorsForCoreScheduling();
}
}
namespace chrome_cleaner {
class ResetShortcutsComponent;
class SystemReportComponent;
}
namespace content {
class BrowserGpuChannelHostFactory;
class BrowserMainLoop;
class BrowserProcessIOThread;
class BrowserTestBase;
class CategorizedWorkerPool;
class DesktopCaptureDevice;
class EmergencyTraceFinalisationCoordinator;
class InProcessUtilityThread;
class NestedMessagePumpAndroid;
class NetworkServiceInstancePrivate;
class PepperPrintSettingsManagerImpl;
class RenderProcessHostImpl;
class RenderWidgetHostViewMac;
class RTCVideoDecoder;
class SandboxHostLinux;
class ScopedAllowWaitForDebugURL;
class ServiceWorkerContextClient;
class ShellPathProvider;
class SynchronousCompositor;
class SynchronousCompositorHost;
class SynchronousCompositorSyncCallBridge;
class TextInputClientMac;
class WaitForProcessesToDumpProfilingInfo;
class WebContentsViewMac;
}  // namespace content
namespace cronet {
class CronetPrefsManager;
class CronetURLRequestContext;
}  // namespace cronet
namespace crosapi {
class LacrosThreadPriorityDelegate;
}  // namespace crosapi
namespace dbus {
class Bus;
}
namespace device {
class UsbContext;
}
namespace disk_cache {
class BackendImpl;
class InFlightIO;
}
namespace functions {
class ExecScriptScopedAllowBaseSyncPrimitives;
}
namespace history_report {
class HistoryReportJniBridge;
}
namespace ios_web_view {
class WebViewBrowserState;
}
namespace leveldb_env {
class DBTracker;
}
namespace location {
namespace nearby {
namespace chrome {
class ScheduledExecutor;
class SubmittableExecutor;
}  // namespace chrome
}  // namespace nearby
}  // namespace location
namespace media {
class AudioInputDevice;
class AudioOutputDevice;
class BlockingUrlProtocol;
class FileVideoCaptureDeviceFactory;
class PaintCanvasVideoRenderer;
}
namespace memory_instrumentation {
class OSMetrics;
}
namespace metrics {
class AndroidMetricsServiceClient;
class CleanExitBeacon;
}
namespace midi {
class TaskService;  // https://crbug.com/796830
}
namespace module_installer {
class ScopedAllowModulePakLoad;
}
namespace mojo {
class CoreLibraryInitializer;
class SyncCallRestrictions;
namespace core {
class ScopedIPCSupport;
}
}
namespace nacl {
namespace nonsfi {
class PluginMainDelegate;
}
}  // namespace nacl
namespace printing {
class LocalPrinterHandlerDefault;
#if defined(OS_MAC)
class PrintBackendServiceImpl;
#endif
class PrintBackendServiceManager;
class PrintJobWorker;
class PrinterQuery;
}
namespace rlz_lib {
class FinancialPing;
}
namespace syncer {
class GetLocalChangesRequest;
class HttpBridge;
}
namespace ui {
class DrmThreadProxy;
}
namespace weblayer {
class BrowserContextImpl;
class ContentBrowserClientImpl;
class ProfileImpl;
class WebLayerPathProvider;
}
namespace net {
class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives;
class MultiThreadedProxyResolverScopedAllowJoinOnIO;
class NetworkChangeNotifierMac;
class NetworkConfigWatcherMacThread;
namespace internal {
class AddressTrackerLinux;
}
}

namespace proxy_resolver {
class ScopedAllowThreadJoinForProxyResolverV8Tracing;
}

namespace remote_cocoa {
class DroppedScreenShotCopierMac;
}  // namespace remote_cocoa

namespace remoting {
class AutoThread;
class ScopedBypassIOThreadRestrictions;
namespace protocol {
class ScopedAllowSyncPrimitivesForWebRtcTransport;
class ScopedAllowThreadJoinForWebRtcTransport;
}
}

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 webrtc {
class DesktopConfigurationMonitor;
}

namespace base {

namespace sequence_manager {
namespace internal {
class TaskQueueImpl;
}
}  // namespace sequence_manager

namespace android {
class JavaHandlerThread;
}

namespace internal {
class GetAppOutputScopedAllowBaseSyncPrimitives;
class JobTaskSource;
class TaskTracker;
}

class AdjustOOMScoreHelper;
class FileDescriptorWatcher;
class FilePath;
class ScopedAllowThreadRecallForStackSamplingProfiler;
class StackSamplingProfiler;
class TestCustomDisallow;
class SimpleThread;
class Thread;

class BooleanWithStack;

bool PathProviderWin(int, FilePath*);

#if DCHECK_IS_ON()
// NOT_TAIL_CALLED if dcheck-is-on so it's always evident who irrevocably
// altered the allowance (dcheck-builds will provide the setter's stack on
// assertion) or who made a failing Assert*() call.
#define INLINE_OR_NOT_TAIL_CALLED BASE_EXPORT NOT_TAIL_CALLED
#define EMPTY_BODY_IF_DCHECK_IS_OFF
#else
// inline if dcheck-is-off so it's no overhead
#define INLINE_OR_NOT_TAIL_CALLED 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  // DCHECK_IS_ON()

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_OR_NOT_TAIL_CALLED void AssertBlockingAllowed()
    EMPTY_BODY_IF_DCHECK_IS_OFF;
INLINE_OR_NOT_TAIL_CALLED void AssertBlockingDisallowedForTesting()
    EMPTY_BODY_IF_DCHECK_IS_OFF;

}  // namespace internal

// Disallows blocking on the current thread.
INLINE_OR_NOT_TAIL_CALLED 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(const ScopedDisallowBlocking&) = delete;
  ScopedDisallowBlocking& operator=(const ScopedDisallowBlocking&) = delete;

  ~ScopedDisallowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;

 private:
#if DCHECK_IS_ON()
  std::unique_ptr<BooleanWithStack> was_disallowed_;
#endif
};

class BASE_EXPORT ScopedAllowBlocking {
 private:
  FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest,
                           NestedAllowRestoresPreviousStack);
  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 ::GaiaConfig;
  friend class ::StartupTabProviderImpl;
  friend class android_webview::ScopedAllowInitGLBindings;
  friend class ash::MojoUtils;  // http://crbug.com/1055467
  friend class ash::BrowserDataMigrator;
  friend class base::AdjustOOMScoreHelper;
  friend class base::StackSamplingProfiler;
  friend class blink::DiskDataAllocator;
  friend class chromecast::CrashUtil;
  friend class content::BrowserProcessIOThread;
  friend class content::NetworkServiceInstancePrivate;
  friend class content::PepperPrintSettingsManagerImpl;
  friend class content::RenderProcessHostImpl;
  friend class content::RenderWidgetHostViewMac;  // http://crbug.com/121917
  friend class content::ShellPathProvider;
  friend class content::WebContentsViewMac;
  friend class cronet::CronetPrefsManager;
  friend class cronet::CronetURLRequestContext;
  friend class crosapi::LacrosThreadPriorityDelegate;
  friend class ios_web_view::WebViewBrowserState;
  friend class media::FileVideoCaptureDeviceFactory;
  friend class memory_instrumentation::OSMetrics;
  friend class metrics::AndroidMetricsServiceClient;
  friend class metrics::CleanExitBeacon;
  friend class module_installer::ScopedAllowModulePakLoad;
  friend class mojo::CoreLibraryInitializer;
  friend class printing::LocalPrinterHandlerDefault;
#if defined(OS_MAC)
  friend class printing::PrintBackendServiceImpl;
#endif
  friend class printing::PrintBackendServiceManager;
  friend class printing::PrintJobWorker;
  friend class remote_cocoa::
      DroppedScreenShotCopierMac;  // https://crbug.com/1148078
  friend class remoting::ScopedBypassIOThreadRestrictions;  // crbug.com/1144161
  friend class web::WebSubThread;
  friend class ::WebEngineBrowserMainParts;
  friend class weblayer::BrowserContextImpl;
  friend class weblayer::ContentBrowserClientImpl;
  friend class weblayer::ProfileImpl;
  friend class weblayer::WebLayerPathProvider;

  friend Profile* ::GetLastProfileMac();  // crbug.com/1176734
  friend bool PathProviderWin(int, FilePath*);
  friend bool chromeos::system::IsCoreSchedulingAvailable();
  friend int chromeos::system::NumberOfProcessorsForCoreScheduling();

  ScopedAllowBlocking(const Location& from_here = Location::Current());
  ~ScopedAllowBlocking();

#if DCHECK_IS_ON()
  std::unique_ptr<BooleanWithStack> was_disallowed_;
#endif

  DISALLOW_COPY_AND_ASSIGN(ScopedAllowBlocking);
};

class ScopedAllowBlockingForTesting {
 public:
  ScopedAllowBlockingForTesting() {}

  ScopedAllowBlockingForTesting(const ScopedAllowBlockingForTesting&) = delete;
  ScopedAllowBlockingForTesting& operator=(
      const ScopedAllowBlockingForTesting&) = delete;

  ~ScopedAllowBlockingForTesting() {}

 private:
#if DCHECK_IS_ON()
  ScopedAllowBlocking scoped_allow_blocking_;
#endif
};

INLINE_OR_NOT_TAIL_CALLED void DisallowBaseSyncPrimitives()
    EMPTY_BODY_IF_DCHECK_IS_OFF;

// Disallows singletons within its scope.
class BASE_EXPORT ScopedDisallowBaseSyncPrimitives {
 public:
  ScopedDisallowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;

  ScopedDisallowBaseSyncPrimitives(const ScopedDisallowBaseSyncPrimitives&) =
      delete;
  ScopedDisallowBaseSyncPrimitives& operator=(
      const ScopedDisallowBaseSyncPrimitives&) = delete;

  ~ScopedDisallowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;

 private:
#if DCHECK_IS_ON()
  std::unique_ptr<BooleanWithStack> was_disallowed_;
#endif
};

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 ::ChromeNSSCryptoModuleDelegate;
  friend class base::internal::GetAppOutputScopedAllowBaseSyncPrimitives;
  friend class base::SimpleThread;
  friend class blink::SourceStream;
  friend class blink::WorkerThread;
  friend class blink::scheduler::WorkerThread;
  friend class chrome_cleaner::ResetShortcutsComponent;
  friend class chrome_cleaner::SystemReportComponent;
  friend class content::BrowserMainLoop;
  friend class content::BrowserProcessIOThread;
  friend class content::ServiceWorkerContextClient;
  friend class device::UsbContext;
  friend class functions::ExecScriptScopedAllowBaseSyncPrimitives;
  friend class history_report::HistoryReportJniBridge;
  friend class internal::TaskTracker;
  friend class leveldb_env::DBTracker;
  friend class location::nearby::chrome::ScheduledExecutor;
  friend class location::nearby::chrome::SubmittableExecutor;
  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 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 blink::VideoFrameResourceProvider;  // http://crbug.com/878070

  ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
  ~ScopedAllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;

#if DCHECK_IS_ON()
  std::unique_ptr<BooleanWithStack> 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 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 blink::WebRtcVideoFrameAdapter;
  friend class blink::LegacyWebRtcVideoFrameAdapter;
  friend class cc::TileTaskManagerImpl;
  friend class content::CategorizedWorkerPool;
  friend class content::DesktopCaptureDevice;
  friend class content::EmergencyTraceFinalisationCoordinator;
  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 content::WaitForProcessesToDumpProfilingInfo;
  friend class media::AudioInputDevice;
  friend class media::AudioOutputDevice;
  friend class media::PaintCanvasVideoRenderer;
  friend class mojo::SyncCallRestrictions;
  friend class net::NetworkConfigWatcherMacThread;
  friend class ui::DrmThreadProxy;
  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 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
  friend class remoting::protocol::
      ScopedAllowSyncPrimitivesForWebRtcTransport;  // http://crbug.com/1198501
  friend class remoting::protocol::
      ScopedAllowThreadJoinForWebRtcTransport;  // http://crbug.com/660081
  // Not used in production yet, https://crbug.com/844078.
  friend class service_manager::ServiceProcessLauncher;
  friend class ui::WindowResizeHelperMac;  // http://crbug.com/902829
  friend class content::TextInputClientMac;  // http://crbug.com/121917

  ScopedAllowBaseSyncPrimitivesOutsideBlockingScope(
      const Location& from_here = Location::Current());

  ~ScopedAllowBaseSyncPrimitivesOutsideBlockingScope();

#if DCHECK_IS_ON()
  std::unique_ptr<BooleanWithStack> was_disallowed_;
#endif

  DISALLOW_COPY_AND_ASSIGN(ScopedAllowBaseSyncPrimitivesOutsideBlockingScope);
};

// Allow base-sync-primitives in tests, doesn't require explicit friend'ing like
// ScopedAllowBaseSyncPrimitives-types aimed at production do.
// Note: For WaitableEvents in the test logic, base::TestWaitableEvent is
// exposed as a convenience to avoid the need for
// ScopedAllowBaseSyncPrimitivesForTesting.
class BASE_EXPORT ScopedAllowBaseSyncPrimitivesForTesting {
 public:
  ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;

  ScopedAllowBaseSyncPrimitivesForTesting(
      const ScopedAllowBaseSyncPrimitivesForTesting&) = delete;
  ScopedAllowBaseSyncPrimitivesForTesting& operator=(
      const ScopedAllowBaseSyncPrimitivesForTesting&) = delete;

  ~ScopedAllowBaseSyncPrimitivesForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;

 private:
#if DCHECK_IS_ON()
  std::unique_ptr<BooleanWithStack> was_disallowed_;
#endif
};

// 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(
      const ScopedAllowUnresponsiveTasksForTesting&) = delete;
  ScopedAllowUnresponsiveTasksForTesting& operator=(
      const ScopedAllowUnresponsiveTasksForTesting&) = delete;

  ~ScopedAllowUnresponsiveTasksForTesting() EMPTY_BODY_IF_DCHECK_IS_OFF;

 private:
#if DCHECK_IS_ON()
  std::unique_ptr<BooleanWithStack> was_disallowed_base_sync_;
  std::unique_ptr<BooleanWithStack> was_disallowed_blocking_;
  std::unique_ptr<BooleanWithStack> was_disallowed_cpu_;
#endif
};

namespace internal {

// Asserts that waiting on a //base sync primitive is allowed in the current
// scope.
INLINE_OR_NOT_TAIL_CALLED void AssertBaseSyncPrimitivesAllowed()
    EMPTY_BODY_IF_DCHECK_IS_OFF;

// Resets all thread restrictions on the current thread.
INLINE_OR_NOT_TAIL_CALLED void ResetThreadRestrictionsForTesting()
    EMPTY_BODY_IF_DCHECK_IS_OFF;

// Check whether the current thread is allowed to use singletons (Singleton /
// LazyInstance).  DCHECKs if not.
INLINE_OR_NOT_TAIL_CALLED void AssertSingletonAllowed()
    EMPTY_BODY_IF_DCHECK_IS_OFF;

}  // namespace internal

// Disallow using singleton on the current thread.
INLINE_OR_NOT_TAIL_CALLED void DisallowSingleton() EMPTY_BODY_IF_DCHECK_IS_OFF;

// Disallows singletons within its scope.
class BASE_EXPORT ScopedDisallowSingleton {
 public:
  ScopedDisallowSingleton() EMPTY_BODY_IF_DCHECK_IS_OFF;

  ScopedDisallowSingleton(const ScopedDisallowSingleton&) = delete;
  ScopedDisallowSingleton& operator=(const ScopedDisallowSingleton&) = delete;

  ~ScopedDisallowSingleton() EMPTY_BODY_IF_DCHECK_IS_OFF;

 private:
#if DCHECK_IS_ON()
  std::unique_ptr<BooleanWithStack> was_disallowed_;
#endif
};

// Asserts that running long CPU work is allowed in the current scope.
INLINE_OR_NOT_TAIL_CALLED void AssertLongCPUWorkAllowed()
    EMPTY_BODY_IF_DCHECK_IS_OFF;

INLINE_OR_NOT_TAIL_CALLED void DisallowUnresponsiveTasks()
    EMPTY_BODY_IF_DCHECK_IS_OFF;

class BASE_EXPORT ThreadRestrictions {
 public:
  ThreadRestrictions() = delete;

  // Constructing a ScopedAllowIO temporarily allows IO for the current
  // thread.  Doing this is almost certainly always incorrect.
  //
  // DEPRECATED. Use ScopedAllowBlocking(ForTesting).
  // TODO(crbug.com/766678): Migrate remaining users.
  class BASE_EXPORT ScopedAllowIO {
   public:
    ScopedAllowIO(const Location& from_here = Location::Current());

    ScopedAllowIO(const ScopedAllowIO&) = delete;
    ScopedAllowIO& operator=(const ScopedAllowIO&) = delete;

    ~ScopedAllowIO();

   private:
#if DCHECK_IS_ON()
    std::unique_ptr<BooleanWithStack> was_disallowed_;
#endif
  };
};

// Friend-only methods to permanently allow the current thread to use
// blocking/sync-primitives calls. Threads start out in the *allowed* state but
// are typically *disallowed* via the above base::Disallow*() methods after
// being initialized.
//
// Only use these to permanently set the allowance on a thread, e.g. on
// shutdown. For temporary allowances, use scopers above.
class BASE_EXPORT PermanentThreadAllowance {
 public:
  // Class is merely a namespace-with-friends.
  PermanentThreadAllowance() = delete;

 private:
  friend class base::TestCustomDisallow;
  friend class content::BrowserMainLoop;
  friend class content::BrowserTestBase;
  friend class web::WebMainLoop;

  static void AllowBlocking() EMPTY_BODY_IF_DCHECK_IS_OFF;
  static void AllowBaseSyncPrimitives() EMPTY_BODY_IF_DCHECK_IS_OFF;
};

// Similar to PermanentThreadAllowance but separate because it's dangerous and
// should have even fewer friends.
class BASE_EXPORT PermanentSingletonAllowance {
 public:
  // Class is merely a namespace-with-friends.
  PermanentSingletonAllowance() = delete;

 private:
  friend class nacl::nonsfi::PluginMainDelegate;

  // Re-allow singletons on this thread. Since //base APIs DisallowSingleton()
  // when they risk running past shutdown, this should only be called in rare
  // cases where the caller knows the process will be killed rather than
  // shutdown.
  static void AllowSingleton() EMPTY_BODY_IF_DCHECK_IS_OFF;
};

#undef INLINE_OR_NOT_TAIL_CALLED
#undef EMPTY_BODY_IF_DCHECK_IS_OFF

}  // namespace base

#endif  // BASE_THREADING_THREAD_RESTRICTIONS_H_
