| // Copyright 2017 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_SCOPED_BLOCKING_CALL_H | |
| #define BASE_THREADING_SCOPED_BLOCKING_CALL_H | |
| #include "base/base_export.h" | |
| #include "base/logging.h" | |
| namespace base { | |
| // 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. | |
| // BlockingType indicates the likelihood that a blocking call will actually | |
| // block. | |
| enum class BlockingType { | |
| // The call might block (e.g. file I/O that might hit in memory cache). | |
| MAY_BLOCK, | |
| // The call will definitely block (e.g. cache already checked and now pinging | |
| // server synchronously). | |
| WILL_BLOCK | |
| }; | |
| namespace internal { | |
| class BlockingObserver; | |
| // Common implementation class for both ScopedBlockingCall and | |
| // ScopedBlockingCallWithBaseSyncPrimitives without assertions. | |
| class BASE_EXPORT UncheckedScopedBlockingCall { | |
| public: | |
| UncheckedScopedBlockingCall(BlockingType blocking_type); | |
| ~UncheckedScopedBlockingCall(); | |
| private: | |
| internal::BlockingObserver* const blocking_observer_; | |
| // Previous ScopedBlockingCall instantiated on this thread. | |
| UncheckedScopedBlockingCall* const previous_scoped_blocking_call_; | |
| // Whether the BlockingType of the current thread was WILL_BLOCK after this | |
| // ScopedBlockingCall was instantiated. | |
| const bool is_will_block_; | |
| DISALLOW_COPY_AND_ASSIGN(UncheckedScopedBlockingCall); | |
| }; | |
| } // namespace internal | |
| // This class must be instantiated in every scope where a blocking call is made | |
| // and serves as a precise annotation of the scope that may/will block for the | |
| // scheduler. When a ScopedBlockingCall is instantiated, it asserts that | |
| // blocking calls are allowed in its scope with a call to | |
| // base::AssertBlockingAllowed(). CPU usage should be minimal within that scope. | |
| // //base APIs that block instantiate their own ScopedBlockingCall; it is not | |
| // necessary to instantiate another ScopedBlockingCall in the scope where these | |
| // APIs are used. Nested ScopedBlockingCalls are supported (mostly a no-op | |
| // except for WILL_BLOCK nested within MAY_BLOCK which will result in immediate | |
| // WILL_BLOCK semantics). | |
| // | |
| // Good: | |
| // Data data; | |
| // { | |
| // ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK); | |
| // data = GetDataFromNetwork(); | |
| // } | |
| // CPUIntensiveProcessing(data); | |
| // | |
| // Bad: | |
| // ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK); | |
| // Data data = GetDataFromNetwork(); | |
| // CPUIntensiveProcessing(data); // CPU usage within a ScopedBlockingCall. | |
| // | |
| // Good: | |
| // Data a; | |
| // Data b; | |
| // { | |
| // ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK); | |
| // a = GetDataFromMemoryCacheOrNetwork(); | |
| // b = GetDataFromMemoryCacheOrNetwork(); | |
| // } | |
| // CPUIntensiveProcessing(a); | |
| // CPUIntensiveProcessing(b); | |
| // | |
| // Bad: | |
| // ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK); | |
| // Data a = GetDataFromMemoryCacheOrNetwork(); | |
| // Data b = GetDataFromMemoryCacheOrNetwork(); | |
| // CPUIntensiveProcessing(a); // CPU usage within a ScopedBlockingCall. | |
| // CPUIntensiveProcessing(b); // CPU usage within a ScopedBlockingCall. | |
| // | |
| // Good: | |
| // base::WaitableEvent waitable_event(...); | |
| // waitable_event.Wait(); | |
| // | |
| // Bad: | |
| // base::WaitableEvent waitable_event(...); | |
| // ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK); | |
| // waitable_event.Wait(); // Wait() instantiates its own ScopedBlockingCall. | |
| // | |
| // When a ScopedBlockingCall is instantiated from a TaskScheduler parallel or | |
| // sequenced task, the thread pool size is incremented to compensate for the | |
| // blocked thread (more or less aggressively depending on BlockingType). | |
| class BASE_EXPORT ScopedBlockingCall | |
| : public internal::UncheckedScopedBlockingCall { | |
| public: | |
| ScopedBlockingCall(BlockingType blocking_type); | |
| ~ScopedBlockingCall() = default; | |
| }; | |
| namespace internal { | |
| // This class must be instantiated in every scope where a sync primitive is | |
| // used. When a ScopedBlockingCallWithBaseSyncPrimitives is instantiated, it | |
| // asserts that sync primitives are allowed in its scope with a call to | |
| // internal::AssertBaseSyncPrimitivesAllowed(). The same guidelines as for | |
| // ScopedBlockingCall should be followed. | |
| class BASE_EXPORT ScopedBlockingCallWithBaseSyncPrimitives | |
| : public UncheckedScopedBlockingCall { | |
| public: | |
| ScopedBlockingCallWithBaseSyncPrimitives(BlockingType blocking_type); | |
| ~ScopedBlockingCallWithBaseSyncPrimitives() = default; | |
| }; | |
| // Interface for an observer to be informed when a thread enters or exits | |
| // the scope of ScopedBlockingCall objects. | |
| class BASE_EXPORT BlockingObserver { | |
| public: | |
| virtual ~BlockingObserver() = default; | |
| // Invoked when a ScopedBlockingCall is instantiated on the observed thread | |
| // where there wasn't an existing ScopedBlockingCall. | |
| virtual void BlockingStarted(BlockingType blocking_type) = 0; | |
| // Invoked when a WILL_BLOCK ScopedBlockingCall is instantiated on the | |
| // observed thread where there was a MAY_BLOCK ScopedBlockingCall but not a | |
| // WILL_BLOCK ScopedBlockingCall. | |
| virtual void BlockingTypeUpgraded() = 0; | |
| // Invoked when the last ScopedBlockingCall on the observed thread is | |
| // destroyed. | |
| virtual void BlockingEnded() = 0; | |
| }; | |
| // Registers |blocking_observer| on the current thread. It is invalid to call | |
| // this on a thread where there is an active ScopedBlockingCall. | |
| BASE_EXPORT void SetBlockingObserverForCurrentThread( | |
| BlockingObserver* blocking_observer); | |
| BASE_EXPORT void ClearBlockingObserverForTesting(); | |
| // Unregisters the |blocking_observer| on the current thread within its scope. | |
| // Used in TaskScheduler tests to prevent calls to //base sync primitives from | |
| // affecting the thread pool capacity. | |
| class BASE_EXPORT ScopedClearBlockingObserverForTesting { | |
| public: | |
| ScopedClearBlockingObserverForTesting(); | |
| ~ScopedClearBlockingObserverForTesting(); | |
| private: | |
| BlockingObserver* const blocking_observer_; | |
| DISALLOW_COPY_AND_ASSIGN(ScopedClearBlockingObserverForTesting); | |
| }; | |
| } // namespace internal | |
| } // namespace base | |
| #endif // BASE_THREADING_SCOPED_BLOCKING_CALL_H |