blob: 516ac0a974abedadf92cc2c2e5a525e61036578e [file] [log] [blame]
// 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