Skip to content

Commit

Permalink
support task executor with vstgui standalone on linux
Browse files Browse the repository at this point in the history
  • Loading branch information
scheffle committed Nov 10, 2024
1 parent 61d31d5 commit 8ad738b
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 48 deletions.
11 changes: 7 additions & 4 deletions vstgui/lib/platform/common/threadpooltaskexecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#pragma once

#include "../iplatformtaskexecutor.h"

#include "../../vstguifwd.h"
#include <atomic>
#include <condition_variable>
#include <functional>
Expand All @@ -25,9 +25,12 @@ struct ThreadPool

~ThreadPool () noexcept
{
stopThreads ();
condition.notify_all ();
joinAllThreads ();
if (started)
{
stopThreads ();
condition.notify_all ();
joinAllThreads ();
}
}

void enqueue (Task&& task) noexcept
Expand Down
14 changes: 14 additions & 0 deletions vstgui/lib/platform/linux/linuxfactory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ std::string LinuxFactory::getResourcePath () const noexcept
return impl->resPath;
}

//-----------------------------------------------------------------------------
void LinuxFactory::setScheduleMainQueueTaskFunc (
LinuxTaskExecutor::ScheduleMainQueueTaskFunc&& func) const noexcept
{
if (auto lte = dynamic_cast<LinuxTaskExecutor*> (impl->taskExecutor.get ()))
{
lte->setScheduleMainQueueTaskFunc (std::move (func));
}
else
{
vstgui_assert (false, "cannot set the func on a custom task executor");
}
}

