Worker thread per plugin instance.

This commit is contained in:
Robin E. R. Davies
2025-07-22 16:02:37 -04:00
parent 7ffbee16cf
commit ea134b46a8
8 changed files with 56 additions and 29 deletions
+1 -1
View File
@@ -163,7 +163,7 @@
"cSpell.ignoreWords": [
"nammodel"
],
"cSpell.enabled": true,
"cSpell.enabled": false,
// Disable all automatic completion suggestions - only show when Ctrl+Space is pressed
"editor.quickSuggestions": {
-1
View File
@@ -46,7 +46,6 @@ namespace pipedal {
virtual int GetNumberOfInputAudioChannels() const = 0;
virtual int GetNumberOfOutputAudioChannels() const = 0;
virtual std::shared_ptr<Lv2PluginInfo> GetPluginInfo(const std::string &uri) const = 0;
virtual std::shared_ptr<HostWorkerThread> GetHostWorkerThread() = 0;
virtual IEffect *CreateEffect(PedalboardItem &pedalboard) = 0;
+8 -4
View File
@@ -41,6 +41,7 @@
#include "AudioHost.hpp"
#include <exception>
#include "RingBufferReader.hpp"
#include "Worker.hpp"
using namespace pipedal;
namespace fs = std::filesystem;
@@ -70,6 +71,11 @@ Lv2Effect::Lv2Effect(
this->bypass = pedalboardItem.isEnabled();
this->workerThread = std::make_unique<HostWorkerThread>();
if (info_->WantsWorkerThread())
{
workerThread->StartThread();
}
// stash a list of known file properties that we want to keep synced.
if (info->piPedalUI())
{
@@ -179,10 +185,8 @@ Lv2Effect::Lv2Effect(
const LV2_Worker_Interface *worker_interface =
(const LV2_Worker_Interface *)lilv_instance_get_extension_data(pInstance,
LV2_WORKER__interface);
if (worker_interface)
{
this->worker = std::make_unique<Worker>(pHost->GetHostWorkerThread(), pInstance, worker_interface);
}
this->worker = std::make_unique<Worker>(workerThread, pInstance, worker_interface);
const LV2_State_Interface *state_interface =
(const LV2_State_Interface *)lilv_instance_get_extension_data(pInstance,
LV2_STATE__interface);
+5 -2
View File
@@ -40,12 +40,12 @@
#include "StateInterface.hpp"
#include "LogFeature.hpp"
namespace pipedal
{
class RealtimeRingBufferWriter;
class IPatchWriterCallback;
class HostWorkerThread;
class Lv2Effect : public IEffect, private LogFeature::LogMessageListener
{
@@ -57,6 +57,10 @@ namespace pipedal
private:
std::shared_ptr<HostWorkerThread> workerThread;
std::unique_ptr<Worker> worker;
std::unordered_map<std::string,int> controlIndex;
FileBrowserFilesFeature fileBrowserFilesFeature;
@@ -70,7 +74,6 @@ namespace pipedal
int numberOfOutputs = 0;
int numberOfMidiInputs = 0;
std::unique_ptr<Worker> worker;
LilvInstance *pInstance;
std::shared_ptr<Lv2PluginInfo> info;
std::vector<float> controlValues;
+8 -5
View File
@@ -47,6 +47,7 @@
#include "StdErrorCapture.hpp"
#include "util.hpp"
#include "ModFileTypes.hpp"
#include <algorithm>
#include "Locale.hpp"
@@ -301,7 +302,6 @@ PluginHost::PluginHost()
this->urids = new Urids(mapFeature);
pHostWorkerThread = std::make_shared<HostWorkerThread>();
}
void PluginHost::OnConfigurationChanged(const JackConfiguration &configuration, const JackChannelSelection &settings)
@@ -1668,10 +1668,6 @@ bool Lv2PluginInfo::isSplit() const
{
return uri_ == SPLIT_PEDALBOARD_ITEM_URI;
}
std::shared_ptr<HostWorkerThread> PluginHost::GetHostWorkerThread()
{
return pHostWorkerThread;
}
class ResourceInfo
{
@@ -1883,6 +1879,13 @@ Lv2PatchPropertyInfo::Lv2PatchPropertyInfo(PluginHost *pluginHost, const LilvNod
}
}
// ffs.
static inline bool contains(const std::vector<std::string> &vec, const std::string &value) {
return std::find(vec.begin(),vec.end(),value) != vec.end();
}
bool Lv2PluginInfo::WantsWorkerThread() const {
return contains(this->required_features_,LV2_WORKER__schedule) || contains(this->supported_features_,LV2_WORKER__schedule);
}
// void PiPedalHostLogError(const std::string &error)
// {
// Lv2Log::error("%s",error.c_str());
+2 -2
View File
@@ -454,6 +454,8 @@ namespace pipedal
LV2_PROPERTY_GETSET(patchProperties)
LV2_PROPERTY_GETSET(hasDefaultState)
bool WantsWorkerThread() const;
const Lv2PortInfo &getPort(const std::string &symbol)
{
for (size_t i = 0; i < ports_.size(); ++i)
@@ -913,7 +915,6 @@ namespace pipedal
}
private:
std::shared_ptr<HostWorkerThread> pHostWorkerThread;
// IHost implementation.
virtual void SetMaxAudioBufferSize(size_t size) { maxBufferSize = size; }
virtual size_t GetMaxAudioBufferSize() const { return maxBufferSize; }
@@ -922,7 +923,6 @@ namespace pipedal
virtual int GetNumberOfInputAudioChannels() const { return numberOfAudioInputChannels; }
virtual int GetNumberOfOutputAudioChannels() const { return numberOfAudioOutputChannels; }
virtual LV2_Feature *const *GetLv2Features() const { return (LV2_Feature *const *)&(this->lv2Features[0]); }
virtual std::shared_ptr<HostWorkerThread> GetHostWorkerThread();
public:
virtual MapFeature &GetMapFeature() override { return this->mapFeature; }
+24 -8
View File
@@ -15,7 +15,7 @@
*/
// (Borrows heavily from worker.c by David Robillard.)
// Copyright (c) 2022 Robin Davies
// Copyright (c) 2024 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
@@ -242,8 +242,19 @@ void HostWorkerThread::ThreadProc() noexcept
HostWorkerThread::HostWorkerThread()
{
this->dataBuffer.resize(16*1024);
pThread = std::make_unique<std::thread>([this]()
{ this->ThreadProc(); });
}
bool HostWorkerThread::StartThread()
{
if (!pThread)
{
if (closed) {
return false;
}
pThread = std::make_unique<std::thread>([this]()
{ this->ThreadProc(); });
}
return true;
}
void HostWorkerThread::Close()
@@ -252,10 +263,12 @@ void HostWorkerThread::Close()
bool sendClose = false;
{
std::lock_guard lock{submitMutex};
if (!closed)
{
closed = true;
sendClose = true;
if (pThread) {
if (!closed)
{
closed = true;
sendClose = true;
}
}
}
if (sendClose)
@@ -278,7 +291,10 @@ LV2_Worker_Status HostWorkerThread::ScheduleWork(Worker *worker, size_t size, co
{
std::lock_guard lock(submitMutex);
if (exiting)
if (!StartThread()) { // ensures thread is running, when plugins haven't declared they want a worker thread.
return LV2_Worker_Status::LV2_WORKER_ERR_NO_SPACE;
}
if (exiting )
{
return LV2_Worker_Status::LV2_WORKER_ERR_NO_SPACE;
}
+8 -6
View File
@@ -1,4 +1,4 @@
// Copyright (c) 2022 Robin Davies
// Copyright (c) 2025 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
@@ -32,6 +32,7 @@
#include "lv2/atom/atom.h"
#include "lv2/worker/worker.h"
#include "condition_variable"
#include <atomic>
#include <map>
#include <string>
@@ -51,10 +52,12 @@ namespace pipedal {
HostWorkerThread();
~HostWorkerThread();
bool StartThread();
void Close();
bool Closed() const { return closed || pThread == nullptr; }
LV2_Worker_Status ScheduleWork(Worker*worker, size_t size, const void*data);
private:
bool closed = false;
std::atomic<bool> closed = false;
std::unique_ptr<std::thread> pThread;
void ThreadProc() noexcept;
@@ -64,8 +67,6 @@ namespace pipedal {
std::vector<uint8_t> dataBuffer;
};
class Worker {
@@ -75,8 +76,8 @@ namespace pipedal {
LilvInstance*lilvInstance;
const LV2_Worker_Interface*workerInterface;
bool closed = false;
bool exiting = false;
std::atomic<bool> closed = false;
std::atomic<bool> exiting = false;
RingBuffer<true,false> responseRingBuffer;
std::vector<uint8_t> responseBuffer;
@@ -94,6 +95,7 @@ namespace pipedal {
public:
Worker(const std::shared_ptr<HostWorkerThread>& pHostWorker,LilvInstance *instance, const LV2_Worker_Interface *iface);
~Worker();
void Close();
LV2_Worker_Status ScheduleWork(