【C语言调用Python】Py_Finalize() 时报 GC 崩溃错误。

发布时间 2023-12-05 16:22:02作者: 阿初

Py_Finalize() 时报 GC 崩溃错误。

记一次有趣的报错随笔。

报错现场

在使用如下的报错代码时,在释放阶段调用Py_Finalize(),报如下Assert崩溃。

原因

结论

在调用函数逻辑里的Exit0中,对变量pModuleDictpClass进行了手动释放,引用计数-1(宏KLP_RELEASE),这两个变量是借用的引用变量,不需要修改引用计数,直接交给Py_Finalize()能够自动回收

修正后的代码只需要删除KLP_RELEASE(pModuleDict);KLP_RELEASE(pClass);即可。

如果其他代码对这两个变量进行了引用计数的增加,才需要手动减少,不然不需要手动释放。

参考:https://stackoverflow.com/questions/6757741/py-finalize-crashes-after-error-in-python

猜想(暴论)

CPython中的Get函数返回的变量都是借用的,不会实际增加引用计数。

报错代码

#define KLP_RELEASE(p) { if (p) { Py_DecRef(p); (p) = NULL; } }

// 调用函数
KL_DLLEXPORT KLcBool KLpePfEyeLaunch()
{
	KLcBool klBool = KL_FALSE;
	PPYOBJECT pModule = NULL;
	PPYOBJECT pModuleDict = NULL;
	PPYOBJECT pClass = NULL;
	PPYOBJECT pClassConstruct = NULL;
	PPYOBJECT pClassIns = NULL;
	PPYOBJECT pClassRet = NULL;

	pModule = PyImport_ImportModule("WizardUltra");
	KLP_PROCESS_ERROR(pModule);

	pModuleDict = PyModule_GetDict(pModule);
	KLP_PROCESS_ERROR(pModuleDict);

	pClass = PyDict_GetItemString(pModuleDict, "CwuKL25COM");
	KLP_PROCESS_ERROR(pClass);

	pClassConstruct = PyInstanceMethod_New(pClass);
	KLP_PROCESS_ERROR(pClassConstruct);

	pClassIns = PyObject_CallObject(pClassConstruct, NULL);
	KLP_PROCESS_ERROR(pClassIns);

	pClassRet = PyObject_CallMethod(pClassIns, "getValue", NULL);
	KLP_PROCESS_ERROR(pClassRet);

	PyArg_Parse(pClassRet, "i", &klBool);
	KLP_PROCESS_ERROR(klBool);

	KLLOG(KLOG_INFO, L"PfEye launch ret: %d", klBool);
	klBool = KL_TRUE;

Exit0:
	KLP_RELEASE(pModule);
    KLP_RELEASE(pModuleDict);
    KLP_RELEASE(pClass);
	KLP_RELEASE(pClassConstruct);
	KLP_RELEASE(pClassIns);
	KLP_RELEASE(pClassRet);
	return klBool;
}

// 释放函数
KL_DLLEXPORT KLcBool KLpUninitPy3(KLPPY3OBJECTLINKCONTAINERDATA_PTR pPy3ObjectData)
{
	KLcBool klBool = KL_FALSE;

	klBool = Py_IsInitialized();
	if (KL_TRUE == klBool)
	{
		Py_Finalize();
	}

	klBool = KL_TRUE;
Exit0:
	return klBool;
}