Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix attempt for X11 runloop problems #986

Merged
merged 2 commits into from
Sep 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 13 additions & 9 deletions plugins/vst/SfizzVstEditor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,8 @@ bool PLUGIN_API SfizzVstEditor::open(void* parent, const VSTGUI::PlatformType& p
config = &x11config;
#endif

Editor* editor = editor_.get();
if (!editor) {
editor = new Editor(*this);
editor_.reset(editor);
}
Editor* editor = new Editor(*this);
editor_.reset(editor);

if (!frame->open(parent, platformType, config)) {
fprintf(stderr, "[sfizz] error opening frame\n");
Expand Down Expand Up @@ -122,12 +119,21 @@ void PLUGIN_API SfizzVstEditor::close()
for (FObject* update : updates_)
update->removeDependent(this);

if (editor_)
if (editor_) {
editor_->close();
editor_ = nullptr;
}

if (frame->getNbReference() != 1)
frame->forget();
else
else {
frame->close();
#if !defined(__APPLE__) && !defined(_WIN32)
// if vstgui is done using the runloop, destroy it
if (!RunLoop::get())
_runLoop = nullptr;
#endif
}
this->frame = nullptr;
}

Expand Down Expand Up @@ -158,8 +164,6 @@ CMessageResult SfizzVstEditor::notify(CBaseObject* sender, const char* message)
// notifier of X11 events is working. If there is, remove this and
// avoid polluting Linux hosts which implement the loop correctly.
runLoop->processSomeEvents();

runLoop->cleanupDeadHandlers();
}
}
#endif
Expand Down
212 changes: 149 additions & 63 deletions plugins/vst/X11RunLoop.cpp
Original file line number Diff line number Diff line change
@@ -1,143 +1,229 @@
// SPDX-License-Identifier: GPL-3.0
// SPDX-License-Identifier: BSD-2-Clause

// This code is part of the sfizz library and is licensed under a BSD 2-clause
// license. You should have receive a LICENSE.md file along with the code.
// If not, contact the sfizz maintainers at https://github.com/sfztools/sfizz

#if !defined(__APPLE__) && !defined(_WIN32)
#include "X11RunLoop.h"
#include "vstgui/lib/platform/linux/x11platform.h"
#include "base/source/fobject.h"
#include <vector>
#include <typeinfo>
#include <cstdio>
#include <cassert>

