tracktion-engine 3.0-10-g034fdde4aa5
Tracktion Engine — High level data model for audio applications

« « « Anklang Documentation
Loading...
Searching...
No Matches
tracktion_BackgroundJobs.h
Go to the documentation of this file.
1 /*
2 ,--. ,--. ,--. ,--.
3 ,-' '-.,--.--.,--,--.,---.| |,-.,-' '-.`--' ,---. ,--,--, Copyright 2024
4 '-. .-'| .--' ,-. | .--'| /'-. .-',--.| .-. || \ Tracktion Software
5 | | | | \ '-' \ `--.| \ \ | | | |' '-' '| || | Corporation
6 `---' `--' `--`--'`---'`--'`--' `---' `--' `---' `--''--' www.tracktion.com
7
8 Tracktion Engine uses a GPL/commercial licence - see LICENCE.md for details.
9*/
10
11namespace tracktion { inline namespace engine
12{
13
14class BackgroundJobManager;
15
16//==============================================================================
18{
19public:
22
23 virtual float getCurrentTaskProgress() = 0;
24 virtual bool canCancel() const { return false; }
25
26 void setManager (BackgroundJobManager&);
27
29 void setName (const juce::String& newName);
30
35
36private:
37 BackgroundJobManager* manager = nullptr;
38};
39
40//==============================================================================
47 private juce::Timer
48{
49public:
51 : pool (8)
52 {
53 }
54
55 ~BackgroundJobManager() override
56 {
57 pool.removeAllJobs (true, 30000);
58 }
59
60 void addJob (ThreadPoolJobWithProgress* job, bool takeOwnership)
61 {
62 if (job == nullptr)
63 return;
64
65 job->setManager (*this);
66 pool.addJob (job, takeOwnership);
67 }
68
69 void removeJob (ThreadPoolJobWithProgress* job, bool interruptIfRunning, int timeOutMilliseconds)
70 {
71 pool.removeJob (job, interruptIfRunning, timeOutMilliseconds);
72 }
73
74 void stopAndDeleteAllRunningJobs()
75 {
76 // Call this twice as the first call may only stop (and not delete) running jobs
77 pool.removeAllJobs (true, 30000);
78 pool.removeAllJobs (true, 5000);
79 jassert (pool.getNumJobs() == 0);
80 }
81
82 //==============================================================================
83 struct JobInfo
84 {
85 JobInfo() {}
86
88 : name (j.getJobName()), progress (j.getCurrentTaskProgress()), jobId (id), canCancel (j.canCancel()) {}
89
90 JobInfo (const JobInfo& o) : name (o.name), progress (o.progress), jobId (o.jobId), canCancel (o.canCancel) {}
91 JobInfo& operator= (const JobInfo& o) { name = o.name; progress = o.progress; jobId = o.jobId; canCancel = o.canCancel; return *this; }
92
93 juce::String name;
94 float progress = 0;
95 int jobId = -1;
96 bool canCancel = false;
97 };
98
99 void signalJobShouldExit (const JobInfo& info)
100 {
101 const juce::ScopedLock sl (jobsLock);
102
103 for (int i = jobs.size(); --i >= 0;)
104 if (auto pair = jobs.getUnchecked (i))
105 if (pair->info.jobId == info.jobId)
106 pair->job.signalJobShouldExit();
107 }
108
109 JobInfo getJobInfo (int index) const noexcept
110 {
111 const juce::ScopedLock sl (jobsLock);
112
113 if (auto j = jobs[index])
114 return j->info;
115
116 return JobInfo();
117 }
118
119 int getNumJobs() const noexcept { const juce::ScopedLock sl (jobsLock); return jobs.size(); }
120 float getTotalProgress() const noexcept { return totalProgress; }
121 juce::ThreadPool& getPool() noexcept { return pool; }
122
123 //==============================================================================
125 {
126 public:
127 virtual ~Listener() {}
128 virtual void backgroundJobsChanged() = 0;
129 };
130
131 void addListener (Listener* l) { listeners.add (l); }
132 void removeListener (Listener* l) { listeners.remove (l); }
133
134private:
135 struct JobInfoPair
136 {
137 JobInfoPair (ThreadPoolJobWithProgress& j, int jobId)
138 : job (j), info (j, jobId) {}
139
140 ThreadPoolJobWithProgress& job;
141 JobInfo info;
143 };
144
145 friend class ThreadPoolJobWithProgress;
147 juce::CriticalSection jobsLock;
148 juce::ThreadPool pool;
150 float totalProgress = 1.0f;
151 int nextJobId = 0;
152
153 void addJobInternal (ThreadPoolJobWithProgress& job)
154 {
155 const juce::ScopedLock sl (jobsLock);
156
157 jobs.add (new JobInfoPair (job, getNextJobId()));
159 startTimerHz (25);
160 }
161
162 void removeJobInternal (ThreadPoolJobWithProgress& job)
163 {
164 const juce::ScopedLock sl (jobsLock);
165
166 for (int i = jobs.size(); --i >= 0;)
167 if (&jobs.getUnchecked (i)->job == &job)
168 jobs.remove (i);
169
171 }
172
173 int getNextJobId() noexcept { return ++nextJobId &= 0xffffff; }
174
175 void updateJobs()
176 {
178 if (app->isInitialising())
179 return;
180
181 float newTotal = 0.0f;
182 int numJobs = 0;
183
184 {
185 const juce::ScopedLock sl (jobsLock);
186
187 for (int i = jobs.size(); --i >= 0;)
188 {
189 if (auto pair = jobs.getUnchecked (i))
190 {
191 const float p = pair->job.getCurrentTaskProgress();
192 jassert (! std::isnan (p));
193 pair->info.progress = p;
194 newTotal += p;
195 ++numJobs;
196 }
197 }
198 }
199
200 newTotal = (numJobs > 0) ? (newTotal / numJobs) : 1.0f;
201
202 if (newTotal > totalProgress)
203 totalProgress = totalProgress + ((newTotal - totalProgress) / 2.0f);
204 else
205 totalProgress = newTotal;
206
207 jassert (totalProgress == totalProgress
208 && juce::isPositiveAndNotGreaterThan (totalProgress, 1.0f));
209
210 if (totalProgress >= 1.0f)
211 stopTimer();
212 }
213
214 void handleAsyncUpdate() override { listeners.call (&Listener::backgroundJobsChanged); }
215 void timerCallback() override { updateJobs(); }
216
218};
219
220inline ThreadPoolJobWithProgress::~ThreadPoolJobWithProgress()
221{
222 jassert (manager == nullptr); // You haven't called prepareForJobDeletion!
223
224 if (manager != nullptr)
225 manager->removeJobInternal (*this);
226}
227
228inline void ThreadPoolJobWithProgress::setManager (BackgroundJobManager& m)
229{
230 manager = &m;
231 manager->addJobInternal (*this);
232 manager->triggerAsyncUpdate();
233}
234
236{
237 setJobName (newName);
238
239 if (manager != nullptr)
240 manager->triggerAsyncUpdate();
241}
242
244{
245 if (manager != nullptr)
246 manager->removeJobInternal (*this);
247
248 manager = nullptr;
249}
250
251}} // namespace tracktion { inline namespace engine
static JUCEApplicationBase * getInstance() noexcept
void call(Callback &&callback)
int size() const noexcept
ObjectClass * getUnchecked(int index) const noexcept
void remove(int indexToRemove, bool deleteObject=true)
ObjectClass * add(ObjectClass *newObject)
void setJobName(const String &newName)
String getJobName() const
ThreadPoolJob(const String &name)
void addJob(ThreadPoolJob *job, bool deleteJobWhenFinished)
int getNumJobs() const noexcept
bool removeAllJobs(bool interruptRunningJobs, int timeOutMilliseconds, JobSelector *selectedJobsToRemove=nullptr)
bool removeJob(ThreadPoolJob *job, bool interruptIfRunning, int timeOutMilliseconds)
void stopTimer() noexcept
void startTimerHz(int timerFrequencyHz) noexcept
Manages a set of background tasks that can be run concurrently on a background thread.
void prepareForJobDeletion()
Call this in your sub-class destructor to to remvoe it from the manager queue before this class's des...
void setName(const juce::String &newName)
Sets the job's name but also updates the manager so the list will reflect it.
T isnan(T... args)
#define jassert(expression)
#define JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(className)
bool isPositiveAndNotGreaterThan(Type1 valueToTest, Type2 upperLimit) noexcept