Skip to content

Unexpected crashes when freeing memory #240

Description

@Monkiiiiiiiiii

My game uses your lua extension to add in game scripting for the users, recentley i noticed a memory leak, when i fixed it in my script i realised that when freeing lua corountine the game crashes

i was running a new corountine each frame

`extends Node
class_name Lua_Script

var timeStart
var ActiveCoroutines:Array[Dictionary]
var Lua:LuaState=LuaState.new()
var BaseFuncBinds:=[]
signal ScriptStop
signal ScriptStart
var MaxExecTime:=25

@export_enum("Client","Server") var runside:int=0

@export var script_enabled:=false:
set(val):
script_enabled=val
if val:
Start()
else:
isRunning=false
ScriptStop.emit()

@export_multiline var code:String
var isRunning:=false
var ISReady:=false
func _ready() -> void:
ISReady=true
if isRunning==false and script_enabled:
Start()

func Start():

if code=="":
	if get_tree() == null:return
	await get_tree().create_timer(.5).timeout
	if code=="":return
	Start()
	return
	
if not ISReady:return
if isRunning==false and script_enabled and RunData.RunSide==runside:
	isRunning=true
	ActiveCoroutines.clear()
	BaseFuncBinds.clear()
	FinishedLastLoop=true
	var setup = LuaSetUp.GetSetUpLua(self)
	print(setup)
	Lua = setup[0]
	var env = setup[1]
	

	var MainLuaF = Lua.load_string(code, "startup_"+name, env)

	if MainLuaF is LuaError:
		print("ErrorStart: "+MainLuaF.message)
		return
	
	elif MainLuaF is LuaFunction:
		StartLuaCoroutine(MainLuaF)
		ScriptStart.emit()
	await get_tree().create_timer(1).timeout
	#print(Lua.globals.to_dictionary())

func StartLuaCoroutine(LuaF:LuaFunction,StartArgs:=[],StartNow:bool=false):
var fn=LuaF
if fn == null:return
var LuaCr:LuaCoroutine=LuaCoroutine.create(fn)
LuaCr

LuaCr.set_hook(_coroutine_hook_func,LuaThread.HOOK_MASK_COUNT,100000)
if LuaCr == null:return
var CrData = {
"co": LuaCr,
"userdata": null,
"active": true,
"StartArgs":StartArgs,
"new":true,
}

#LuaCr.failed.connect(LuaFail.bind(CrData))
ActiveCoroutines.append(CrData)
if StartNow:
	RunCoroutineByData(CrData)

func LuaFail(Data):
ActiveCoroutines.erase(Data)

func BindFunctionToBaseFunc(fun:LuaFunction,toBind:=""):
for x in BaseFuncBinds:
if x.Function == fun and x.Task==toBind:return
BaseFuncBinds.append({"Function"=fun,"Task"=toBind})

func RunBaseFunc(Name:String,Args:=[]):
if isRunning ==false or script_enabled==false:return
for i in BaseFuncBinds:
if i.Task == Name:
StartLuaCoroutine(i.Function,Args)

func _coroutine_hook_func(debug_info: LuaDebug):
if Time.get_ticks_msec()-LastYield > MaxExecTime:
return LuaThread.HOOK_YIELD

var LastYield:=Time.get_ticks_msec()
var FinishedLastLoop:=true
func _process(delta: float) -> void:

if not isRunning or not script_enabled or FinishedLastLoop==false : return


RunBaseFunc("process",[delta])

FinishedLastLoop=false
LastYield=Time.get_ticks_msec()
for Data in ActiveCoroutines:
	
	
	if not isRunning or not script_enabled: continue
	if Time.get_ticks_msec()-LastYield > MaxExecTime:
		print("TookTooLong")
		await get_tree().process_frame
		LastYield=Time.get_ticks_msec()
	RunCoroutineByData(Data)

FinishedLastLoop=true
Lua.collect_garbage()

func RunCoroutineByData(Data):
if Data["active"]==true:
if Data["co"]==null or not Data["co"] is LuaCoroutine:
EraseCor(Data)
return
if not Data["co"] is LuaCoroutine:
EraseCor(Data)
return

	var Res
	if Data["new"]:
		if Data["StartArgs"] == []:
			Res = Data["co"].resume()
		else:
			Res = Data["co"].resumev(Data["StartArgs"])
		Data["new"]=false
	else:
		
		Res = Data["co"].resume()
	
	if Res is LuaError:
		print("ERROR "+Res.message)
		EraseCor(Data)
		return
	if Data["co"].status == LuaCoroutine.STATUS_DEAD:
		EraseCor(Data)
		return
	if Data["co"].status == LuaCoroutine.STATUS_YIELD:
		Yielded(Data,Res)
		return
else:
	EraseCor(Data)

func EraseCor(CorData:Dictionary):

if ActiveCoroutines.has(CorData):
	var CRx:LuaCoroutine=CorData.co 
	if CorData.has("co"):
		#CorData.co=null
		#CorData.co.free()
		
		var CR:LuaCoroutine=CorData.co
		print(CR.status)
		#CR.failed.disconnect(LuaFail)
	#await get_tree().process_frame
	ActiveCoroutines.erase(CorData)
	#await get_tree().process_frame
	#print(CRx.get_reference_count())

func Yielded(Cor:Dictionary,Res):
if Res and Res.size()>1:
match Res[0]:
"sleep":
Cor["active"]=false
await get_tree().create_timer(float(Res[1])).timeout
Cor["active"]=true

`

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions