-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #277 - Stack overflow in Bin Mon message handler #278
Conversation
This ensures message handlers for both Bin mon and LS mons are launched in a new thread to overcome NNG stack limits
// Called by NNG callback on thread with small stack. | ||
// Launch task in another thread to overcome stack limitation. | ||
auto future = std::async(std::launch::async, [this, sceneStore]() { | ||
handler_(sceneStore); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This bit isn't ideal. I think this causes a copy. Using std::move makes the compiler complain because sceneStore
is apparently a const here... not sure if that happens as part of the capture list?
I think as future.get() is blocking, we are safe in terms of lifetime.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe this is fixed by d216065
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would marking the lambda as mutable fix the const warning? I can't get a small example that looks the same to emit that warning so I'm guessing a bit. I should probably check out the project and try it properly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to clarify as the later commit makes my message confusing. This code causes a compiler warning;
auto future = std::async(std::launch::async, [this, sceneStore]() {
handler_(std::move(sceneStore));
});
The current code (below) it's happy with, but I'm not sure if this still causes a copy from the capture list to passing in to handle_
;
auto future = std::async(std::launch::async, [this, sceneStore = std::move(sceneStore)]() {
handler_(sceneStore);
});
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think the mutable thing fixes it, so
auto future = std::async(std::launch::async, [this, sceneStore = std::move(sceneStore)]() mutable {
handler_(std::move(sceneStore));
}
I'm a bit rusty, but I think under the hood a lambda creates a function object with the captures as data members, and by default the call operator is const so that repeated calls to the same lambda with the same arguments produce the same results, so it's as if you did
struct MyLambda {
void operator()() const {
that->handler(std::move(sceneStore)); // won't compile as sceneStore is const on a const object
}
MonitoringMetadataReceiver* that;
proto::SceneStore sceneStore;
};
MonitoringMetadataReceiver::handleReceive() {
// snip ...
MyLambda const lambda{this, std::move(sceneStore)};
auto future = std::async(std::launch::async, lambda);
}
The mutable keyword just removes the const
s
Bit weird, but if anything, the rest of the language is wrong and lambdas have the correct defaults.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh thanks for the explanation - that makes sense why it complains!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure why the handler doesn't just take a const reference, maybe it should? (I've not checked to see what it's doing at the other end, if it needs to keep hold of a copy then passing by value makes sense)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Two handlers... both just read, so I think that makes sense just to pass a const ref.
Bin plugin does it all in https://github.com/ebu/ear-production-suite/blob/main/ear-production-suite-plugins/lib/src/binaural_monitoring_backend.cpp#L203
LS plugins a bit more complicated as it gets passed through a few funcs, but non of them need a non-const:
https://github.com/ebu/ear-production-suite/blob/main/ear-production-suite-plugins/lib/src/monitoring_backend.cpp#L50
https://github.com/ebu/ear-production-suite/blob/main/ear-production-suite-plugins/lib/src/monitoring_backend.cpp#L60
https://github.com/ebu/ear-production-suite/blob/main/ear-production-suite-plugins/lib/src/scene_gains_calculator.cpp#L56
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to const refs in ab7adb9
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, could capture by reference in the async lambda too (as we're immediately blocking on it's completion)
auto future = std::async(std::launch::async, [this, &sceneStore]() {
handler_(sceneStore);
});
future.get(); //blocking
As per #277, a crash would occur (probably only on windows) if the
AdmPresetDefinitionsHandler
was first instantiated as a singleton via an NNG message, due to the NNG threads having a small stack. We had previously overcome a similar issue in the LS monitoring plugins by wrappingSceneGainsCalculator::update
code in a thread. I used a similar fix here, but it made sense for the thread launching to occur further up the call stack since for both the LS mons and Bin mon, the message handler call stems fromMonitoringMetadataReceiver::handleReceive
LS mon call stack from message received to
SceneGainsCalculator::update
where a new thread would be launched:Bin mon call stack from message received to stack overflow (still on NNG thread)
Fix was to launch message handlers in new threads from the common
MonitoringMetadataReceiver::handleReceive
method.Closes #277