--On Fri, Jun 10, 2022 at 2:51 PM Lei Zhang <the...@chromium.org> wrote:Would the extra work of writing a base wrapper around
absl::FunctionRef be helpful here? Both for developer education, and
maybe to fit in raw_ptr use.I kinda see it as a negative for education, as folks who learn about the absl type elsewhere won't know our type. But maybe for raw_ptr.. wondering what others think of removing raw_ptr from these use cases.
On Fri, Jun 10, 2022 at 11:43 AM <dan...@chromium.org> wrote:
>
> Ok these are all good points. I think what we're missing is some education, so people understand it's all non-owning and can't outlive what it refers to, the same as an int& can't outlive it's int.
>
> Would you be willing to write a ToTT or whatever kinda piece to share on chromium-dev when we allow this?
>
> Should we be concerned that absl won't be using raw_ptr?
>
> On Fri, Jun 10, 2022 at 2:04 PM Daniel Cheng <dch...@chromium.org> wrote:
>>
>> I guess I wonder how hard we want to try here.
>>
>> We can try to block direct fields but I feel like there would be quite a bit of complexity to also block:
>> - absl::optional<absl::FunctionRef>
>> - std::unique_ptr<absl::FunctionRef>
>> - std::vector<absl::FunctionRef>
>> - std::pair<..., absl::FunctionRef>
>> - absl::FunctionRef*
>> - et cetera
>>
>> I do think it's possible to make it work. But in my ideal world, we'd just allow the straightforward thing to be written and make it easy to audit absl::FunctionRef fields that got added (same for other potentially risky constructs where there /should/ be limited usage of the risky form). Maybe we could add a clang-tidy warning to highlight the danger instead of a hard compiler error?
>>
>> Daniel
>>
>> On Fri, 10 Jun 2022 at 08:33, Jeremy Roman <jbr...@chromium.org> wrote:
>>>
>>>
>>>
>>> On Fri, Jun 10, 2022 at 10:13 AM <dan...@chromium.org> wrote:
>>>>
>>>> On Fri, Jun 10, 2022 at 5:38 AM Daniel Cheng <dch...@chromium.org> wrote:
>>>>>
>>>>> Today, Chrome uses base::OnceCallback and base::RepeatingCallback to own a reference to a callable object. Because of the ownership requirements, the base callback implementation enforces certain restrictions to make it harder to write bugs. One of these restrictions is that base::BindOnce / base::BindRepeating do not allow capturing lambdas to reduce the likelihood of lifetime bugs such as:
>>>>>
>>>>> // https://godbolt.org/z/hdb1T1Wvh
>>>>> #include <functional>
>>>>> int main() {
>>>>> std::function<int()> f;
>>>>> {
>>>>> int i = 0;
>>>>> f = [&] { return i; };
>>>>> }
>>>>> return f();
>>>>> }
>>>>>
>>>>> However, there are times when it is both safe and useful to use a capturing lambda. One common example is a function that iterate over a collection and synchronously applies the callable object to each item, e.g. blink::LocalFrameView::ForAllChildViewsAndPlugins (which uses a templated function to allow the use of capturing lambdas) and content::WebContents::ForEachRenderFrameHost (which must use base::RepeatingCallback since it is a virtual method that cannot be templated).
>>>>>
>>>>> The latter is particularly unfortunate; since base::BindRepeating does not allow capturing lambdas, "capturing" parameters can be quite verbose, e.g.
>>>>>
>>>>> // Get the list of RenderFrameHosts from the current page.
>>>>> proxy_->GetWebContents()->GetPrimaryMainFrame()->ForEachRenderFrameHost(
>>>>> base::BindRepeating(
>>>>> [](std::vector<content::GlobalRenderFrameHostId>*
>>>>> render_frame_host_ids,
>>>>> std::vector<mojo::Remote<blink::mojom::TextFragmentReceiver>>*
>>>>> text_fragment_remotes,
>>>>> content::RenderFrameHost* rfh) {
>>>>> render_frame_host_ids->push_back(rfh->GetGlobalId());
>>>>> mojo::Remote<blink::mojom::TextFragmentReceiver> remote;
>>>>> text_fragment_remotes->push_back(std::move(remote));
>>>>> },
>>>>> &render_frame_host_ids_, &text_fragment_remotes_));
>>>>>
>>>>> In contrast, using absl::FunctionRef allows callers to be simplified and reduces the amount of code duplication. The above example would look something like this instead:
>>>>>
>>>>> // Get the list of RenderFrameHosts from the current page.
>>>>> proxy_->GetWebContents()->GetPrimaryMainFrame()->ForEachRenderFrameHost(
>>>>> [this](content::RenderFrameHost* rfh) {
>>>>> render_frame_host_ids_->push_back(rfh->GetGlobalId());
>>>>> mojo::Remote<blink::mojom::TextFragmentReceiver> remote;
>>>>> text_fragment_remotes_->push_back(std::move(remote));
>>>>> });
>>>>>
>>>>> I'd like to propose that we allow the use of absl::FunctionRef and encourage its use in places where a non-owning reference to a callable object is sufficient. It has additional benefits of being cheap to construct / copy / destroy. For example, content::WebContents::ForEachRenderFrameHost() requires a heap allocation just to adapt between its RenderFrameHost* and RenderFrameHostImpl* overloads: with absl::FunctionRef, no heap allocation would be required.
>>>>
>>>>
>>>> Do you think we can ban it appearing as a class member - including in template instantiations?
>>>
>>>
>>> Might be nice, but it's problematic in largely the same way that, say, base::StringPiece is, so I'm not sure we should block on doing so. (Maybe a little worse because it does an indirect call through it so a bad one might be a little more useful as an exploit primitive than being able to access memory.)
>>>
>>> It also wouldn't necessarily be an error to have it as a member in similarly short-lived objects passed as arguments, though it wouldn't be the end of the world if you had to expressly mark cases where that happens, if it happens at all.
>>>
>>>>>
>>>>>
>>>>> Daniel
>>>>>
>>>>> --
>>>>> You received this message because you are subscribed to the Google Groups "cxx" group.
>>>>> To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
>>>>> To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAF3XrKqUZORDJ2yQsZNc%3DYJnFOkfiy9eHSEaVsM8Mtw%3DR9tk%2Bg%40mail.gmail.com.
>>>>
>>>> --
>>>> You received this message because you are subscribed to the Google Groups "cxx" group.
>>>> To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
>>>> To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAHtyhaSBOksE6Zt7o8J8kc8UCE%2BduYVMyMGMMnon8oKK5Yk5eQ%40mail.gmail.com.
>
> --
> You received this message because you are subscribed to the Google Groups "cxx" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
> To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAHtyhaQ5hunvmEgxKYe5KoE2g2fxa-NO4pLZg2xdUYCbmgxQsw%40mail.gmail.com.
You received this message because you are subscribed to the Google Groups "cxx" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cxx+uns...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAHtyhaQ5aV6JgUp%2BrSo71rWzciKg9vCM878TpT_ip-1VMJEn4g%40mail.gmail.com.
You received this message because you are subscribed to the Google Groups "memory-safety-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email to memory-safety-...@chromium.org.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/memory-safety-dev/CAA_NCUEEJrPLB%3DThLNg9H1RKUYwaSXDDZHSWQV3vj0wYqAVWRg%40mail.gmail.com.
For more options, visit https://groups.google.com/a/chromium.org/d/optout.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CABg10jyy0wU0wsq-abhEW86Tf5Z0JuA17P2L_GEoECkmx5xENQ%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAJTZ7LLx%2BkL5-pJhcP7h4ZxSnKo%3Dw-UoFiT00oEnjmWbWNFyKw%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CACuR13dqS-Bk_kHvzQCGcB0Ufp-HMak-fZmULKFyAoMi1hQFKQ%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CADsXd2OmzbOWUacX1HzrdhamtzgEru%3DBm6320a9xt84wOjen8Q%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CAKqK8MkQUw6xg%2BV%2BCVCYa5NcMezoFoAuN_sRZOz5crf_vQY9dw%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/memory-safety-dev/CAHtyhaTeq0KyChSVoS8MSQA%3DRzmJHzPnZZTbax6xNJOYVsYizw%40mail.gmail.com.
I'm working on writing this up, but I'm not entirely sure how to resolve the OnceCallback / RepeatingCallback -> FunctionRef implicit conversion.FunctionRef doesn't have any notion of how many times it can run; does that mean we should only allow an implicit conversion from RepeatingCallback?
If we don't do this, should we add something like RepeatingCallbackRef/OnceCallbackRef?
If we had operator() rather than Run it would just work for RepeatingCallback. Otherwise, it's of course possible to adapt, like:[&callback](auto&& args...) { std::move(callback).Run(std::forward<decltype(args)>(args)...); }and the same without std::move for RepeatingCallback. (Or a version without template voodoo if you know the concrete types involved. We could provide a helper function or conversion operator if we wished. In any case I don't think this confines callers from using callbacks.On Thu, Jun 16, 2022 at 5:25 PM K. Moon <km...@chromium.org> wrote:If we don't do this, should we add something like RepeatingCallbackRef/OnceCallbackRef?How would this be different from a normal reference to RepeatingCallback/OnceCallback?
On Fri, Jun 17, 2022 at 10:48 AM Jeremy Roman <jbr...@chromium.org> wrote:If we had operator() rather than Run it would just work for RepeatingCallback. Otherwise, it's of course possible to adapt, like:[&callback](auto&& args...) { std::move(callback).Run(std::forward<decltype(args)>(args)...); }and the same without std::move for RepeatingCallback. (Or a version without template voodoo if you know the concrete types involved. We could provide a helper function or conversion operator if we wished. In any case I don't think this confines callers from using callbacks.On Thu, Jun 16, 2022 at 5:25 PM K. Moon <km...@chromium.org> wrote:If we don't do this, should we add something like RepeatingCallbackRef/OnceCallbackRef?How would this be different from a normal reference to RepeatingCallback/OnceCallback?It should be different in the same way FunctionRef is different or it's not adding anything.
This class is suitable for use wherever a "const std::function<>&"
would be used without making a copy. ForEach functions and other versions of
the visitor pattern are a good example of when this class should be used.
FWIW, the absl::FunctionRef documentation has this to say:This class is suitable for use wherever a "const std::function<>&"
would be used without making a copy. ForEach functions and other versions of
the visitor pattern are a good example of when this class should be used.So only supporting RepeatingCallback might not be that bad an outcome. I'm sure there are cases where a OnceCallback would be useful (boilerplate wrapper, I guess), but probably much rarer.On Fri, Jun 17, 2022 at 8:56 AM K. Moon <km...@chromium.org> wrote:I think base::*Callback& would require a heap allocation, while base::*CallbackRef wouldn't? I thought this was the key motivation behind supporting something like this in the first place.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CACwGi-58fS1kPv3SdXnjuCO9aSLOyZqEzrHQjU%2B6%3DS25K%2BNZwQ%40mail.gmail.com.
I agree, but I think danakj@'s arguments are that:1. It shouldn't be hard to adapt an existing OnceCallback/RepeatingCallback to a signature that only takes a FunctionRef.2. It's useful to capture the once vs. repeating distinction in the type system, just as we do with *Callback.std::function and absl::FunctionRef are unconcerned with (2), so if that's important, I don't think absl::FunctionRef satisfies our requirements. In which case, the question is whether that requirement is useful for the situations that absl::FunctionRef is appropriate. (As a thought experiment, I do wonder now if some sort of Once<std::function<foo()>> or std::function<Once<foo()>> wrapper would be able to express the same requirements.)(1) possibly is not that valuable, since the whole point of an API taking a FunctionRef is to avoid the overhead of *Callback. It should be easy to adapt this case, but also inconvenient enough to perhaps encourage a better design, so I don't think this is as critical as (2).
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/memory-safety-dev/CACwGi-5xk_yb9YzDHXAX7mivmPJ7T1YF0tkkE9AE8f2445Smmg%40mail.gmail.com.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/memory-safety-dev/CACuR13cWfsxBOmRVaiOBVyMCApOdZz%2ByFJB8_z2QugTdujiv4w%40mail.gmail.com.
I think we should distinguish between:- allowing the conversion when making the actual call- converting between function pointer typesI see absl::FunctionRef's implicit conversions as far more in the spirit of the latter than the former. For example, it's not legal to pass a void (*)(Base*) to a void (*)(Derived*), even though it's possible to invoke a void (*)(Base*) by passing it a Derived*. There are times when it's convenient of course—but that seems to be relatively rare at the moment, based on my initial conversion attempts. It's always possible to consider ways to relax the conversions in the future (e.g. allow Derived -> Base conversions but not non-void -> void).
On Mon, Jul 18, 2022 at 12:17 PM Daniel Cheng <dch...@chromium.org> wrote:I think we should distinguish between:- allowing the conversion when making the actual call- converting between function pointer typesI see absl::FunctionRef's implicit conversions as far more in the spirit of the latter than the former. For example, it's not legal to pass a void (*)(Base*) to a void (*)(Derived*), even though it's possible to invoke a void (*)(Base*) by passing it a Derived*. There are times when it's convenient of course—but that seems to be relatively rare at the moment, based on my initial conversion attempts. It's always possible to consider ways to relax the conversions in the future (e.g. allow Derived -> Base conversions but not non-void -> void).The reason this isn't possible with function pointers seems to me to have more to do with ABI level considerations than API level considerations.The other approach would be to identify cases we think are more likely to be problematic and introduce (bypassable?) warnings for those exact cases, rather than changing the semantics of existing behavior.
I'd also register my generic resistance to swimming upstream against both the C++ standard library and Abseil (and by extension, Google internal C++) without any Chromium-specific reason.
On Mon, 18 Jul 2022 at 10:47, Jeremy Roman <jbr...@chromium.org> wrote:On Mon, Jul 18, 2022 at 12:17 PM Daniel Cheng <dch...@chromium.org> wrote:I think we should distinguish between:- allowing the conversion when making the actual call- converting between function pointer typesI see absl::FunctionRef's implicit conversions as far more in the spirit of the latter than the former. For example, it's not legal to pass a void (*)(Base*) to a void (*)(Derived*), even though it's possible to invoke a void (*)(Base*) by passing it a Derived*. There are times when it's convenient of course—but that seems to be relatively rare at the moment, based on my initial conversion attempts. It's always possible to consider ways to relax the conversions in the future (e.g. allow Derived -> Base conversions but not non-void -> void).The reason this isn't possible with function pointers seems to me to have more to do with ABI level considerations than API level considerations.The other approach would be to identify cases we think are more likely to be problematic and introduce (bypassable?) warnings for those exact cases, rather than changing the semantics of existing behavior.I guess that would require clang plugin modifications, which I can't say I'm enthusiastic about.I'd also register my generic resistance to swimming upstream against both the C++ standard library and Abseil (and by extension, Google internal C++) without any Chromium-specific reason.Well, there are reasons though :)1. absl::FunctionRef's adaptability is a mismatch from base::{Once,Repeating}Callback [1]. What should be a straightforward migration becomes much trickier when our core constructs have pitfalls like this. It *can* be worked around, but this brings me to #2.2. In general, Chrome has been trending towards encouraging constructs that are resistant to misuse, *even if* that means some divergence from the C++ standard library. Some differences are less obvious (e.g. explicitly crashing when trying to deref a null optional rather than treating it as UB), but some are more obvious (e.g. base::expected does not have operator*).
3. It's always much easier to relax restrictions rather than try to add them later. Why not start out with the use cases we know we need, and work on allowing more in the future?
On Mon, Jul 18, 2022 at 2:39 PM Daniel Cheng <dch...@chromium.org> wrote:On Mon, 18 Jul 2022 at 10:47, Jeremy Roman <jbr...@chromium.org> wrote:On Mon, Jul 18, 2022 at 12:17 PM Daniel Cheng <dch...@chromium.org> wrote:I think we should distinguish between:- allowing the conversion when making the actual call- converting between function pointer typesI see absl::FunctionRef's implicit conversions as far more in the spirit of the latter than the former. For example, it's not legal to pass a void (*)(Base*) to a void (*)(Derived*), even though it's possible to invoke a void (*)(Base*) by passing it a Derived*. There are times when it's convenient of course—but that seems to be relatively rare at the moment, based on my initial conversion attempts. It's always possible to consider ways to relax the conversions in the future (e.g. allow Derived -> Base conversions but not non-void -> void).The reason this isn't possible with function pointers seems to me to have more to do with ABI level considerations than API level considerations.The other approach would be to identify cases we think are more likely to be problematic and introduce (bypassable?) warnings for those exact cases, rather than changing the semantics of existing behavior.I guess that would require clang plugin modifications, which I can't say I'm enthusiastic about.I'd also register my generic resistance to swimming upstream against both the C++ standard library and Abseil (and by extension, Google internal C++) without any Chromium-specific reason.Well, there are reasons though :)1. absl::FunctionRef's adaptability is a mismatch from base::{Once,Repeating}Callback [1]. What should be a straightforward migration becomes much trickier when our core constructs have pitfalls like this. It *can* be worked around, but this brings me to #2.2. In general, Chrome has been trending towards encouraging constructs that are resistant to misuse, *even if* that means some divergence from the C++ standard library. Some differences are less obvious (e.g. explicitly crashing when trying to deref a null optional rather than treating it as UB), but some are more obvious (e.g. base::expected does not have operator*).I do admit to wondering why some of these are so bad for Chromium but perfectly fine in Google servers. I suspect this boils down to the individual opinions of the people making the decisions.
To view this discussion on the web visit https://groups.google.com/a/chromium.org/d/msgid/cxx/CACuR13f%3DcCH-vqFeB6z-Mp%3DK5gEzUYVpd73a49gBj04WnuGNsQ%40mail.gmail.com.
On Mon, Jul 18, 2022 at 2:39 PM Daniel Cheng <dch...@chromium.org> wrote:On Mon, 18 Jul 2022 at 10:47, Jeremy Roman <jbr...@chromium.org> wrote:On Mon, Jul 18, 2022 at 12:17 PM Daniel Cheng <dch...@chromium.org> wrote:I think we should distinguish between:- allowing the conversion when making the actual call- converting between function pointer typesI see absl::FunctionRef's implicit conversions as far more in the spirit of the latter than the former. For example, it's not legal to pass a void (*)(Base*) to a void (*)(Derived*), even though it's possible to invoke a void (*)(Base*) by passing it a Derived*. There are times when it's convenient of course—but that seems to be relatively rare at the moment, based on my initial conversion attempts. It's always possible to consider ways to relax the conversions in the future (e.g. allow Derived -> Base conversions but not non-void -> void).The reason this isn't possible with function pointers seems to me to have more to do with ABI level considerations than API level considerations.The other approach would be to identify cases we think are more likely to be problematic and introduce (bypassable?) warnings for those exact cases, rather than changing the semantics of existing behavior.I guess that would require clang plugin modifications, which I can't say I'm enthusiastic about.I'd also register my generic resistance to swimming upstream against both the C++ standard library and Abseil (and by extension, Google internal C++) without any Chromium-specific reason.Well, there are reasons though :)1. absl::FunctionRef's adaptability is a mismatch from base::{Once,Repeating}Callback [1]. What should be a straightforward migration becomes much trickier when our core constructs have pitfalls like this. It *can* be worked around, but this brings me to #2.2. In general, Chrome has been trending towards encouraging constructs that are resistant to misuse, *even if* that means some divergence from the C++ standard library. Some differences are less obvious (e.g. explicitly crashing when trying to deref a null optional rather than treating it as UB), but some are more obvious (e.g. base::expected does not have operator*).I do admit to wondering why some of these are so bad for Chromium but perfectly fine in Google servers. I suspect this boils down to the individual opinions of the people making the decisions.
On Mon, Jul 18, 2022 at 6:55 PM Daniel Cheng <dch...@chromium.org> wrote:On Mon, Jul 18, 2022 at 2:39 PM Daniel Cheng <dch...@chromium.org> wrote:On Mon, 18 Jul 2022 at 10:47, Jeremy Roman <jbr...@chromium.org> wrote:On Mon, Jul 18, 2022 at 12:17 PM Daniel Cheng <dch...@chromium.org> wrote:I think we should distinguish between:- allowing the conversion when making the actual call- converting between function pointer typesI see absl::FunctionRef's implicit conversions as far more in the spirit of the latter than the former. For example, it's not legal to pass a void (*)(Base*) to a void (*)(Derived*), even though it's possible to invoke a void (*)(Base*) by passing it a Derived*. There are times when it's convenient of course—but that seems to be relatively rare at the moment, based on my initial conversion attempts. It's always possible to consider ways to relax the conversions in the future (e.g. allow Derived -> Base conversions but not non-void -> void).The reason this isn't possible with function pointers seems to me to have more to do with ABI level considerations than API level considerations.The other approach would be to identify cases we think are more likely to be problematic and introduce (bypassable?) warnings for those exact cases, rather than changing the semantics of existing behavior.I guess that would require clang plugin modifications, which I can't say I'm enthusiastic about.I'd also register my generic resistance to swimming upstream against both the C++ standard library and Abseil (and by extension, Google internal C++) without any Chromium-specific reason.Well, there are reasons though :)1. absl::FunctionRef's adaptability is a mismatch from base::{Once,Repeating}Callback [1]. What should be a straightforward migration becomes much trickier when our core constructs have pitfalls like this. It *can* be worked around, but this brings me to #2.2. In general, Chrome has been trending towards encouraging constructs that are resistant to misuse, *even if* that means some divergence from the C++ standard library. Some differences are less obvious (e.g. explicitly crashing when trying to deref a null optional rather than treating it as UB), but some are more obvious (e.g. base::expected does not have operator*).I do admit to wondering why some of these are so bad for Chromium but perfectly fine in Google servers. I suspect this boils down to the individual opinions of the people making the decisions.I think it is for similar reasons why we need to use raw_ptr<T> instead of T*. Chrome is required to handle an enormous amount of arbitrary (and malicious) inputs, and has full access to users' machines. The threat model differs, which has produced different requirements, and as we continue to invest in hardening and improving the Chrome codebase, they continue to diverge along similar axes. go/google3-builds-for-chrome-binaries
I don't have an opinion on FunctionRef, but FWIW, I've always found ForEachRenderFrameHost()'s overloaded behavior based on the callback's return type to be clever, but confusing. I find myself always checking the ForEachRenderFrameHost() to see what's allowed, as well as the call site to see which version is being invoked. I think not using overloading would be an improvement in this case.
This behavior seems to trace back to the C++ standard's definition of invocability (such as https://en.cppreference.com/w/cpp/types/is_invocable), which explicitly allows the conversion of any return type to void. I can see this being useful in some cases, but in any case, I think it might be unfair to single out FunctionRef for this concern, as it seems like the same problem can exist when using any API that relies on this definition of invocability. If it's something we want to prevent, a compiler plugin does sound like a better approach to me.