UI: Destroy the frontend API after OBS_FRONTEND_EVENT_EXIT

The frontend API was never explicitly cleaned up which has lead to
several crashes from plugins continuing to call it even after OBS itself
has been destroyed. We now destroy the API after the exit event, so
further calls by plugins will be no-ops instead of potentially accessing
destroyed OBS state.

This also required some changes to our own use of the API, as we relied
on the OBSBasic destructor to clean up some callbacks, by which point
the API should have already been destroyed.
This commit is contained in:
Richard Stanway 2023-02-28 23:34:44 +01:00 committed by Lain
parent f7eb634788
commit d997a56fb6
5 changed files with 11 additions and 9 deletions

View File

@ -13,7 +13,7 @@ void obs_frontend_set_callbacks_internal(obs_frontend_callbacks *callbacks)
static inline bool callbacks_valid_(const char *func_name)
{
if (!c) {
blog(LOG_WARNING, "Tried to call %s with no callbacks!",
blog(LOG_ERROR, "Tried to call %s with no callbacks!",
func_name);
return false;
}

View File

@ -640,6 +640,9 @@ void SourceTreeModel::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)
stm->SceneChanged();
break;
case OBS_FRONTEND_EVENT_EXIT:
stm->Clear();
obs_frontend_remove_event_callback(OBSFrontendEvent, stm);
break;
case OBS_FRONTEND_EVENT_SCENE_COLLECTION_CLEANUP:
stm->Clear();
break;
@ -860,11 +863,6 @@ SourceTreeModel::SourceTreeModel(SourceTree *st_)
obs_frontend_add_event_callback(OBSFrontendEvent, this);
}
SourceTreeModel::~SourceTreeModel()
{
obs_frontend_remove_event_callback(OBSFrontendEvent, this);
}
int SourceTreeModel::rowCount(const QModelIndex &parent) const
{
return parent.isValid() ? 0 : items.count();

View File

@ -132,7 +132,6 @@ class SourceTreeModel : public QAbstractListModel {
public:
explicit SourceTreeModel(SourceTree *st);
~SourceTreeModel();
virtual int rowCount(const QModelIndex &parent) const override;
virtual QVariant data(const QModelIndex &index,

View File

@ -5076,6 +5076,10 @@ void OBSBasic::closeEvent(QCloseEvent *event)
if (api)
api->on_event(OBS_FRONTEND_EVENT_EXIT);
// Destroys the frontend API so plugins can't continue calling it
obs_frontend_set_callbacks_internal(nullptr);
api = nullptr;
QMetaObject::invokeMethod(App(), "quit", Qt::QueuedConnection);
}

View File

@ -29,6 +29,9 @@ void OBSBasicStats::OBSFrontendEvent(enum obs_frontend_event event, void *ptr)
case OBS_FRONTEND_EVENT_RECORDING_STOPPED:
stats->ResetRecTimeLeft();
break;
case OBS_FRONTEND_EVENT_EXIT:
obs_frontend_remove_event_callback(OBSFrontendEvent, stats);
break;
default:
break;
}
@ -234,8 +237,6 @@ void OBSBasicStats::closeEvent(QCloseEvent *event)
OBSBasicStats::~OBSBasicStats()
{
obs_frontend_remove_event_callback(OBSFrontendEvent, this);
delete shortcutFilter;
os_cpu_usage_info_destroy(cpu_info);
}