DISTRHO Plugin Framework
Runner.hpp
1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2022 Filipe Coelho <falktx@falktx.com>
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
6  * or without fee is hereby granted, provided that the above copyright notice and this
7  * permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #ifndef DISTRHO_RUNNER_HPP_INCLUDED
18 #define DISTRHO_RUNNER_HPP_INCLUDED
19 
20 #include "../DistrhoUtils.hpp"
21 
22 #ifndef DISTRHO_OS_WASM
23 # include "Thread.hpp"
24 #else
25 # include "String.hpp"
26 # include <emscripten/html5.h>
27 #endif
28 
30 
31 #ifdef DISTRHO_RUNNER_INDIRECT_WASM_CALLS
32 long d_emscripten_set_interval(void (*)(void*), double, void*);
33 void d_emscripten_clear_interval(long);
34 #else
35 # define d_emscripten_set_interval emscripten_set_interval
36 # define d_emscripten_clear_interval emscripten_clear_interval
37 #endif
38 
39 // -------------------------------------------------------------------------------------------------------------------
40 // Runner class
41 
42 /**
43  Runner class for DPF.
44 
45  This is a handy class that handles "idle" time in either background or main thread,
46  whichever is more suitable to the target platform.
47  Typically background threads on desktop platforms, main thread on web.
48 
49  A single function is expected to be implemented by subclasses,
50  which directly allows it to stop the runner by returning false.
51 
52  You can use it for quick operations that do not need to be handled in the main thread if possible.
53  The target is to spread out execution over many runs, instead of spending a lot of time on a single task.
54  */
55 class Runner
56 {
57 protected:
58  /*
59  * Constructor.
60  */
61  Runner(const char* const runnerName = nullptr) noexcept
62  #ifndef DISTRHO_OS_WASM
63  : fRunnerThread(this, runnerName),
64  fTimeInterval(0)
65  #else
66  : fRunnerName(runnerName),
67  fIntervalId(0)
68  #endif
69  {
70  }
71 
72  /*
73  * Destructor.
74  */
75  virtual ~Runner() /*noexcept*/
76  {
77  DISTRHO_SAFE_ASSERT(! isRunnerActive());
78 
79  stopRunner();
80  }
81 
82  /*
83  * Virtual function to be implemented by the subclass.
84  * Return true to keep running, false to stop execution.
85  */
86  virtual bool run() = 0;
87 
88  /*
89  * Check if the runner should stop.
90  * To be called from inside the runner to know if a stop request has been made.
91  */
92  bool shouldRunnerStop() const noexcept
93  {
94  #ifndef DISTRHO_OS_WASM
95  return fRunnerThread.shouldThreadExit();
96  #else
97  return fIntervalId == 0;
98  #endif
99  }
100 
101  // ---------------------------------------------------------------------------------------------------------------
102 
103 public:
104  /*
105  * Check if the runner is active.
106  */
107  bool isRunnerActive() noexcept
108  {
109  #ifndef DISTRHO_OS_WASM
110  return fRunnerThread.isThreadRunning();
111  #else
112  return fIntervalId != 0;
113  #endif
114  }
115 
116  /*
117  * Start the runner.
118  */
119  bool startRunner(const uint timeIntervalMilliseconds = 0) noexcept
120  {
121  #ifndef DISTRHO_OS_WASM
122  DISTRHO_SAFE_ASSERT_RETURN(!fRunnerThread.isThreadRunning(), false);
123  fTimeInterval = timeIntervalMilliseconds;
124  return fRunnerThread.startThread();
125  #else
126  DISTRHO_SAFE_ASSERT_RETURN(fIntervalId == 0, false);
127  fIntervalId = d_emscripten_set_interval(_entryPoint, timeIntervalMilliseconds, this);
128  return true;
129  #endif
130  }
131 
132  /*
133  * Stop the runner.
134  * This will signal the runner to stop if active, and wait until it finishes.
135  */
136  bool stopRunner() noexcept
137  {
138  #ifndef DISTRHO_OS_WASM
139  return fRunnerThread.stopThread(-1);
140  #else
141  signalRunnerShouldStop();
142  return true;
143  #endif
144  }
145 
146  /*
147  * Tell the runner to stop as soon as possible.
148  */
149  void signalRunnerShouldStop() noexcept
150  {
151  #ifndef DISTRHO_OS_WASM
152  fRunnerThread.signalThreadShouldExit();
153  #else
154  if (fIntervalId != 0)
155  {
156  d_emscripten_clear_interval(fIntervalId);
157  fIntervalId = 0;
158  }
159  #endif
160  }
161 
162  // ---------------------------------------------------------------------------------------------------------------
163 
164  /*
165  * Returns the name of the runner.
166  * This is the name that gets set in the constructor.
167  */
168  const String& getRunnerName() const noexcept
169  {
170  #ifndef DISTRHO_OS_WASM
171  return fRunnerThread.getThreadName();
172  #else
173  return fRunnerName;
174  #endif
175  }
176 
177  // ---------------------------------------------------------------------------------------------------------------
178 
179 private:
180 #ifndef DISTRHO_OS_WASM
181  class RunnerThread : public Thread
182  {
183  Runner* const runner;
184 
185  public:
186  RunnerThread(Runner* const r, const char* const rn)
187  : Thread(rn),
188  runner(r) {}
189 
190  protected:
191  void run() override
192  {
193  const uint timeInterval = runner->fTimeInterval;
194 
195  while (!shouldThreadExit())
196  {
197  bool stillRunning = false;
198 
199  try {
200  stillRunning = runner->run();
201  } catch(...) {}
202 
203  if (stillRunning && !shouldThreadExit())
204  {
205  if (timeInterval != 0)
206  d_msleep(timeInterval);
207 
208  // FIXME
209  // pthread_yield();
210  continue;
211  }
212 
213  break;
214  }
215  }
216  } fRunnerThread;
217 
218  uint fTimeInterval;
219 #else
220  const String fRunnerName;
221  long fIntervalId;
222 
223  void _runEntryPoint() noexcept
224  {
225  bool stillRunning = false;
226 
227  try {
228  stillRunning = run();
229  } catch(...) {}
230 
231  if (fIntervalId != 0 && !stillRunning)
232  {
233  d_emscripten_clear_interval(fIntervalId);
234  fIntervalId = 0;
235  }
236  }
237 
238  static void _entryPoint(void* const userData) noexcept
239  {
240  static_cast<Runner*>(userData)->_runEntryPoint();
241  }
242 #endif
243 
244  DISTRHO_DECLARE_NON_COPYABLE(Runner)
245 };
246 
247 // -------------------------------------------------------------------------------------------------------------------
248 
250 
251 #endif // DISTRHO_RUNNER_HPP_INCLUDED
Definition: Runner.hpp:56
Definition: String.hpp:35
Definition: Thread.hpp:38
#define END_NAMESPACE_DISTRHO
Definition: DistrhoInfo.hpp:949
#define START_NAMESPACE_DISTRHO
Definition: DistrhoInfo.hpp:943