aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJim Carroll <thecarrolls@jiminger.com>2012-12-06 09:37:28 -0800
committerJim Carroll <thecarrolls@jiminger.com>2012-12-06 09:37:28 -0800
commit8cd02887839fa1db12a676216fe6af7878ac008a (patch)
tree816b74f7f7d24af364e565f7cf1b3f1b690d0fb2
parent6555ade1978cc1e740931bf8b8fbb4c3da2bfea8 (diff)
parent7fe785597649b4666fb384bb86fe0c0d429dfc44 (diff)
Merge pull request #1889 from jimfcarroll/python-cleanup-fix
Python "leak" fix. Closes #13624
-rw-r--r--xbmc/interfaces/legacy/Addon.cpp4
-rw-r--r--xbmc/interfaces/legacy/AddonCallback.h2
-rw-r--r--xbmc/interfaces/legacy/AddonClass.cpp4
-rw-r--r--xbmc/interfaces/legacy/AddonClass.h3
-rw-r--r--xbmc/interfaces/legacy/CallbackHandler.cpp4
-rw-r--r--xbmc/interfaces/legacy/CallbackHandler.h4
-rw-r--r--xbmc/interfaces/legacy/LanguageHook.cpp6
-rw-r--r--xbmc/interfaces/legacy/LanguageHook.h54
-rw-r--r--xbmc/interfaces/legacy/ModuleXbmc.cpp2
-rw-r--r--xbmc/interfaces/legacy/Monitor.cpp6
-rw-r--r--xbmc/interfaces/legacy/Player.cpp4
-rw-r--r--xbmc/interfaces/legacy/Window.cpp6
-rw-r--r--xbmc/interfaces/python/CallbackHandler.cpp22
-rw-r--r--xbmc/interfaces/python/CallbackHandler.h4
-rw-r--r--xbmc/interfaces/python/LanguageHook.cpp100
-rw-r--r--xbmc/interfaces/python/LanguageHook.h50
-rw-r--r--xbmc/interfaces/python/PythonSwig.cpp.template2
-rw-r--r--xbmc/interfaces/python/XBPyThread.cpp54
-rw-r--r--xbmc/interfaces/python/swig.cpp87
-rw-r--r--xbmc/interfaces/python/swig.h40
20 files changed, 337 insertions, 121 deletions
diff --git a/xbmc/interfaces/legacy/Addon.cpp b/xbmc/interfaces/legacy/Addon.cpp
index bc44dd9473..1c3574aa33 100644
--- a/xbmc/interfaces/legacy/Addon.cpp
+++ b/xbmc/interfaces/legacy/Addon.cpp
@@ -33,9 +33,9 @@ namespace XBMCAddon
{
namespace xbmcaddon
{
- String Addon::getDefaultId() { return languageHook == NULL ? emptyString : languageHook->getAddonId(); }
+ String Addon::getDefaultId() { return languageHook == NULL ? emptyString : languageHook->GetAddonId(); }
- String Addon::getAddonVersion() { return languageHook == NULL ? emptyString : languageHook->getAddonVersion(); }
+ String Addon::getAddonVersion() { return languageHook == NULL ? emptyString : languageHook->GetAddonVersion(); }
Addon::Addon(const char* cid) throw (AddonException) : AddonClass("Addon")
{
diff --git a/xbmc/interfaces/legacy/AddonCallback.h b/xbmc/interfaces/legacy/AddonCallback.h
index 159cb171a4..366fcd63e8 100644
--- a/xbmc/interfaces/legacy/AddonCallback.h
+++ b/xbmc/interfaces/legacy/AddonCallback.h
@@ -45,7 +45,7 @@ namespace XBMCAddon
{
// if there is a LanguageHook, it should be set already.
if (languageHook != NULL)
- setHandler(languageHook->getCallbackHandler());
+ setHandler(languageHook->GetCallbackHandler());
}
virtual ~AddonCallback();
diff --git a/xbmc/interfaces/legacy/AddonClass.cpp b/xbmc/interfaces/legacy/AddonClass.cpp
index eda9a042c4..fb5319ec12 100644
--- a/xbmc/interfaces/legacy/AddonClass.cpp
+++ b/xbmc/interfaces/legacy/AddonClass.cpp
@@ -61,7 +61,7 @@ namespace XBMCAddon
#endif
// check to see if we have a language hook that was prepared for this instantiation
- languageHook = LanguageHook::getLanguageHook();
+ languageHook = LanguageHook::GetLanguageHook();
if (languageHook != NULL)
{
languageHook->Acquire();
@@ -70,7 +70,7 @@ namespace XBMCAddon
// this AddonClass (actually - its subclass - but whatever). So we
// will now reset the Tls. This avoids issues if the constructor of the
// subclass throws an exception.
- LanguageHook::clearLanguageHook();
+ LanguageHook::ClearLanguageHook();
}
}
diff --git a/xbmc/interfaces/legacy/AddonClass.h b/xbmc/interfaces/legacy/AddonClass.h
index d9e991c155..ec76bd4748 100644
--- a/xbmc/interfaces/legacy/AddonClass.h
+++ b/xbmc/interfaces/legacy/AddonClass.h
@@ -101,6 +101,9 @@ namespace XBMCAddon
AddonClass(const char* classname);
virtual ~AddonClass();
+ inline const String& GetClassname() const { return classname; }
+ inline LanguageHook* GetLanguageHook() { return languageHook; }
+
/**
* This method should be called while holding a Synchronize
* on the object. It will prevent the deallocation during
diff --git a/xbmc/interfaces/legacy/CallbackHandler.cpp b/xbmc/interfaces/legacy/CallbackHandler.cpp
index 0cedb85c7c..22ad958563 100644
--- a/xbmc/interfaces/legacy/CallbackHandler.cpp
+++ b/xbmc/interfaces/legacy/CallbackHandler.cpp
@@ -84,7 +84,7 @@ namespace XBMCAddon
AddonClass::Ref<AsynchCallbackMessage> p(*iter);
// only call when we are in the right thread state
- if(p->handler->isThreadStateOk())
+ if(p->handler->isStateOk(p->cb->getObject()))
{
// remove it from the queue. No matter what we're done with
// this. Even if it doesn't execute for some reason.
@@ -140,7 +140,7 @@ namespace XBMCAddon
{
AddonClass::Ref<AsynchCallbackMessage> p(*iter);
- if(p->handler->shouldRemoveCallback(userData))
+ if(p->handler->shouldRemoveCallback(p->cb->getObject(),userData))
{
#ifdef ENABLE_TRACE_API
CLog::Log(LOGDEBUG,"%sNEWADDON removing callback 0x%lx for PyThreadState 0x%lx from queue", _tg.getSpaces(),(long)(p->cb.get()) ,(long)userData);
diff --git a/xbmc/interfaces/legacy/CallbackHandler.h b/xbmc/interfaces/legacy/CallbackHandler.h
index 81793fdd04..b4d6ff5c16 100644
--- a/xbmc/interfaces/legacy/CallbackHandler.h
+++ b/xbmc/interfaces/legacy/CallbackHandler.h
@@ -64,8 +64,8 @@ namespace XBMCAddon
static void makePendingCalls();
static void clearPendingCalls(void* userData);
- virtual bool isThreadStateOk() = 0;
- virtual bool shouldRemoveCallback(void* userData) = 0;
+ virtual bool isStateOk(AddonClass* obj) = 0;
+ virtual bool shouldRemoveCallback(AddonClass* obj, void* userData) = 0;
};
}
diff --git a/xbmc/interfaces/legacy/LanguageHook.cpp b/xbmc/interfaces/legacy/LanguageHook.cpp
index 65db0853a7..3f0af14c8f 100644
--- a/xbmc/interfaces/legacy/LanguageHook.cpp
+++ b/xbmc/interfaces/legacy/LanguageHook.cpp
@@ -33,19 +33,19 @@ namespace XBMCAddon
static bool threadLocalInitilialized = false;
static xbmcutil::InitFlag initer(threadLocalInitilialized);
- void LanguageHook::setLanguageHook(LanguageHook* languageHook)
+ void LanguageHook::SetLanguageHook(LanguageHook* languageHook)
{
TRACE;
languageHook->Acquire();
addonLanguageHookTls.set(languageHook);
}
- LanguageHook* LanguageHook::getLanguageHook()
+ LanguageHook* LanguageHook::GetLanguageHook()
{
return threadLocalInitilialized ? addonLanguageHookTls.get() : NULL;
}
- void LanguageHook::clearLanguageHook()
+ void LanguageHook::ClearLanguageHook()
{
LanguageHook* lh = addonLanguageHookTls.get();
addonLanguageHookTls.set(NULL);
diff --git a/xbmc/interfaces/legacy/LanguageHook.h b/xbmc/interfaces/legacy/LanguageHook.h
index ae671bd162..e6873ada5b 100644
--- a/xbmc/interfaces/legacy/LanguageHook.h
+++ b/xbmc/interfaces/legacy/LanguageHook.h
@@ -58,7 +58,7 @@ namespace XBMCAddon
* Python to run by using Py_BEGIN_ALLOW_THREADS. This is
* the place to put that functionality
*/
- virtual void delayedCallOpen() { }
+ virtual void DelayedCallOpen() { }
/**
* If the scripting language needs special handling for calls
@@ -72,15 +72,15 @@ namespace XBMCAddon
* state using Py_END_ALLOW_THREADS. This is the place to put
* that functionality
*/
- virtual void delayedCallClose() { }
+ virtual void DelayedCallClose() { }
- virtual void makePendingCalls() {}
+ virtual void MakePendingCalls() {}
/**
* For scripting languages that need a global callback handler, this
* method should be overloaded to supply one.
*/
- virtual CallbackHandler* getCallbackHandler() { return NULL; }
+ virtual CallbackHandler* GetCallbackHandler() { return NULL; }
/**
* This is a callback method that can be overriden to receive a callback
@@ -89,7 +89,7 @@ namespace XBMCAddon
* cannot assume the subclasses have been built or that calling a
* virtual function on the AddonClass will work as expected.
*/
- virtual void constructing(AddonClass* beingConstructed) { }
+ virtual void Constructing(AddonClass* beingConstructed) { }
/**
* This is a callback method that can be overriden to receive a callback
@@ -98,7 +98,7 @@ namespace XBMCAddon
* should assume the subclasses have been torn down and that calling a
* virtual function on the AddonClass will not work as expected.
*/
- virtual void destructing(AddonClass* beingDestructed) { }
+ virtual void Destructing(AddonClass* beingDestructed) { }
/**
* This method should be done a different way but since the only other way
@@ -110,23 +110,23 @@ namespace XBMCAddon
* to use scripting language specific calls. So until I figure out a
* better way to do this, this is how I need to retrieve it.
*/
- virtual String getAddonId() { return emptyString; }
- virtual String getAddonVersion() { return emptyString; }
-
- virtual void registerPlayerCallback(IPlayerCallback* player) = 0;
- virtual void unregisterPlayerCallback(IPlayerCallback* player) = 0;
- virtual void registerMonitorCallback(XBMCAddon::xbmc::Monitor* player) = 0;
- virtual void unregisterMonitorCallback(XBMCAddon::xbmc::Monitor* player) = 0;
- virtual bool waitForEvent(CEvent& hEvent, unsigned int milliseconds) = 0;
-
- static void setLanguageHook(LanguageHook* languageHook);
- static LanguageHook* getLanguageHook();
- static void clearLanguageHook();
+ virtual String GetAddonId() { return emptyString; }
+ virtual String GetAddonVersion() { return emptyString; }
+
+ virtual void RegisterPlayerCallback(IPlayerCallback* player) = 0;
+ virtual void UnregisterPlayerCallback(IPlayerCallback* player) = 0;
+ virtual void RegisterMonitorCallback(XBMCAddon::xbmc::Monitor* player) = 0;
+ virtual void UnregisterMonitorCallback(XBMCAddon::xbmc::Monitor* player) = 0;
+ virtual bool WaitForEvent(CEvent& hEvent, unsigned int milliseconds) = 0;
+
+ static void SetLanguageHook(LanguageHook* languageHook);
+ static LanguageHook* GetLanguageHook();
+ static void ClearLanguageHook();
};
/**
- * This class can be used to access the language hook's delayedCallOpen
- * and delayedCallClose. It should be used whenever an API method
+ * This class can be used to access the language hook's DelayedCallOpen
+ * and DelayedCallClose. It should be used whenever an API method
* is written such that it can block for an indefinite amount of time
* since certain scripting languages (like Python) need to do extra
* work for delayed calls (like free the python locks and handle
@@ -139,15 +139,15 @@ namespace XBMCAddon
public:
inline DelayedCallGuard(LanguageHook* languageHook_) : languageHook(languageHook_), clearOnExit(false)
- { if (languageHook) languageHook->delayedCallOpen(); }
+ { if (languageHook) languageHook->DelayedCallOpen(); }
- inline DelayedCallGuard() : languageHook(LanguageHook::getLanguageHook()), clearOnExit(false)
- { if (languageHook) languageHook->delayedCallOpen(); }
+ inline DelayedCallGuard() : languageHook(LanguageHook::GetLanguageHook()), clearOnExit(false)
+ { if (languageHook) languageHook->DelayedCallOpen(); }
inline ~DelayedCallGuard()
{
- if (clearOnExit) LanguageHook::clearLanguageHook();
- if (languageHook) languageHook->delayedCallClose();
+ if (clearOnExit) LanguageHook::ClearLanguageHook();
+ if (languageHook) languageHook->DelayedCallClose();
}
inline LanguageHook* getLanguageHook() { return languageHook; }
@@ -156,8 +156,8 @@ namespace XBMCAddon
class SetLanguageHookGuard
{
public:
- inline SetLanguageHookGuard(LanguageHook* languageHook) { LanguageHook::setLanguageHook(languageHook); }
- inline ~SetLanguageHookGuard() { LanguageHook::clearLanguageHook(); }
+ inline SetLanguageHookGuard(LanguageHook* languageHook) { LanguageHook::SetLanguageHook(languageHook); }
+ inline ~SetLanguageHookGuard() { LanguageHook::ClearLanguageHook(); }
};
}
diff --git a/xbmc/interfaces/legacy/ModuleXbmc.cpp b/xbmc/interfaces/legacy/ModuleXbmc.cpp
index 5369a82117..e03dc76ab2 100644
--- a/xbmc/interfaces/legacy/ModuleXbmc.cpp
+++ b/xbmc/interfaces/legacy/ModuleXbmc.cpp
@@ -155,7 +155,7 @@ namespace XBMCAddon
::Sleep(nextSleep);
}
if (lh != NULL)
- lh->makePendingCalls();
+ lh->MakePendingCalls();
}
}
diff --git a/xbmc/interfaces/legacy/Monitor.cpp b/xbmc/interfaces/legacy/Monitor.cpp
index 53b589c1a0..9cc1bcb0ea 100644
--- a/xbmc/interfaces/legacy/Monitor.cpp
+++ b/xbmc/interfaces/legacy/Monitor.cpp
@@ -29,8 +29,8 @@ namespace XBMCAddon
{
if (languageHook)
{
- Id = languageHook->getAddonId();
- languageHook->registerMonitorCallback(this);
+ Id = languageHook->GetAddonId();
+ languageHook->RegisterMonitorCallback(this);
}
}
@@ -42,7 +42,7 @@ namespace XBMCAddon
if (languageHook)
{
DelayedCallGuard dc;
- languageHook->unregisterMonitorCallback(this);
+ languageHook->UnregisterMonitorCallback(this);
}
}
}
diff --git a/xbmc/interfaces/legacy/Player.cpp b/xbmc/interfaces/legacy/Player.cpp
index 449a24d82d..f644bfb7b3 100644
--- a/xbmc/interfaces/legacy/Player.cpp
+++ b/xbmc/interfaces/legacy/Player.cpp
@@ -50,7 +50,7 @@ namespace XBMCAddon
if (languageHook)
{
DelayedCallGuard dc(languageHook);
- languageHook->registerPlayerCallback(this);
+ languageHook->RegisterPlayerCallback(this);
}
}
@@ -62,7 +62,7 @@ namespace XBMCAddon
if (languageHook)
{
DelayedCallGuard dc(languageHook);
- languageHook->unregisterPlayerCallback(this);
+ languageHook->UnregisterPlayerCallback(this);
}
}
diff --git a/xbmc/interfaces/legacy/Window.cpp b/xbmc/interfaces/legacy/Window.cpp
index 0a577e8af4..aae3631243 100644
--- a/xbmc/interfaces/legacy/Window.cpp
+++ b/xbmc/interfaces/legacy/Window.cpp
@@ -397,7 +397,7 @@ namespace XBMCAddon
{
TRACE;
// DO NOT MAKE THIS A DELAYED CALL!!!!
- bool ret = languageHook == NULL ? m_actionEvent.WaitMSec(milliseconds) : languageHook->waitForEvent(m_actionEvent,milliseconds);
+ bool ret = languageHook == NULL ? m_actionEvent.WaitMSec(milliseconds) : languageHook->WaitForEvent(m_actionEvent,milliseconds);
if (ret)
m_actionEvent.Reset();
return ret;
@@ -706,7 +706,7 @@ namespace XBMCAddon
// Window_Close(self, NULL);
// break;
// }
- languageHook->makePendingCalls(); // MakePendingCalls
+ languageHook->MakePendingCalls(); // MakePendingCalls
bool stillWaiting;
do
@@ -715,7 +715,7 @@ namespace XBMCAddon
DelayedCallGuard dcguard(languageHook);
stillWaiting = WaitForActionEvent(100) ? false : true;
}
- languageHook->makePendingCalls();
+ languageHook->MakePendingCalls();
} while (stillWaiting);
}
}
diff --git a/xbmc/interfaces/python/CallbackHandler.cpp b/xbmc/interfaces/python/CallbackHandler.cpp
index 21d401ea0a..01707b964a 100644
--- a/xbmc/interfaces/python/CallbackHandler.cpp
+++ b/xbmc/interfaces/python/CallbackHandler.cpp
@@ -20,6 +20,7 @@
*/
#include "CallbackHandler.h"
+#include "LanguageHook.h"
namespace XBMCAddon
{
@@ -42,10 +43,18 @@ namespace XBMCAddon
* Now we are answering the question as to whether or not we are in the
* PyThreadState that we were in when we started.
*/
- bool PythonCallbackHandler::isThreadStateOk()
+ bool PythonCallbackHandler::isStateOk(AddonClass* obj)
{
TRACE;
- return objectThreadState == PyThreadState_Get();
+ PyThreadState* state = PyThreadState_Get();
+ if (objectThreadState == state)
+ {
+ // make sure the interpreter is still active.
+ AddonClass::Ref<XBMCAddon::Python::LanguageHook> lh(XBMCAddon::Python::LanguageHook::GetIfExists(state->interp));
+ if (lh.isNotNull() && lh->HasRegisteredAddonClassInstance(obj) && lh.get() == obj->GetLanguageHook())
+ return true;
+ }
+ return false;
}
/**
@@ -55,10 +64,15 @@ namespace XBMCAddon
* TODO: This is a stupid way to get this information back to the handler.
* there should be a more language neutral means.
*/
- bool PythonCallbackHandler::shouldRemoveCallback(void* threadState)
+ bool PythonCallbackHandler::shouldRemoveCallback(AddonClass* obj, void* threadState)
{
TRACE;
- return threadState == objectThreadState;
+ if (threadState == objectThreadState)
+ return true;
+
+ // we also want to remove the callback if the language hook no longer exists.
+ // this is a belt-and-suspenders cleanup mechanism
+ return ! XBMCAddon::Python::LanguageHook::IsAddonClassInstanceRegistered(obj);
}
}
}
diff --git a/xbmc/interfaces/python/CallbackHandler.h b/xbmc/interfaces/python/CallbackHandler.h
index 3c219c8fd8..e8f1836e49 100644
--- a/xbmc/interfaces/python/CallbackHandler.h
+++ b/xbmc/interfaces/python/CallbackHandler.h
@@ -44,8 +44,8 @@ namespace XBMCAddon
* handling callbacks in the appropriate thread.
*/
PythonCallbackHandler();
- virtual bool isThreadStateOk();
- virtual bool shouldRemoveCallback(void* threadState);
+ virtual bool isStateOk(AddonClass* obj);
+ virtual bool shouldRemoveCallback(AddonClass* obj, void* threadState);
};
}
}
diff --git a/xbmc/interfaces/python/LanguageHook.cpp b/xbmc/interfaces/python/LanguageHook.cpp
index 788495fb70..63a4b3932a 100644
--- a/xbmc/interfaces/python/LanguageHook.cpp
+++ b/xbmc/interfaces/python/LanguageHook.cpp
@@ -21,6 +21,7 @@
#include "LanguageHook.h"
+#include "CallbackHandler.h"
#include "XBPython.h"
#include "interfaces/legacy/AddonUtils.h"
@@ -33,45 +34,65 @@ namespace XBMCAddon
{
static AddonClass::Ref<LanguageHook> instance;
- static CCriticalSection ccrit;
- static bool isInited = false;
- static xbmcutil::InitFlag flag(isInited);
+ static CCriticalSection hooksMutex;
+ std::map<PyInterpreterState*,AddonClass::Ref<LanguageHook> > LanguageHook::hooks;
// vtab instantiation
- LanguageHook::~LanguageHook() { }
+ LanguageHook::~LanguageHook()
+ {
+ TRACE;
+ XBMCAddon::LanguageHook::deallocating();
+ }
- void LanguageHook::makePendingCalls()
+ void LanguageHook::MakePendingCalls()
{
+ TRACE;
PythonCallbackHandler::makePendingCalls();
}
- void LanguageHook::delayedCallOpen()
+ void LanguageHook::DelayedCallOpen()
{
TRACE;
PyGILLock::releaseGil();
}
- void LanguageHook::delayedCallClose()
+ void LanguageHook::DelayedCallClose()
{
TRACE;
PyGILLock::acquireGil();
}
- LanguageHook* LanguageHook::getInstance()
+ void LanguageHook::RegisterMe()
{
- if (!isInited) // in this case we're being called from a static initializer
- {
- if (instance.isNull())
- instance = new LanguageHook();
- }
- else
+ TRACE;
+ CSingleLock lock(hooksMutex);
+ hooks[m_interp] = AddonClass::Ref<LanguageHook>(this);
+ }
+
+ void LanguageHook::UnregisterMe()
+ {
+ TRACE;
+ CSingleLock lock(hooksMutex);
+ hooks.erase(m_interp);
+ }
+
+ AddonClass::Ref<LanguageHook> LanguageHook::GetIfExists(PyInterpreterState* interp)
+ {
+ TRACE;
+ CSingleLock lock(hooksMutex);
+ std::map<PyInterpreterState*,AddonClass::Ref<LanguageHook> >::iterator iter = hooks.find(interp);
+ return iter == hooks.end() ? AddonClass::Ref<LanguageHook>(NULL) : AddonClass::Ref<LanguageHook>(iter->second);
+ }
+
+ bool LanguageHook::IsAddonClassInstanceRegistered(AddonClass* obj)
+ {
+ for (std::map<PyInterpreterState*,AddonClass::Ref<LanguageHook> >::iterator iter = hooks.begin();
+ iter != hooks.end(); iter++)
{
- CSingleLock lock (ccrit);
- if (instance.isNull())
- instance = new LanguageHook();
+ if ((iter->second)->HasRegisteredAddonClassInstance(obj))
+ return true;
}
-
- return instance.get();
+ return false;
}
/**
@@ -86,13 +107,15 @@ namespace XBMCAddon
* See PythonCallbackHandler for more details
* See PythonCallbackHandler::PythonCallbackHandler for more details
*/
- XBMCAddon::CallbackHandler* LanguageHook::getCallbackHandler()
+ XBMCAddon::CallbackHandler* LanguageHook::GetCallbackHandler()
{
+ TRACE;
return new PythonCallbackHandler();
}
- String LanguageHook::getAddonId()
+ String LanguageHook::GetAddonId()
{
+ TRACE;
const char* id = NULL;
// Get a reference to the main module
@@ -106,8 +129,9 @@ namespace XBMCAddon
return id;
}
- String LanguageHook::getAddonVersion()
+ String LanguageHook::GetAddonVersion()
{
+ TRACE;
// Get a reference to the main module
// and global dictionary
PyObject* main_module = PyImport_AddModule((char*)"__main__");
@@ -119,14 +143,36 @@ namespace XBMCAddon
return version;
}
- void LanguageHook::registerPlayerCallback(IPlayerCallback* player) { g_pythonParser.RegisterPythonPlayerCallBack(player); }
- void LanguageHook::unregisterPlayerCallback(IPlayerCallback* player) { g_pythonParser.UnregisterPythonPlayerCallBack(player); }
- void LanguageHook::registerMonitorCallback(XBMCAddon::xbmc::Monitor* monitor) { g_pythonParser.RegisterPythonMonitorCallBack(monitor); }
- void LanguageHook::unregisterMonitorCallback(XBMCAddon::xbmc::Monitor* monitor) { g_pythonParser.UnregisterPythonMonitorCallBack(monitor); }
+ void LanguageHook::RegisterPlayerCallback(IPlayerCallback* player) { TRACE; g_pythonParser.RegisterPythonPlayerCallBack(player); }
+ void LanguageHook::UnregisterPlayerCallback(IPlayerCallback* player) { TRACE; g_pythonParser.UnregisterPythonPlayerCallBack(player); }
+ void LanguageHook::RegisterMonitorCallback(XBMCAddon::xbmc::Monitor* monitor) { TRACE; g_pythonParser.RegisterPythonMonitorCallBack(monitor); }
+ void LanguageHook::UnregisterMonitorCallback(XBMCAddon::xbmc::Monitor* monitor) { TRACE; g_pythonParser.UnregisterPythonMonitorCallBack(monitor); }
- bool LanguageHook::waitForEvent(CEvent& hEvent, unsigned int milliseconds)
+ bool LanguageHook::WaitForEvent(CEvent& hEvent, unsigned int milliseconds)
{
+ TRACE;
return g_pythonParser.WaitForEvent(hEvent,milliseconds);
}
+
+ void LanguageHook::RegisterAddonClassInstance(AddonClass* obj)
+ {
+ TRACE;
+ Synchronize l(*this);
+ currentObjects.insert(obj);
+ }
+
+ void LanguageHook::UnregisterAddonClassInstance(AddonClass* obj)
+ {
+ TRACE;
+ Synchronize l(*this);
+ currentObjects.erase(obj);
+ }
+
+ bool LanguageHook::HasRegisteredAddonClassInstance(AddonClass* obj)
+ {
+ TRACE;
+ Synchronize l(*this);
+ return currentObjects.find(obj) != currentObjects.end();
+ }
}
}
diff --git a/xbmc/interfaces/python/LanguageHook.h b/xbmc/interfaces/python/LanguageHook.h
index 34c1fcf063..63142d371b 100644
--- a/xbmc/interfaces/python/LanguageHook.h
+++ b/xbmc/interfaces/python/LanguageHook.h
@@ -28,10 +28,12 @@
#include <Python.h>
#include "interfaces/legacy/LanguageHook.h"
-#include "interfaces/python/CallbackHandler.h"
#include "threads/ThreadLocal.h"
#include "threads/Event.h"
+#include <set>
+#include <map>
+
namespace XBMCAddon
{
namespace Python
@@ -45,15 +47,22 @@ namespace XBMCAddon
*/
class LanguageHook : public XBMCAddon::LanguageHook
{
- LanguageHook() : XBMCAddon::LanguageHook("Python::LanguageHook") { }
+ PyInterpreterState* m_interp;
+ CCriticalSection crit;
+ std::set<AddonClass*> currentObjects;
+
+ static std::map<PyInterpreterState*,AddonClass::Ref<LanguageHook> > hooks;
public:
+ inline LanguageHook(PyInterpreterState* interp) :
+ XBMCAddon::LanguageHook("Python::LanguageHook"), m_interp(interp) { }
+
virtual ~LanguageHook();
- virtual void delayedCallOpen();
- virtual void delayedCallClose();
- virtual void makePendingCalls();
+ virtual void DelayedCallOpen();
+ virtual void DelayedCallClose();
+ virtual void MakePendingCalls();
/**
* PythonCallbackHandler expects to be instantiated PER AddonClass instance
@@ -67,18 +76,31 @@ namespace XBMCAddon
* See PythonCallbackHandler for more details
* See PythonCallbackHandler::PythonCallbackHandler for more details
*/
- virtual XBMCAddon::CallbackHandler* getCallbackHandler();
+ virtual XBMCAddon::CallbackHandler* GetCallbackHandler();
+
+ virtual String GetAddonId();
+ virtual String GetAddonVersion();
+
+ virtual void RegisterPlayerCallback(IPlayerCallback* player);
+ virtual void UnregisterPlayerCallback(IPlayerCallback* player);
+ virtual void RegisterMonitorCallback(XBMCAddon::xbmc::Monitor* monitor);
+ virtual void UnregisterMonitorCallback(XBMCAddon::xbmc::Monitor* monitor);
+ virtual bool WaitForEvent(CEvent& hEvent, unsigned int milliseconds);
+
+ static AddonClass::Ref<LanguageHook> GetIfExists(PyInterpreterState* interp);
+ static bool IsAddonClassInstanceRegistered(AddonClass* obj);
- virtual String getAddonId();
- virtual String getAddonVersion();
+ void RegisterAddonClassInstance(AddonClass* obj);
+ void UnregisterAddonClassInstance(AddonClass* obj);
+ bool HasRegisteredAddonClassInstance(AddonClass* obj);
+ inline bool HasRegisteredAddonClasses() { Synchronize l(*this); return currentObjects.size() > 0; }
- virtual void registerPlayerCallback(IPlayerCallback* player);
- virtual void unregisterPlayerCallback(IPlayerCallback* player);
- virtual void registerMonitorCallback(XBMCAddon::xbmc::Monitor* monitor);
- virtual void unregisterMonitorCallback(XBMCAddon::xbmc::Monitor* monitor);
- virtual bool waitForEvent(CEvent& hEvent, unsigned int milliseconds);
+ // You should hold the lock on the LanguageHook itself if you're
+ // going to do anything with the set that gets returned.
+ inline std::set<AddonClass*>& GetRegisteredAddonClasses() { return currentObjects; }
- static LanguageHook* getInstance();
+ void UnregisterMe();
+ void RegisterMe();
};
}
}
diff --git a/xbmc/interfaces/python/PythonSwig.cpp.template b/xbmc/interfaces/python/PythonSwig.cpp.template
index d3c106a8a9..67efb4e1b5 100644
--- a/xbmc/interfaces/python/PythonSwig.cpp.template
+++ b/xbmc/interfaces/python/PythonSwig.cpp.template
@@ -180,7 +180,7 @@ void doMethod(method, MethodType methodType)
}
// now do the method call itself
if (!destructor) {
- if (constructor || !clazz) { %> XBMCAddon::SetLanguageHookGuard slhg(XBMCAddon::Python::LanguageHook::getInstance());<% println() }
+ if (constructor || !clazz) { %> XBMCAddon::SetLanguageHookGuard slhg(XBMCAddon::Python::LanguageHook::GetIfExists(PyThreadState_Get()->interp).get());<% println() }
%> <%
if (returns != "void") { %>apiResult = (${SwigTypeParser.SwigType_lstr(returns)})<% }
if (clazz && !constructor) {
diff --git a/xbmc/interfaces/python/XBPyThread.cpp b/xbmc/interfaces/python/XBPyThread.cpp
index 1ffad6d49a..82ca80f782 100644
--- a/xbmc/interfaces/python/XBPyThread.cpp
+++ b/xbmc/interfaces/python/XBPyThread.cpp
@@ -42,6 +42,7 @@
#include "XBPyThread.h"
#include "XBPython.h"
+#include "LanguageHook.h"
#include "interfaces/legacy/Exception.h"
#include "interfaces/legacy/CallbackHandler.h"
@@ -158,6 +159,9 @@ void XBPyThread::Process()
// swap in my thread state
PyThreadState_Swap(state);
+ XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook> languageHook(new XBMCAddon::Python::LanguageHook(state->interp));
+ languageHook->RegisterMe();
+
m_pExecuter->InitializeInterpreter(addon);
CLog::Log(LOGDEBUG, "%s - The source file to load is %s", __FUNCTION__, m_source);
@@ -369,8 +373,56 @@ void XBPyThread::Process()
m_pExecuter->DeInitializeInterpreter();
Py_EndInterpreter(state);
- PyThreadState_Swap(NULL);
+ // This is a total hack. Python doesn't necessarily release
+ // all of the objects associated with the interpreter when
+ // you end the interpreter. As a result there are objects
+ // managed by the windowing system that still receive events
+ // until python decides to clean them up. Python will eventually
+ // clean them up on the creation or ending of a subsequent
+ // interpreter. So we are going to keep creating and ending
+ // interpreters until we have no more python objects hanging
+ // around.
+ int countLimit;
+ for (countLimit = 0; languageHook->HasRegisteredAddonClasses() && countLimit < 10; countLimit++)
+ {
+ PyThreadState* tmpstate = Py_NewInterpreter();
+ Py_EndInterpreter(tmpstate);
+ }
+
+ // If necessary and successfull, debug log the results.
+ if (countLimit > 0 && !languageHook->HasRegisteredAddonClasses())
+ CLog::Log(LOGDEBUG,"It took %d Py_NewInterpreter/Py_EndInterpreter calls"
+ " to clean up the classes leftover from running \"%s.\"",
+ countLimit,m_source);
+
+ // If not successful, produce an error message detailing what's been left behind
+ if (languageHook->HasRegisteredAddonClasses())
+ {
+ CStdString message;
+ message.Format("The python script \"%s\" has left several "
+ "classes in memory that should have been cleaned up. The classes include: ",
+ m_source);
+
+ { XBMCAddon::AddonClass::Synchronize l(*(languageHook.get()));
+ std::set<XBMCAddon::AddonClass*>& acs = languageHook->GetRegisteredAddonClasses();
+ bool firstTime = true;
+ for (std::set<XBMCAddon::AddonClass*>::iterator iter = acs.begin();
+ iter != acs.end(); iter++)
+ {
+ if (!firstTime) message += ",";
+ else firstTime = false;
+ message += (*iter)->GetClassname().c_str();
+ }
+ }
+
+ CLog::Log(LOGERROR, "%s", message.c_str());
+ }
+
+ // unregister the language hook
+ languageHook->UnregisterMe();
+
+ PyThreadState_Swap(NULL);
PyEval_ReleaseLock();
}
diff --git a/xbmc/interfaces/python/swig.cpp b/xbmc/interfaces/python/swig.cpp
index d8840692c6..2a7475f160 100644
--- a/xbmc/interfaces/python/swig.cpp
+++ b/xbmc/interfaces/python/swig.cpp
@@ -19,7 +19,9 @@
*
*/
-#include "interfaces/python/swig.h"
+#include "LanguageHook.h"
+#include "swig.h"
+
#include <string>
namespace PythonBindings
@@ -202,7 +204,8 @@ namespace PythonBindings
const char* methodNamespacePrefix, const char* methodNameForErrorString) throw (XBMCAddon::WrongTypeException)
{
if (pythonType == NULL || pythonType->magicNumber != XBMC_PYTHON_TYPE_MAGIC_NUMBER)
- throw XBMCAddon::WrongTypeException("Non api type passed in place of the expected type \"%s.\"",expectedType);
+ throw XBMCAddon::WrongTypeException("Non api type passed to \"%s\" in place of the expected type \"%s.\"",
+ methodNameForErrorString, expectedType);
if (!isParameterRightType(typeInfo->swigType,expectedType,methodNamespacePrefix))
{
// maybe it's a child class
@@ -216,5 +219,85 @@ namespace PythonBindings
return ((PyHolder*)pythonType)->pSelf;
}
+ /**
+ * This method is a helper for the generated API. It's called prior to any API
+ * class constructor being returned from the generated code to Python
+ */
+ void prepareForReturn(XBMCAddon::AddonClass* c)
+ {
+ TRACE;
+ if(c) {
+ c->Acquire();
+ PyThreadState* state = PyThreadState_Get();
+ XBMCAddon::Python::LanguageHook::GetIfExists(state->interp)->RegisterAddonClassInstance(c);
+ }
+ }
+
+ static bool handleInterpRegistrationForClean(XBMCAddon::AddonClass* c)
+ {
+ if(c){
+ PyThreadState* state = PyThreadState_Get();
+ XBMCAddon::AddonClass::Ref<XBMCAddon::Python::LanguageHook> lh =
+ XBMCAddon::Python::LanguageHook::GetIfExists(state->interp);
+ if (lh.isNotNull()) lh->UnregisterAddonClassInstance(c);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * This method is a helper for the generated API. It's called prior to any API
+ * class destructor being dealloc-ed from the generated code from Python
+ */
+ void cleanForDealloc(XBMCAddon::AddonClass* c)
+ {
+ TRACE;
+ if (handleInterpRegistrationForClean(c))
+ c->Release();
+ }
+
+ /**
+ * This method is a helper for the generated API. It's called prior to any API
+ * class destructor being dealloc-ed from the generated code from Python
+ *
+ * There is a Catch-22 in the destruction of a Window. 'dispose' needs to be
+ * called on destruction but cannot be called from the destructor.
+ * This overrides the default cleanForDealloc to resolve that.
+ */
+ void cleanForDealloc(XBMCAddon::xbmcgui::Window* c)
+ {
+ TRACE;
+ if (handleInterpRegistrationForClean(c))
+ {
+ c->dispose();
+ c->Release();
+ }
+ }
+
+ /**
+ * This method allows for conversion of the native api Type to the Python type
+ *
+ * NOTE: swigTypeString must be in the data segment. That is, it should be an explicit string since
+ * the const char* is stored in a PyHolder struct and never deleted.
+ */
+ PyObject* makePythonInstance(void* api, PyTypeObject* typeObj, TypeInfo* typeInfo, bool incrementRefCount)
+ {
+ // null api types result in Py_None
+ if (!api)
+ {
+ Py_INCREF(Py_None);
+ return Py_None;
+ }
+
+ PyHolder* self = (PyHolder*)typeObj->tp_alloc(typeObj,0);
+ if (!self) return NULL;
+ self->magicNumber = XBMC_PYTHON_TYPE_MAGIC_NUMBER;
+ self->typeInfo = typeInfo;
+ self->pSelf = api;
+ if (incrementRefCount)
+ Py_INCREF((PyObject*)self);
+ return (PyObject*)self;
+ }
+
}
diff --git a/xbmc/interfaces/python/swig.h b/xbmc/interfaces/python/swig.h
index 22f899cbd6..c47f2af51f 100644
--- a/xbmc/interfaces/python/swig.h
+++ b/xbmc/interfaces/python/swig.h
@@ -104,14 +104,27 @@ namespace PythonBindings
doretrieveApiInstance(((PyHolder*)pythonType),((PyHolder*)pythonType)->typeInfo, expectedType, methodNamespacePrefix, methodNameForErrorString);
}
- inline void prepareForReturn(XBMCAddon::AddonClass* c) { if(c) c->Acquire(); }
+ /**
+ * This method is a helper for the generated API. It's called prior to any API
+ * class constructor being returned from the generated code to Python
+ */
+ void prepareForReturn(XBMCAddon::AddonClass* c);
- inline void cleanForDealloc(XBMCAddon::AddonClass* c) { if(c) c->Release(); }
+ /**
+ * This method is a helper for the generated API. It's called prior to any API
+ * class destructor being dealloc-ed from the generated code from Python
+ */
+ void cleanForDealloc(XBMCAddon::AddonClass* c);
/**
- * There is a Catch-22 in the destruction of a Window. This resolves that.
+ * This method is a helper for the generated API. It's called prior to any API
+ * class destructor being dealloc-ed from the generated code from Python
+ *
+ * There is a Catch-22 in the destruction of a Window. 'dispose' needs to be
+ * called on destruction but cannot be called from the destructor.
+ * This overrides the default cleanForDealloc to resolve that.
*/
- inline void cleanForDealloc(XBMCAddon::xbmcgui::Window* c) { if(c) { c->dispose(); c->Release(); } }
+ void cleanForDealloc(XBMCAddon::xbmcgui::Window* c);
/**
* This method allows for conversion of the native api Type to the Python type
@@ -119,24 +132,7 @@ namespace PythonBindings
* NOTE: swigTypeString must be in the data segment. That is, it should be an explicit string since
* the const char* is stored in a PyHolder struct and never deleted.
*/
- inline PyObject* makePythonInstance(void* api, PyTypeObject* typeObj, TypeInfo* typeInfo, bool incrementRefCount)
- {
- // null api types result in Py_None
- if (!api)
- {
- Py_INCREF(Py_None);
- return Py_None;
- }
-
- PyHolder* self = (PyHolder*)typeObj->tp_alloc(typeObj,0);
- if (!self) return NULL;
- self->magicNumber = XBMC_PYTHON_TYPE_MAGIC_NUMBER;
- self->typeInfo = typeInfo;
- self->pSelf = api;
- if (incrementRefCount)
- Py_INCREF((PyObject*)self);
- return (PyObject*)self;
- }
+ PyObject* makePythonInstance(void* api, PyTypeObject* typeObj, TypeInfo* typeInfo, bool incrementRefCount);
class Director
{