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() { 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(); deleteUnusedValueWrappers();
#ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD #ifdef OVERTE_SCRIPT_USE_AFTER_DELETE_GUARD
_wasDestroyed = true; _wasDestroyed = true;
@ -272,14 +279,14 @@ void ScriptEngineV8::perManagerLoopIterationCleanup() {
void ScriptEngineV8::disconnectSignalProxies() { void ScriptEngineV8::disconnectSignalProxies() {
_signalProxySetLock.lockForRead(); _signalProxySetLock.lockForRead();
while (!_signalProxySet.empty()) { while (!_signalProxySet.empty()) {
auto proxy = *_signalProxySet.begin();
_signalProxySetLock.unlock(); _signalProxySetLock.unlock();
delete *_signalProxySet.begin(); delete proxy;
_signalProxySetLock.lockForRead(); _signalProxySetLock.lockForRead();
} }
_signalProxySetLock.unlock(); _signalProxySetLock.unlock();
} }
void ScriptEngineV8::deleteUnusedValueWrappers() { void ScriptEngineV8::deleteUnusedValueWrappers() {
while (!_scriptValueWrappersToDelete.empty()) { while (!_scriptValueWrappersToDelete.empty()) {
auto wrapper = _scriptValueWrappersToDelete.dequeue(); auto wrapper = _scriptValueWrappersToDelete.dequeue();

View file

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

View file

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

View file

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