Keep the argument tuple owned by varfunc for its full per-call lifetime.
ConvertFunc destroys varfunc before ScopedFuncCall, so the tuple is released
exactly once while the GIL is still held, including failed callbacks.
Clear the argument tuple after calling a Python callback so varfunc's
destructor cannot decrement it a second time. This prevents repeated
zero-argument callbacks from losing references to the shared empty tuple.
Skip callback reference cleanup after Python has shut down, since Qt can
destroy pending callback closures during QApplication teardown.
Validation:
- Regression harness using this header and bundled Python 3.6.4: 20,000
callbacks with balanced references, nonempty arguments, exception paths,
deferred release, and callback destruction after Py_Finalize.
- Windows x64 Release build: no warnings or errors.
- MI 8 remote replay: 1.rdc -> 2.rdc -> 1.rdc -> 2.rdc; normal exit with
a pending callback, exit code 0.
Build and device checks used the existing development checkout, which also
contains separate local changes excluded from this commit.
* Anything obtained via the aliased interfaces will act as if it's a List[], but
any globals from the repl will be rdcarray typed so we need to handle looking
up the 'real' type for that.
* During capture shutdown on Python < 3.13 the replay thread will try to remove
itself as a debuggable thread which requires being able to run python (holding
the GIL). If the UI thread is the one triggering the capture shutdown and is
running a call from a python script it may hold the GIL while waiting for the
replay thread to close, deadlocking.
* To alleviate this, while the UI thread is about to block on the replay thread
we try to release the python state if we are holding it so that the replay
thread can run python code if needed.
* We already do this in a manual wrapper around BlockInvoke.
* The worst one was Persistant -> Persistent. This involved renaming
PersistentConfig but the impact of that is considered minimal enough to be
worth fixing.
* We enforce default and copy constructors where possible, this isn't very
pythonic but we can't use copy.deepcopy on our types.
* The structs that have specialised constructors e.g. FloatVector or Subresource
also have those documented though we have no way to enforce it.
* This is necessary as they expect to be run on the UI thread, but can be
fetched from python scripts running on the python script thread. We block
invoke over to the UI and suspend python's GIL to avoid deadlocks.