//-----------------------------------------------------------------------------
uint64_t LinuxFactory::getTicks () const noexcept
{
Expand Down
4 changes: 4 additions & 0 deletions vstgui/lib/platform/linux/linuxfactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "../platformfactory.h"
#include "linuxtaskexecutor.h"

//-----------------------------------------------------------------------------
namespace VSTGUI {
Expand All @@ -19,6 +20,9 @@ class LinuxFactory final : public IPlatformFactory
void setResourcePath (const std::string& path) const noexcept;
std::string getResourcePath () const noexcept;

void setScheduleMainQueueTaskFunc (
LinuxTaskExecutor::ScheduleMainQueueTaskFunc&& func) const noexcept;

void finalize () noexcept final;

/** Return platform ticks (millisecond resolution)
Expand Down
124 changes: 85 additions & 39 deletions vstgui/lib/platform/linux/linuxtaskexecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,123 @@
// distribution and at http://github.com/steinbergmedia/vstgui/LICENSE

#include "linuxtaskexecutor.h"
#include "../common/threadpooltaskexecutor.h"
#include "../../vstguidebug.h"

//------------------------------------------------------------------------
namespace VSTGUI {

// TODO: This is currently a dummy implementation which executes the tasks directly

//------------------------------------------------------------------------
struct LinuxTaskExecutor::Queue
struct LinuxTaskExecutor::Impl
{
Queue (uint64_t qId) : queueID ({qId}) {}
virtual ~Queue () noexcept = default;
virtual void schedule (Tasks::Task&& task) const = 0;

Tasks::Queue queueID;
using SerialQueueVector = std::vector<std::unique_ptr<Tasks::Detail::SerialQueue>>;

Tasks::Detail::ThreadPool threadPool {std::thread::hardware_concurrency ()};
uint64_t queueIdentifierCounter {};
SerialQueueVector serialQueues;
std::mutex serialQueueMutex;
ScheduleMainQueueTaskFunc scheduleMainQueueTaskFunc;
Tasks::Queue mainQueue {0u};
Tasks::Queue backgroundQueue {1u};

void waitAllTasksExecuted (SerialQueueVector::const_iterator it) const
{
while ((*it)->empty () == false)
std::this_thread::sleep_for (std::chrono::milliseconds (1));
}

SerialQueueVector::const_iterator findQueue (uint64_t identifier) const
{
return std::find_if (serialQueues.begin (), serialQueues.end (),
[&] (const auto& el) { return el->getIdentifier (); });
}
};

//------------------------------------------------------------------------
struct LinuxTaskExecutor::MainQueue : LinuxTaskExecutor::Queue
{
using Queue::Queue;
void schedule (Tasks::Task&& task) const final { task (); }
};
LinuxTaskExecutor::LinuxTaskExecutor () { impl = std::make_unique<Impl> (); }

//------------------------------------------------------------------------
struct LinuxTaskExecutor::BackgroundQueue : LinuxTaskExecutor::Queue
{
using Queue::Queue;
void schedule (Tasks::Task&& task) const final { task (); }
};
LinuxTaskExecutor::~LinuxTaskExecutor () noexcept {}

//------------------------------------------------------------------------
LinuxTaskExecutor::LinuxTaskExecutor ()
void LinuxTaskExecutor::setScheduleMainQueueTaskFunc (ScheduleMainQueueTaskFunc&& func)
{
mainQueue = std::make_unique<MainQueue> (0u);
backgroundQueue = std::make_unique<BackgroundQueue> (1u);
impl->scheduleMainQueueTaskFunc = std::move (func);
}

//------------------------------------------------------------------------
LinuxTaskExecutor::~LinuxTaskExecutor () noexcept {}
const Tasks::Queue& LinuxTaskExecutor::getMainQueue () const { return impl->mainQueue; }

//------------------------------------------------------------------------
const Tasks::Queue& LinuxTaskExecutor::getMainQueue () const { return mainQueue->queueID; }
const Tasks::Queue& LinuxTaskExecutor::getBackgroundQueue () const { return impl->backgroundQueue; }

//------------------------------------------------------------------------
const Tasks::Queue& LinuxTaskExecutor::getBackgroundQueue () const
Tasks::Queue LinuxTaskExecutor::makeSerialQueue (const char* name) const
{
return backgroundQueue->queueID;
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
impl->serialQueues.emplace_back (std::make_unique<Tasks::Detail::SerialQueue> (
impl->threadPool, ++impl->queueIdentifierCounter, name));
return {impl->queueIdentifierCounter};
}

//------------------------------------------------------------------------
Tasks::Queue LinuxTaskExecutor::makeSerialQueue (const char* name) const { return {3}; }

void LinuxTaskExecutor::releaseSerialQueue (const Tasks::Queue& queue) const {}
void LinuxTaskExecutor::releaseSerialQueue (const Tasks::Queue& queue) const
{
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
auto it = impl->findQueue (queue.identifier);
if (it != impl->serialQueues.end ())
{
impl->waitAllTasksExecuted (it);
impl->serialQueues.erase (it);
}
}

//------------------------------------------------------------------------
void LinuxTaskExecutor::schedule (const Tasks::Queue& queue, Tasks::Task&& task) const
{
const Queue* q = nullptr;
if (queue.identifier == mainQueue->queueID.identifier)
q = mainQueue.get ();
else if (queue.identifier == backgroundQueue->queueID.identifier)
q = backgroundQueue.get ();
if (q)
q->schedule (std::move (task));
if (queue == getMainQueue ())
{
if (impl->scheduleMainQueueTaskFunc)
impl->scheduleMainQueueTaskFunc (std::move (task));
}
else if (queue == getBackgroundQueue ())
{
impl->threadPool.enqueue (std::move (task));
}
else
{
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
auto it = impl->findQueue (queue.identifier);
if (it != impl->serialQueues.end ())
(*it)->schedule (std::move (task));
}
}

//------------------------------------------------------------------------
void LinuxTaskExecutor::waitAllTasksExecuted (const Tasks::Queue& queue) const {}
void LinuxTaskExecutor::waitAllTasksExecuted (const Tasks::Queue& queue) const
{
if (queue == impl->backgroundQueue)
{
while (!impl->threadPool.empty ())
std::this_thread::sleep_for (std::chrono::milliseconds (1));
}
else
{
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
auto it = impl->findQueue (queue.identifier);
if (it != impl->serialQueues.end ())
impl->waitAllTasksExecuted (it);
}
}

//------------------------------------------------------------------------
void LinuxTaskExecutor::waitAllTasksExecuted () const {}
void LinuxTaskExecutor::waitAllTasksExecuted () const
{
{
std::lock_guard<std::mutex> lock (impl->serialQueueMutex);
for (auto it = impl->serialQueues.begin (); it != impl->serialQueues.end (); ++it)
impl->waitAllTasksExecuted (it);
}
waitAllTasksExecuted (impl->backgroundQueue);
}

//------------------------------------------------------------------------
} // VSTGUI
11 changes: 6 additions & 5 deletions vstgui/lib/platform/linux/linuxtaskexecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#pragma once

#include "../iplatformtaskexecutor.h"
#include <functional>

//------------------------------------------------------------------------
namespace VSTGUI {
Expand All @@ -23,12 +24,12 @@ struct LinuxTaskExecutor final : IPlatformTaskExecutor
void waitAllTasksExecuted (const Tasks::Queue& queue) const final;
void waitAllTasksExecuted () const final;

using ScheduleMainQueueTaskFunc = std::function<void (Tasks::Task&&)>;
void setScheduleMainQueueTaskFunc (ScheduleMainQueueTaskFunc&& func);

private:
struct Queue;
struct MainQueue;
struct BackgroundQueue;
std::unique_ptr<Queue> mainQueue;
std::unique_ptr<Queue> backgroundQueue;
struct Impl;
std::unique_ptr<Impl> impl;
};

//------------------------------------------------------------------------
Expand Down
12 changes: 12 additions & 0 deletions vstgui/standalone/source/platform/gdk/gdkapplication.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,17 @@ bool Application::init (int argc, char* argv[])
config.callback (AlertResult::Error);
};

getPlatformFactory ().asLinuxFactory ()->setScheduleMainQueueTaskFunc ([] (auto&& task) {
auto idleSource = Glib::IdleSource::create ();
idleSource->set_priority (Glib::PRIORITY_DEFAULT);
idleSource->connect ([task = std::move (task), idleSource] () {
task ();
idleSource->destroy ();
return true;
});
idleSource->attach ();
});

auto appAccess = Detail::getApplicationPlatformAccess ();
vstgui_assert (appAccess);
IPlatformApplication::OpenFilesList openFilesList;
Expand All @@ -115,6 +126,7 @@ bool Application::init (int argc, char* argv[])
isInitialized = true;
doCommandUpdate ();
});

return true;
}

Expand Down

0 comments on commit 8ad738b

Please sign in to comment.