namespace VSTGUI {

RunLoop::RunLoop(Steinberg::FUnknown* runLoop)
: runLoop(runLoop)
{
}
struct RunLoop::Impl {
struct EventHandler;
struct TimerHandler;

RunLoop::~RunLoop() {}
using EventHandlers = std::vector<Steinberg::IPtr<EventHandler>>;
using TimerHandlers = std::vector<Steinberg::IPtr<TimerHandler>>;

SharedPointer<RunLoop> RunLoop::get()
{
return X11::RunLoop::get().cast<VSTGUI::RunLoop>();
}
EventHandlers eventHandlers;
TimerHandlers timerHandlers;
Steinberg::FUnknownPtr<Steinberg::Linux::IRunLoop> runLoop;
};

struct RunLoop::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject {
//------------------------------------------------------------------------------
struct RunLoop::Impl::EventHandler final : Steinberg::Linux::IEventHandler, public Steinberg::FObject {
X11::IEventHandler* handler { nullptr };
bool alive { false };

void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override
{
if (alive && handler)
handler->onEvent();
}
void PLUGIN_API onFDIsSet(Steinberg::Linux::FileDescriptor) override;

DELEGATE_REFCOUNT(Steinberg::FObject)
DEFINE_INTERFACES
DEF_INTERFACE(Steinberg::Linux::IEventHandler)
END_DEFINE_INTERFACES(Steinberg::FObject)
};

struct RunLoop::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject {
struct RunLoop::Impl::TimerHandler final : Steinberg::Linux::ITimerHandler, public Steinberg::FObject {
X11::ITimerHandler* handler { nullptr };
bool alive { false };

void PLUGIN_API onTimer() override
{
if (alive && handler)
handler->onTimer();
}
void PLUGIN_API onTimer() override;

DELEGATE_REFCOUNT(Steinberg::FObject)
DEFINE_INTERFACES
DEF_INTERFACE(Steinberg::Linux::ITimerHandler)
END_DEFINE_INTERFACES(Steinberg::FObject)
};

//------------------------------------------------------------------------------
void PLUGIN_API RunLoop::Impl::EventHandler::onFDIsSet(Steinberg::Linux::FileDescriptor)
{
SharedPointer<RunLoop> runLoop = RunLoop::get();
if (!runLoop) {
fprintf(stderr, "[x11] event has fired without active runloop\n");
return;
}

if (alive && handler)
handler->onEvent();
}

void PLUGIN_API RunLoop::Impl::TimerHandler::onTimer()
{
SharedPointer<RunLoop> runLoop = RunLoop::get();
if (!runLoop) {
fprintf(stderr, "[x11] timer has fired without active runloop\n");
return;
}

if (alive && handler)
handler->onTimer();
}

//------------------------------------------------------------------------------
RunLoop::RunLoop(Steinberg::FUnknown* runLoop)
: impl(new Impl)
{
impl->runLoop = runLoop;
}

RunLoop::~RunLoop()
{
//dumpCurrentState();

if (0) {
// remove any leftover handlers
for (size_t i = 0; i < impl->eventHandlers.size(); ++i) {
const auto& eh = impl->eventHandlers[i];
if (eh->alive && eh->handler) {
impl->runLoop->unregisterEventHandler(eh.get());
}
}
for (size_t i = 0; i < impl->timerHandlers.size(); ++i) {
const auto& th = impl->timerHandlers[i];
if (th->alive && th->handler) {
impl->runLoop->unregisterTimer(th.get());
}
}
}
}

SharedPointer<RunLoop> RunLoop::get()
{
return X11::RunLoop::get().cast<VSTGUI::RunLoop>();
}

void RunLoop::processSomeEvents()
{
for (size_t i = 0; i < eventHandlers.size(); ++i) {
const auto& eh = eventHandlers[i];
for (size_t i = 0; i < impl->eventHandlers.size(); ++i) {
const auto& eh = impl->eventHandlers[i];
if (eh->alive && eh->handler) {
eh->handler->onEvent();
}
}
}

void RunLoop::cleanupDeadHandlers()
void RunLoop::dumpCurrentState()
{
for (size_t i = 0; i < eventHandlers.size(); ++i) {
const auto& eh = eventHandlers[i];
if (!eh->alive) {
runLoop->unregisterEventHandler(eh);
eventHandlers.erase(eventHandlers.begin() + i--);
}
fprintf(stderr, "=== X11 runloop ===\n");

fprintf(stderr, "\t" "Event slots:\n");
for (size_t i = 0, n = impl->eventHandlers.size(); i < n; ++i) {
Impl::EventHandler *eh = impl->eventHandlers[i].get();
fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, eh->alive, eh->handler, (eh->alive && eh->handler) ? typeid(*eh->handler).name() : "");
}
for (size_t i = 0; i < timerHandlers.size(); ++i) {
const auto& th = timerHandlers[i];
if (!th->alive) {
runLoop->unregisterTimer(th);
timerHandlers.erase(timerHandlers.begin() + i--);
}

fprintf(stderr, "\t" "Timer slots:\n");
for (size_t i = 0, n = impl->timerHandlers.size(); i < n; ++i) {
Impl::TimerHandler *th = impl->timerHandlers[i].get();
fprintf(stderr, "\t\t" "(%lu) alive=%d handler=%p type=%s\n", i, th->alive, th->handler, (th->alive && th->handler) ? typeid(*th->handler).name() : "");
}

fprintf(stderr, "===/X11 runloop ===\n");
}

template <class T>
static void insertHandler(std::vector<Steinberg::IPtr<T>>& list, Steinberg::IPtr<T> handler)
{
size_t i = 0;
size_t n = list.size();
while (i < n && list[i]->alive)
++i;
if (i < n)
list[i] = handler;
else
list.emplace_back(handler);
}

template <class T, class U>
static size_t findHandler(const std::vector<Steinberg::IPtr<T>>& list, U* handler)
{
for (size_t i = 0, n = list.size(); i < n; ++i) {
if (list[i]->alive && list[i]->handler == handler)
return i;
}
return ~size_t(0);
}

bool RunLoop::registerEventHandler(int fd, X11::IEventHandler* handler)
{
if (!runLoop)
if (!impl->runLoop)
return false;

auto smtgHandler = Steinberg::owned(new EventHandler());
auto smtgHandler = Steinberg::owned(new Impl::EventHandler);
smtgHandler->handler = handler;
smtgHandler->alive = true;
if (runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) {
eventHandlers.push_back(smtgHandler);
if (impl->runLoop->registerEventHandler(smtgHandler, fd) == Steinberg::kResultTrue) {
insertHandler(impl->eventHandlers, smtgHandler);
return true;
}
return false;
}

bool RunLoop::unregisterEventHandler(X11::IEventHandler* handler)
{
if (!runLoop)
if (!impl->runLoop)
return false;

for (size_t i = 0; i < eventHandlers.size(); ++i) {
const auto& eh = eventHandlers[i];
if (eh->alive && eh->handler == handler) {
eh->alive = false;
return true;
}
}
return false;
size_t index = findHandler(impl->eventHandlers, handler);
if (index == ~size_t(0))
return false;

Impl::EventHandler *eh = impl->eventHandlers[index].get();
if (!impl->runLoop->unregisterEventHandler(eh))
return false;

eh->alive = false;
return true;
}

bool RunLoop::registerTimer(uint64_t interval, X11::ITimerHandler* handler)
{
if (!runLoop)
if (!impl->runLoop)
return false;

auto smtgHandler = Steinberg::owned(new TimerHandler());
auto smtgHandler = Steinberg::owned(new Impl::TimerHandler);
smtgHandler->handler = handler;
smtgHandler->alive = true;
if (runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) {
timerHandlers.push_back(smtgHandler);
if (impl->runLoop->registerTimer(smtgHandler, interval) == Steinberg::kResultTrue) {
insertHandler(impl->timerHandlers, smtgHandler);
return true;
}
return false;
}

bool RunLoop::unregisterTimer(X11::ITimerHandler* handler)
{
if (!runLoop)
if (!impl->runLoop)
return false;

for (size_t i = 0; i < timerHandlers.size(); ++i) {
const auto& th = timerHandlers[i];
if (th->alive && th->handler == handler) {
th->alive = false;
return true;
}
}
return false;
size_t index = findHandler(impl->timerHandlers, handler);
if (index == ~size_t(0))
return false;

Impl::TimerHandler *th = impl->timerHandlers[index].get();
if (!impl->runLoop->unregisterTimer(th))
return false;

th->alive = false;
return true;
}

} // namespace VSTGUI
Expand Down
Loading