Skip to content
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

Is it possible to initialize a UJavascriptContext not on the Main thread? #259

Open
nickfrev opened this issue Aug 6, 2019 · 3 comments
Open

Comments

@nickfrev
Copy link

@nickfrev nickfrev commented Aug 6, 2019

I'm not initializing the UJavascriptContext when the game begins. I am starting it on a trigger (Such as a user touching a block). Doing this I've noticed a large pause of about 1-2 seconds when calling the below code. I've tried to initialize the Context in a thread and pass it back to the main thread when it's finished but that causes an Access violation error. The code runs fine in the main thread.

The chunk of code I'm testing with:

UJavascriptIsolate* Isolate = nullptr;
Isolate = NewObject<UJavascriptIsolate>();
Isolate->Init(false);
Isolate->AddToRoot();
auto* Context = Isolate->CreateContext();

Is there a way to do this properly?

My current set-up:
I started a fresh project using the C++ First Person Template. I have added a single custom class (Source Code below). I attach this class to a cube with a contact trigger. When triggered the custom class calls LoadJSIsolate(). There is a boolean to toggle between loading on the main thread and loading on a secondary thread. When run on the main thread, everything works. When running in a secondary thread it throws an Access violation error:

Unhandled exception at 0x00007FF87DCD156E (UE4Editor-V8.dll) in UE4Editor.exe: 0xC0000005: Access violation reading location 0xFFFFFFFFFFFFFFFF.

Code:
Spell.h

// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "JavascriptIsolate.h"
#include "JavascriptComponent.h"
#include "IV8.h"
#include "Spell.generated.h"


UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) )
class SPELLCRAFT_API USpell : public UActorComponent
{
	GENERATED_BODY()

public:	
	// Sets default values for this component's properties
	USpell();

	FAsyncTask<class JavascriptIsolateGeneratorTask>* LoadJSEngineAsyncTask;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	bool Main_Thread;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UJavascriptContext *Source_Context;

	UFUNCTION()
	void CatchEngineIsolates(UJavascriptContext* SourceContext);

protected:
	// Called when the game starts
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;

	UFUNCTION(BlueprintCallable, Category = "Components")
	void LoadJSIsolate();
};

//==========================
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FLoadJSEngine_IsolatePass, class UJavascriptContext*, SourceContext);
class JavascriptIsolateGeneratorTask : public FNonAbandonableTask {
private:

public:
	JavascriptIsolateGeneratorTask();
	~JavascriptIsolateGeneratorTask();

	FLoadJSEngine_IsolatePass IsolatePass;

	// required by UE4
	FORCEINLINE TStatId GetStatId() const {
		RETURN_QUICK_DECLARE_CYCLE_STAT(JavascriptIsolateGeneratorTask, STATGROUP_ThreadPoolAsyncTasks);
	}

	void DoWork();
};

Spell.cpp

// Fill out your copyright notice in the Description page of Project Settings.

#include "Spell.h"
#include "JavascriptIsolate.h"
#include "JavascriptContext.h"
#include "IV8.h"

// Sets default values for this component's properties
USpell::USpell()
{
	// Set this component to be initialized when the game starts, and to be ticked every frame.  You can turn these features
	// off to improve performance if you don't need them.
	PrimaryComponentTick.bCanEverTick = true;

	Main_Thread = true;
	// ...
}

// Called when the game starts
void USpell::BeginPlay()
{
	Super::BeginPlay();

	// ...
	
}


// Called every frame
void USpell::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
	Super::TickComponent(DeltaTime, TickType, ThisTickFunction);

	// ...
}

void USpell::CatchEngineIsolates(UJavascriptContext* SourceContext) {
	this->Source_Context = SourceContext;

	UE_LOG(LogTemp, Warning, TEXT("Secondary Thread JS Context Done"));
}

void USpell::LoadJSIsolate() {
	if (Main_Thread) {
		UJavascriptIsolate* Isolate = nullptr;
		Isolate = NewObject<UJavascriptIsolate>();
		Isolate->Init(false);
		auto* Context = Isolate->CreateContext();
		UE_LOG(LogTemp, Warning, TEXT("Main Thread JS Context Done"));
	} else {
		LoadJSEngineAsyncTask = new FAsyncTask<JavascriptIsolateGeneratorTask>();
		LoadJSEngineAsyncTask->GetTask().IsolatePass.AddDynamic(this, &USpell::CatchEngineIsolates);
		LoadJSEngineAsyncTask->StartBackgroundTask();
		UE_LOG(LogTemp, Warning, TEXT("Task Started"));
	}
}

//==========================

JavascriptIsolateGeneratorTask::JavascriptIsolateGeneratorTask() {
}

JavascriptIsolateGeneratorTask::~JavascriptIsolateGeneratorTask() {
	UE_LOG(LogTemp, Warning, TEXT("Task Finished"));
}

void JavascriptIsolateGeneratorTask::DoWork() {

	UE_LOG(LogTemp, Warning, TEXT("Do Work - Start"));

	UJavascriptIsolate* Isolate = nullptr;
	Isolate = NewObject<UJavascriptIsolate>();
	Isolate->Init(false);
	UJavascriptContext* Context = nullptr;
	Context = Isolate->CreateContext();
	
	UE_LOG(LogTemp, Warning, TEXT("Do Work - End"));

	// Broadcast Delegate
	IsolatePass.Broadcast(Context);
}
@crocuis

This comment has been minimized.

Copy link
Contributor

@crocuis crocuis commented Aug 6, 2019

Have you created multiple isolates in the main thread?

@nickfrev

This comment has been minimized.

Copy link
Author

@nickfrev nickfrev commented Aug 6, 2019

I'm currently only working with the one isolate at the moment.

I added more information about my set-up in the initial question.

@nakosung

This comment has been minimized.

Copy link
Member

@nakosung nakosung commented Aug 20, 2019

There might be some pieces of code which run on main thread to process timer things and so on. If you want to tick your isolate within other threads, I think the codes I mentioned should be guarded or off-loaded to other threads for thread safety.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants
You can’t perform that action at this time.