Prevent stack corruption when using C++ EventChannel#36882
Prevent stack corruption when using C++ EventChannel#36882auto-submit[bot] merged 6 commits intoflutter:mainfrom
EventChannel#36882Conversation
|
This pull request has been changed to a draft. The currently pending flutter-gold status will not be able to resolve until a new commit is pushed or the change is marked ready for review again. |
shell/platform/common/client_wrapper/event_channel_unittests.cc
Outdated
Show resolved
Hide resolved
| const MethodCodec<T>* codec = codec_; | ||
| const std::string channel_name = name_; | ||
| const BinaryMessenger* messenger = messenger_; | ||
| bool is_listening = false; |
There was a problem hiding this comment.
Instead of mutating a captured variable, don't you think it would be more clear to capture a reference to a bool on the heap?
There was a problem hiding this comment.
I didn't mean literally a C++ reference obviously, a std::unique_ptr<bool> just to be clear =)
There was a problem hiding this comment.
I agree mutating a captured variable is very subtle. It looks like I'll need to use std::shared_ptr per this comment:
There was a problem hiding this comment.
Updated using shared_ptr, let me know if you have additional feedback! :)
There was a problem hiding this comment.
Did the mutable lands not end up working then?
There was a problem hiding this comment.
With fml::MakeCopyable there will only be one instance of the bool.
MakeCopyable wraps a single instance of the lambda in a reference-counted object.
There was a problem hiding this comment.
The client wrapper code is external to the engine; it's an artifact that is copied into projects, to provide a more usable API than the C API we have to provide at the library level. So we're can't use FML, we'd have to duplicate that code into the wrapper if we wanted to use it.
There was a problem hiding this comment.
Here's a more simple and portable implementation of MakeCopyable:
#include <functional>
#include <iostream>
namespace {
template <typename T>
class MakeCopyable {
public:
MakeCopyable(T&& val) : ptr_(std::make_shared<T>(std::move(val))) {}
template <typename... ArgType>
auto operator()(ArgType&&... args) const {
return ptr_->operator()(std::forward<ArgType>(args)...);
}
private:
std::shared_ptr<T> ptr_;
};
}
void RunTwice(const std::function<void()>& func) {
func();
func();
}
int main() {
int32_t adder = 10;
RunTwice(MakeCopyable([adder, tally = std::make_unique<int32_t>(0)]() {
std::cout << adder + *tally << std::endl;
*tally += 1;
}));
}There was a problem hiding this comment.
Thanks Jason for the info on fml::MakeCopyable, I wasn't aware of that!
Like Stuart mentioned, the client wrapper is code that's copied into the user's project, which the user can freely reference. I lean towards being conservative in adding new types to the client wrapper (though I realize MakeCopyable could likely be hidden as an implementation detail).
It seems folks are leaning more towards the mutable lambda now, I'll switch back to that 😄
There was a problem hiding this comment.
though I realize
MakeCopyablecould likely be hidden as an implementation detail
It's non-trivial to hide most things in the wrapper since it's heavily templated.
Background
Updating the engine to Visual Studio 2019 was reverted as it broke the framework tree. The root cause is that the
EventChannel's handler stores and mutates data on theEventChannelitself. This is problematic as theEventChannel's and the handler's lifetimes are decoupled (theEventChanneldoes not own its handler). If theEventChannelis destroyed, the handler reads/mutates deallocated memory. See this comment for more info: flutter/flutter#113135 (comment)This change decouples the lifetimes of the
EventChanneland its handler by making the handler own its own state.Addresses flutter/flutter#113728
Pre-launch Checklist
writing and running engine tests.
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.