Step into Chromium (101) - 浅谈 Bind 的设计和实现
一点碎碎念
其实很早以前就好奇这种东西了,最早接触到的是 C++98 的 std::bind_1st 和 std::bind_2nd,后来是 C++11 的 std::bind,现在看看 Chromium 里面也有一套 Bind ~
什么是 Bind
Bind 在本文中是指 base::Bind{Once,Repeating}。对于 base::BindOnce,返回值是一个 base::OnceCallback,字面意思就是只能执行一次的回调函数;而 base::BindRepeating 则返回一个 base::RepeatingCallback,也就是可以执行多次的回调。Bind 的作用就是将可调用的对象(包括 lambda 表达式、普通函数、成员函数等)和一些预定义的参数值“绑定”在一起,成为一个新的可调用对象,常用于回调函数需要上下文的情况。
让我们先回顾一下,在 C 的时期,我们的一个回调函数如果需要上下文,通常是怎么实现的呢?
#include <stdio.h>
typedef void (*CallbackType)(int foo, void* context);
CallbackType g_callback = NULL;
void* g_context = NULL;
void SetCallback(CallbackType callback, void* context){
g_callback = callback;
g_context = context;
}
void DoSomething(int number){
if(g_callback != NULL){
g_callback(number, g_context);
}
}
void MyCallback(int foo, void* context){
printf("%i, %s\n", foo, (char*)context);
}
int main(){
const char* myContext = "Hello, World!";
SetCallback(MyCallback, (void*)myContext);
DoSomething(42);
return 0;
}这里我们就用一个全局变量来保存了上下文,并在 DoSomething() 中进行了提取和调用。SetCallback() 就相当于 Bind,而 Bind 的使命就是提供一个安全的回调封装器。
在 Chromium 中,我们的 Bind 是这样定义的:
// Bind as OnceCallback.
template <typename Functor, typename... Args>
inline auto BindOnce(Functor&& functor, Args&&... args) {
return internal::BindHelper<OnceCallback>::Bind(
std::forward<Functor>(functor), std::forward<Args>(args)...);
}
// Bind as RepeatingCallback.
template <typename Functor, typename... Args>
inline auto BindRepeating(Functor&& functor, Args&&... args) {
return internal::BindHelper<RepeatingCallback>::Bind(
std::forward<Functor>(functor), std::forward<Args>(args)...);
}这是两个模板 Wrapper,只是完美转发参数到 internal::BindHelper<T>::Bind 里面。
Bind 的流程
概述
让我们来看 BindHelper。这是一个总入口,也是模板类,
template <template <typename> class CallbackT>
struct BindHelper { ... };为了简单,我们先不考虑它的模板参数 CallbackT,但是下面用到它的地方,我们要知道它要么是 OnceCallback,要么是 RepeatingCallback。它的核心方法是 Bind(),定义如下:
template <typename Functor, typename... Args>
static auto Bind(Functor&& functor, Args&&... args) {
if constexpr (std::conjunction_v<
OnceCallbackFunctorIsValid<Functor>,
NoBindArgToOnceCallbackIsBasePassed<Args...>,
BindImplWouldSucceed<Functor, Args...>>) {
return BindImpl(std::forward<Functor>(functor),
std::forward<Args>(args)...);
} else {
return BindFailedCheckPreviousErrors();
}
}可以看到这里先用了几个编译期模板检查,以便在绑定不兼容的时候直接报编译错误。
提示
std::conjunction_v 是一个模板元,用于对其中的类型参数取 && 运算并返回 static constexpr bool 类型值。
核心是 BindImpl(),我们深入:
template <typename Functor, typename... Args>
static auto BindImpl(Functor&& functor, Args&&... args) {
// There are a lot of variables and type aliases here. An example will be
// illustrative. Assume we call:
// ```
// struct S {
// double f(int, const std::string&);
// } s;
// int16_t i;
// BindOnce(&S::f, Unretained(&s), i);
// ```
// This means our template params are:
// ```
// template <typename> class CallbackT = OnceCallback
// typename Functor = double (S::*)(int, const std::string&)
// typename... Args =
// UnretainedWrapper<S, unretained_traits::MayNotDangle>, int16_t
// ```
// And the implementation below is effectively:
// ```
// using Traits = struct {
// using RunType = double(S*, int, const std::string&);
// static constexpr bool is_method = true;
// static constexpr bool is_nullable = true;
// static constexpr bool is_callback = false;
// static constexpr bool is_stateless = true;
// ...
// };
// using ValidatedUnwrappedTypes = struct {
// using Type = TypeList<S*, int16_t>;
// static constexpr bool value = true;
// };
// using BoundArgsList = TypeList<S*, int16_t>;
// using RunParamsList = TypeList<S*, int, const std::string&>;
// using BoundParamsList = TypeList<S*, int>;
// using ValidatedBindState = struct {
// using Type =
// BindState<double (S::*)(int, const std::string&),
// UnretainedWrapper<S, unretained_traits::MayNotDangle>,
// int16_t>;
// static constexpr bool value = true;
// };
// if constexpr (true) {
// using UnboundRunType = double(const std::string&);
// using CallbackType = OnceCallback<double(const std::string&)>;
// ...
// ```
using Traits = FunctorTraits<TransformToUnwrappedType<kIsOnce, Functor&&>,
TransformToUnwrappedType<kIsOnce, Args&&>...>;
if constexpr (TraitsAreInstantiable<Traits>::value) {
using ValidatedUnwrappedTypes =
ValidateUnwrappedTypeList<kIsOnce, Traits::is_method, Args&&...>;
using BoundArgsList = TypeList<Args...>;
using RunParamsList = ExtractArgs<typename Traits::RunType>;
using BoundParamsList = TakeTypeListItem<sizeof...(Args), RunParamsList>;
using ValidatedBindState =
ValidateBindStateType<Traits::is_method, Traits::is_nullable,
Traits::is_callback, Functor, Args...>;
// This conditional checks if each of the `args` matches to the
// corresponding param of the target function. This check does not affect
// the behavior of `Bind()`, but its error message should be more
// readable.
if constexpr (std::conjunction_v<
NotFunctionRef<Functor>, IsStateless<Traits>,
ValidatedUnwrappedTypes,
ParamsCanBeBound<
Traits::is_method,
std::make_index_sequence<sizeof...(Args)>,
BoundArgsList,
typename ValidatedUnwrappedTypes::Type,
BoundParamsList>,
ValidatedBindState>) {
using UnboundRunType =
MakeFunctionType<ExtractReturnType<typename Traits::RunType>,
DropTypeListItem<sizeof...(Args), RunParamsList>>;
using CallbackType = CallbackT<UnboundRunType>;
// Store the invoke func into `PolymorphicInvoke` before casting it to
// `InvokeFuncStorage`, so that we can ensure its type matches to
// `PolymorphicInvoke`, to which `CallbackType` will cast back.
typename CallbackType::PolymorphicInvoke invoke_func;
using Invoker =
Invoker<Traits, typename ValidatedBindState::Type, UnboundRunType>;
if constexpr (kIsOnce) {
invoke_func = Invoker::RunOnce;
} else {
invoke_func = Invoker::Run;
}
return CallbackType(ValidatedBindState::Type::Create(
reinterpret_cast<BindStateBase::InvokeFuncStorage>(invoke_func),
std::forward<Functor>(functor), std::forward<Args>(args)...));
}
}
}
// Special cases for binding to a `Callback` without extra bound arguments.
// `OnceCallback` passed to `OnceCallback`, or `RepeatingCallback` passed to
// `RepeatingCallback`.
template <typename T>
requires is_instantiation<T, CallbackT>
static T BindImpl(T callback) {
// Guard against null pointers accidentally ending up in posted tasks,
// causing hard-to-debug crashes.
CHECK(callback);
return callback;
}这里一共有两个定义,后面这个是用于从 CallbackT 原地转换的,满足向已有的 Callback 进行空参绑定的情况。
重点是上面的一种情况,阅读代码容易知道整个 Bind 的流程是:
- 萃取函数特性
- 参数类型解封装(Unwrap)
- 提取待绑定的参数列表(BoundArgsList)
- 提取目标函数(Functor)的参数列表(RunParamsList)
- 提取目标函数的被绑定参数列表(BoundParams)
- 组装 BindState 类型
- 获取剩余参数类型,并组装成最终可调用对象原型(UnboundRunType)
- 确定运行器(Invoker)
- 创建可调用对象
其中,2 ~ 5 是比较枯燥的偏特化萃取,分析起来未免乏味,不作展开。
函数特性的萃取
这个步骤在 BindImpl() 中以这样的形式完成:
using Traits = FunctorTraits<TransformToUnwrappedType<kIsOnce, Functor&&>,
TransformToUnwrappedType<kIsOnce, Args&&>...>;主要还是关注外层的萃取 FunctorTraits<>,以下面形式定义:
// `FunctorTraits<>`
//
// See description at top of file. This must be declared here so it can be
// referenced in `DecayedFunctorTraits`.
template <typename Functor, typename... BoundArgs>
struct FunctorTraits;这里提示我们看最顶头的注释,翻译一下:
相关信息
Concepts:
Functor一个可调用的类型,可以被移动。所有函数指针和Callback<>都可纳入其中,即便调用的语法不一样。RunType一个用于Callback<>::Run()的函数类型(和函数指针相反)。通常是为了便利而定义的。(Bound)Args一个存储参数的类型集合。
Types:
ForceVoidReturn<>一个用于强制转换函数签名到void返回值的帮助类。FunctorTraits<>用于确定正确的 RunType 和调用行为的类型特性。函数签名适配器被用于其上。StorageTraits<>决定一个绑定参数如何存储在BindState<>的类型特性。InvokeHelper<>接受一个 Functor 和参数并实际调用它。也处理对于WeakPtr<>的特别语义支持。
本质上是从Invoker<>分离而来,以避免创建多个版本的Invoker<>。Invoker<>解封装柯里化的参数并执行 Functor。BindState<>存储柯里化的参数,是进入Bind()系统的主入口。
我们转到它的两个特化实现:
// For most functors, the traits should not depend on how the functor is passed,
// so decay the functor.
template <typename Functor, typename... BoundArgs>
// This requirement avoids "implicit instantiation of undefined template" errors
// when the underlying `DecayedFunctorTraits<>` cannot be instantiated. Instead,
// this template will also not be instantiated, and the caller can detect and
// handle that.
requires IsComplete<DecayedFunctorTraits<std::decay_t<Functor>, BoundArgs...>>
struct FunctorTraits<Functor, BoundArgs...>
: DecayedFunctorTraits<std::decay_t<Functor>, BoundArgs...> {};
// For `overloaded operator()()`s, it's possible the ref qualifiers of the
// functor matter, so be careful to use the undecayed type.
template <typename Functor, typename... BoundArgs>
requires HasOverloadedCallOp<Functor, BoundArgs...>
struct FunctorTraits<Functor, BoundArgs...> {
// For an overloaded operator()(), it is not possible to resolve the
// actual declared type. Since it is invocable with the bound args, make up a
// signature based on their types.
using RunType = decltype(std::declval<Functor>()(
std::declval<BoundArgs>()...))(std::decay_t<BoundArgs>...);
static constexpr bool is_method = false;
static constexpr bool is_nullable = false;
static constexpr bool is_callback = false;
static constexpr bool is_stateless = std::is_empty_v<std::decay_t<Functor>>;
template <typename RunFunctor, typename... RunArgs>
static ExtractReturnType<RunType> Invoke(RunFunctor&& functor,
RunArgs&&... args) {
return std::forward<RunFunctor>(functor)(std::forward<RunArgs>(args)...);
}
};这里要先搞明白 std::decay_t 是什么玩意。
std::decay_t from CppReference
Performs the type conversions equivalent to the ones performed when passing function arguments by value. Formally:
If T is “array of U” or reference to it, the member typedef type is U*.
Otherwise, if T is a function type F or reference to one, the member typedef type is std::add_pointer<F>::type.
Otherwise, the member typedef type is std::remove_cv<std::remove_reference<T>::type>::type.
If the program adds specializations for std::decay, the behavior is undefined.
其实说白了就是把一个类型还原到最基本的底层类型上,这种底层类型被用于参数传递,当然也可以用来确定存储类型。
所以上面的两个特化,一个是针对可以直接得到原始函数类型的,那么就使用 DecayedFunctorTraits<> 来提供,另一个就是对于重载 operator() 的可调用对象(比如存在左值引用、右值引用的版本),没法拿到实际的定义类型,只能委屈求全,要求在这种情况下必须完全绑定。
总结一下,应该可以把类型萃取的结果以下面的表格展示:
| Functor 类型 | 特化因子 | RunType | is_method | is_nullable | is_callback | is_stateless |
|---|---|---|---|---|---|---|
| 可调用对象(operator() 已重载) | requires HasOverloadedCallOp<Functor, BoundArgs...> | decltype(std::declval<Functor>()(std::declval<BoundArgs>()...))(std::decay_t<BoundArgs>...) | F | F | F | std::is_empty_v<Functor> |
| 可调用对象(operator() 未重载) | requires HasNonOverloadedCallOp<Functor> | ExtractCallableRunType<Functor> | F | F | F | std::is_empty_v<Functor> |
| 普通函数 (含 noexcept 版本) | R (*)(Args...) | R(Args...) | F | T | F | T |
| 类成员函数(含 noexcept 版本) | R (Receiver::*)(Args...) | R(Receiver*, Args...) | T | T | F | T |
| 类成员函数 const (含 noexcept 版本) | R (Receiver::*)(Args...) const | R(const Receiver*, Args...) | T | T | F | T |
| IgnoreResult | IgnoreResultHelper<T> | typename ForceVoidReturn<typename FunctorTraits<T, BoundArgs...>::RunType>::RunType | - | - | - | - |
| OnceCallback | OnceCallback<R(Args...)> | R(Args...) | F | T | T | T |
| RepeatingCallback | RepeatingCallback<R(Args...)> | R(Args...) | F | T | T | T |
注:- 指继承自FunctorTraits<T, BoundArgs...>。
注
值得注意的是,对于可调用对象,重载 operator() 与否走了两条截然不同的路径,其特化因子定义如下:
// True when `Functor` has a non-overloaded `operator()()`, e.g.:
// struct S1 {
// int operator()(int);
// };
// static_assert(HasNonOverloadedCallOp<S1>);
//
// int i = 0;
// auto f = [i] {};
// static_assert(HasNonOverloadedCallOp<decltype(f)>);
//
// struct S2 {
// int operator()(int);
// std::string operator()(std::string);
// };
// static_assert(!HasNonOverloadedCallOp<S2>);
//
// static_assert(!HasNonOverloadedCallOp<void(*)()>);
//
// struct S3 {};
// static_assert(!HasNonOverloadedCallOp<S3>);
// ```
template <typename Functor>
concept HasNonOverloadedCallOp = requires { &Functor::operator(); };也就是能直接取到 operator() 地址的情况下就可以认为没有重载。而对于重载的判断,必须满足:
- 可用绑定的参数直接调用
- 被
std::decay_t后的 Functor 也必须有重载 - 不是函数指针
- 不是 ObjC 的块指针 (Apple only)
// True when `Functor` has an overloaded `operator()()` that can be invoked with
// the provided `BoundArgs`.
//
// Do not decay `Functor` before testing this, lest it give an incorrect result
// for overloads with different ref-qualifiers.
template <typename Functor, typename... BoundArgs>
concept HasOverloadedCallOp = requires {
// The functor must be invocable with the bound args.
requires requires(Functor&& f, BoundArgs&&... args) {
std::forward<Functor>(f)(std::forward<BoundArgs>(args)...);
};
// Now exclude invocables that are not cases of overloaded `operator()()`s:
// * `operator()()` exists, but isn't overloaded
requires !HasNonOverloadedCallOp<std::decay_t<Functor>>;
// * Function pointer (doesn't have `operator()()`)
requires !std::is_pointer_v<std::decay_t<Functor>>;
// * Block pointer (doesn't have `operator()()`)
requires !IsObjCArcBlockPointer<std::decay_t<Functor>>;
};而对于普通函数重载的情况,并不需要这样的判定,因为在调用 Bind 的时候,如果不显式指定 Functor 的版本,就会原地报错说明有歧义:
void foo(int){}
void foo(int, int){}
void bar(){
base::BindOnce(foo,1); // Error, Reference to overloaded function could not be resolved
base::BindOnce<void (int,int)>(foo,1).Run(2); // OK!
}组装 BindState 类型
这一步在 BindImpl() 里面对应:
using ValidatedBindState =
ValidateBindStateType<Traits::is_method, Traits::is_nullable,
Traits::is_callback, Functor, Args...>;跟进 ValidateBindStateType<>,这也是一个准备特化的东西:
// Used to determine and validate the appropriate `BindState`. The
// specializations below cover all cases. The members are similar in intent to
// those in `StorageTraits`; see comments there.
template <bool is_method,
bool is_nullable,
bool is_callback,
typename Functor,
typename... BoundArgs>
struct ValidateBindStateType;BindState 的预处理
这里也比较复杂,我们拆开看一下:
首先,对于非类成员函数:
using Type = BindState<false,
is_nullable,
is_callback,
std::decay_t<Functor>,
typename ValidateStorageTraits<BoundArgs>::Type...>;注意高亮行,传入的是 std::decay_t<Functor>。
然后,对于 static 无参型类成员函数,很简单的情况呢:
using Type = BindState<true, is_nullable, is_callback, std::decay_t<Functor>>;剩下就是普通成员函数了,特化如下:
template <bool is_nullable,
bool is_callback,
typename Functor,
typename Receiver,
typename... BoundArgs>
struct ValidateBindStateType<true,
is_nullable,
is_callback,
Functor,
Receiver,
BoundArgs...> {
private:
using DecayedReceiver = std::decay_t<Receiver>;
using ReceiverStorageType =
typename MethodReceiverStorage<DecayedReceiver>::Type;
// ...
public:
using Type = BindState<true,
is_nullable,
is_callback,
std::decay_t<Functor>,
ReceiverStorageType,
typename ValidateStorageTraits<BoundArgs>::Type...>;
static constexpr bool value =
std::conjunction_v<FirstBoundArgIsNotArray<>,
ReceiverIsNotRawRef<>,
ReceiverIsNotRawPtr<>,
typename ValidateBindStateTypeCommonChecks<
BoundArgs...>::CommonCheckResult>;
};注意上面的高亮行!这里从 BoundArgs 里面解出来第一个参数叫做 Recevier,正是一般类成员函数的第一个隐藏参数 this。非常重要!在这里,我们先知道它对于某些类型的 Receiver,在运行时会有特殊的处理。
Chromium 对于声明周期的管理有强烈的要求,为此专门弄了一套类型包装,在这里给到 Recevier 的限制条件是:
- 不是数组
- 不是
raw_ref<T> - 不是原始指针或者
raw_ptr<T>,或者解引用一次后是引用计数类型
注
容易知道,这里的设计初衷就是,要么调用者明确知道不会悬挂并且使用 base::Unretained() 封装一次,或者其内部类型已经被引用计数,不产生悬挂指针/引用。
在 BindState 这个阶段,我们要确定的是 Receiver 怎么存储,这里是用了 ReceiverStorageType<>,也是模板类,可以概括如下:
- 对于
base::Unretained()封装过的类型,使用UnretainedRefWrapperReceiver<> - 如果是指针类型(虽然前面强制不允许,但是还有别的链路允许),改为存储
scoped_refptr<RemovePointerT<T>> - 对于其他类型,总是存储其原始类型
BindState 的结构
以下摘录一点 BindState 的定义:
// `BindState<>`
//
// This stores all the state passed into `Bind()`.
template <bool is_method,
bool is_nullable,
bool is_callback,
typename Functor,
typename... BoundArgs>
struct BindState final : BindStateBase {
private:
using BoundArgsTuple = std::tuple<BoundArgs...>;
public:
template <typename ForwardFunctor, typename... ForwardBoundArgs>
static BindState* Create(BindStateBase::InvokeFuncStorage invoke_func,
ForwardFunctor&& functor,
ForwardBoundArgs&&... bound_args) {
if constexpr (is_method) {
VerifyMethodReceiver(bound_args...);
}
return new BindState(invoke_func, std::forward<ForwardFunctor>(functor),
std::forward<ForwardBoundArgs>(bound_args)...);
}
Functor functor_;
BoundArgsTuple bound_args_;
// ...
};可以知道,这东西从 BindStateBase 接口继承,以便提供一致的调用语义。
而绑定参数的底层存储就是 std::tuple<>。让我们回到 BoundArgs 的构造上来,来源于 typename ValidateStorageTraits<BoundArgs>::Type... (看一下前面的链子,也就是对于每个绑定参数取 ValidateStorageTraits<>):
template <typename T>
using ValidateStorageTraits = StorageTraits<std::decay_t<T>>;这里又指向了 StorageTraits<>,也是一个类模板,总结一下行为:
- 对于
T*或者raw_ptr<T>类型,为了安全存储的是UnretainedWrapper<T>,内部在可能的时候使用raw_ptr<T>来帮助发现悬挂问题 - 对于
std::reference_wrapper,存储的是UnretainedRefWrapper<T>,内部使用raw_ref<T> - 其他情况,按原类型值储存
确定 Invoker
我们先来看下 Invoker 是如何定义的:
template <typename Traits, typename StorageType, typename UnboundRunType>
struct Invoker;这也是一个类模板,随后通过唯一的特化来分离 UnboundRunType:
template <typename Traits,
typename StorageType,
typename R,
typename... UnboundArgs>
struct Invoker<Traits, StorageType, R(UnboundArgs...)> {
private:
using Indices = std::make_index_sequence<
std::tuple_size_v<decltype(StorageType::bound_args_)>>;
public:
static R RunOnce(BindStateBase* base,
PassingType<UnboundArgs>... unbound_args) {
auto* const storage = static_cast<StorageType*>(base);
return RunImpl(std::move(storage->functor_),
std::move(storage->bound_args_), Indices(),
std::forward<UnboundArgs>(unbound_args)...);
}
static R Run(BindStateBase* base, PassingType<UnboundArgs>... unbound_args) {
auto* const storage = static_cast<const StorageType*>(base);
return RunImpl(storage->functor_, storage->bound_args_, Indices(),
std::forward<UnboundArgs>(unbound_args)...);
}
//...
};不难看出运行时的逻辑就是代理到 RunImpl(),如果是只运行一次,那么就把 Storage 里面的东西都 move 掉。
接下来我们聚焦 RunImpl():
template <typename Functor, typename BoundArgsTuple, size_t... indices>
static inline R RunImpl(Functor&& functor,
BoundArgsTuple&& bound,
std::index_sequence<indices...>,
UnboundArgs&&... unbound_args) {
#if PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
RawPtrAsanBoundArgTracker raw_ptr_asan_bound_arg_tracker;
raw_ptr_asan_bound_arg_tracker.AddArgs(
std::get<indices>(std::forward<BoundArgsTuple>(bound))...,
std::forward<UnboundArgs>(unbound_args)...);
#endif // PA_BUILDFLAG(USE_ASAN_BACKUP_REF_PTR)
using DecayedArgsTuple = std::decay_t<BoundArgsTuple>;
static constexpr bool kIsWeakCall =
kIsWeakMethod<Traits::is_method,
std::tuple_element_t<indices, DecayedArgsTuple>...>;
if constexpr (WeakCallReturnsVoid<kIsWeakCall>::value) {
// Do not `Unwrap()` here, as that immediately triggers dangling pointer
// detection. Dangling pointer detection should only be triggered if the
// callback is not cancelled, but cancellation status is not determined
// until later inside the `InvokeHelper::MakeItSo()` specialization for
// weak calls.
//
// Dangling pointers when invoking a cancelled callback are not considered
// a memory safety error because protecting raw pointers usage with weak
// receivers (where the weak receiver usually own the pointed objects) is
// a common and broadly used pattern in the codebase.
return InvokeHelper<kIsWeakCall, Traits, R, indices...>::MakeItSo(
std::forward<Functor>(functor), std::forward<BoundArgsTuple>(bound),
std::forward<UnboundArgs>(unbound_args)...);
}
}看起来没什么出奇的对吧?注意高亮的部分,实现上是这样的:
// `kIsWeakMethod` is a helper that determines if we are binding a `WeakPtr<>`
// to a method. It is used internally by `Bind()` to select the correct
// `InvokeHelper` that will no-op itself in the event the `WeakPtr<>` for the
// target object is invalidated.
//
// The first argument should be the type of the object that will be received by
// the method.
template <bool is_method, typename... Args>
inline constexpr bool kIsWeakMethod = false;
template <typename T, typename... Args>
inline constexpr bool kIsWeakMethod<true, T, Args...> =
IsWeakReceiver<T>::value;这里的特化表明,如果是一个成员函数,并且首个绑定的参数是一个 WeakReceiver,那么就适用特判策略。那么什么是 WeakReceiver 呢?
// An injection point to control `this` pointer behavior on a method invocation.
// If `IsWeakReceiver<T>::value` is `true` and `T` is used as a method receiver,
// `Bind()` cancels the method invocation if the receiver tests as false.
// ```
// struct S {
// void f() {}
// };
//
// WeakPtr<S> weak_s = nullptr;
// BindOnce(&S::f, weak_s).Run(); // `S::f()` is not called.
// ```
template <typename T>
struct IsWeakReceiver : std::bool_constant<is_instantiation<T, WeakPtr>> {};
template <typename T>
struct IsWeakReceiver<std::reference_wrapper<T>> : IsWeakReceiver<T> {};这里就明确,WeakReceiver 是指一个 WeakPtr 或其引用。针对这样的场景,主要是在 Callback 被调用时检查其有效性,如果无效则取消实际调用。而这个检查和调用是通过 InvokeHelper 实现的:
// `InvokeHelper<>`
//
// There are 2 logical `InvokeHelper<>` specializations: normal, weak.
//
// The normal type just calls the underlying runnable.
//
// Weak calls need special syntax that is applied to the first argument to check
// if they should no-op themselves.
template <bool is_weak_call,
typename Traits,
typename ReturnType,
size_t... indices>
struct InvokeHelper;
template <typename Traits, typename ReturnType, size_t... indices>
struct InvokeHelper<false, Traits, ReturnType, indices...> {
template <typename Functor, typename BoundArgsTuple, typename... RunArgs>
static inline ReturnType MakeItSo(Functor&& functor,
BoundArgsTuple&& bound,
RunArgs&&... args) {
return Traits::Invoke(
Unwrap(std::forward<Functor>(functor)),
Unwrap(std::get<indices>(std::forward<BoundArgsTuple>(bound)))...,
std::forward<RunArgs>(args)...);
}
};
template <typename Traits,
typename ReturnType,
size_t index_target,
size_t... index_tail>
struct InvokeHelper<true, Traits, ReturnType, index_target, index_tail...> {
template <typename Functor, typename BoundArgsTuple, typename... RunArgs>
static inline void MakeItSo(Functor&& functor,
BoundArgsTuple&& bound,
RunArgs&&... args) {
static_assert(index_target == 0);
// Note the validity of the weak pointer should be tested _after_ it is
// unwrapped, otherwise it creates a race for weak pointer implementations
// that allow cross-thread usage and perform `Lock()` in `Unwrap()` traits.
const auto& target = Unwrap(std::get<0>(bound));
if (!target) {
return;
}
Traits::Invoke(
Unwrap(std::forward<Functor>(functor)), target,
Unwrap(std::get<index_tail>(std::forward<BoundArgsTuple>(bound)))...,
std::forward<RunArgs>(args)...);
}
};Callback 的组装和调用
Callback 接收 BindStateBase 指针并使用 BindStateHolder 持有,随后在调用阶段,以 OnceCallback 为例:
R Run(Args... args) && {
CHECK(!is_null());
// Move the callback instance into a local variable before the invocation,
// that ensures the internal state is cleared after the invocation.
// It's not safe to touch |this| after the invocation, since running the
// bound function may destroy |this|.
internal::BindStateHolder holder = std::move(holder_);
PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(holder.polymorphic_invoke());
return f(holder.bind_state().get(), std::forward<Args>(args)...);
}这里的 f 对应的就是 Invoker 的 Run() 或者 RunOnce() (取决于 BindOnce()/BindRepeating())。
至此,整个 Bind 和 Callback 的链路就接上了。
提示
OnceCallback 不接受非右值形式的调用,主要是利用了这个:
// Non-consuming `Run()` is disallowed for `OnceCallback`.
R Run(Args... args) const& {
static_assert(!sizeof(*this),
"OnceCallback::Run() may only be invoked on a non-const "
"rvalue, i.e. std::move(callback).Run().");
NOTREACHED();
}RepeatingCallback 的实现上就没有这个限制,且内部的 holder 会作为成员一直保持存在,不会在调用过程中被 move 掉:
R Run(Args... args) const& {
CHECK(!is_null());
// Keep `bind_state` alive at least until after the invocation to ensure all
// bound `Unretained` arguments remain protected by MiraclePtr.
scoped_refptr<internal::BindStateBase> bind_state = holder_.bind_state();
PolymorphicInvoke f =
reinterpret_cast<PolymorphicInvoke>(holder_.polymorphic_invoke());
return f(bind_state.get(), std::forward<Args>(args)...);
}从注释中也知道,holder 的主要工作就是提供一个围绕 scoped_refptr<BindStateBase> 的封装。
结语
实际看下来,很多代码都是对生命周期管理的 workaround,充分展现出 Chromium 的设计意图。
代码很长,看起来很痛苦,但是捋顺了还是很有意思的。当然本文难免有不周之处,仅供参考,欢迎各位读者指正!