Support external task cancellation mechanisms in base::Callback::IsCancelled
This CL cleans up the implementation of base::Callback::IsCancelled(), and exposes an injection point as BindCancellationChecker, so that external code can implement the cancellation handling. Plus, this CL adds a specialization of BindCancellationChecker for blink::TaskRunner::Runner, so that the blink task scheduler can handle a cancellation of a task posted via WebTaskRunner::postCancellableTask() properly. Review-Url: https://codereview.chromium.org/2487493004 Cr-Commit-Position: refs/heads/master@{#434511}
This commit is contained in:
base
third_party/WebKit/Source/platform
@@ -179,6 +179,9 @@ struct BindUnwrapTraits;
|
|||||||
|
|
||||||
namespace internal {
|
namespace internal {
|
||||||
|
|
||||||
|
template <typename Functor, typename SFINAE = void>
|
||||||
|
struct FunctorTraits;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
class UnretainedWrapper {
|
class UnretainedWrapper {
|
||||||
public:
|
public:
|
||||||
@@ -521,6 +524,48 @@ struct BindUnwrapTraits<internal::PassedWrapper<T>> {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// CallbackCancellationTraits allows customization of Callback's cancellation
|
||||||
|
// semantics. By default, callbacks are not cancellable. A specialization should
|
||||||
|
// set is_cancellable = true and implement an IsCancelled() that returns if the
|
||||||
|
// callback should be cancelled.
|
||||||
|
template <typename Functor, typename BoundArgsTuple, typename SFINAE = void>
|
||||||
|
struct CallbackCancellationTraits {
|
||||||
|
static constexpr bool is_cancellable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specialization for method bound to weak pointer receiver.
|
||||||
|
template <typename Functor, typename... BoundArgs>
|
||||||
|
struct CallbackCancellationTraits<
|
||||||
|
Functor,
|
||||||
|
std::tuple<BoundArgs...>,
|
||||||
|
typename std::enable_if<
|
||||||
|
internal::IsWeakMethod<internal::FunctorTraits<Functor>::is_method,
|
||||||
|
BoundArgs...>::value>::type> {
|
||||||
|
static constexpr bool is_cancellable = true;
|
||||||
|
|
||||||
|
template <typename Receiver, typename... Args>
|
||||||
|
static bool IsCancelled(const Functor&,
|
||||||
|
const Receiver& receiver,
|
||||||
|
const Args&...) {
|
||||||
|
return !receiver;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Specialization for a nested bind.
|
||||||
|
template <typename Signature,
|
||||||
|
typename... BoundArgs,
|
||||||
|
internal::CopyMode copy_mode,
|
||||||
|
internal::RepeatMode repeat_mode>
|
||||||
|
struct CallbackCancellationTraits<Callback<Signature, copy_mode, repeat_mode>,
|
||||||
|
std::tuple<BoundArgs...>> {
|
||||||
|
static constexpr bool is_cancellable = true;
|
||||||
|
|
||||||
|
template <typename Functor>
|
||||||
|
static bool IsCancelled(const Functor& functor, const BoundArgs&...) {
|
||||||
|
return functor.IsCancelled();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace base
|
} // namespace base
|
||||||
|
|
||||||
#endif // BASE_BIND_HELPERS_H_
|
#endif // BASE_BIND_HELPERS_H_
|
||||||
|
@@ -130,7 +130,7 @@ struct ForceVoidReturn<R(Args...)> {
|
|||||||
// FunctorTraits<>
|
// FunctorTraits<>
|
||||||
//
|
//
|
||||||
// See description at top of file.
|
// See description at top of file.
|
||||||
template <typename Functor, typename SFINAE = void>
|
template <typename Functor, typename SFINAE>
|
||||||
struct FunctorTraits;
|
struct FunctorTraits;
|
||||||
|
|
||||||
// For a callable type that is convertible to the corresponding function type.
|
// For a callable type that is convertible to the corresponding function type.
|
||||||
@@ -387,43 +387,24 @@ IsNull(const Functor&) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename Functor, typename... BoundArgs>
|
// Used by ApplyCancellationTraits below.
|
||||||
struct BindState;
|
template <typename Functor, typename BoundArgsTuple, size_t... indices>
|
||||||
|
bool ApplyCancellationTraitsImpl(const Functor& functor,
|
||||||
|
const BoundArgsTuple& bound_args,
|
||||||
|
IndexSequence<indices...>) {
|
||||||
|
return CallbackCancellationTraits<Functor, BoundArgsTuple>::IsCancelled(
|
||||||
|
functor, base::get<indices>(bound_args)...);
|
||||||
|
}
|
||||||
|
|
||||||
template <typename BindStateType, typename SFINAE = void>
|
// Relays |base| to corresponding CallbackCancellationTraits<>::Run(). Returns
|
||||||
struct CancellationChecker {
|
// true if the callback |base| represents is canceled.
|
||||||
static constexpr bool is_cancellable = false;
|
template <typename BindStateType>
|
||||||
static bool Run(const BindStateBase*) {
|
bool ApplyCancellationTraits(const BindStateBase* base) {
|
||||||
return false;
|
const BindStateType* storage = static_cast<const BindStateType*>(base);
|
||||||
}
|
static constexpr size_t num_bound_args =
|
||||||
};
|
std::tuple_size<decltype(storage->bound_args_)>::value;
|
||||||
|
return ApplyCancellationTraitsImpl(storage->functor_, storage->bound_args_,
|
||||||
template <typename Functor, typename... BoundArgs>
|
MakeIndexSequence<num_bound_args>());
|
||||||
struct CancellationChecker<
|
|
||||||
BindState<Functor, BoundArgs...>,
|
|
||||||
typename std::enable_if<IsWeakMethod<FunctorTraits<Functor>::is_method,
|
|
||||||
BoundArgs...>::value>::type> {
|
|
||||||
static constexpr bool is_cancellable = true;
|
|
||||||
static bool Run(const BindStateBase* base) {
|
|
||||||
using BindStateType = BindState<Functor, BoundArgs...>;
|
|
||||||
const BindStateType* bind_state = static_cast<const BindStateType*>(base);
|
|
||||||
return !base::get<0>(bind_state->bound_args_);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Signature,
|
|
||||||
typename... BoundArgs,
|
|
||||||
CopyMode copy_mode,
|
|
||||||
RepeatMode repeat_mode>
|
|
||||||
struct CancellationChecker<
|
|
||||||
BindState<Callback<Signature, copy_mode, repeat_mode>, BoundArgs...>> {
|
|
||||||
static constexpr bool is_cancellable = true;
|
|
||||||
static bool Run(const BindStateBase* base) {
|
|
||||||
using Functor = Callback<Signature, copy_mode, repeat_mode>;
|
|
||||||
using BindStateType = BindState<Functor, BoundArgs...>;
|
|
||||||
const BindStateType* bind_state = static_cast<const BindStateType*>(base);
|
|
||||||
return bind_state->functor_.IsCancelled();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Template helpers to detect using Bind() on a base::Callback without any
|
// Template helpers to detect using Bind() on a base::Callback without any
|
||||||
@@ -449,14 +430,17 @@ struct BindingCallbackWithNoArgs<Callback<Signature, copy_mode, repeat_mode>,
|
|||||||
template <typename Functor, typename... BoundArgs>
|
template <typename Functor, typename... BoundArgs>
|
||||||
struct BindState final : BindStateBase {
|
struct BindState final : BindStateBase {
|
||||||
using IsCancellable = std::integral_constant<
|
using IsCancellable = std::integral_constant<
|
||||||
bool, CancellationChecker<BindState>::is_cancellable>;
|
bool,
|
||||||
|
CallbackCancellationTraits<Functor,
|
||||||
|
std::tuple<BoundArgs...>>::is_cancellable>;
|
||||||
|
|
||||||
template <typename ForwardFunctor, typename... ForwardBoundArgs>
|
template <typename ForwardFunctor, typename... ForwardBoundArgs>
|
||||||
explicit BindState(BindStateBase::InvokeFuncStorage invoke_func,
|
explicit BindState(BindStateBase::InvokeFuncStorage invoke_func,
|
||||||
ForwardFunctor&& functor,
|
ForwardFunctor&& functor,
|
||||||
ForwardBoundArgs&&... bound_args)
|
ForwardBoundArgs&&... bound_args)
|
||||||
// IsCancellable is std::false_type if the CancellationChecker<>::Run
|
// IsCancellable is std::false_type if
|
||||||
// returns always false. Otherwise, it's std::true_type.
|
// CallbackCancellationTraits<>::IsCancelled returns always false.
|
||||||
|
// Otherwise, it's std::true_type.
|
||||||
: BindState(IsCancellable{},
|
: BindState(IsCancellable{},
|
||||||
invoke_func,
|
invoke_func,
|
||||||
std::forward<ForwardFunctor>(functor),
|
std::forward<ForwardFunctor>(functor),
|
||||||
@@ -476,8 +460,9 @@ struct BindState final : BindStateBase {
|
|||||||
BindStateBase::InvokeFuncStorage invoke_func,
|
BindStateBase::InvokeFuncStorage invoke_func,
|
||||||
ForwardFunctor&& functor,
|
ForwardFunctor&& functor,
|
||||||
ForwardBoundArgs&&... bound_args)
|
ForwardBoundArgs&&... bound_args)
|
||||||
: BindStateBase(invoke_func, &Destroy,
|
: BindStateBase(invoke_func,
|
||||||
&CancellationChecker<BindState>::Run),
|
&Destroy,
|
||||||
|
&ApplyCancellationTraits<BindState>),
|
||||||
functor_(std::forward<ForwardFunctor>(functor)),
|
functor_(std::forward<ForwardFunctor>(functor)),
|
||||||
bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
|
bound_args_(std::forward<ForwardBoundArgs>(bound_args)...) {
|
||||||
DCHECK(!IsNull(functor_));
|
DCHECK(!IsNull(functor_));
|
||||||
|
1
third_party/WebKit/Source/platform/DEPS
vendored
1
third_party/WebKit/Source/platform/DEPS
vendored
@@ -2,6 +2,7 @@ include_rules = [
|
|||||||
# To whitelist base/ stuff Blink is allowed to include, we list up all
|
# To whitelist base/ stuff Blink is allowed to include, we list up all
|
||||||
# directories and files instead of writing 'base/'.
|
# directories and files instead of writing 'base/'.
|
||||||
"+base/bind.h",
|
"+base/bind.h",
|
||||||
|
"+base/bind_helpers.h",
|
||||||
"+base/callback.h",
|
"+base/callback.h",
|
||||||
"+base/callback_forward.h",
|
"+base/callback_forward.h",
|
||||||
"+base/files",
|
"+base/files",
|
||||||
|
@@ -4,8 +4,29 @@
|
|||||||
|
|
||||||
#include "platform/WebTaskRunner.h"
|
#include "platform/WebTaskRunner.h"
|
||||||
|
|
||||||
|
#include "base/bind_helpers.h"
|
||||||
#include "base/single_thread_task_runner.h"
|
#include "base/single_thread_task_runner.h"
|
||||||
|
|
||||||
|
namespace base {
|
||||||
|
|
||||||
|
using RunnerMethodType =
|
||||||
|
void (blink::TaskHandle::Runner::*)(const blink::TaskHandle&);
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct CallbackCancellationTraits<
|
||||||
|
RunnerMethodType,
|
||||||
|
std::tuple<WTF::WeakPtr<blink::TaskHandle::Runner>, blink::TaskHandle>> {
|
||||||
|
static constexpr bool is_cancellable = true;
|
||||||
|
|
||||||
|
static bool IsCancelled(RunnerMethodType,
|
||||||
|
const WTF::WeakPtr<blink::TaskHandle::Runner>&,
|
||||||
|
const blink::TaskHandle& handle) {
|
||||||
|
return !handle.isActive();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace base
|
||||||
|
|
||||||
namespace blink {
|
namespace blink {
|
||||||
|
|
||||||
class TaskHandle::Runner : public WTF::ThreadSafeRefCounted<Runner> {
|
class TaskHandle::Runner : public WTF::ThreadSafeRefCounted<Runner> {
|
||||||
@@ -15,7 +36,7 @@ class TaskHandle::Runner : public WTF::ThreadSafeRefCounted<Runner> {
|
|||||||
|
|
||||||
WTF::WeakPtr<Runner> asWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
|
WTF::WeakPtr<Runner> asWeakPtr() { return m_weakPtrFactory.createWeakPtr(); }
|
||||||
|
|
||||||
bool isActive() { return static_cast<bool>(m_task); }
|
bool isActive() const { return m_task && !m_task->isCancelled(); }
|
||||||
|
|
||||||
void cancel() {
|
void cancel() {
|
||||||
std::unique_ptr<WTF::Closure> task = std::move(m_task);
|
std::unique_ptr<WTF::Closure> task = std::move(m_task);
|
||||||
|
@@ -46,8 +46,9 @@ class BLINK_PLATFORM_EXPORT TaskHandle {
|
|||||||
TaskHandle(TaskHandle&&);
|
TaskHandle(TaskHandle&&);
|
||||||
TaskHandle& operator=(TaskHandle&&);
|
TaskHandle& operator=(TaskHandle&&);
|
||||||
|
|
||||||
private:
|
|
||||||
class Runner;
|
class Runner;
|
||||||
|
|
||||||
|
private:
|
||||||
friend class WebTaskRunner;
|
friend class WebTaskRunner;
|
||||||
|
|
||||||
explicit TaskHandle(RefPtr<Runner>);
|
explicit TaskHandle(RefPtr<Runner>);
|
||||||
|
@@ -18,6 +18,23 @@ void getIsActive(bool* isActive, TaskHandle* handle) {
|
|||||||
*isActive = handle->isActive();
|
*isActive = handle->isActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CancellationTestHelper {
|
||||||
|
public:
|
||||||
|
CancellationTestHelper() : m_weakPtrFactory(this) {}
|
||||||
|
|
||||||
|
WeakPtr<CancellationTestHelper> createWeakPtr() {
|
||||||
|
return m_weakPtrFactory.createWeakPtr();
|
||||||
|
}
|
||||||
|
|
||||||
|
void revokeWeakPtrs() { m_weakPtrFactory.revokeAll(); }
|
||||||
|
void incrementCounter() { ++m_counter; }
|
||||||
|
int counter() const { return m_counter; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_counter = 0;
|
||||||
|
WeakPtrFactory<CancellationTestHelper> m_weakPtrFactory;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
TEST(WebTaskRunnerTest, PostCancellableTaskTest) {
|
TEST(WebTaskRunnerTest, PostCancellableTaskTest) {
|
||||||
@@ -99,4 +116,38 @@ TEST(WebTaskRunnerTest, PostCancellableTaskTest) {
|
|||||||
EXPECT_FALSE(handle.isActive());
|
EXPECT_FALSE(handle.isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(WebTaskRunnerTest, CancellationCheckerTest) {
|
||||||
|
scheduler::FakeWebTaskRunner taskRunner;
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
TaskHandle handle = taskRunner.postCancellableTask(
|
||||||
|
BLINK_FROM_HERE, WTF::bind(&increment, WTF::unretained(&count)));
|
||||||
|
EXPECT_EQ(0, count);
|
||||||
|
|
||||||
|
// TaskHandle::isActive should detect the deletion of posted task.
|
||||||
|
auto queue = taskRunner.takePendingTasksForTesting();
|
||||||
|
ASSERT_EQ(1u, queue.size());
|
||||||
|
EXPECT_FALSE(queue[0].IsCancelled());
|
||||||
|
EXPECT_TRUE(handle.isActive());
|
||||||
|
queue.clear();
|
||||||
|
EXPECT_FALSE(handle.isActive());
|
||||||
|
EXPECT_EQ(0, count);
|
||||||
|
|
||||||
|
count = 0;
|
||||||
|
CancellationTestHelper helper;
|
||||||
|
handle = taskRunner.postCancellableTask(
|
||||||
|
BLINK_FROM_HERE, WTF::bind(&CancellationTestHelper::incrementCounter,
|
||||||
|
helper.createWeakPtr()));
|
||||||
|
EXPECT_EQ(0, helper.counter());
|
||||||
|
|
||||||
|
// The cancellation of the posted task should be propagated to TaskHandle.
|
||||||
|
queue = taskRunner.takePendingTasksForTesting();
|
||||||
|
ASSERT_EQ(1u, queue.size());
|
||||||
|
EXPECT_FALSE(queue[0].IsCancelled());
|
||||||
|
EXPECT_TRUE(handle.isActive());
|
||||||
|
helper.revokeWeakPtrs();
|
||||||
|
EXPECT_TRUE(queue[0].IsCancelled());
|
||||||
|
EXPECT_FALSE(handle.isActive());
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace blink
|
} // namespace blink
|
||||||
|
@@ -115,5 +115,9 @@ void FakeWebTaskRunner::runUntilIdle() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::deque<base::Closure> FakeWebTaskRunner::takePendingTasksForTesting() {
|
||||||
|
return std::move(data_->task_queue_);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace scheduler
|
} // namespace scheduler
|
||||||
} // namespace blink
|
} // namespace blink
|
||||||
|
@@ -5,11 +5,13 @@
|
|||||||
#ifndef THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_TEST_FAKE_WEB_TASK_RUNNER_H_
|
#ifndef THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_TEST_FAKE_WEB_TASK_RUNNER_H_
|
||||||
#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_TEST_FAKE_WEB_TASK_RUNNER_H_
|
#define THIRD_PARTY_WEBKIT_SOURCE_PLATFORM_SCHEDULER_TEST_FAKE_WEB_TASK_RUNNER_H_
|
||||||
|
|
||||||
|
#include <deque>
|
||||||
|
|
||||||
#include "base/macros.h"
|
#include "base/macros.h"
|
||||||
|
#include "base/memory/ref_counted.h"
|
||||||
#include "platform/WebTaskRunner.h"
|
#include "platform/WebTaskRunner.h"
|
||||||
#include "wtf/PassRefPtr.h"
|
#include "wtf/PassRefPtr.h"
|
||||||
#include "wtf/RefPtr.h"
|
#include "wtf/RefPtr.h"
|
||||||
#include "base/memory/ref_counted.h"
|
|
||||||
|
|
||||||
namespace blink {
|
namespace blink {
|
||||||
namespace scheduler {
|
namespace scheduler {
|
||||||
@@ -35,6 +37,7 @@ class FakeWebTaskRunner : public WebTaskRunner {
|
|||||||
SingleThreadTaskRunner* toSingleThreadTaskRunner() override;
|
SingleThreadTaskRunner* toSingleThreadTaskRunner() override;
|
||||||
|
|
||||||
void runUntilIdle();
|
void runUntilIdle();
|
||||||
|
std::deque<base::Closure> takePendingTasksForTesting();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Data;
|
class Data;
|
||||||
|
Reference in New Issue
Block a user