Fix access-after-delete during enbtity script engine cleanup

This commit is contained in:
Karol Suprynowicz 2024-11-14 15:23:52 +01:00
parent ad5b6d0db7
commit fe8290468d
5 changed files with 26 additions and 4 deletions

View file

@ -0,0 +1,10 @@
//
// Created by ksuprynowicz on 14.11.24.
//
#ifndef OVERTE_SCRIPTENGINEDEBUGFLAGS_H
#define OVERTE_SCRIPTENGINEDEBUGFLAGS_H
#define OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
#endif //OVERTE_SCRIPTENGINEDEBUGFLAGS_H

View file

@ -258,6 +258,13 @@ ScriptEngineV8::ScriptEngineV8(ScriptManager *manager) : ScriptEngine(manager),
}
ScriptEngineV8::~ScriptEngineV8() {
// Process remaining events to avoid problems with `deleteLater` calling destructor of script proxies after script engine has been deleted:
{
QEventLoop loop;
loop.processEvents();
}
// This is necessary for script engines that don't run in ScriptManager::run(), for example entity scripts:
disconnectSignalProxies();
deleteUnusedValueWrappers();
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
_wasDestroyed = true;
@ -272,14 +279,14 @@ void ScriptEngineV8::perManagerLoopIterationCleanup() {
void ScriptEngineV8::disconnectSignalProxies() {
_signalProxySetLock.lockForRead();
while (!_signalProxySet.empty()) {
auto proxy = *_signalProxySet.begin();
_signalProxySetLock.unlock();
delete *_signalProxySet.begin();
delete proxy;
_signalProxySetLock.lockForRead();
}
_signalProxySetLock.unlock();
}
void ScriptEngineV8::deleteUnusedValueWrappers() {
while (!_scriptValueWrappersToDelete.empty()) {
auto wrapper = _scriptValueWrappersToDelete.dequeue();

View file

@ -35,6 +35,7 @@
#include "libplatform/libplatform.h"
#include "v8.h"
#include "ScriptEngineDebugFlags.h"
#include "../ScriptEngine.h"
#include "../ScriptManager.h"
#include "../ScriptException.h"
@ -58,8 +59,6 @@ using ScriptContextV8Pointer = std::shared_ptr<ScriptContextV8Wrapper>;
const double GARBAGE_COLLECTION_TIME_LIMIT_S = 1.0;
#define OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
Q_DECLARE_METATYPE(ScriptEngine::FunctionSignature)
/// [V8] Implements ScriptEngine for V8 and translates calls for QScriptEngine

View file

@ -1217,7 +1217,9 @@ ScriptSignalV8Proxy::~ScriptSignalV8Proxy() {
_objectLifetime.Reset();
_v8Context.Reset();
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
Q_ASSERT(!_wasDeleted);
Q_ASSERT(!_engine->_wasDestroyed);
_wasDeleted = true;
#endif
_engine->_signalProxySetLock.lockForWrite();
_engine->_signalProxySet.remove(this);

View file

@ -23,6 +23,7 @@
#include <QtCore/QPointer>
#include <QtCore/QString>
#include "ScriptEngineDebugFlags.h"
#include "../ScriptEngine.h"
#include "../Scriptable.h"
#include "ScriptEngineV8.h"
@ -295,6 +296,9 @@ private: // storage
// Call counter for debugging purposes. It can be used to determine which signals are overwhelming script engine.
int _callCounter{0};
float _totalCallTime_s{ 0.0 };
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
std::atomic<bool> _wasDeleted{false};
#endif
Q_DISABLE_COPY(ScriptSignalV8Proxy)
};