Fix V8 memory leak and persistend handle problems

This commit is contained in:
ksuprynowicz 2023-02-26 21:07:13 +01:00
parent 84fd17cb66
commit 12f239b18c
6 changed files with 44 additions and 15 deletions

View file

@ -1645,8 +1645,11 @@ bool EntityScriptingInterface::getServerScriptStatus(const QUuid& entityID, cons
} }
connect(request, &GetScriptStatusRequest::finished, manager, [callback](GetScriptStatusRequest* request) mutable { connect(request, &GetScriptStatusRequest::finished, manager, [callback](GetScriptStatusRequest* request) mutable {
QString statusString = EntityScriptStatus_::valueToKey(request->getStatus());
auto engine = callback.engine(); auto engine = callback.engine();
// V8TODO: it seems to sometimes be called on a wrong thread, reading to script engine crashes. I added an assert for now
Q_ASSERT(QThread::currentThread() == engine->thread());
Q_ASSERT(QThread::currentThread() == engine->manager()->thread());
QString statusString = EntityScriptStatus_::valueToKey(request->getStatus());
ScriptValueList args { engine->newValue(request->getResponseReceived()), engine->newValue(request->getIsRunning()), engine->newValue(statusString.toLower()), engine->newValue(request->getErrorInfo()) }; ScriptValueList args { engine->newValue(request->getResponseReceived()), engine->newValue(request->getIsRunning()), engine->newValue(statusString.toLower()), engine->newValue(request->getErrorInfo()) };
callback.call(ScriptValue(), args); callback.call(ScriptValue(), args);
request->deleteLater(); request->deleteLater();

View file

@ -846,7 +846,7 @@ ScriptValue ScriptEngineV8::evaluateInClosure(const ScriptValue& _closure,
v8::Local<v8::Value> closureGlobal; v8::Local<v8::Value> closureGlobal;
ScriptValueV8Wrapper* unwrappedClosure; ScriptValueV8Wrapper* unwrappedClosure;
ScriptProgramV8Wrapper* unwrappedProgram; ScriptProgramV8Wrapper* unwrappedProgram;
v8::Local<v8::Context> oldContext = getContext(); //v8::Local<v8::Context> oldContext = getContext();
{ {
v8::Context::Scope contextScope(getContext()); v8::Context::Scope contextScope(getContext());
@ -1180,7 +1180,9 @@ ScriptContextV8Pointer ScriptEngineV8::pushContext(v8::Local<v8::Context> contex
_contexts.append(std::make_shared<ScriptContextV8Wrapper>(this, context, ScriptContextPointer())); _contexts.append(std::make_shared<ScriptContextV8Wrapper>(this, context, ScriptContextPointer()));
v8::Context::Scope contextScope(context); v8::Context::Scope contextScope(context);
static volatile int debug_context_id = 1; static volatile int debug_context_id = 1;
context->Global()->Set(context, v8::String::NewFromUtf8(_v8Isolate, "debug_context_id").ToLocalChecked(), v8::Integer::New(_v8Isolate, debug_context_id)); if (!context->Global()->Set(context, v8::String::NewFromUtf8(_v8Isolate, "debug_context_id").ToLocalChecked(), v8::Integer::New(_v8Isolate, debug_context_id)).FromMaybe(false)) {
Q_ASSERT(false);
}
debug_context_id++; debug_context_id++;
return _contexts.last(); return _contexts.last();
} }

View file

@ -504,12 +504,12 @@ void ScriptObjectV8Proxy::v8GetPropertyNames(const v8::PropertyCallbackInfo<v8::
return; return;
} }
V8ScriptValue object(proxy->_engine, objectV8); V8ScriptValue object(proxy->_engine, objectV8);
uint id; //uint id;
v8::Local<v8::Array> properties = proxy->getPropertyNames(); v8::Local<v8::Array> properties = proxy->getPropertyNames();
v8::Local<v8::Array> objectProperties; v8::Local<v8::Array> objectProperties;
uint32_t propertiesLength = properties->Length(); uint32_t propertiesLength = properties->Length();
if (info.This()->GetInternalField(2).As<v8::Object>()->GetPropertyNames(context).ToLocal(&objectProperties)) { if (info.This()->GetInternalField(2).As<v8::Object>()->GetPropertyNames(context).ToLocal(&objectProperties)) {
for (int n = 0; n < objectProperties->Length(); n++) { for (uint32_t n = 0; n < objectProperties->Length(); n++) {
if(!properties->Set(context, propertiesLength+n, objectProperties->Get(context, n).ToLocalChecked()).FromMaybe(false)) { if(!properties->Set(context, propertiesLength+n, objectProperties->Get(context, n).ToLocalChecked()).FromMaybe(false)) {
qDebug(scriptengine) << "ScriptObjectV8Proxy::v8GetPropertyNames: Cannot add member name"; qDebug(scriptengine) << "ScriptObjectV8Proxy::v8GetPropertyNames: Cannot add member name";
} }
@ -1262,7 +1262,7 @@ int ScriptSignalV8Proxy::qt_metacall(QMetaObject::Call call, int id, void** argu
} }
//}); //});
_totalCallTime_s += callTimer.elapsed() / 1000.0; _totalCallTime_s += callTimer.elapsed() / 1000.0f;
return -1; return -1;
} }

View file

@ -550,6 +550,7 @@ QObject* ScriptValueV8Wrapper::toQObject() const {
return dest.value<QObject*>(); return dest.value<QObject*>();
} else { } else {
Q_ASSERT(false); Q_ASSERT(false);
return nullptr;
} }
} else { } else {
Q_ASSERT(false); Q_ASSERT(false);

View file

@ -23,16 +23,30 @@ template <typename T>
class V8ScriptValueTemplate { class V8ScriptValueTemplate {
public: public:
V8ScriptValueTemplate() = delete; V8ScriptValueTemplate() = delete;
/*enum class Ownership {
Script = 0,
Value = 1
};*/
//V8ScriptValueTemplate(v8::Isolate *isolate, v8::Local<T> value) : _isolate(isolate) { //V8ScriptValueTemplate(v8::Isolate *isolate, v8::Local<T> value) : _isolate(isolate) {
//_value.reset(v8::UniquePersistent<T>::New(_isolate, value)); //_value.reset(v8::UniquePersistent<T>::New(_isolate, value));
//}; //};
V8ScriptValueTemplate(ScriptEngineV8 *engine, const v8::Local<T> value) : _engine(engine) { // V8TODO: Persistent handles need to be constructed and destructed in the same thread
// Adding asserts might be a good idea
V8ScriptValueTemplate(ScriptEngineV8 *engine, const v8::Local<T> value/*, V8ScriptValueTemplate::Ownership ownership*/) : _engine(engine) {
v8::Locker locker(_engine->getIsolate()); v8::Locker locker(_engine->getIsolate());
v8::Isolate::Scope isolateScope(_engine->getIsolate()); v8::Isolate::Scope isolateScope(_engine->getIsolate());
v8::HandleScope handleScope(_engine->getIsolate()); v8::HandleScope handleScope(_engine->getIsolate());
v8::Context::Scope(_engine->getContext()); v8::Context::Scope(_engine->getContext());
_value.reset(new v8::Persistent<T>(_engine->getIsolate(), value)); /*if (ownership == V8ScriptValueTemplate::Ownership::Script) {
//_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), value)); _value.reset(new v8::WeakPersistent<T>(_engine->getIsolate(), value));
return;
} else if (ownership == V8ScriptValueTemplate::Ownership::Value) {
_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), value));
return;
}
Q_ASSERT(false);*/
_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), value));
}; };
V8ScriptValueTemplate& operator= (const V8ScriptValueTemplate &source) { V8ScriptValueTemplate& operator= (const V8ScriptValueTemplate &source) {
@ -41,7 +55,7 @@ public:
v8::HandleScope handleScope(_engine->getIsolate()); v8::HandleScope handleScope(_engine->getIsolate());
v8::Context::Scope(_engine->getContext()); v8::Context::Scope(_engine->getContext());
_engine = source.getEngine(); _engine = source.getEngine();
_value.reset(new v8::Persistent<T>(_engine->getIsolate(), source.constGet())); _value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), source.constGet()));
//_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), source.constGet())); //_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), source.constGet()));
return *this; return *this;
}; };
@ -52,7 +66,7 @@ public:
v8::HandleScope handleScope(_engine->getIsolate()); v8::HandleScope handleScope(_engine->getIsolate());
v8::Context::Scope(_engine->getContext()); v8::Context::Scope(_engine->getContext());
//_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), v8::Local<T>())); //_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), v8::Local<T>()));
_value.reset(new v8::Persistent<T>(_engine->getIsolate(), v8::Local<T>())); _value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), v8::Local<T>()));
}; };
V8ScriptValueTemplate(const V8ScriptValueTemplate &copied) : _engine(copied.getEngine()) { V8ScriptValueTemplate(const V8ScriptValueTemplate &copied) : _engine(copied.getEngine()) {
@ -61,7 +75,7 @@ public:
v8::HandleScope handleScope(_engine->getIsolate()); v8::HandleScope handleScope(_engine->getIsolate());
v8::Context::Scope(_engine->getContext()); v8::Context::Scope(_engine->getContext());
//_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), copied.constGet())); //_value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), copied.constGet()));
_value.reset(new v8::Persistent<T>(_engine->getIsolate(), copied.constGet())); _value.reset(new v8::UniquePersistent<T>(_engine->getIsolate(), copied.constGet()));
} }
v8::Local<T> get() { v8::Local<T> get() {
@ -106,11 +120,19 @@ public:
void reset(v8::Isolate *isolate, v8::Local<T>) { void reset(v8::Isolate *isolate, v8::Local<T>) {
Q_ASSERT(false); Q_ASSERT(false);
}; };
// V8TODO: add thread safe destructors to all objects that have persistent handles
~V8ScriptValueTemplate() {
v8::Locker locker(_engine->getIsolate());
v8::Isolate::Scope isolateScope(_engine->getIsolate());
v8::HandleScope handleScope(_engine->getIsolate());
//v8::Context::Scope(_engine->getContext());
_value->Reset();
}
private: private:
// std::shared_ptr<v8::UniquePersistent<T>> _value; // std::shared_ptr<v8::UniquePersistent<T>> _value;
// V8TODO: Persistent needs reset to release object? It may cause memory leaks here // V8TODO: Persistent needs reset to release object? It may cause memory leaks here
std::shared_ptr<v8::Persistent<T>> _value; std::shared_ptr<v8::UniquePersistent<T>> _value;
// V8TODO: maybe make it weak // V8TODO: maybe make it weak
// does it need , CopyablePersistentTraits<Value>? // does it need , CopyablePersistentTraits<Value>?
// V8TODO: is context needed at all? // V8TODO: is context needed at all?

View file

@ -203,12 +203,13 @@ Script.include("/~/system/libraries/utils.js");
if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0)) { if ((controllerData.triggerClicks[this.hand] === 0 && controllerData.secondaryValues[this.hand] === 0)) {
var stopRunning = false; var stopRunning = false;
controllerData.nearbyOverlayIDs[this.hand].forEach(function(overlayID) { // V8TODO: check if this doesn't break anything
/*controllerData.nearbyOverlayIDs[this.hand].forEach(function(overlayID) {
var overlayName = Overlays.getProperty(overlayID, "name"); var overlayName = Overlays.getProperty(overlayID, "name");
if (overlayName === "KeyboardAnchor") { if (overlayName === "KeyboardAnchor") {
stopRunning = true; stopRunning = true;
} }
}); });*/
if (stopRunning) { if (stopRunning) {
return this.exitModule(); return this.exitModule();