Skip to content

mono gc not set static field back after object moved when enable interp mode #100065

Closed

Description

Description

We are embeding mono in UnrealEngine, when we upgrade .ne8.0(v8.0.2), we found sgen-gc will not set static field back after object move when enable hotreloadcomponent.

Reproduction Steps

We have not separate code to reproduct it, but I will describe it as detailed as possible.

we have restore object in static field, when next pass it to internal call the object address after the gc have move it, the value not set back.
I show the code first:

public partial class FTicker : ScriptingWrapperOfType
{
	public static FTicker Internal_GetCoreTicker_Cache = null;
	[MethodImpl(MethodImplOptions.InternalCall)]
	private static extern FTicker Internal_GetCoreTicker();

	public static FTicker GetCoreTicker() 
	{
		if(Internal_GetCoreTicker_Cache == null)
		{
			Internal_GetCoreTicker_Cache = Internal_GetCoreTicker();
		}

		return Internal_GetCoreTicker_Cache;
	}

	[MethodImpl(MethodImplOptions.InternalCall)]
	public static extern void TearDownCoreTicker();

	[MethodImpl(MethodImplOptions.InternalCall)]
	public extern FDelegateHandle AddTicker(TDelegate__float_Ret_bool InDelegate, float InDelay = 0.0f);
}

the internal call

static FDelegateHandle_Proxy Internal_FTicker_AddTicker(MonoObject* self, MonoDelegate* InDelegate, float InDelay)
{
        // these code is use for debug this problem
	MonoImage* EngineImage = GetScriptingAssemblyManager().FindImage("UnrealEngine");
	if (EngineImage)
	{
		MonoClass* TickerClass = mono_class_from_name(EngineImage, "UnrealEngine", "FTicker");
		if (TickerClass)
		{
			MonoClassField* Internal_GetCoreTicker_Cache = mono_class_get_field_from_name(TickerClass, "Internal_GetCoreTicker_Cache");
			if (Internal_GetCoreTicker_Cache)
			{
				MonoObject* data = nullptr;
				MONO_ENTER_GC_UNSAFE
				mono_field_static_get_value(mono_class_vtable(mono_domain_get(), TickerClass), Internal_GetCoreTicker_Cache, &data);
				MONO_EXIT_GC_UNSAFE
			}
		}
	}
        // below is the real logic
	TRACE_INTERALCALL_PROFILER_EVENT_SCOPE
	TDelegate<bool(float)> InDelegate__Temp;
	BindScriptIfException<bool, float>(InDelegate__Temp, InDelegate, true);
	return FDelegateHandle_Proxy::Proxy(ReadonlyScriptingObjectConverter<FTicker>(self)->AddTicker(InDelegate__Temp, InDelay));
}

the code logic execute as below :

tickHandle = FTicker.GetCoreTicker().AddTicker(OnTick);
// gc occured
tickHandle1 = FTicker.GetCoreTicker().AddTicker(OnTick1);

// but the second call `Internal_FTicker_AddTicker` the `self` object the vtable have set null and then can't get the object class. 

I show the behaviors with hotreload component enable or not.

if we not enable hotreload component, just use jit execute mode, it work correctly:

the first call AddTicker, we can checkout the self object:

image

and we check the Internal_GetCoreTicker_Cache static field:

image

the second call the self object address have changed:

image

if we enable hotreload component, the static field will not write back:
the first call:

image

the second call: the self object address have not been changed but the vtable is null

image

Expected behavior

work correctly

Actual behavior

crash

Regression?

.net7.0 work correctly

Known Workarounds

no

Configuration

windows
build v8.0.2 tag
set excute mode mono_jit_set_aot_mode(MONO_AOT_MODE_INTERP_ONLY);

Other information

No response

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions