From 7cce49dddeb0a911574da89993025d85b8bd9290 Mon Sep 17 00:00:00 2001 From: montellese Date: Sat, 31 Jan 2015 15:29:59 +0100 Subject: [PATCH] python: extend ILanguageInvocationHandler to not have to call g_pythonParser directly in CPythonInvoker --- .../generic/ILanguageInvocationHandler.h | 4 + xbmc/interfaces/generic/ILanguageInvoker.h | 28 ++ xbmc/interfaces/python/PythonInvoker.cpp | 10 +- xbmc/interfaces/python/XBPython.cpp | 266 +++++++++--------- xbmc/interfaces/python/XBPython.h | 9 +- 5 files changed, 177 insertions(+), 140 deletions(-) diff --git a/xbmc/interfaces/generic/ILanguageInvocationHandler.h b/xbmc/interfaces/generic/ILanguageInvocationHandler.h index 716aab0b84df0..af87ae9fcaf04 100644 --- a/xbmc/interfaces/generic/ILanguageInvocationHandler.h +++ b/xbmc/interfaces/generic/ILanguageInvocationHandler.h @@ -29,10 +29,14 @@ class ILanguageInvocationHandler virtual bool Initialize() { return true; } virtual void Process() { } + virtual void PulseGlobalEvent() { } virtual void Uninitialize() { } + virtual bool OnScriptInitialized(ILanguageInvoker *invoker) { return true; } virtual void OnScriptStarted(ILanguageInvoker *invoker) { } + virtual void OnScriptAbortRequested(ILanguageInvoker *invoker) { } virtual void OnScriptEnded(ILanguageInvoker *invoker) { } + virtual void OnScriptFinalized(ILanguageInvoker *invoker) { } virtual ILanguageInvoker* CreateInvoker() = 0; }; diff --git a/xbmc/interfaces/generic/ILanguageInvoker.h b/xbmc/interfaces/generic/ILanguageInvoker.h index ebd192f3c11d1..8275fb31a4a42 100644 --- a/xbmc/interfaces/generic/ILanguageInvoker.h +++ b/xbmc/interfaces/generic/ILanguageInvoker.h @@ -56,6 +56,7 @@ class ILanguageInvoker void SetId(int id) { m_id = id; } int GetId() const { return m_id; } + const ADDON::AddonPtr& GetAddon() const { return m_addon; } void SetAddon(const ADDON::AddonPtr &addon) { m_addon = addon; } InvokerState GetState() const { return m_state; } bool IsActive() const { return GetState() > InvokerStateUninitialized && GetState() < InvokerStateDone; } @@ -68,17 +69,44 @@ class ILanguageInvoker virtual bool execute(const std::string &script, const std::vector &arguments) = 0; virtual bool stop(bool abort) = 0; + virtual void pulseGlobalEvent() + { + if (m_invocationHandler) + m_invocationHandler->PulseGlobalEvent(); + } + + virtual bool onExecutionInitialized() + { + if (m_invocationHandler == NULL) + return false; + + return m_invocationHandler->OnScriptInitialized(this); + } + + virtual void onAbortRequested() + { + if (m_invocationHandler) + m_invocationHandler->OnScriptAbortRequested(this); + } + virtual void onExecutionFailed() { if (m_invocationHandler) m_invocationHandler->OnScriptEnded(this); } + virtual void onExecutionDone() { if (m_invocationHandler) m_invocationHandler->OnScriptEnded(this); } + virtual void onExecutionFinalized() + { + if (m_invocationHandler) + m_invocationHandler->OnScriptFinalized(this); + } + void setState(InvokerState state) { if (state <= m_state) diff --git a/xbmc/interfaces/python/PythonInvoker.cpp b/xbmc/interfaces/python/PythonInvoker.cpp index f770714ef8bb5..8c654e8d83831 100644 --- a/xbmc/interfaces/python/PythonInvoker.cpp +++ b/xbmc/interfaces/python/PythonInvoker.cpp @@ -113,7 +113,7 @@ CPythonInvoker::~CPythonInvoker() CLog::Log(LOGDEBUG, "CPythonInvoker(%d): waiting for python thread \"%s\" to stop", GetId(), (!m_sourceFile.empty() ? m_sourceFile.c_str() : "unknown script")); Stop(true); - g_pythonParser.PulseGlobalEvent(); + pulseGlobalEvent(); if (m_argv != NULL) { @@ -121,7 +121,7 @@ CPythonInvoker::~CPythonInvoker() delete [] m_argv[i]; delete [] m_argv; } - g_pythonParser.FinalizeScript(); + onExecutionFinalized(); } bool CPythonInvoker::Execute(const std::string &script, const std::vector &arguments /* = std::vector() */) @@ -135,7 +135,7 @@ bool CPythonInvoker::Execute(const std::string &script, const std::vectorID()); + onAbortRequested(); PyObject *m; m = PyImport_AddModule((char*)"xbmc"); @@ -491,7 +491,7 @@ bool CPythonInvoker::stop(bool abort) } // If a dialog entered its doModal(), we need to wake it to see the exception - g_pythonParser.PulseGlobalEvent(); + pulseGlobalEvent(); } if (old != NULL) diff --git a/xbmc/interfaces/python/XBPython.cpp b/xbmc/interfaces/python/XBPython.cpp index cba242d6f1311..f179b12dca006 100644 --- a/xbmc/interfaces/python/XBPython.cpp +++ b/xbmc/interfaces/python/XBPython.cpp @@ -380,22 +380,6 @@ void XBPython::OnCleanFinished(const std::string &library) } } -void XBPython::OnAbortRequested(const std::string &ID) -{ - XBMC_TRACE; - LOCK_AND_COPY(std::vector,tmp,m_vecMonitorCallbackList); - for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it) - { - if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList,(*it))) - { - if (ID.empty()) - (*it)->OnAbortRequested(); - else if ((*it)->GetId() == ID) - (*it)->OnAbortRequested(); - } - } -} - void XBPython::OnNotification(const std::string &sender, const std::string &method, const std::string &data) { XBMC_TRACE; @@ -466,118 +450,6 @@ void XBPython::UnloadExtensionLibs() m_extensions.clear(); } -/** -* Should be called before executing a script -*/ -bool XBPython::InitializeEngine() -{ - XBMC_TRACE; - CLog::Log(LOGINFO, "initializing python engine."); - CSingleLock lock(m_critSection); - m_iDllScriptCounter++; - if (!m_bInitialized) - { - // first we check if all necessary files are installed -#ifndef TARGET_POSIX - if(!FileExist("special://xbmc/system/python/DLLs/_socket.pyd") || - !FileExist("special://xbmc/system/python/DLLs/_ssl.pyd") || - !FileExist("special://xbmc/system/python/DLLs/bz2.pyd") || - !FileExist("special://xbmc/system/python/DLLs/pyexpat.pyd") || - !FileExist("special://xbmc/system/python/DLLs/select.pyd") || - !FileExist("special://xbmc/system/python/DLLs/unicodedata.pyd")) - { - CLog::Log(LOGERROR, "Python: Missing files, unable to execute script"); - Finalize(); - return false; - } -#endif - - -// Darwin packs .pyo files, we need PYTHONOPTIMIZE on in order to load them. -#if defined(TARGET_DARWIN) - setenv("PYTHONOPTIMIZE", "1", 1); -#endif - // Info about interesting python envvars available - // at http://docs.python.org/using/cmdline.html#environment-variables - -#if !defined(TARGET_WINDOWS) && !defined(TARGET_ANDROID) - /* PYTHONOPTIMIZE is set off intentionally when using external Python. - Reason for this is because we cannot be sure what version of Python - was used to compile the various Python object files (i.e. .pyo, - .pyc, etc.). */ - // check if we are running as real xbmc.app or just binary - if (!CUtil::GetFrameworksPath(true).empty()) - { - // using external python, it's build looking for xxx/lib/python2.6 - // so point it to frameworks which is where python2.6 is located - setenv("PYTHONHOME", CSpecialProtocol::TranslatePath("special://frameworks").c_str(), 1); - setenv("PYTHONPATH", CSpecialProtocol::TranslatePath("special://frameworks").c_str(), 1); - CLog::Log(LOGDEBUG, "PYTHONHOME -> %s", CSpecialProtocol::TranslatePath("special://frameworks").c_str()); - CLog::Log(LOGDEBUG, "PYTHONPATH -> %s", CSpecialProtocol::TranslatePath("special://frameworks").c_str()); - } - setenv("PYTHONCASEOK", "1", 1); //This line should really be removed -#elif defined(TARGET_WINDOWS) - // because the third party build of python is compiled with vs2008 we need - // a hack to set the PYTHONPATH - std::string buf; - buf = "PYTHONPATH=" + CSpecialProtocol::TranslatePath("special://xbmc/system/python/DLLs") + ";" + CSpecialProtocol::TranslatePath("special://xbmc/system/python/Lib"); - CEnvironment::putenv(buf); - buf = "PYTHONOPTIMIZE=1"; - CEnvironment::putenv(buf); - buf = "PYTHONHOME=" + CSpecialProtocol::TranslatePath("special://xbmc/system/python"); - CEnvironment::putenv(buf); - buf = "OS=win32"; - CEnvironment::putenv(buf); - -#elif defined(TARGET_ANDROID) - std::string apkPath = getenv("XBMC_ANDROID_APK"); - apkPath += "/assets/python2.6"; - setenv("PYTHONHOME",apkPath.c_str(), 1); - setenv("PYTHONPATH", "", 1); - setenv("PYTHONOPTIMIZE","",1); - setenv("PYTHONNOUSERSITE","1",1); -#endif - - if (PyEval_ThreadsInitialized()) - PyEval_AcquireLock(); - else - PyEval_InitThreads(); - - Py_Initialize(); - PyEval_ReleaseLock(); - - // If this is not the first time we initialize Python, the interpreter - // lock already exists and we need to lock it as PyEval_InitThreads - // would not do that in that case. - PyEval_AcquireLock(); - char* python_argv[1] = { (char*)"" } ; - PySys_SetArgv(1, python_argv); - - if (!(m_mainThreadState = PyThreadState_Get())) - CLog::Log(LOGERROR, "Python threadstate is NULL."); - PyEval_ReleaseLock(); - - m_bInitialized = true; - } - - return m_bInitialized; -} - -/** -* Should be called when a script is finished -*/ -void XBPython::FinalizeScript() -{ - XBMC_TRACE; - CSingleLock lock(m_critSection); - // for linux - we never release the library. its loaded and stays in memory. - if (m_iDllScriptCounter) - m_iDllScriptCounter--; - else - CLog::Log(LOGERROR, "Python script counter attempted to become negative"); - m_endtime = XbmcThreads::SystemClockMillis(); -} - // Always called with the lock held on m_critSection void XBPython::Finalize() { @@ -631,7 +503,7 @@ void XBPython::Uninitialize() lock.Leave(); //unlock here because the python thread might lock when it exits // cleanup threads that are still running - tmpvec.clear(); // boost releases the XBPyThreads which, if deleted, calls FinalizeScript + tmpvec.clear(); // boost releases the XBPyThreads which, if deleted, calls OnScriptFinalized } void XBPython::Process() @@ -654,7 +526,7 @@ void XBPython::Process() lock.Leave(); //delete scripts which are done - tmpvec.clear(); // boost releases the XBPyThreads which, if deleted, calls FinalizeScript + tmpvec.clear(); // boost releases the XBPyThreads which, if deleted, calls OnScriptFinalized CSingleLock l2(m_critSection); if(m_iDllScriptCounter == 0 && (XbmcThreads::SystemClockMillis() - m_endtime) > 10000 ) @@ -664,6 +536,103 @@ void XBPython::Process() } } +bool XBPython::OnScriptInitialized(ILanguageInvoker *invoker) +{ + if (invoker == NULL) + return false; + + XBMC_TRACE; + CLog::Log(LOGINFO, "initializing python engine."); + CSingleLock lock(m_critSection); + m_iDllScriptCounter++; + if (!m_bInitialized) + { + // first we check if all necessary files are installed +#ifndef TARGET_POSIX + if (!FileExist("special://xbmc/system/python/DLLs/_socket.pyd") || + !FileExist("special://xbmc/system/python/DLLs/_ssl.pyd") || + !FileExist("special://xbmc/system/python/DLLs/bz2.pyd") || + !FileExist("special://xbmc/system/python/DLLs/pyexpat.pyd") || + !FileExist("special://xbmc/system/python/DLLs/select.pyd") || + !FileExist("special://xbmc/system/python/DLLs/unicodedata.pyd")) + { + CLog::Log(LOGERROR, "Python: Missing files, unable to execute script"); + Finalize(); + return false; + } +#endif + + + // Darwin packs .pyo files, we need PYTHONOPTIMIZE on in order to load them. +#if defined(TARGET_DARWIN) + setenv("PYTHONOPTIMIZE", "1", 1); +#endif + // Info about interesting python envvars available + // at http://docs.python.org/using/cmdline.html#environment-variables + +#if !defined(TARGET_WINDOWS) && !defined(TARGET_ANDROID) + /* PYTHONOPTIMIZE is set off intentionally when using external Python. + Reason for this is because we cannot be sure what version of Python + was used to compile the various Python object files (i.e. .pyo, + .pyc, etc.). */ + // check if we are running as real xbmc.app or just binary + if (!CUtil::GetFrameworksPath(true).empty()) + { + // using external python, it's build looking for xxx/lib/python2.6 + // so point it to frameworks which is where python2.6 is located + setenv("PYTHONHOME", CSpecialProtocol::TranslatePath("special://frameworks").c_str(), 1); + setenv("PYTHONPATH", CSpecialProtocol::TranslatePath("special://frameworks").c_str(), 1); + CLog::Log(LOGDEBUG, "PYTHONHOME -> %s", CSpecialProtocol::TranslatePath("special://frameworks").c_str()); + CLog::Log(LOGDEBUG, "PYTHONPATH -> %s", CSpecialProtocol::TranslatePath("special://frameworks").c_str()); + } + setenv("PYTHONCASEOK", "1", 1); //This line should really be removed +#elif defined(TARGET_WINDOWS) + // because the third party build of python is compiled with vs2008 we need + // a hack to set the PYTHONPATH + std::string buf; + buf = "PYTHONPATH=" + CSpecialProtocol::TranslatePath("special://xbmc/system/python/DLLs") + ";" + CSpecialProtocol::TranslatePath("special://xbmc/system/python/Lib"); + CEnvironment::putenv(buf); + buf = "PYTHONOPTIMIZE=1"; + CEnvironment::putenv(buf); + buf = "PYTHONHOME=" + CSpecialProtocol::TranslatePath("special://xbmc/system/python"); + CEnvironment::putenv(buf); + buf = "OS=win32"; + CEnvironment::putenv(buf); + +#elif defined(TARGET_ANDROID) + std::string apkPath = getenv("XBMC_ANDROID_APK"); + apkPath += "/assets/python2.6"; + setenv("PYTHONHOME", apkPath.c_str(), 1); + setenv("PYTHONPATH", "", 1); + setenv("PYTHONOPTIMIZE", "", 1); + setenv("PYTHONNOUSERSITE", "1", 1); +#endif + + if (PyEval_ThreadsInitialized()) + PyEval_AcquireLock(); + else + PyEval_InitThreads(); + + Py_Initialize(); + PyEval_ReleaseLock(); + + // If this is not the first time we initialize Python, the interpreter + // lock already exists and we need to lock it as PyEval_InitThreads + // would not do that in that case. + PyEval_AcquireLock(); + char* python_argv[1] = { (char*)"" }; + PySys_SetArgv(1, python_argv); + + if (!(m_mainThreadState = PyThreadState_Get())) + CLog::Log(LOGERROR, "Python threadstate is NULL."); + PyEval_ReleaseLock(); + + m_bInitialized = true; + } + + return m_bInitialized; +} + void XBPython::OnScriptStarted(ILanguageInvoker *invoker) { if (invoker == NULL) @@ -680,6 +649,31 @@ void XBPython::OnScriptStarted(ILanguageInvoker *invoker) m_vecPyList.push_back(inf); } +void XBPython::OnScriptAbortRequested(ILanguageInvoker *invoker) +{ + XBMC_TRACE; + + std::string addonId; + if (invoker != NULL) + { + const ADDON::AddonPtr& addon = invoker->GetAddon(); + if (addon != NULL) + addonId = addon->ID(); + } + + LOCK_AND_COPY(std::vector, tmp, m_vecMonitorCallbackList); + for (MonitorCallbackList::iterator it = tmp.begin(); (it != tmp.end()); ++it) + { + if (CHECK_FOR_ENTRY(m_vecMonitorCallbackList, (*it))) + { + if (addonId.empty()) + (*it)->OnAbortRequested(); + else if ((*it)->GetId() == addonId) + (*it)->OnAbortRequested(); + } + } +} + void XBPython::OnScriptEnded(ILanguageInvoker *invoker) { CSingleLock lock(m_vecPyList); @@ -698,6 +692,18 @@ void XBPython::OnScriptEnded(ILanguageInvoker *invoker) } } +void XBPython::OnScriptFinalized(ILanguageInvoker *invoker) +{ + XBMC_TRACE; + CSingleLock lock(m_critSection); + // for linux - we never release the library. its loaded and stays in memory. + if (m_iDllScriptCounter) + m_iDllScriptCounter--; + else + CLog::Log(LOGERROR, "Python script counter attempted to become negative"); + m_endtime = XbmcThreads::SystemClockMillis(); +} + ILanguageInvoker* XBPython::CreateInvoker() { return new CAddonPythonInvoker(this); diff --git a/xbmc/interfaces/python/XBPython.h b/xbmc/interfaces/python/XBPython.h index 819523355e18c..972281a4e97e1 100644 --- a/xbmc/interfaces/python/XBPython.h +++ b/xbmc/interfaces/python/XBPython.h @@ -89,19 +89,18 @@ class XBPython : void OnScanFinished(const std::string &library); void OnCleanStarted(const std::string &library); void OnCleanFinished(const std::string &library); - void OnAbortRequested(const std::string &ID=""); void OnNotification(const std::string &sender, const std::string &method, const std::string &data); virtual void Process(); + virtual void PulseGlobalEvent(); virtual void Uninitialize(); + virtual bool OnScriptInitialized(ILanguageInvoker *invoker); virtual void OnScriptStarted(ILanguageInvoker *invoker); + virtual void OnScriptAbortRequested(ILanguageInvoker *invoker); virtual void OnScriptEnded(ILanguageInvoker *invoker); + virtual void OnScriptFinalized(ILanguageInvoker *invoker); virtual ILanguageInvoker* CreateInvoker(); - bool InitializeEngine(); - void FinalizeScript(); - - void PulseGlobalEvent(); bool WaitForEvent(CEvent& hEvent, unsigned int milliseconds); void RegisterExtensionLib(LibraryLoader *pLib);