0

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:
tzik
2016-11-25 07:56:36 -08:00
committed by Commit bot
parent bf7e60b426
commit 6c92eabcfe
8 changed files with 156 additions and 45 deletions

@@ -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_));

@@ -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;