Audacity 3.2.0
PluginHost.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 @file PluginHost.cpp
6
7 @author Vitaly Sverchinsky
8
9 Part of lib-module-manager library
10
11**********************************************************************/
12
13#include "PluginHost.h"
14
15#include <wx/app.h>
16#include <wx/log.h>
17#include <wx/module.h>
18#include <wx/process.h>
19
20#include "BasicUI.h"
21#include "PathList.h"
22#include "FileNames.h"
23#include "ModuleManager.h"
24#include "IPCClient.h"
26#include "PluginManager.h"
27
28namespace
29{
30 //Attempts to instantiate plugin module and put plugin descriptors into result
31 void Discover(detail::PluginValidationResult& result, const wxString& providerId, const wxString& pluginPath)
32 {
33 try
34 {
35 if(auto provider = ModuleManager::Get().CreateProviderInstance(providerId, wxEmptyString))
36 {
37 TranslatableString errorMessage{};
38 auto validator = provider->MakeValidator();
39 auto numPlugins = provider->DiscoverPluginsAtPath(
40 pluginPath, errorMessage, [&](PluginProvider *provider, ComponentInterface *ident)
41 {
42 //Workaround: use DefaultRegistrationCallback to create all descriptors for us
43 //and then put a copy into result
45 if(const auto ptr = PluginManager::Get().GetPlugin(id))
46 {
47 auto desc = *ptr;
48 try
49 {
50 if(validator)
51 validator->Validate(*ident);
52 }
53 catch(...)
54 {
55 desc.SetEnabled(false);
56 desc.SetValid(false);
57 }
58 result.Add(std::move(desc));
59 }
60 return id;
61 });
62 if(!errorMessage.empty())
63 result.SetError(errorMessage.Debug());
64 else if(numPlugins == 0)
65 result.SetError("no plugins found");
66 }
67 else
68 result.SetError("provider not found");
69 }
70 catch(...)
71 {
72 result.SetError("unknown error");
73 }
74 }
75}
76
77PluginHost::PluginHost(int connectPort)
78{
80
81 wxFileName configFileName{ FileNames::Configuration() };
82 auto pConfig = std::make_unique<FileConfig>(
83 AppName, wxEmptyString, configFileName.GetFullPath(),
84 wxEmptyString, wxCONFIG_USE_LOCAL_FILE);
85 pConfig->Init();
86 InitPreferences(std::move(pConfig));
87
88 auto& moduleManager = ModuleManager::Get();
89 moduleManager.Initialize();
90 moduleManager.DiscoverProviders();
91
92 mClient = std::make_unique<IPCClient>(connectPort, *this);
93}
94
95void PluginHost::OnConnect(IPCChannel& channel) noexcept
96{
97 std::lock_guard lck(mSync);
98 mChannel = &channel;
99}
100
102{
103 Stop();
104}
105
107{
108 Stop();
109}
110
111void PluginHost::OnDataAvailable(const void* data, size_t size) noexcept
112{
113 try
114 {
115 mInputMessageReader.ConsumeBytes(data, size);
116 if(mInputMessageReader.CanPop())
117 {
118 {
119 std::lock_guard lck(mSync);
120 assert(!mRequest);
121 mRequest = mInputMessageReader.Pop();
122 }
123 mRequestCondition.notify_one();
124 }
125 }
126 catch(...)
127 {
128 Stop();
129 }
130}
131
133{
134 std::unique_lock lck(mSync);
135 mRequestCondition.wait(lck, [this]{ return !mRunning || mRequest.has_value(); });
136
137 if(!mRunning)
138 return false;
139
140 if(mRequest)
141 {
142 if(mChannel)
143 detail::PutMessage(*mChannel, wxEmptyString);
144
145 std::optional<wxString> request;
146 mRequest.swap(request);
147
148 lck.unlock();
149
150 wxString providerId;
151 wxString pluginPath;
153 if(detail::ParseRequestString(*request, providerId, pluginPath))
154 Discover(result, providerId, pluginPath);
155 else
156 result.SetError("malformed request string");
157
158 XMLStringWriter xmlWriter;
159 result.WriteXML(xmlWriter);
160
161 lck.lock();
162 if(mChannel)
163 detail::PutMessage(*mChannel, xmlWriter);
164 }
165
166 return true;
167}
168
169
170void PluginHost::Stop() noexcept
171{
172 try
173 {
174 {
175 std::lock_guard lck(mSync);//may throw
176 mRunning = false;
177 mChannel = nullptr;
178 }
179 }
180 catch(...)
181 {
182 //If something went wrong with mutex locking we'll try to
183 //awake main thread if it's blocked on condition variable.
184 //Attempt to relock the mutex there should throw as well(?)...
185 //which, in turn, will result in std::terminate being called
186 }
187 mRequestCondition.notify_one();
188}
189
190bool PluginHost::Start(int connectPort)
191{
192 const auto cmd = wxString::Format("\"%s\" %s %d",
195 connectPort);
196
197 auto process = std::make_unique<wxProcess>();
198 process->Detach();
199 if(wxExecute(cmd, wxEXEC_ASYNC, process.get()) != 0)
200 {
201 //process will delete itself upon termination
202 process.release();
203 return true;
204 }
205 return false;
206}
207
208bool PluginHost::IsHostProcess(int argc, wxChar** argv)
209{
210 return argc >= 3 && wxStrcmp(argv[1], HostArgument) == 0;
211}
212
213class PluginHostModule final :
214 public wxModule
215{
216public:
217 DECLARE_DYNAMIC_CLASS(PluginHostModule)
218
219 bool OnInit() override
220 {
221 if(PluginHost::IsHostProcess(wxTheApp->argc, wxTheApp->argv))
222 {
223 long connectPort;
224 if(!wxTheApp->argv[2].ToLong(&connectPort))
225 return false;
226
227 //log messages will appear in a separate window
228 //redirect to log file later
229 wxLog::EnableLogging(false);
230
231 //Handle requests...
232 PluginHost host(connectPort);
233 while(host.Serve()) { }
234 //...and terminate app
235 return false;
236 }
237 //do noting if current process isn't a host process
238 return true;
239 }
240
241 void OnExit() override
242 {
243
244 }
245};
Toolkit-neutral facade for basic user interface services.
const TranslatableString desc
Definition: ExportPCM.cpp:58
static CommandHandlerObject & ident(AudacityProject &project)
const std::wstring AppName
This program's name.
IMPLEMENT_DYNAMIC_CLASS(PluginHostModule, wxModule)
void InitPreferences(std::unique_ptr< FileConfig > uPrefs)
Definition: Prefs.cpp:201
int id
ComponentInterface provides name / vendor / version functions to identify plugins....
Interface for sending data from client to server or vice versa, complemented by IPCChannelStatusCallb...
Definition: IPCChannel.h:22
static ModuleManager & Get()
static const FilePath & GetExecutablePath()
Internal class, processes plugin validation requests from the main app. Request is a simple string fo...
Definition: PluginHost.h:36
void OnConnectionError() noexcept override
Called when connection attempt fails.
Definition: PluginHost.cpp:106
void OnDisconnect() noexcept override
Invalidates IPCChannel passed as argument in OnConnect.
Definition: PluginHost.cpp:101
static constexpr auto HostArgument
Definition: PluginHost.h:37
std::mutex mSync
Definition: PluginHost.h:42
void OnDataAvailable(const void *data, size_t size) noexcept override
Called when data chunk received as a result of a preceding call to IPCChannel::Send....
Definition: PluginHost.cpp:111
void Stop() noexcept
Definition: PluginHost.cpp:170
static bool Start(int connectPort)
Attempts to start a host application (should be called from the main application)
Definition: PluginHost.cpp:190
std::optional< wxString > mRequest
Definition: PluginHost.h:44
std::unique_ptr< IPCClient > mClient
Definition: PluginHost.h:39
static bool IsHostProcess(int argc, wxChar **argv)
Returns true if current process is considered to be a plugin host process.
Definition: PluginHost.cpp:208
bool mRunning
Definition: PluginHost.h:46
IPCChannel * mChannel
Definition: PluginHost.h:40
bool Serve()
Definition: PluginHost.cpp:132
PluginHost(int connectPort)
Definition: PluginHost.cpp:77
std::condition_variable mRequestCondition
Definition: PluginHost.h:43
void OnConnect(IPCChannel &channel) noexcept override
Called when connection established.
Definition: PluginHost.cpp:95
bool OnInit() override
Definition: PluginHost.cpp:219
void OnExit() override
Definition: PluginHost.cpp:241
static PluginManager & Get()
static const PluginID & DefaultRegistrationCallback(PluginProvider *provider, ComponentInterface *ident)
Holds a msgid for the translation catalog; may also bind format arguments.
Wrapper to output XML data to strings.
Definition: XMLWriter.h:140
void WriteXML(XMLWriter &writer) const
void SetError(const wxString &msg)
void Add(PluginDescriptor &&desc)
FILES_API FilePath Configuration()
FILES_API void InitializePathList()
void Discover(detail::PluginValidationResult &result, const wxString &providerId, const wxString &pluginPath)
Definition: PluginHost.cpp:31
bool ParseRequestString(const wxString &req, wxString &providerId, wxString &pluginPath)
void PutMessage(IPCChannel &channel, const wxString &value)