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/log.h>
16#include <wx/module.h>
17#include <wx/process.h>
18
19#include "BasicUI.h"
20#include "CommandLineArgs.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) -> const PluginID&
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{
81
82 auto& moduleManager = ModuleManager::Get();
83 moduleManager.Initialize();
84 moduleManager.DiscoverProviders();
85
86 mClient = std::make_unique<IPCClient>(connectPort, *this);
87}
88
89void PluginHost::OnConnect(IPCChannel& channel) noexcept
90{
91 std::lock_guard lck(mSync);
92 mChannel = &channel;
93}
94
96{
97 Stop();
98}
99
101{
102 Stop();
103}
104
105void PluginHost::OnDataAvailable(const void* data, size_t size) noexcept
106{
107 try
108 {
109 mInputMessageReader.ConsumeBytes(data, size);
110 if(mInputMessageReader.CanPop())
111 {
112 {
113 std::lock_guard lck(mSync);
114 assert(!mRequest);
115 mRequest = mInputMessageReader.Pop();
116 }
117 mRequestCondition.notify_one();
118 }
119 }
120 catch(...)
121 {
122 Stop();
123 }
124}
125
127{
128 std::unique_lock lck(mSync);
129 mRequestCondition.wait(lck, [this]{ return !mRunning || mRequest.has_value(); });
130
131 if(!mRunning)
132 return false;
133
134 if(mRequest)
135 {
136 if(mChannel)
137 detail::PutMessage(*mChannel, wxEmptyString);
138
139 std::optional<wxString> request;
140 mRequest.swap(request);
141
142 lck.unlock();
143
144 wxString providerId;
145 wxString pluginPath;
147 if(detail::ParseRequestString(*request, providerId, pluginPath))
148 Discover(result, providerId, pluginPath);
149 else
150 result.SetError("malformed request string");
151
152 XMLStringWriter xmlWriter;
153 result.WriteXML(xmlWriter);
154
155 lck.lock();
156 if(mChannel)
157 detail::PutMessage(*mChannel, xmlWriter);
158 }
159
160 return true;
161}
162
163
164void PluginHost::Stop() noexcept
165{
166 try
167 {
168 {
169 std::lock_guard lck(mSync);//may throw
170 mRunning = false;
171 mChannel = nullptr;
172 }
173 }
174 catch(...)
175 {
176 //If something went wrong with mutex locking we'll try to
177 //awake main thread if it's blocked on condition variable.
178 //Attempt to relock the mutex there should throw as well(?)...
179 //which, in turn, will result in std::terminate being called
180 }
181 mRequestCondition.notify_one();
182}
183
184bool PluginHost::Start(int connectPort)
185{
186 const auto cmd = wxString::Format("\"%s\" %s %d",
189 connectPort);
190
191 auto process = std::make_unique<wxProcess>();
192 process->Detach();
193 if(wxExecute(cmd, wxEXEC_ASYNC, process.get()) != 0)
194 {
195 //process will delete itself upon termination
196 process.release();
197 return true;
198 }
199 return false;
200}
201
203{
204 return CommandLineArgs::argc >= 3 &&
205 wxStrcmp(CommandLineArgs::argv[1], HostArgument) == 0;
206}
207
208class PluginHostModule final :
209 public wxModule
210{
211public:
212 DECLARE_DYNAMIC_CLASS(PluginHostModule)
213
214 bool OnInit() override
215 {
217 {
218 long connectPort;
219 if(!wxString{ CommandLineArgs::argv[2] }.ToLong(&connectPort))
220 return false;
221
222 //log messages will appear in a separate window
223 //redirect to log file later
224 wxLog::EnableLogging(false);
225
226 //Handle requests...
227 PluginHost host(connectPort);
228 while(host.Serve()) { }
229 //...and terminate app
230 return false;
231 }
232 //do nothing if current process isn't a host process
233 return true;
234 }
235
236 void OnExit() override
237 {
238
239 }
240};
Toolkit-neutral facade for basic user interface services.
wxString PluginID
static CommandHandlerObject & ident(AudacityProject &project)
IMPLEMENT_DYNAMIC_CLASS(PluginHostModule, wxModule)
void InitPreferences(std::unique_ptr< audacity::BasicSettings > uPrefs)
Definition: Prefs.cpp:231
int id
ComponentInterface provides name / vendor / version functions to identify plugins....
static result_type Call(Arguments &&...arguments)
Null check of the installed function is done for you.
Interface for sending data from client to server or vice versa, complemented by IPCChannelStatusCallb...
Definition: IPCChannel.h:22
static ModuleManager & Get()
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:100
void OnDisconnect() noexcept override
Invalidates IPCChannel passed as argument in OnConnect.
Definition: PluginHost.cpp:95
static bool IsHostProcess()
Returns true if current process is considered to be a plugin host process.
Definition: PluginHost.cpp:202
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:105
void Stop() noexcept
Definition: PluginHost.cpp:164
static bool Start(int connectPort)
Attempts to start a host application (should be called from the main application)
Definition: PluginHost.cpp:184
std::optional< wxString > mRequest
Definition: PluginHost.h:44
std::unique_ptr< IPCClient > mClient
Definition: PluginHost.h:39
bool mRunning
Definition: PluginHost.h:46
IPCChannel * mChannel
Definition: PluginHost.h:40
bool Serve()
Definition: PluginHost.cpp:126
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:89
bool OnInit() override
Definition: PluginHost.cpp:214
void OnExit() override
Definition: PluginHost.cpp:236
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:139
void WriteXML(XMLWriter &writer) const
void SetError(const wxString &msg)
void Add(PluginDescriptor &&desc)
UTILITY_API const char *const * argv
A copy of argv; responsibility of application startup to assign it.
UTILITY_API int argc
A copy of argc; responsibility of application startup to assign it.
FILES_API void InitializePathList()
std::string FILES_API GetExecutablePath()
const TranslatableString desc
Definition: ExportPCM.cpp:51
void Discover(detail::PluginValidationResult &result, const wxString &providerId, const wxString &pluginPath)
Definition: PluginHost.cpp:31
const PluginDescriptor * GetPlugin(const PluginID &ID)
bool ParseRequestString(const wxString &req, wxString &providerId, wxString &pluginPath)
void PutMessage(IPCChannel &channel, const wxString &value)