Linux Timer RunLoop assertion failure in Timer::stop()

In a previous thread, the creation of timers on linux was discussed and solved with a patch to the linux RunLoop implementation.
I made a request to the REAPER devs and they apparently implemented (or improved?) the new runloop in v7.17: VST3: support IPluginFactory3::setHostContext with IRunLoop support on Linux.

Originally, I faced two issues with the RunLoop on Linux Reaper:

  1. The DataExchange iMessage fallback did not work (transmit) on linux with reaper.
    This has apparently been resolved with v7.17.

  2. Timer::stop() issued by the vstgui4 animator when closing the editor window leads to

vst3sdk/vstgui4/vstgui/lib/platform/linux/x11timer.cpp:38: Assertion 'runLoop' failed. Timer only works of run loop was set

This issue has not been resolved and I’m now not so sure any more the reaper devs can do anything about it on their side, since I can observe the timer working through the Animator and DataExchange, which must mean the RunLoop is working as expected?

It’s confusing to me since Timer::start() and Timer::stop() use identical code for retrieving the RunLoop, yet it only fails in Timer::stop():

	auto runLoop = RunLoop::get ();
	vstgui_assert (runLoop, "Timer only works of run loop was set");

Also, since VST3PluginTestHost is win/mac only, what’s the recommended testing procedure on linux?

To facilitate easier testing, here is my animator-based TimerTester view (does not draw anything):

TimerTest.h

#ifndef TimerTest_H
#define TimerTest_H

#include "vstgui/vstgui.h"

namespace VSTGUI {
	class TimerTest : public CView, public Animation::IAnimationTarget
	{
	public:
		TimerTest(const CRect& size);

		bool attached(CView* parent) override;
		bool removed(CView* parent) override;

		void setViewSize(const CRect& rect, bool invalid) override;
		void parentSizeChanged() override;
		//void setVisible(bool state) override;
		//void invalidRect(const CRect& rect) override;
	protected:
		virtual void animationStart(CView* view, IdStringPtr name) override;
		virtual void animationTick(CView* view, IdStringPtr name, float pos) override;
		virtual void animationFinished(CView* view, IdStringPtr name, bool wasCanceled) override;

		float zRotation;
	};
}

#endif //TimerTest_H

TimerTest.cpp

#include "TimerTest.h"

namespace VSTGUI {
	TimerTest::TimerTest(const CRect& size) : CView(size) {
		zRotation = 0.f;
	}

	bool TimerTest::attached(CView* parent)
	{
		if (!CView::attached(parent))
		{
			return false;
		}
		addAnimation("zR", this, new Animation::RepeatTimingFunction(new Animation::LinearTimingFunction(8000), -1, false));
		return true;
	}

	bool TimerTest::removed(CView* parent)
	{
		return true;
	}

	void TimerTest::setViewSize(const CRect& rect, bool invalid)
	{
		CView::setViewSize(rect, invalid);
	}

	void TimerTest::parentSizeChanged()
	{
		CView::parentSizeChanged();
	}

	void TimerTest::animationStart(CView* view, IdStringPtr name) {}
	void TimerTest::animationTick(CView* view, IdStringPtr name, float pos)
	{
		if (strcmp(name, "zR") == 0)
			zRotation = pos * 360.f;
	}
	void TimerTest::animationFinished(CView* view, IdStringPtr name, bool wasCanceled) {}
}

Adding removeAllAnimations(); to removed() does not change anything.

TimerTestFactory.cpp

#include "vstgui/vstgui.h"
#include "vstgui/vstgui_uidescription.h"
#include "vstgui/uidescription/detail/uiviewcreatorattributes.h"

#include "TimerTest.h"

#define TimerTest_default_w 200
#define TimerTest_default_h 200

namespace VSTGUI {
	class TimerTestFactory : public ViewCreatorAdapter
	{
	public:
		TimerTestFactory()
		{
			// register in view factory
			UIViewFactory::registerViewCreator(*this);
		}
		IdStringPtr getViewName() const override
		{
			// provide display name
			return "TimerTest";
		}
		IdStringPtr getBaseViewName() const override
		{
			// provide parent class
			return UIViewCreator::kCView;
		}
		CView* create(const UIAttributes& attributes, const IUIDescription* description) const override
		{
			// set default size
			CRect size(CPoint(45, 45), CPoint(TimerTest_default_w, TimerTest_default_h));
			// return new instance
			return new TimerTest(size);
		}

	};

	// create a static factory instance so that it registers itself with the view factory
	TimerTestFactory __gRegisterTimerTestFactory;

} // namespace VSTGUI

Removing the assertion in Timer::stop () makes it fail on a different assertion:

/vst3sdk/vstgui4/vstgui/lib/cview.cpp:250: Assertion 'isAttached () == false' failed. View is still attached

even with removeAllAnimations(); in removed().

Please help me solve this issue.

Tested with SDK 3.7.12 (to build the plugin) and reaper versions up to 7.19 dev0802.
If I can test/provide anything else, let me know!

It could very well be that VSTGUI does not support the new run loop mechanism yet.
So someone needs to add support to it I guess.

So the new runLoop is there and works but why would
RunLoop::get() return nothing?
Is there an official linux host that uses the latest VST3SDK / an other good way to test on linux?
Win and mac have VST3PluginTestHost, is there something alike for linux?
Or should I just assume Reaper implements it properly?