-
-
Notifications
You must be signed in to change notification settings - Fork 449
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: avoid crash from channelPointRewardAdded callback #4942
Conversation
I just received |
auto *rewardClone = new QString(rewardId); | ||
auto *targetClone = new QString(target); | ||
auto *contentClone = new QString(originalContent); |
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.
These don't need to be heap allocated, as they're effectively on the heap already (in the case of Qt5, you'll have three layers of indirection) and copied into the lambda.
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.
Are you sure? Here's some logs from a recent crash I captured:
chatterino.twitch: [TwitchChannel "removed" ] Channel point reward added: "b37475ab-5bb3-4787-bdf9-d8c3582b62f5" , "Ask me anything" , true
chatterino.twitch: TwitchChannel reward added callback: "b37475ab-5bb3-4787-bdf9-d8c3582b62f5" - ""
chatterino.twitch: TwitchChannel reward added callback: "" -
Segmentation fault (core dumped)
Notice that rewardId
has become an empty QString (and seemingly isn't even properly printed in the last callback log)
Maybe I'm reading this wrong, but I think the use of We might want to use a |
Working on full refactor à la #4943 (review) |
Closes #2432
Closes #3942
Problem
It is possible for
channelPointRewardAdded
connections to be created that have a long lifetime (take this as a given, I explain how this happens in the "Follow-up" section). Upon callback lambda invocation, the captured variables will be released. However, if the invocation is unrelated to the desiredrewardId
, releasing the captured variables will cause a seg fault when a matching invocation occurs later on. (Technically this matching invocation will take the form of empty reward ids because of the earlier variable destruction)Solution
Clone objects used in lambda scope to avoid early destruction (also ensure
reward
is properly copied for each signal connection)Follow-up
While this PR avoids a common crash, there are remaining edge cases to be tackled in future PRs.
It is possible thatScratch this, I assumed multiple threads were at playchannelPointRewardAdded
is invoked before a signal connection is made inIrcMessageHandler
(whenisChannelPointRewardKnown
yields false). This is the scenario I described before. When this happens, now the first redemption associated with the reward will never be posted to chat (and the cloned objects are not freed). To solve this, there should be a mutex that preventschannelPointRewardAdded.invoke
when we are in the!channel->isChannelPointRewardKnown(rewardId)
block (and after acquiring the mutex, we should checkisChannelPointRewardKnown
once more)It is possible that the pubsub socket is not connected when the text redemption occurs. As a result, a signal connection will be made, but
invoke
would not be called. Again, the cloned objects would not be freed (until the next redemption for the same reward occurs, while pubsub is connected). So, we ought to implement a timeout where if x seconds pass without signal invocation, we release the message regardless.