diff options
author | Jim Carroll <jim@dontcallme.com> | 2019-10-20 15:51:20 -0400 |
---|---|---|
committer | Jim Carroll <jim@dontcallme.com> | 2019-10-20 15:51:20 -0400 |
commit | 312ee95425f3d47215093e3fd0c67de1f42c8c28 (patch) | |
tree | 1a8a0aa857b01dfafb0b6cd0464d41e0d079129f | |
parent | 8679e4d5e298b3a7bae575c0fbb69bd8e405694f (diff) |
Revert "Revert "Reusepython""
-rw-r--r-- | addons/xbmc.addon/metadata.xsd | 1 | ||||
-rw-r--r-- | xbmc/addons/addoninfo/AddonInfoBuilder.cpp | 5 | ||||
-rw-r--r-- | xbmc/filesystem/PluginDirectory.cpp | 19 | ||||
-rw-r--r-- | xbmc/filesystem/PluginDirectory.h | 2 | ||||
-rw-r--r-- | xbmc/interfaces/generic/ILanguageInvocationHandler.h | 2 | ||||
-rw-r--r-- | xbmc/interfaces/generic/ILanguageInvoker.cpp | 6 | ||||
-rw-r--r-- | xbmc/interfaces/generic/ILanguageInvoker.h | 4 | ||||
-rw-r--r-- | xbmc/interfaces/generic/LanguageInvokerThread.cpp | 49 | ||||
-rw-r--r-- | xbmc/interfaces/generic/LanguageInvokerThread.h | 15 | ||||
-rw-r--r-- | xbmc/interfaces/generic/ScriptInvocationManager.cpp | 101 | ||||
-rw-r--r-- | xbmc/interfaces/generic/ScriptInvocationManager.h | 38 | ||||
-rw-r--r-- | xbmc/interfaces/python/PythonInvoker.cpp | 384 | ||||
-rw-r--r-- | xbmc/interfaces/python/PythonInvoker.h | 9 | ||||
-rw-r--r-- | xbmc/interfaces/python/XBPython.cpp | 6 | ||||
-rw-r--r-- | xbmc/interfaces/python/XBPython.h | 2 |
15 files changed, 424 insertions, 219 deletions
diff --git a/addons/xbmc.addon/metadata.xsd b/addons/xbmc.addon/metadata.xsd index 03fbf241f5..89058e9e47 100644 --- a/addons/xbmc.addon/metadata.xsd +++ b/addons/xbmc.addon/metadata.xsd @@ -16,6 +16,7 @@ <xs:element name="email" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="broken" type="xs:string" minOccurs="0" maxOccurs="1"/> <xs:element name="news" type="xs:string" minOccurs="0" maxOccurs="1"/> + <xs:element name="reuselanguageinvoker" type="xs:boolean" minOccurs="0" maxOccurs="1"/> <xs:element name="assets" type="assetsList" minOccurs="0" maxOccurs="1"/> </xs:choice> <xs:attribute name="point" type="xs:string" use="required"/> diff --git a/xbmc/addons/addoninfo/AddonInfoBuilder.cpp b/xbmc/addons/addoninfo/AddonInfoBuilder.cpp index ef80cb513d..233789587d 100644 --- a/xbmc/addons/addoninfo/AddonInfoBuilder.cpp +++ b/xbmc/addons/addoninfo/AddonInfoBuilder.cpp @@ -318,6 +318,11 @@ bool CAddonInfoBuilder::ParseXML(const AddonInfoPtr& addon, const TiXmlElement* if (element && element->GetText() != nullptr) addon->AddExtraInfo("language", element->GetText()); + /* Parse addon.xml "<reuselanguageinvoker">...</reuselanguageinvoker>" */ + element = child->FirstChildElement("reuselanguageinvoker"); + if (element && element->GetText() != nullptr) + addon->AddExtraInfo("reuselanguageinvoker", element->GetText()); + /* Parse addon.xml "<noicon">...</noicon>" */ if (addon->m_icon.empty()) { diff --git a/xbmc/filesystem/PluginDirectory.cpp b/xbmc/filesystem/PluginDirectory.cpp index aaa4b94d30..6766f86bbc 100644 --- a/xbmc/filesystem/PluginDirectory.cpp +++ b/xbmc/filesystem/PluginDirectory.cpp @@ -86,6 +86,12 @@ int CPluginDirectory::getNewHandle(CPluginDirectory *cp) return handle; } +void CPluginDirectory::reuseHandle(int handle, CPluginDirectory *cp) +{ + CSingleLock lock(m_handleLock); + globalHandles[handle] = cp; +} + void CPluginDirectory::removeHandle(int handle) { CSingleLock lock(m_handleLock); @@ -124,7 +130,12 @@ bool CPluginDirectory::StartScript(const std::string& strPath, bool retrievingDi std::string basePath(url.Get()); // reset our wait event, and grab a new handle m_fetchComplete.Reset(); - int handle = getNewHandle(this); + int handle = CScriptInvocationManager::GetInstance().GetReusablePluginHandle(m_addon->LibPath()); + + if (handle < 0) + handle = getNewHandle(this); + else + reuseHandle(handle, this); // clear out our status variables m_fileResult->Reset(); @@ -151,7 +162,11 @@ bool CPluginDirectory::StartScript(const std::string& strPath, bool retrievingDi CLog::Log(LOGDEBUG, "%s - calling plugin %s('%s','%s','%s','%s')", __FUNCTION__, m_addon->Name().c_str(), argv[0].c_str(), argv[1].c_str(), argv[2].c_str(), argv[3].c_str()); bool success = false; std::string file = m_addon->LibPath(); - int id = CScriptInvocationManager::GetInstance().ExecuteAsync(file, m_addon, argv); + bool reuseLanguageInvoker = false; + if (m_addon->ExtraInfo().find("reuselanguageinvoker") != m_addon->ExtraInfo().end()) + reuseLanguageInvoker = m_addon->ExtraInfo().at("reuselanguageinvoker") == "true"; + + int id = CScriptInvocationManager::GetInstance().ExecuteAsync(file, m_addon, argv, reuseLanguageInvoker, handle); if (id >= 0) { // wait for our script to finish std::string scriptName = m_addon->Name(); diff --git a/xbmc/filesystem/PluginDirectory.h b/xbmc/filesystem/PluginDirectory.h index 3eb8bfaf48..039c29d52a 100644 --- a/xbmc/filesystem/PluginDirectory.h +++ b/xbmc/filesystem/PluginDirectory.h @@ -75,6 +75,8 @@ private: static std::map<int,CPluginDirectory*> globalHandles; static int getNewHandle(CPluginDirectory *cp); + static void reuseHandle(int handle, CPluginDirectory *cp); + static void removeHandle(int handle); static CPluginDirectory *dirFromHandle(int handle); static CCriticalSection m_handleLock; diff --git a/xbmc/interfaces/generic/ILanguageInvocationHandler.h b/xbmc/interfaces/generic/ILanguageInvocationHandler.h index 3057132cfb..e3912355ca 100644 --- a/xbmc/interfaces/generic/ILanguageInvocationHandler.h +++ b/xbmc/interfaces/generic/ILanguageInvocationHandler.h @@ -24,7 +24,7 @@ public: virtual bool OnScriptInitialized(ILanguageInvoker *invoker) { return true; } virtual void OnScriptStarted(ILanguageInvoker *invoker) { } virtual void OnScriptAbortRequested(ILanguageInvoker *invoker) { } - virtual void OnScriptEnded(ILanguageInvoker *invoker) { } + virtual void OnExecutionEnded(ILanguageInvoker *invoker) { } virtual void OnScriptFinalized(ILanguageInvoker *invoker) { } virtual ILanguageInvoker* CreateInvoker() = 0; diff --git a/xbmc/interfaces/generic/ILanguageInvoker.cpp b/xbmc/interfaces/generic/ILanguageInvoker.cpp index 0578c75978..9918c583ac 100644 --- a/xbmc/interfaces/generic/ILanguageInvoker.cpp +++ b/xbmc/interfaces/generic/ILanguageInvoker.cpp @@ -36,7 +36,7 @@ bool ILanguageInvoker::Stop(bool abort /* = false */) bool ILanguageInvoker::IsActive() const { - return GetState() > InvokerStateUninitialized && GetState() < InvokerStateDone; + return GetState() > InvokerStateUninitialized && GetState() < InvokerStateScriptDone; } bool ILanguageInvoker::IsRunning() const @@ -72,13 +72,13 @@ void ILanguageInvoker::onAbortRequested() void ILanguageInvoker::onExecutionFailed() { if (m_invocationHandler) - m_invocationHandler->OnScriptEnded(this); + m_invocationHandler->OnExecutionEnded(this); } void ILanguageInvoker::onExecutionDone() { if (m_invocationHandler) - m_invocationHandler->OnScriptEnded(this); + m_invocationHandler->OnExecutionEnded(this); } void ILanguageInvoker::onExecutionFinalized() diff --git a/xbmc/interfaces/generic/ILanguageInvoker.h b/xbmc/interfaces/generic/ILanguageInvoker.h index 3a8f6819fc..29cc422793 100644 --- a/xbmc/interfaces/generic/ILanguageInvoker.h +++ b/xbmc/interfaces/generic/ILanguageInvoker.h @@ -22,7 +22,8 @@ typedef enum { InvokerStateInitialized, InvokerStateRunning, InvokerStateStopping, - InvokerStateDone, + InvokerStateScriptDone, + InvokerStateExecutionDone, InvokerStateFailed } InvokerState; @@ -43,6 +44,7 @@ public: InvokerState GetState() const { return m_state; } bool IsActive() const; bool IsRunning() const; + void Reset() { m_state = InvokerStateUninitialized; }; protected: friend class CLanguageInvokerThread; diff --git a/xbmc/interfaces/generic/LanguageInvokerThread.cpp b/xbmc/interfaces/generic/LanguageInvokerThread.cpp index 81c923ec61..87359b00c4 100644 --- a/xbmc/interfaces/generic/LanguageInvokerThread.cpp +++ b/xbmc/interfaces/generic/LanguageInvokerThread.cpp @@ -10,11 +10,12 @@ #include "ScriptInvocationManager.h" -CLanguageInvokerThread::CLanguageInvokerThread(LanguageInvokerPtr invoker, CScriptInvocationManager* invocationManager) +CLanguageInvokerThread::CLanguageInvokerThread(LanguageInvokerPtr invoker, CScriptInvocationManager *invocationManager, bool reuseable) : ILanguageInvoker(NULL), CThread("LanguageInvoker"), m_invoker(invoker), - m_invocationManager(invocationManager) + m_invocationManager(invocationManager), + m_reusable(reuseable) { } CLanguageInvokerThread::~CLanguageInvokerThread() @@ -22,7 +23,7 @@ CLanguageInvokerThread::~CLanguageInvokerThread() Stop(true); } -InvokerState CLanguageInvokerThread::GetState() +InvokerState CLanguageInvokerThread::GetState() const { if (m_invoker == NULL) return InvokerStateFailed; @@ -30,6 +31,12 @@ InvokerState CLanguageInvokerThread::GetState() return m_invoker->GetState(); } +void CLanguageInvokerThread::Release() +{ + m_bStop = true; + m_condition.notify_one(); +} + bool CLanguageInvokerThread::execute(const std::string &script, const std::vector<std::string> &arguments) { if (m_invoker == NULL || script.empty()) @@ -38,7 +45,17 @@ bool CLanguageInvokerThread::execute(const std::string &script, const std::vecto m_script = script; m_args = arguments; - Create(); + if (CThread::IsRunning()) + { + std::unique_lock<std::mutex> lck(m_mutex); + m_restart = true; + m_condition.notify_one(); + } + else + Create(); + + //Todo wait until running + return true; } @@ -50,14 +67,16 @@ bool CLanguageInvokerThread::stop(bool wait) if (!CThread::IsRunning()) return false; + Release(); + bool result = true; - if (m_invoker->GetState() < InvokerStateDone) + if (m_invoker->GetState() < InvokerStateExecutionDone) { // stop the language-specific invoker result = m_invoker->Stop(wait); - // stop the thread - CThread::StopThread(wait); } + // stop the thread + CThread::StopThread(wait); return result; } @@ -77,7 +96,17 @@ void CLanguageInvokerThread::Process() if (m_invoker == NULL) return; - m_invoker->Execute(m_script, m_args); + std::unique_lock<std::mutex> lckdl(m_mutex); + do { + m_restart = false; + m_invoker->Execute(m_script, m_args); + + if (m_invoker->GetState() != InvokerStateScriptDone) + m_reusable = false; + + m_condition.wait(lckdl, [this] {return m_bStop || m_restart || !m_reusable; }); + + } while (m_reusable && !m_bStop); } void CLanguageInvokerThread::OnExit() @@ -86,7 +115,7 @@ void CLanguageInvokerThread::OnExit() return; m_invoker->onExecutionDone(); - m_invocationManager->OnScriptEnded(GetId()); + m_invocationManager->OnExecutionDone(GetId()); } void CLanguageInvokerThread::OnException() @@ -95,5 +124,5 @@ void CLanguageInvokerThread::OnException() return; m_invoker->onExecutionFailed(); - m_invocationManager->OnScriptEnded(GetId()); + m_invocationManager->OnExecutionDone(GetId()); }
\ No newline at end of file diff --git a/xbmc/interfaces/generic/LanguageInvokerThread.h b/xbmc/interfaces/generic/LanguageInvokerThread.h index 5bd1b60098..81e4915ff5 100644 --- a/xbmc/interfaces/generic/LanguageInvokerThread.h +++ b/xbmc/interfaces/generic/LanguageInvokerThread.h @@ -19,10 +19,15 @@ class CScriptInvocationManager; class CLanguageInvokerThread : public ILanguageInvoker, protected CThread { public: - CLanguageInvokerThread(LanguageInvokerPtr invoker, CScriptInvocationManager* invocationManager); + CLanguageInvokerThread(LanguageInvokerPtr invoker, CScriptInvocationManager *invocationManager, bool reuseable); ~CLanguageInvokerThread() override; - virtual InvokerState GetState(); + virtual InvokerState GetState() const; + + const std::string &GetScript() const { return m_script; }; + LanguageInvokerPtr GetInvoker() const { return m_invoker; }; + bool Reuseable(const std::string &script) const { return !m_bStop && m_reusable && GetState() == InvokerStateScriptDone && m_script == script; }; + virtual void Release(); protected: bool execute(const std::string &script, const std::vector<std::string> &arguments) override; @@ -38,4 +43,10 @@ private: CScriptInvocationManager *m_invocationManager; std::string m_script; std::vector<std::string> m_args; + + std::mutex m_mutex; + std::condition_variable m_condition; + bool m_restart = false; + bool m_reusable = false; }; + diff --git a/xbmc/interfaces/generic/ScriptInvocationManager.cpp b/xbmc/interfaces/generic/ScriptInvocationManager.cpp index 22945b682f..373aaeb911 100644 --- a/xbmc/interfaces/generic/ScriptInvocationManager.cpp +++ b/xbmc/interfaces/generic/ScriptInvocationManager.cpp @@ -77,6 +77,9 @@ void CScriptInvocationManager::Uninitialize() // execute Process() once more to handle the remaining scripts Process(); + // it is safe to relese early, thread must be in m_scripts too + m_lastInvokerThread = nullptr; + // make sure all scripts are done std::vector<LanguageInvokerThread> tempList; for (const auto& script : m_scripts) @@ -96,9 +99,11 @@ void CScriptInvocationManager::Uninitialize() if (!it.done) it.thread->Stop(true); } - tempList.clear(); lock.Enter(); + + tempList.clear(); + // uninitialize all invocation handlers and then remove them for (auto& it : m_invocationHandlers) it.second->Uninitialize(); @@ -175,12 +180,39 @@ bool CScriptInvocationManager::HasLanguageInvoker(const std::string &script) con return it != m_invocationHandlers.end() && it->second != NULL; } -LanguageInvokerPtr CScriptInvocationManager::GetLanguageInvoker(const std::string& script) const +int CScriptInvocationManager::GetReusablePluginHandle(const std::string &script) { + CSingleLock lock(m_critSection); + + if (m_lastInvokerThread) + { + if (m_lastInvokerThread->Reuseable(script)) + return m_lastPluginHandle; + m_lastInvokerThread->Release(); + m_lastInvokerThread = nullptr; + } + return -1; +} + +LanguageInvokerPtr CScriptInvocationManager::GetLanguageInvoker(const std::string &script) +{ + CSingleLock lock(m_critSection); + + if (m_lastInvokerThread) + { + if (m_lastInvokerThread->Reuseable(script)) + { + CLog::Log(LOGDEBUG, "%s - Reusing LanguageInvokerThread %d for script %s", __FUNCTION__, m_lastInvokerThread->GetId(), script.c_str()); + m_lastInvokerThread->GetInvoker()->Reset(); + return m_lastInvokerThread->GetInvoker(); + } + m_lastInvokerThread->Release(); + m_lastInvokerThread = nullptr; + } + std::string extension = URIUtils::GetExtension(script); StringUtils::ToLower(extension); - CSingleLock lock(m_critSection); std::map<std::string, ILanguageInvocationHandler*>::const_iterator it = m_invocationHandlers.find(extension); if (it != m_invocationHandlers.end() && it->second != NULL) return LanguageInvokerPtr(it->second->CreateInvoker()); @@ -188,7 +220,11 @@ LanguageInvokerPtr CScriptInvocationManager::GetLanguageInvoker(const std::strin return LanguageInvokerPtr(); } -int CScriptInvocationManager::ExecuteAsync(const std::string &script, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */) +int CScriptInvocationManager::ExecuteAsync(const std::string &script, + const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, + const std::vector<std::string> &arguments /* = std::vector<std::string>() */, + bool reuseable /* = false */, + int pluginHandle /* = -1 */) { if (script.empty()) return -1; @@ -200,10 +236,15 @@ int CScriptInvocationManager::ExecuteAsync(const std::string &script, const ADDO } LanguageInvokerPtr invoker = GetLanguageInvoker(script); - return ExecuteAsync(script, invoker, addon, arguments); + return ExecuteAsync(script, invoker, addon, arguments, reuseable, pluginHandle); } -int CScriptInvocationManager::ExecuteAsync(const std::string &script, LanguageInvokerPtr languageInvoker, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */) +int CScriptInvocationManager::ExecuteAsync(const std::string &script, + LanguageInvokerPtr languageInvoker, + const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, + const std::vector<std::string> &arguments /* = std::vector<std::string>() */, + bool reuseable /* = false */, + int pluginHandle /* = -1 */) { if (script.empty() || languageInvoker == NULL) return -1; @@ -214,26 +255,47 @@ int CScriptInvocationManager::ExecuteAsync(const std::string &script, LanguageIn return -1; } - CLanguageInvokerThreadPtr invokerThread = CLanguageInvokerThreadPtr(new CLanguageInvokerThread(languageInvoker, this)); - if (invokerThread == NULL) + CSingleLock lock(m_critSection); + + if (m_lastInvokerThread && m_lastInvokerThread->GetInvoker() == languageInvoker) + { + if (addon != NULL) + m_lastInvokerThread->SetAddon(addon); + + // After we leave the lock, m_lastInvokerThread can be released -> copy! + CLanguageInvokerThreadPtr invokerThread = m_lastInvokerThread; + lock.Leave(); + invokerThread->Execute(script, arguments); + + return invokerThread->GetId(); + } + + m_lastInvokerThread = CLanguageInvokerThreadPtr(new CLanguageInvokerThread(languageInvoker, this, reuseable)); + if (m_lastInvokerThread == NULL) return -1; if (addon != NULL) - invokerThread->SetAddon(addon); + m_lastInvokerThread->SetAddon(addon); - CSingleLock lock(m_critSection); - invokerThread->SetId(m_nextId++); + m_lastInvokerThread->SetId(m_nextId++); + m_lastPluginHandle = pluginHandle; - LanguageInvokerThread thread = {invokerThread, script, false}; - m_scripts.insert(std::make_pair(invokerThread->GetId(), thread)); - m_scriptPaths.insert(std::make_pair(script, invokerThread->GetId())); + LanguageInvokerThread thread = { m_lastInvokerThread, script, false }; + m_scripts.insert(std::make_pair(m_lastInvokerThread->GetId(), thread)); + m_scriptPaths.insert(std::make_pair(script, m_lastInvokerThread->GetId())); + // After we leave the lock, m_lastInvokerThread can be released -> copy! + CLanguageInvokerThreadPtr invokerThread = m_lastInvokerThread; lock.Leave(); invokerThread->Execute(script, arguments); return invokerThread->GetId(); } -int CScriptInvocationManager::ExecuteSync(const std::string &script, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */, uint32_t timeoutMs /* = 0 */, bool waitShutdown /* = false */) +int CScriptInvocationManager::ExecuteSync(const std::string &script, + const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, + const std::vector<std::string> &arguments /* = std::vector<std::string>() */, + uint32_t timeoutMs /* = 0 */, + bool waitShutdown /* = false */) { if (script.empty()) return -1; @@ -248,7 +310,12 @@ int CScriptInvocationManager::ExecuteSync(const std::string &script, const ADDON return ExecuteSync(script, invoker, addon, arguments, timeoutMs, waitShutdown); } -int CScriptInvocationManager::ExecuteSync(const std::string &script, LanguageInvokerPtr languageInvoker, const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, const std::vector<std::string> &arguments /* = std::vector<std::string>() */, uint32_t timeoutMs /* = 0 */, bool waitShutdown /* = false */) +int CScriptInvocationManager::ExecuteSync(const std::string &script, + LanguageInvokerPtr languageInvoker, + const ADDON::AddonPtr &addon /* = ADDON::AddonPtr() */, + const std::vector<std::string> &arguments /* = std::vector<std::string>() */, + uint32_t timeoutMs /* = 0 */, + bool waitShutdown /* = false */) { int scriptId = ExecuteAsync(script, languageInvoker, addon, arguments); if (scriptId < 0) @@ -322,7 +389,7 @@ bool CScriptInvocationManager::IsRunning(const std::string& scriptPath) const return IsRunning(it->second); } -void CScriptInvocationManager::OnScriptEnded(int scriptId) +void CScriptInvocationManager::OnExecutionDone(int scriptId) { if (scriptId < 0) return; diff --git a/xbmc/interfaces/generic/ScriptInvocationManager.h b/xbmc/interfaces/generic/ScriptInvocationManager.h index ac1fec79ec..cf87d1383f 100644 --- a/xbmc/interfaces/generic/ScriptInvocationManager.h +++ b/xbmc/interfaces/generic/ScriptInvocationManager.h @@ -32,7 +32,12 @@ public: void RegisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler, const std::set<std::string> &extensions); void UnregisterLanguageInvocationHandler(ILanguageInvocationHandler *invocationHandler); bool HasLanguageInvoker(const std::string &script) const; - LanguageInvokerPtr GetLanguageInvoker(const std::string& script) const; + LanguageInvokerPtr GetLanguageInvoker(const std::string &script); + + /*! + * \brief Returns addon_handle if last reusable invoker is ready to use. + */ + int GetReusablePluginHandle(const std::string &script); /*! * \brief Executes the given script asynchronously in a separate thread. @@ -42,7 +47,11 @@ public: * \param arguments (Optional) List of arguments passed to the script * \return -1 if an error occurred, otherwise the ID of the script */ - int ExecuteAsync(const std::string &script, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>()); + int ExecuteAsync(const std::string &script, + const ADDON::AddonPtr &addon = ADDON::AddonPtr(), + const std::vector<std::string> &arguments = std::vector<std::string>(), + bool reuseable = false, + int pluginHandle = -1); /*! * \brief Executes the given script asynchronously in a separate thread. * @@ -52,7 +61,12 @@ public: * \param arguments (Optional) List of arguments passed to the script * \return -1 if an error occurred, otherwise the ID of the script */ - int ExecuteAsync(const std::string &script, LanguageInvokerPtr languageInvoker, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>()); + int ExecuteAsync(const std::string &script, + LanguageInvokerPtr languageInvoker, + const ADDON::AddonPtr &addon = ADDON::AddonPtr(), + const std::vector<std::string> &arguments = std::vector<std::string>(), + bool reuseable = false, + int pluginHandle = -1); /*! * \brief Executes the given script synchronously. @@ -70,7 +84,11 @@ public: * \param waitShutdown (Optional) Whether to wait when having to forcefully stop the script's execution or not. * \return -1 if an error occurred, 0 if the script terminated or ETIMEDOUT if the given timeout expired */ - int ExecuteSync(const std::string &script, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>(), uint32_t timeoutMs = 0, bool waitShutdown = false); + int ExecuteSync(const std::string &script, + const ADDON::AddonPtr &addon = ADDON::AddonPtr(), + const std::vector<std::string> &arguments = std::vector<std::string>(), + uint32_t timeoutMs = 0, + bool waitShutdown = false); /*! * \brief Executes the given script synchronously. * @@ -88,7 +106,12 @@ public: * \param waitShutdown (Optional) Whether to wait when having to forcefully stop the script's execution or not. * \return -1 if an error occurred, 0 if the script terminated or ETIMEDOUT if the given timeout expired */ - int ExecuteSync(const std::string &script, LanguageInvokerPtr languageInvoker, const ADDON::AddonPtr &addon = ADDON::AddonPtr(), const std::vector<std::string> &arguments = std::vector<std::string>(), uint32_t timeoutMs = 0, bool waitShutdown = false); + int ExecuteSync(const std::string &script, + LanguageInvokerPtr languageInvoker, + const ADDON::AddonPtr &addon = ADDON::AddonPtr(), + const std::vector<std::string> &arguments = std::vector<std::string>(), + uint32_t timeoutMs = 0, + bool waitShutdown = false); bool Stop(int scriptId, bool wait = false); bool Stop(const std::string &scriptPath, bool wait = false); @@ -98,7 +121,7 @@ public: protected: friend class CLanguageInvokerThread; - void OnScriptEnded(int scriptId); + void OnExecutionDone(int scriptId); private: CScriptInvocationManager() = default; @@ -118,6 +141,9 @@ private: LanguageInvocationHandlerMap m_invocationHandlers; LanguageInvokerThreadMap m_scripts; + CLanguageInvokerThreadPtr m_lastInvokerThread; + int m_lastPluginHandle; + std::map<std::string, int> m_scriptPaths; int m_nextId = 0; mutable CCriticalSection m_critSection; diff --git a/xbmc/interfaces/python/PythonInvoker.cpp b/xbmc/interfaces/python/PythonInvoker.cpp index 4342e05dcd..9019585b0f 100644 --- a/xbmc/interfaces/python/PythonInvoker.cpp +++ b/xbmc/interfaces/python/PythonInvoker.cpp @@ -23,8 +23,6 @@ #include "windowing/GraphicContext.h" #include "guilib/GUIWindowManager.h" #include "guilib/LocalizeStrings.h" -#include "interfaces/legacy/Addon.h" -#include "interfaces/python/LanguageHook.h" #include "interfaces/python/PyContext.h" #include "interfaces/python/pythreadstate.h" #include "interfaces/python/swig.h" @@ -110,7 +108,7 @@ CPythonInvoker::~CPythonInvoker() if (GetId() < 0) return; - if (GetState() < InvokerStateDone) + if (GetState() < InvokerStateExecutionDone) CLog::Log(LOGDEBUG, "CPythonInvoker(%d): waiting for python thread \"%s\" to stop", GetId(), (!m_sourceFile.empty() ? m_sourceFile.c_str() : "unknown script")); Stop(true); @@ -152,6 +150,7 @@ bool CPythonInvoker::execute(const std::string& script, const std::vector<std::w { // copy the code/script into a local string buffer m_sourceFile = script; + m_pythonPath.clear(); // copy the arguments into a local buffer unsigned int argc = arguments.size(); @@ -160,113 +159,129 @@ bool CPythonInvoker::execute(const std::string& script, const std::vector<std::w CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): start processing", GetId(), m_sourceFile.c_str()); - // get the global lock - extern PyThreadState* savestate; - PyEval_RestoreThread(savestate); - PyThreadState* state = Py_NewInterpreter(); - if (state == NULL) + std::string realFilename(CSpecialProtocol::TranslatePath(m_sourceFile)); + std::string scriptDir = URIUtils::GetDirectory(realFilename); + URIUtils::RemoveSlashAtEnd(scriptDir); + + // set m_threadState if it's not set. + PyThreadState* l_threadState = nullptr; + bool newInterp = false; { - PyEval_ReleaseLock(); - CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread state!", GetId(), m_sourceFile.c_str()); - return false; + if (!m_threadState) { + // TODO: Re-write everything. + // this is a TOTAL hack. We need the GIL but we need to borrow a PyThreadState in order to get it + // as of Python 3.2 since PyEval_AcquireLock is deprecated + extern PyThreadState* savestate; + PyEval_RestoreThread(savestate); + l_threadState = Py_NewInterpreter(); + PyEval_ReleaseThread(l_threadState); + if (l_threadState == NULL) + { + CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): FAILED to get thread m_threadState!", GetId(), m_sourceFile.c_str()); + return false; + } + newInterp = true; + } else + l_threadState = m_threadState; } - // swap in my thread state - PyThreadState_Swap(state); - XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook> languageHook(new XBMCAddon::Python::PythonLanguageHook(state->interp)); - languageHook->RegisterMe(); + // get the GIL + PyEval_RestoreThread(l_threadState); + if (newInterp) + { + m_languageHook = new XBMCAddon::Python::PythonLanguageHook(l_threadState->interp); + m_languageHook->RegisterMe(); - onInitialization(); - setState(InvokerStateInitialized); + onInitialization(); + setState(InvokerStateInitialized); - std::string realFilename(CSpecialProtocol::TranslatePath(m_sourceFile)); - if (realFilename == m_sourceFile) - CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\"", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str()); - else - CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\" (\"%s\")", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), realFilename.c_str()); + if (realFilename == m_sourceFile) + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\"", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str()); + else + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): the source file to load is \"%s\" (\"%s\")", GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), realFilename.c_str()); - // get path from script file name and add python path's - // this is used for python so it will search modules from script path first - std::string scriptDir = URIUtils::GetDirectory(realFilename); - URIUtils::RemoveSlashAtEnd(scriptDir); - addPath(scriptDir); + // get path from script file name and add python path's + // this is used for python so it will search modules from script path first + addPath(scriptDir); - // add all addon module dependencies to path - if (m_addon) - { - std::set<std::string> paths; - getAddonModuleDeps(m_addon, paths); - for (const auto& it : paths) - addPath(it); - } - else - { // for backwards compatibility. - // we don't have any addon so just add all addon modules installed - CLog::Log(LOGWARNING, "CPythonInvoker(%d): Script invoked without an addon. Adding all addon " + // add all addon module dependencies to path + if (m_addon) + { + std::set<std::string> paths; + getAddonModuleDeps(m_addon, paths); + for (const auto& it : paths) + addPath(it); + } + else + { // for backwards compatibility. + // we don't have any addon so just add all addon modules installed + CLog::Log(LOGWARNING, "CPythonInvoker(%d): Script invoked without an addon. Adding all addon " "modules installed to python path as fallback. This behaviour will be removed in future " "version.", GetId()); - ADDON::VECADDONS addons; - CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::ADDON_SCRIPT_MODULE); - for (const auto& addon : addons) - addPath(CSpecialProtocol::TranslatePath(addon->LibPath())); - } + ADDON::VECADDONS addons; + CServiceBroker::GetAddonMgr().GetAddons(addons, ADDON::ADDON_SCRIPT_MODULE); + for (unsigned int i = 0; i < addons.size(); ++i) + addPath(CSpecialProtocol::TranslatePath(addons[i]->LibPath())); + } - // we want to use sys.path so it includes site-packages - // if this fails, default to using Py_GetPath - PyObject* sysMod(PyImport_ImportModule("sys")); // must call Py_DECREF when finished - PyObject* sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete - PyObject* pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete + // we want to use sys.path so it includes site-packages + // if this fails, default to using Py_GetPath + PyObject *sysMod(PyImport_ImportModule("sys")); // must call Py_DECREF when finished + PyObject *sysModDict(PyModule_GetDict(sysMod)); // borrowed ref, no need to delete + PyObject *pathObj(PyDict_GetItemString(sysModDict, "path")); // borrowed ref, no need to delete - if (pathObj != NULL && PyList_Check(pathObj)) - { - for (int i = 0; i < PyList_Size(pathObj); i++) + if (pathObj != NULL && PyList_Check(pathObj)) { - PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete - if (e != NULL && PyUnicode_Check(e)) - addPath(PyUnicode_AsUTF8(e)); // returns internal data, don't delete or modify + for (int i = 0; i < PyList_Size(pathObj); i++) { + PyObject *e = PyList_GetItem(pathObj, i); // borrowed ref, no need to delete + if (e != NULL && PyUnicode_Check(e)) + addPath(PyUnicode_AsUTF8(e)); // returns internal data, don't delete or modify + } + } + else + { + std::string GetPath; + g_charsetConverter.wToUTF8(Py_GetPath(), GetPath); + addPath(GetPath); } - } - else - { - std::string GetPath; - g_charsetConverter.wToUTF8(Py_GetPath(), GetPath); - addPath(GetPath); - } - - Py_DECREF(sysMod); // release ref to sysMod - // set current directory and python's path. - PySys_SetArgv(argc, &argv[0]); + Py_DECREF(sysMod); // release ref to sysMod #ifdef TARGET_WINDOWS - std::string pyPathUtf8; - g_charsetConverter.systemToUtf8(m_pythonPath, pyPathUtf8, false); - CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_sourceFile.c_str(), pyPathUtf8.c_str()); + std::string pyPathUtf8; + g_charsetConverter.systemToUtf8(m_pythonPath, pyPathUtf8, false); + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_sourceFile.c_str(), pyPathUtf8.c_str()); #else // ! TARGET_WINDOWS - CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_sourceFile.c_str(), m_pythonPath.c_str()); + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): setting the Python path to %s", GetId(), m_sourceFile.c_str(), m_pythonPath.c_str()); #endif // ! TARGET_WINDOWS - std::wstring pypath; - g_charsetConverter.utf8ToW(m_pythonPath, pypath); - PySys_SetPath(pypath.c_str()); + std::wstring pypath; + g_charsetConverter.utf8ToW(m_pythonPath, pypath); + PySys_SetPath(pypath.c_str()); + + { // set the m_threadState to this new interp + CSingleLock lockMe(m_critical); + m_threadState = l_threadState; + } + } + else + // swap in my thread m_threadState + PyThreadState_Swap(m_threadState); + + // set current directory and python's path. + PySys_SetArgv(argc, &argv[0]); CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): entering source directory %s", GetId(), m_sourceFile.c_str(), scriptDir.c_str()); PyObject* module = PyImport_AddModule("__main__"); PyObject* moduleDict = PyModule_GetDict(module); - // when we are done initing we store thread state so we can be aborted - PyThreadState_Swap(NULL); - PyEval_ReleaseLock(); - // we need to check if we was asked to abort before we had inited bool stopping = false; - { CSingleLock lock(m_critical); - m_threadState = state; + { + GilSafeSingleLock lock(m_critical); stopping = m_stop; } - PyEval_AcquireThread(state); - bool failed = false; std::string exceptionType, exceptionValue, exceptionTraceback; if (!stopping) @@ -316,17 +331,17 @@ bool CPythonInvoker::execute(const std::string& script, const std::vector<std::w } } - bool systemExitThrown = false; + m_systemExitThrown = false; InvokerState stateToSet; if (!failed && !PyErr_Occurred()) { CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script successfully run", GetId(), m_sourceFile.c_str()); - stateToSet = InvokerStateDone; + stateToSet = InvokerStateScriptDone; onSuccess(); } else if (PyErr_ExceptionMatches(PyExc_SystemExit)) { - systemExitThrown = true; + m_systemExitThrown = true; CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): script aborted", GetId(), m_sourceFile.c_str()); stateToSet = InvokerStateFailed; onAbort(); @@ -351,6 +366,7 @@ bool CPythonInvoker::execute(const std::string& script, const std::vector<std::w onError(exceptionType, exceptionValue, exceptionTraceback); } + CSingleLock lock(m_critical); // no need to do anything else because the script has already stopped if (failed) { @@ -358,74 +374,40 @@ bool CPythonInvoker::execute(const std::string& script, const std::vector<std::w return true; } - PyObject* m = PyImport_AddModule("xbmc"); - if (m == NULL || PyObject_SetAttrString(m, "abortRequested", PyBool_FromLong(1))) - CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_sourceFile.c_str()); - - // make sure all sub threads have finished - for (PyThreadState* s = state->interp->tstate_head, *old = NULL; s;) + if (m_threadState) { - if (s == state) - { - s = s->next; - continue; - } - if (old != s) - { - CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %" PRIu64, GetId(), m_sourceFile.c_str(), (uint64_t)s->thread_id); - old = s; - } - - CPyThreadState pyState; - Sleep(100); - pyState.Restore(); - - s = state->interp->tstate_head; - } + PyObject *m = PyImport_AddModule("xbmc"); + if (m == NULL || PyObject_SetAttrString(m, "abortRequested", PyBool_FromLong(1))) + CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_sourceFile.c_str()); - // pending calls must be cleared out - XBMCAddon::RetardedAsyncCallbackHandler::clearPendingCalls(state); + // make sure all sub threads have finished + for (PyThreadState *old = nullptr; m_threadState != nullptr;) + { + PyThreadState *s = m_threadState->interp->tstate_head; + for (; s && s == m_threadState;) + s = s->next; - PyThreadState_Swap(NULL); - PyEval_ReleaseLock(); + if (!s) + break; - // set stopped event - this allows ::stop to run and kill remaining threads - // this event has to be fired without holding m_critical - // also the GIL (PyEval_RestoreThread) must not be held - // if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!) - m_stoppedEvent.Set(); + if (old != s) + { + CLog::Log(LOGINFO, "CPythonInvoker(%d, %s): waiting on thread %" PRIu64, GetId(), m_sourceFile.c_str(), (uint64_t)s->thread_id); + old = s; + } - { CSingleLock lock(m_critical); - m_threadState = NULL; + lock.Leave(); + CPyThreadState pyState; + Sleep(100); + pyState.Restore(); + lock.Enter(); + } } - PyEval_RestoreThread(state); - - onDeinitialization(); - - // run the gc before finishing - // - // if the script exited by throwing a SystemExit exception then going back - // into the interpreter causes this python bug to get hit: - // http://bugs.python.org/issue10582 - // and that causes major failures. So we are not going to go back in - // to run the GC if that's the case. - if (!m_stop && languageHook->HasRegisteredAddonClasses() && !systemExitThrown && - PyRun_SimpleString(GC_SCRIPT) == -1) - CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to run the gc to clean up after running prior to shutting down the Interpreter", GetId(), m_sourceFile.c_str()); - - Py_EndInterpreter(state); - - // If we still have objects left around, produce an error message detailing what's been left behind - if (languageHook->HasRegisteredAddonClasses()) - CLog::Log(LOGWARNING, "CPythonInvoker(%d, %s): the python script \"%s\" has left several " - "classes in memory that we couldn't clean up. The classes include: %s", - GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), getListOfAddonClassesAsString(languageHook).c_str()); - - // unregister the language hook - languageHook->UnregisterMe(); + // pending calls must be cleared out + XBMCAddon::RetardedAsyncCallbackHandler::clearPendingCalls(m_threadState); - PyEval_ReleaseLock(); + PyEval_ReleaseThread(m_threadState); setState(stateToSet); @@ -467,28 +449,35 @@ bool CPythonInvoker::stop(bool abort) CSingleLock lock(m_critical); m_stop = true; - if (!IsRunning()) + if (!IsRunning() && !m_threadState) return false; - setState(InvokerStateStopping); - if (m_threadState != NULL) { - PyEval_RestoreThread((PyThreadState*)m_threadState); - PyThreadState* old = PyThreadState_Swap((PyThreadState*)m_threadState); + if (IsRunning()) + { + setState(InvokerStateStopping); + lock.Leave(); - //tell xbmc.Monitor to call onAbortRequested() - if (m_addon != NULL) - onAbortRequested(); + PyEval_RestoreThread((PyThreadState*)m_threadState); - PyObject* m; - m = PyImport_AddModule("xbmc"); - if (m == NULL || PyObject_SetAttrString(m, "abortRequested", PyBool_FromLong(1))) - CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_sourceFile.c_str()); + //tell xbmc.Monitor to call onAbortRequested() + if (m_addon) + { + CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): trigger Monitor abort request", GetId(), m_sourceFile.c_str()); + onAbortRequested(); + } - PyThreadState_Swap(old); - old = NULL; - PyEval_ReleaseLock(); + PyObject *m; + m = PyImport_AddModule("xbmc"); + if (m == NULL || PyObject_SetAttrString(m, "abortRequested", PyBool_FromLong(1))) + CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to set abortRequested", GetId(), m_sourceFile.c_str()); + + PyEval_ReleaseThread(m_threadState); + } + else + //Release the lock while waiting for threads to finish + lock.Leave(); XbmcThreads::EndTime timeout(PYTHON_SCRIPT_TIMEOUT); while (!m_stoppedEvent.WaitMSec(15)) @@ -508,22 +497,24 @@ bool CPythonInvoker::stop(bool abort) } } + lock.Enter(); + + setState(InvokerStateExecutionDone); + // Useful for add-on performance metrics if (!timeout.IsTimePast()) CLog::Log(LOGDEBUG, "CPythonInvoker(%d, %s): script termination took %dms", GetId(), m_sourceFile.c_str(), PYTHON_SCRIPT_TIMEOUT - timeout.MillisLeft()); - // everything which didn't exit by now gets killed - { - // grabbing the PyLock while holding the m_critical is asking for a deadlock - CSingleExit ex2(m_critical); - PyEval_RestoreThread((PyThreadState*)m_threadState); - } - // Since we released the m_critical it's possible that the state is cleaned up // so we need to recheck for m_threadState == NULL if (m_threadState != NULL) { - old = PyThreadState_Swap((PyThreadState*)m_threadState); + { + // grabbing the PyLock while holding the m_critical is asking for a deadlock + CSingleExit ex2(m_critical); + PyEval_RestoreThread((PyThreadState*)m_threadState); + } + for (PyThreadState* state = ((PyThreadState*)m_threadState)->interp->tstate_head; state; state = state->next) { // Raise a SystemExit exception in python threads @@ -534,18 +525,67 @@ bool CPythonInvoker::stop(bool abort) // If a dialog entered its doModal(), we need to wake it to see the exception pulseGlobalEvent(); - } - - if (old != NULL) - PyThreadState_Swap(old); + PyEval_ReleaseThread(m_threadState); + m_threadState = nullptr; + } lock.Leave(); - PyEval_ReleaseLock(); + + setState(InvokerStateFailed); } return true; } +// Always called from Invoker thread +void CPythonInvoker::onExecutionDone() +{ + CSingleLock lock(m_critical); + if (m_threadState != NULL) + { + CLog::Log(LOGDEBUG, "%s(%d, %s)", __FUNCTION__, GetId(), m_sourceFile.c_str()); + + PyEval_RestoreThread(m_threadState); + + onDeinitialization(); + + // run the gc before finishing + // + // if the script exited by throwing a SystemExit exception then going back + // into the interpreter causes this python bug to get hit: + // http://bugs.python.org/issue10582 + // and that causes major failures. So we are not going to go back in + // to run the GC if that's the case. + if (!m_stop && m_languageHook->HasRegisteredAddonClasses() && !m_systemExitThrown && + PyRun_SimpleString(GC_SCRIPT) == -1) + CLog::Log(LOGERROR, "CPythonInvoker(%d, %s): failed to run the gc to clean up after running prior to shutting down the Interpreter", GetId(), m_sourceFile.c_str()); + + Py_EndInterpreter(m_threadState); + + // If we still have objects left around, produce an error message detailing what's been left behind + if (m_languageHook->HasRegisteredAddonClasses()) + CLog::Log(LOGWARNING, "CPythonInvoker(%d, %s): the python script \"%s\" has left several " + "classes in memory that we couldn't clean up. The classes include: %s", + GetId(), m_sourceFile.c_str(), m_sourceFile.c_str(), getListOfAddonClassesAsString(m_languageHook).c_str()); + + // unregister the language hook + m_languageHook->UnregisterMe(); + + PyEval_ReleaseLock(); + + // set stopped event - this allows ::stop to run and kill remaining threads + // this event has to be fired without holding m_critical + // also the GIL (PyEval_AcquireLock) must not be held + // if not obeyed there is still no deadlock because ::stop waits with timeout (smart one!) + m_stoppedEvent.Set(); + + m_threadState = nullptr; + + setState(InvokerStateExecutionDone); + } + ILanguageInvoker::onExecutionDone(); +} + void CPythonInvoker::onExecutionFailed() { PyThreadState_Swap(NULL); diff --git a/xbmc/interfaces/python/PythonInvoker.h b/xbmc/interfaces/python/PythonInvoker.h index b597e16b0a..b494ff2478 100644 --- a/xbmc/interfaces/python/PythonInvoker.h +++ b/xbmc/interfaces/python/PythonInvoker.h @@ -9,6 +9,8 @@ #pragma once #include "interfaces/generic/ILanguageInvoker.h" +#include "interfaces/legacy/Addon.h" +#include "interfaces/python/LanguageHook.h" #include "threads/CriticalSection.h" #include "threads/Event.h" @@ -17,6 +19,7 @@ #include <vector> typedef struct _object PyObject; +struct _ts; class CPythonInvoker : public ILanguageInvoker { @@ -35,6 +38,7 @@ protected: bool execute(const std::string &script, const std::vector<std::string> &arguments) override; virtual void executeScript(FILE* fp, const std::string& script, PyObject* moduleDict); bool stop(bool abort) override; + void onExecutionDone() override; void onExecutionFailed() override; // custom virtual methods @@ -61,9 +65,12 @@ private: FILE* PyFile_AsFileWithMode(PyObject* py_file, const char* mode); std::string m_pythonPath; - void* m_threadState; + _ts *m_threadState; bool m_stop; CEvent m_stoppedEvent; + XBMCAddon::AddonClass::Ref<XBMCAddon::Python::PythonLanguageHook> m_languageHook; + bool m_systemExitThrown = false; + static CCriticalSection s_critical; }; diff --git a/xbmc/interfaces/python/XBPython.cpp b/xbmc/interfaces/python/XBPython.cpp index b54c779c14..3dfdfb5b8d 100644 --- a/xbmc/interfaces/python/XBPython.cpp +++ b/xbmc/interfaces/python/XBPython.cpp @@ -647,7 +647,7 @@ void XBPython::OnScriptAbortRequested(ILanguageInvoker *invoker) } } -void XBPython::OnScriptEnded(ILanguageInvoker* invoker) +void XBPython::OnExecutionEnded(ILanguageInvoker *invoker) { CSingleLock lock(m_vecPyList); PyList::iterator it = m_vecPyList.begin(); @@ -656,9 +656,9 @@ void XBPython::OnScriptEnded(ILanguageInvoker* invoker) if (it->id == invoker->GetId()) { if (it->pyThread->IsStopping()) - CLog::Log(LOGINFO, "Python script interrupted by user"); + CLog::Log(LOGINFO, "Python interpreter interrupted by user"); else - CLog::Log(LOGINFO, "Python script stopped"); + CLog::Log(LOGINFO, "Python interpreter stopped"); it->bDone = true; } ++it; diff --git a/xbmc/interfaces/python/XBPython.h b/xbmc/interfaces/python/XBPython.h index d6121052d3..2bad14d90b 100644 --- a/xbmc/interfaces/python/XBPython.h +++ b/xbmc/interfaces/python/XBPython.h @@ -91,7 +91,7 @@ public: bool OnScriptInitialized(ILanguageInvoker *invoker) override; void OnScriptStarted(ILanguageInvoker *invoker) override; void OnScriptAbortRequested(ILanguageInvoker *invoker) override; - void OnScriptEnded(ILanguageInvoker* invoker) override; + void OnExecutionEnded(ILanguageInvoker *invoker) override; void OnScriptFinalized(ILanguageInvoker *invoker) override; ILanguageInvoker* CreateInvoker() override; |