| // 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 { | |
| // 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; | |
| } | |
| // This class must be instantiated in every scope where a blocking call is made. | |
| // 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. | |
| // | |
| // 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: | |
| ScopedBlockingCall(BlockingType blocking_type); | |
| ~ScopedBlockingCall(); | |
| private: | |
| internal::BlockingObserver* const blocking_observer_; | |
| // Previous ScopedBlockingCall instantiated on this thread. | |
| ScopedBlockingCall* 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(ScopedBlockingCall); | |
| }; | |
| namespace internal { | |
| // 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 |