-
-
Couldn't load subscription status.
- Fork 8.8k
frontend: fix leaving FE canvases in bad state #12728
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
base: master
Are you sure you want to change the base?
Conversation
e1734c5 to
f460a38
Compare
The `obs_frontend_remove_canvas` is called, the destructor of OBS::Canvas calls obs_canvas_release() on an object which is both allocated on the heap and used as reference. This causes the `canvases` collection on the main window to become corrupt, and crash the next time you try to add another frontend canvas. This PR modifies the canvases vector to be represented as a map of obs_canvas_t* to a shared pointer to OBS::Canvas. This will preserve both the OBS::Canvas manager instance, and pointer to obs_canvas_t* for reference by obs_frontend_canvas_remove(), ensuring that the right objects will be used when called upon.
f460a38 to
fdb9420
Compare
|
Could you please provide a minimal reproducer for us (e.g. a small plugin that does nothing else but cause the issue)? At first glance I don't think that entangling two different reference counting systems (canvases themselves and So the core issue (if I understand this right), is that when The destructor itself marks the @derrod Is this possibly just an unbalanced retain/release pair? |
I think it is, but in a different way than you might expect. The issue, I think, is the signal handling: I think there are at least three different ways we can deal with that, but I don't particularly like any of them right now:
QMetaObject::invokeMethod(static_cast<OBSBasic *>(data), "RemoveCanvas", Qt::QueuedConnection,
Q_ARG(OBSCanvas, OBSCanvas(canvas)));But that feels a bit brittle.
obs_canvas_t *removed = canvas;
canvas = nullptr;
obs_canvas_remove(removed);
obs_canvas_release(removed);By setting This seems like perhaps the safest option?
We can add one line to OBS::Canvas tmp = std::move(*canvas_it);This will ensure that the destructor called during |
|
Ok, so I get this right:
First naive question is why the frontend listens to canvas removal events if it was itself the cause of the removal - if it weren't for the double destruction this would have potentially led to an endless loop, so it's obviously an architectural issue and the suggested changes just paper over the core issue. The current design suggests that So in that sense it seems wrong to me that
That sounds like the more correct fix to me, because it triggers the canvas removal at the "source of truth". What this also suggests to me is that having this implemented in the "frontend API" is wrong - canvases are not a frontend concern, but (as implemented) a |
|
To be completely honest, it feels to me that adding a flag which would mark an
The solution I suggested assumes that you would want to retain as much of the existing code as possible, that more in-depth refactoring of OBS::Canvas would not be welcome, and that clarity and comprehensibility is preferred over (non-critical) optimization in this particular code path. I was pretty challenging to wrap my head around what was going on in I usually stick to the approach "clarity first, optimization later", which works for most code out of the hot path. |
While I appreciate that notion, that is precisely what we want much less of in the code base - if something is broken at a core level, it needs to be fixed/refactored at that core level. OBS has too many band-aids over core issues that rear their ugly head time and time again, requiring even more piles of workarounds to make it do what it's supposed to do. And particularly for such a "new" feature it is important that we nip those issues in the bud early and comprehensively, particularly if it exposes an underlying design issue. |
Description
This PR modifies the canvases vector to be
represented as a map of obs_canvas_t* to a
shared pointer to OBS::Canvas.
This will preserve both the OBS::Canvas manager
instance, and pointer to obs_canvas_t* for
reference by obs_frontend_canvas_remove(),
ensuring that the right objects will be used
when called upon.
Motivation and Context
When the
obs_frontend_remove_canvasis called,the destructor of OBS::Canvas calls
obs_canvas_release() on an object which is
both allocated on the heap and added as reference
to the canvases vector causing a double deallocation.
This causes the
canvasescollection on the mainwindow to become corrupt, and crash the next time
you try to add another frontend canvas.
This effectively prevents the canvases collection
from being managed properly by plug-ins.
How Has This Been Tested?
Ran obs_frontend_add_canvas() followed by obs_frontend_remove_canvas() followed by obs_frontend_add_canvas() before and after the change.
Before change: crash
After change: smooth operation
Types of changes
Checklist: