/*
** Function: 当进程中有多个模块要使用python时,只能公用同一套python环境,此时python环境的初始化和反初始化需要协调,因此提供一个公共模块来对
** python环境的初始化和反初始化进行控制,避免各个模块随意修改改python环境导致模块无法并存。
** Usage: 使用方法,让这些使用python的模块都依赖PythonInst,一些初始化和环境配置的动作统一调PythonInst接口完成。
*/
#ifdef PYTHONINST_EXPORTS
#define PYTHONINST_API __declspec(dllexport)
#else
#define PYTHONINST_API __declspec(dllimport)
#endif
#define PYTHONINST_CONST_HANDLE 1
#ifdef __cplusplus
extern "C"
{
#endif
/*
** @ Name: Python_Init
** @ Brief: Python初始化,如果本接口是被首次调用,将检测Python环境是否已初始化,如果未初始化则执行初始化,否则直接返回PYTHONINST_CONST_HANDLE;
** 如果不是首次调用,则返回PYTHONINST_CONST_HANDLE或者返回一个新建句柄。
** @ Param: const wchar_t* strPythonHome 设置python工作目录(Python_Init第一次被调用时才生效)。
** @ Return: void* 返回引用句柄(接口失败时返回NULL)。该句柄是本模块调用初始化接口的句柄凭证,在模块需要退出时,传递给Fini进行解引用。
*/
PYTHONINST_API void* __stdcall Python_Init(const wchar_t* strPythonHome);
/*
** @ Name: Python_Fini
** @ Brief: Python反初始化,如果hHandle为有效句柄,则引用计数减少(减到0时将销毁Python解释器),如果句柄无效则不做任何处理。
** @ Param: void* hHandle 由Python_Init创建得到的句柄,hHandle可能是PYTHONINST_CONST_HANDLE / 有效句柄 / NULL
** @ Return: void
*/
PYTHONINST_API void __stdcall Python_Fini(void* hHandle);
/*
** @ Name: Python_SetGlobalPyHome
** @ Brief: 设置全局的Python工作空间,等同于Python_Init传入的参数,在Python_Init首次被调用之前传入有效。
** @ Param: const wchar_t* strPath Python工作空间路径,设为NULL时,表示使用默认路径作为Python工作空间。
** @ Return: int 返回0表示成功,否则表示错误码。
*/
PYTHONINST_API int __stdcall Python_SetGlobalPythonHome(const wchar_t* strPath);
/*
** @ Name: Python_AppendPythonHome
** @ Brief: 追加Python工作空间路径。
** @ Param: const wchar_t* strPath Python工作空间路径。
** @ Return: int 返回0表示成功,否则表示错误码。
*/
PYTHONINST_API int __stdcall Python_AppendPythonHome(const wchar_t* strPath);
/*
** @ Name: Python_SetGlobalInstance
** @ Brief: 使能/关闭 全局Python实例功能(Python_Init首次被调用之前传入有效)。如果开启,则在Python_Init被调用时,额外创建一个全局的伴生
** Handle,即便所有的实例都被Fini(全局Handle除外),python解释器因为全局实例未销毁从而继续被保留,其他模块还可以继续使用python解释器。
** @ Param: int enable 是否开启,1表示开启,0表示关闭。
** @ Return: int 返回0表示成功,否则表示错误码。
*/
PYTHONINST_API int __stdcall Python_SetGlobalInstanceEnable(int enable);
/*
** @ Name: Python_GetGlobalInstance
** @ Brief: 获取全局Python实例句柄。全局实例句柄在运行过程中会失效,外部不要做缓存,需要时来查询一下即可。
** @ Param: void
** @ Return: void* 全局实例句柄。
*/
PYTHONINST_API void* __stdcall Python_GetGlobalInstance();
#ifdef __cplusplus
}
#endif
#include <python.h>
#include <map>
#include <mutex>
#include <memory>
#define PYTHONINST_EXPORTS
#include "PythonInst.h"
class PythonInstance
{
public:
static PythonInstance* CreateInstance(const wchar_t* strPythonHome);
static void DestroyInstance(PythonInstance* handle);
~PythonInstance();
static int PreparePythonHome(const wchar_t* strPythonHome);
static int SetGlobalInstEnable(int bEnable);
static int SetPythonHome(const wchar_t* strPythonHome, bool append=false);
static PythonInstance* GetGlobalInst();
private:
static std::mutex s_PyInitMutex;
static std::map<void*, std::shared_ptr<PythonInstance>> s_mapInst;
static std::wstring s_strPythonHome;
static int s_bGloobalInst;
static PythonInstance* s_GlobalHandle;
private:
PythonInstance();
static void SetPythonGlobal();
static int CreateGlobalInstWhenEnable();
static void ResetIfGlobalInst(void* handle);
};
class PythonThreadLocker // python lock guard
{
public:
PythonThreadLocker()
{
//if (PyGILState_Check() != 1)
state = PyGILState_Ensure();
}
~PythonThreadLocker()
{
//if(state != PyGILState_UNLOCKED)
PyGILState_Release(state);
}
private:
PyGILState_STATE state = PyGILState_UNLOCKED;
};
typedef std::shared_ptr<PythonInstance> PythonInstancePtr;
std::mutex PythonInstance::s_PyInitMutex;
std::map<void*, PythonInstancePtr> PythonInstance::s_mapInst;
std::wstring PythonInstance::s_strPythonHome = L"";
int PythonInstance::s_bGloobalInst = 0;
PythonInstance* PythonInstance::s_GlobalHandle = NULL;
PythonInstance::PythonInstance()
{}
PythonInstance::~PythonInstance()
{}
void PythonInstance::SetPythonGlobal()
{
if (0 != s_strPythonHome.compare(L""))
{
const wchar_t* envStr = Py_GetPythonHome();
wchar_t env[1024] = {};
if (envStr != NULL)
{
_snwprintf_s(env, 1024,1023, L"%s;%s", envStr, s_strPythonHome.c_str());
Py_SetPythonHome(env);
}
else
Py_SetPythonHome(s_strPythonHome.c_str());
}
}
int PythonInstance::SetPythonHome(const wchar_t* strPythonHome, bool append )
{
if (NULL != strPythonHome && Py_IsInitialized())
{
PythonThreadLocker lock;
if (append)
{
const wchar_t* envStr = Py_GetPythonHome();
if (envStr != NULL)
{
wchar_t env[1024] = {};
_snwprintf_s(env, 1024, 1023, L"%s;%s", envStr, strPythonHome);
Py_SetPythonHome(env);
}
else
Py_SetPythonHome(strPythonHome);
}
else
Py_SetPythonHome(strPythonHome);
return 0;
}
return -1;
}
int PythonInstance::CreateGlobalInstWhenEnable()
{
if (0 != s_bGloobalInst)
{
std::shared_ptr<PythonInstance> pInst(new PythonInstance());
if (nullptr == pInst)
{
return -1;
}
s_mapInst[pInst.get()] = pInst;
ResetIfGlobalInst(s_GlobalHandle);
s_GlobalHandle = pInst.get();
}
return 0;
}
void PythonInstance::ResetIfGlobalInst(void* handle)
{
if (handle == s_GlobalHandle)
{
auto it = s_mapInst.find(s_GlobalHandle);
if (it != s_mapInst.end())
s_mapInst.erase(s_GlobalHandle);
s_GlobalHandle = NULL;
}
}
#define GLOBAL_MODE_RETURN() {if(s_GlobalHandle != NULL) \
{\
auto it = s_mapInst.find(s_GlobalHandle); \
if(it != s_mapInst.end()) \
{\
return (PythonInstance*)PYTHONINST_CONST_HANDLE;\
}\
}\
}
PythonInstance* PythonInstance::CreateInstance(const wchar_t* strPythonHome)
{
std::lock_guard<std::mutex> cLock(s_PyInitMutex);
PythonInstancePtr pInst = PythonInstancePtr(new PythonInstance);
if (NULL == pInst)
return NULL;
if (0 == s_mapInst.size())
{
if (Py_IsInitialized())
{
// 这种情况说明模块没有调用本库的Python_Init接口完成python初始化,而是在其他地方对python环境初始化了,本
// 库不负责引用计数管理,直接返回(避免Destroy接口内部在销毁句柄时报错,返回非NULL)
return (PythonInstance*)PYTHONINST_CONST_HANDLE;
}
// 先进行全局设置
SetPythonGlobal();
//如果入参提供了工作空间,则更新为用户指定的工作空间
SetPythonHome(strPythonHome);
// 初始化
Py_Initialize();
if (!Py_IsInitialized())
return NULL;
/*if (!PyEval_ThreadsInitialized()) // depreacted from python 3.7 使能多线程,自3.7以后,该接口功能合并到了Py_Initialize
{
PyEval_InitThreads();
}*/
CreateGlobalInstWhenEnable();//如果开启了全局实例,则额外创建一个全局伴生实例
}
GLOBAL_MODE_RETURN();
s_mapInst[pInst.get()] = pInst;
return pInst.get();
}
void PythonInstance::DestroyInstance(PythonInstance* handle)
{
std::lock_guard<std::mutex> cLock(s_PyInitMutex);
if (NULL == handle)
return;
auto it = s_mapInst.find(handle);
if (it != s_mapInst.end())
s_mapInst.erase(it);
ResetIfGlobalInst(handle);
if (0 != s_mapInst.size())
return;
try {
PyGILState_STATE gilOk=PyGILState_Ensure();
Py_Finalize();
s_GlobalHandle = NULL;
//PyGILState_Release(gilOk);//no need to release, because things has been deinitialized by Py_Finalize.
}
catch (...)
{
PyErr_SetString(PyExc_RuntimeError,"PythonInstance Deinitialization Failed.");
}
}
int PythonInstance::PreparePythonHome(const wchar_t* strPythonHome)
{
std::lock_guard<std::mutex> cLock(s_PyInitMutex);
if (NULL == strPythonHome)
s_strPythonHome = L"";
else
s_strPythonHome = strPythonHome;
return 0;
}
int PythonInstance::SetGlobalInstEnable(int bEnable)
{
std::lock_guard<std::mutex> cLock(s_PyInitMutex);
s_bGloobalInst = bEnable;
return 0;
}
PythonInstance* PythonInstance::GetGlobalInst()
{
std::lock_guard<std::mutex> cLock(s_PyInitMutex);
return s_GlobalHandle;
}
PYTHONINST_API void* __stdcall Python_Init(const wchar_t* strPythonHome)
{
return PythonInstance::CreateInstance(strPythonHome);
}
PYTHONINST_API void __stdcall Python_Fini(void* hHandle)
{
return PythonInstance::DestroyInstance((PythonInstance*)hHandle);
}
PYTHONINST_API int __stdcall Python_SetGlobalPythonHome(const wchar_t* strPath)
{
return PythonInstance::PreparePythonHome(strPath);
}
PYTHONINST_API int __stdcall Python_AppendPythonHome(const wchar_t* strPath)
{
return PythonInstance::SetPythonHome(strPath,true);
}
PYTHONINST_API int __stdcall Python_SetGlobalInstanceEnable(int enable)
{
return PythonInstance::SetGlobalInstEnable(enable);
}
PYTHONINST_API void* __stdcall Python_GetGlobalInstance()
{
return PythonInstance::GetGlobalInst();
}
众所周知,cc++中调用任何python 接口前,必须对python 解释器进行初始化,并进行必要的环境配置,这些初始化和环境配置是全局生效的,如果这些接口放到各个模块内部执行,将不利于管理,所以需要提炼一个公共模块,对python的全局环境进行初始化和配置,增加防护措施,以使各个模块和谐共存。