Audacity  2.2.2
ODManager.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity - A Digital Audio Editor
4  Copyright 1999-2010 Audacity Team
5  File License: wxWidgets
6 
7  Michael Chinen
8 
9 ******************************************************************//*******************************************************************/
16 
17 #include "../Audacity.h"
18 #include "ODManager.h"
19 #include "ODTask.h"
20 #include "ODTaskThread.h"
21 #include "ODWaveTrackTaskQueue.h"
22 #include "../Project.h"
23 #include <NonGuiThread.h>
24 #include <wx/utils.h>
25 #include <wx/wx.h>
26 #include <wx/thread.h>
27 #include <wx/event.h>
28 
30 static bool gManagerCreated=false;
31 static bool gPause=false; //to be loaded in and used with Pause/Resume before ODMan init.
33 static bool sHasLoadedOD=false;
34 
35 std::unique_ptr<ODManager> ODManager::pMan{};
36 //init the accessor function pointer - use the first time version of the interface fetcher
37 //first we need to typedef the function pointer type because the compiler doesn't support it in the raw
38 typedef ODManager* (*pfodman)();
40 
41 //libsndfile is not threadsafe - this deals with it
43 
44 DEFINE_EVENT_TYPE(EVT_ODTASK_UPDATE)
45 
46 //using this with wxStringArray::Sort will give you a list that
47 //is alphabetical, without depending on case. If you use the
48 //default sort, you will get strings with 'R' before 'a', because it is in caps.
49 int CompareNoCaseFileName(const wxString& first, const wxString& second)
50 {
51  return first.CmpNoCase(second);
52 }
53 
54 //private constructor - Singleton.
56 {
57  mTerminate = false;
58  mTerminated = false;
59  mPause = gPause;
60 
61  //must set up the queue condition
62  mQueueNotEmptyCond = std::make_unique<ODCondition>(&mQueueNotEmptyCondLock);
63 }
64 
65 //private destructor - DELETE with static method Quit()
67 {
68  mTerminateMutex.Lock();
69  mTerminate = true;
70  mTerminateMutex.Unlock();
71 
72  //This while loop waits for ODTasks to finish and the DELETE removes all tasks from the Queue.
73  //This function is called from the main audacity event thread, so there should not be more requests for pMan
74  mTerminatedMutex.Lock();
75  while (!mTerminated)
76  {
77  mTerminatedMutex.Unlock();
78  wxThread::Sleep(200);
79 
80  //signal the queue not empty condition since the ODMan thread will wait on the queue condition
82  mQueueNotEmptyCond->Signal();
83  mQueueNotEmptyCondLock.Unlock();
84 
85  mTerminatedMutex.Lock();
86  }
87  mTerminatedMutex.Unlock();
88 
89  //get rid of all the queues. The queues get rid of the tasks, so we don't worry abut them.
90  //nothing else should be running on OD related threads at this point, so we don't lock.
91  mQueues.clear();
92 }
93 
96 {
97  mTasksMutex.Lock();
98  mTasks.push_back(task);
99  mTasksMutex.Unlock();
100  //signal the queue not empty condition.
101  bool paused;
102 
103  mPauseLock.Lock();
104  paused=mPause;
105  mPauseLock.Unlock();
106 
107  //don't signal if we are paused since if we wake up the loop it will start processing other tasks while paused
108  if(!paused)
109  mQueueNotEmptyCond->Signal();
110 }
111 
113 {
114  bool paused;
115 
116  mPauseLock.Lock();
117  paused=mPause;
118  mPauseLock.Unlock();
119  //don't signal if we are paused
120  if(!paused)
121  mQueueNotEmptyCond->Signal();
122 }
123 
126 {
127  mTasksMutex.Lock();
128  //linear search okay for now, (probably only 1-5 tasks exist at a time.)
129  for(unsigned int i=0;i<mTasks.size();i++)
130  {
131  if(mTasks[i]==task)
132  {
133  mTasks.erase(mTasks.begin()+i);
134  break;
135  }
136  }
137  mTasksMutex.Unlock();
138 
139 }
140 
145 void ODManager::AddNewTask(movable_ptr<ODTask> &&mtask, bool lockMutex)
146 {
147  auto task = mtask.get();
148  ODWaveTrackTaskQueue* queue = NULL;
149 
150  if(lockMutex)
151  mQueuesMutex.Lock();
152  for(unsigned int i=0;i<mQueues.size();i++)
153  {
154  //search for a task containing the lead track. wavetrack removal is threadsafe and bound to the mQueuesMutex
155  //note that GetWaveTrack is not threadsafe, but we are assuming task is not running on a different thread yet.
156  if(mQueues[i]->ContainsWaveTrack(task->GetWaveTrack(0)))
157  queue = mQueues[i].get();
158  }
159 
160  if(queue)
161  {
162  //Add it to the existing queue but keep the lock since this reference can be deleted.
163  queue->AddTask(std::move(mtask));
164  if(lockMutex)
165  mQueuesMutex.Unlock();
166  }
167  else
168  {
169  //Make a NEW one, add it to the local track queue, and to the immediate running task list,
170  //since this task is definitely at the head
171  auto newqueue = make_movable<ODWaveTrackTaskQueue>();
172  newqueue->AddTask(std::move(mtask));
173  mQueues.push_back(std::move(newqueue));
174  if(lockMutex)
175  mQueuesMutex.Unlock();
176 
177  AddTask(task);
178  }
179 }
180 
181 //that switches out the mutex/null check.
183 {
184  gODInitedMutex.Lock();
185  if(!pMan)
186  {
187  pMan.reset(safenew ODManager());
188  pMan->Init();
189  gManagerCreated = true;
190  }
191  gODInitedMutex.Unlock();
192 
193  //change the accessor function to use the quicker method.
195 
196  return pMan.get();
197 }
198 
199 //faster method of instance fetching once init is done
201 {
202  return pMan.get();
203 }
204 
206 {
207  bool ret;
208  gODInitedMutex.Lock();
209  ret= gManagerCreated;
210  gODInitedMutex.Unlock();
211  return ret;
212 }
213 
216 {
217  mCurrentThreads = 0;
218  mMaxThreads = 5;
219 
220  // wxLogDebug(wxT("Initializing ODManager...Creating manager thread"));
221  // This is a detached thread, so it deletes itself when it finishes
222  // ... except on Mac where we we don't use wxThread for reasons unexplained
224 
225 // startThread->SetPriority(0);//default of 50.
226  startThread->Create();
227 // wxPrintf("starting thread from init\n");
228  startThread->Run();
229 
230 // wxPrintf("started thread from init\n");
231  //destruction of thread is taken care of by thread library
232 }
233 
235 {
236  mCurrentThreadsMutex.Lock();
237  mCurrentThreads--;
238  mCurrentThreadsMutex.Unlock();
239 }
240 
243 {
244  bool tasksInArray;
245  bool paused;
246  int numQueues=0;
247 
248  mNeedsDraw=0;
249 
250  //wxLog calls not threadsafe. are printfs? thread-messy for sure, but safe?
251 // wxPrintf("ODManager thread strating \n");
252  //TODO: Figure out why this has no effect at all.
253  //wxThread::This()->SetPriority(30);
254  mTerminateMutex.Lock();
255  while(!mTerminate)
256  {
257  mTerminateMutex.Unlock();
258 // wxPrintf("ODManager thread running \n");
259 
260  //we should look at our WaveTrack queues to see if we can process a NEW task to the running queue.
261  UpdateQueues();
262 
263  //start some threads if necessary
264 
265  mTasksMutex.Lock();
266  tasksInArray = mTasks.size()>0;
267  mTasksMutex.Unlock();
268 
269  mPauseLock.Lock();
270  paused=mPause;
271  mPauseLock.Unlock();
272 
273  mCurrentThreadsMutex.Lock();
274  // keep adding tasks if there is work to do, up to the limit.
275  while(!paused && tasksInArray && (mCurrentThreads < mMaxThreads))
276  {
277  mCurrentThreads++;
278  mCurrentThreadsMutex.Unlock();
279 
280  mTasksMutex.Lock();
281  //detach a NEW thread.
282  // This is a detached thread, so it deletes itself when it finishes
283  // ... except on Mac where we we don't use wxThread for reasons unexplained
284  auto thread = safenew ODTaskThread(mTasks[0]);//task);
285  //thread->SetPriority(10);//default is 50.
286  thread->Create();
287  thread->Run();
288 
289  mTasks.erase(mTasks.begin());
290  tasksInArray = mTasks.size()>0;
291  mTasksMutex.Unlock();
292 
293  mCurrentThreadsMutex.Lock();
294  }
295 
296  mCurrentThreadsMutex.Unlock();
297  //use a conditon variable to block here instead of a sleep.
298 
299  // JKC: If there are no tasks ready to run, or we're paused then
300  // we wait for there to be tasks in the queue.
301  {
302  ODLocker locker{ &mQueueNotEmptyCondLock };
303  if( (!tasksInArray) || paused)
304  mQueueNotEmptyCond->Wait();
305  }
306 
307  //if there is some ODTask running, then there will be something in the queue. If so then redraw to show progress
308  mQueuesMutex.Lock();
309  mNeedsDraw += mQueues.size()>0?1:0;
310  numQueues=mQueues.size();
311  mQueuesMutex.Unlock();
312 
313  //redraw the current project only (ODTasks will send a redraw on complete even if the projects are in the background)
314  //we don't want to redraw at a faster rate when we have more queues because
315  //this means the CPU is already taxed. This if statement normalizes the rate
316  if((mNeedsDraw>numQueues) && numQueues)
317  {
318  mNeedsDraw=0;
319  wxCommandEvent event( EVT_ODTASK_UPDATE );
322  if(proj)
323  proj->GetEventHandler()->AddPendingEvent(event);
324  }
325  mTerminateMutex.Lock();
326  }
327  mTerminateMutex.Unlock();
328 
329  mTerminatedMutex.Lock();
330  mTerminated=true;
331  mTerminatedMutex.Unlock();
332 
333  //wxLogDebug Not thread safe.
334  //wxPrintf("ODManager thread terminating\n");
335 }
336 
337 //static function that prevents ODTasks from being scheduled
338 //does not stop currently running tasks from completing their immediate subtask,
339 //but presumably they will finish within a second
340 void ODManager::Pauser::Pause(bool pause)
341 {
342  if(IsInstanceCreated())
343  {
344  pMan->mPauseLock.Lock();
345  pMan->mPause = pause;
346  pMan->mPauseLock.Unlock();
347 
348  if(!pause)
349  //we should check the queue again.
350  pMan->mQueueNotEmptyCond->Signal();
351  }
352  else
353  {
354  gPause=pause;
355  }
356 }
357 
359 {
360  Pause(false);
361 }
362 
364 {
365  if(IsInstanceCreated())
366  {
367  pMan.reset();
368  }
369 }
370 
373 {
374  mQueuesMutex.Lock();
375  for(unsigned int i=0;i<mQueues.size();i++)
376  {
377  if(mQueues[i]->ContainsWaveTrack(track))
378  mQueues[i]->RemoveWaveTrack(track);
379  }
380  mQueuesMutex.Unlock();
381 }
382 
385 {
386  mQueuesMutex.Lock();
387  for(unsigned int i=0;i<mQueues.size();i++)
388  {
389  mQueues[i]->ReplaceWaveTrack(oldTrack,newTrack);
390  }
391  mQueuesMutex.Unlock();
392 }
393 
396 {
397  ODWaveTrackTaskQueue* owner=NULL;
398  mQueuesMutex.Lock();
399  for(unsigned int i=0;i<mQueues.size();i++)
400  {
401  if(mQueues[i]->ContainsWaveTrack(track))
402  {
403  owner = mQueues[i].get();
404  break;
405  }
406  }
407  if(owner)
408  owner->MakeWaveTrackIndependent(track);
409 
410  mQueuesMutex.Unlock();
411 }
412 
418 bool ODManager::MakeWaveTrackDependent(WaveTrack* dependentTrack,WaveTrack* masterTrack)
419 {
420  //First, check to see if the task lists are mergeable. If so, we can simply add this track to the other task and queue,
421  //then DELETE this one.
422  ODWaveTrackTaskQueue* masterQueue=NULL;
423  ODWaveTrackTaskQueue* dependentQueue=NULL;
424  unsigned int dependentIndex = 0;
425  bool canMerge = false;
426 
427  mQueuesMutex.Lock();
428  for(unsigned int i=0;i<mQueues.size();i++)
429  {
430  if(mQueues[i]->ContainsWaveTrack(masterTrack))
431  {
432  masterQueue = mQueues[i].get();
433  }
434  else if(mQueues[i]->ContainsWaveTrack(dependentTrack))
435  {
436  dependentQueue = mQueues[i].get();
437  dependentIndex = i;
438  }
439 
440  }
441  if(masterQueue&&dependentQueue)
442  canMerge=masterQueue->CanMergeWith(dependentQueue);
443 
444  //otherwise we need to let dependentTrack's queue live on. We'll have to wait till the conflicting tasks are done.
445  if(!canMerge)
446  {
447  mQueuesMutex.Unlock();
448  return false;
449  }
450  //then we add dependentTrack to the masterTrack's queue - this will allow future ODScheduling to affect them together.
451  //this sets the NeedODUpdateFlag since we don't want the head task to finish without haven't dealt with the depednent
452  masterQueue->MergeWaveTrack(dependentTrack);
453 
454  //finally remove the dependent track
455  mQueues.erase(mQueues.begin()+dependentIndex);
456  mQueuesMutex.Unlock();
457  return true;
458 }
459 
460 
464 void ODManager::DemandTrackUpdate(WaveTrack* track, double seconds)
465 {
466  mQueuesMutex.Lock();
467  for(unsigned int i=0;i<mQueues.size();i++)
468  {
469  mQueues[i]->DemandTrackUpdate(track,seconds);
470  }
471  mQueuesMutex.Unlock();
472 }
473 
477 {
478  mQueuesMutex.Lock();
479  for(unsigned int i=0;i<mQueues.size();i++)
480  {
481  if(mQueues[i]->IsFrontTaskComplete())
482  {
483  //this should DELETE and remove the front task instance.
484  mQueues[i]->RemoveFrontTask();
485  //schedule next.
486  if(!mQueues[i]->IsEmpty())
487  {
488  //we need to release the lock on the queue vector before using the task vector's lock or we deadlock
489  //so get a temp.
490  ODWaveTrackTaskQueue* queue = mQueues[i].get();
491 
492  AddTask(queue->GetFrontTask());
493  }
494  }
495 
496  //if the queue is empty DELETE it.
497  if(mQueues[i]->IsEmpty())
498  {
499  mQueues.erase(mQueues.begin()+i);
500  i--;
501  }
502  }
503  mQueuesMutex.Unlock();
504 }
505 
506 //static
509 {
510  sHasLoadedOD = true;
511 }
512 
513 //static
516 {
517  sHasLoadedOD = false;
518 }
519 
520 //static
523 {
524  return sHasLoadedOD;
525 }
526 
528 void ODManager::FillTipForWaveTrack( const WaveTrack * t, wxString &tip )
529 {
530  mQueuesMutex.Lock();
531  for(unsigned int i=0;i<mQueues.size();i++)
532  {
533  mQueues[i]->FillTipForWaveTrack(t, tip);
534  }
535  mQueuesMutex.Unlock();
536 }
537 
540 {
541  float total=0.0;
542  mQueuesMutex.Lock();
543  for(unsigned int i=0;i<mQueues.size();i++)
544  {
545  total+=mQueues[i]->GetFrontTask()->PercentComplete();
546  }
547  mQueuesMutex.Unlock();
548 
549  //avoid div by zero and be thread smart.
550  int totalTasks = GetTotalNumTasks();
551  return (float) total/(totalTasks>0?totalTasks:1);
552 }
553 
556 {
557  int ret=0;
558  mQueuesMutex.Lock();
559  for(unsigned int i=0;i<mQueues.size();i++)
560  {
561  ret+=mQueues[i]->GetNumTasks();
562  }
563  mQueuesMutex.Unlock();
564  return ret;
565 }
void MergeWaveTrack(WaveTrack *track)
void AddNewTask(movable_ptr< ODTask > &&mtask, bool lockMutex=true)
Adds a wavetrack, creates a queue member.
Definition: ODManager.cpp:145
static bool IsInstanceCreated()
returns whether or not the singleton instance was created yet
Definition: ODManager.cpp:205
static ODManager *(* Instance)()
Definition: ODManager.h:49
A singleton that manages currently running Tasks on an arbitrary number of threads.
Definition: ODManager.h:43
void DecrementCurrentThreads()
Reduces the count of current threads running. Meant to be called when ODTaskThreads end in their own ...
Definition: ODManager.cpp:234
void DemandTrackUpdate(WaveTrack *track, double seconds)
changes the tasks associated with this Waveform to process the task from a different point in the tra...
Definition: ODManager.cpp:464
std::unique_ptr< T > movable_ptr
Definition: MemoryX.h:734
static void Resume()
Definition: ODManager.cpp:358
ODTask * GetFrontTask()
Schedules the front task for immediate execution.
void SignalTaskQueueLoop()
Wakes the queue loop up by signalling its condition variable.
Definition: ODManager.cpp:112
static void Quit()
Kills the ODMananger Thread.
Definition: ODManager.cpp:363
void ReplaceWaveTrack(WaveTrack *oldTrack, WaveTrack *newTrack)
replace the wavetrack whose wavecache the gui watches for updates
Definition: ODManager.cpp:384
volatile int mCurrentThreads
Number of threads currently running. Accessed thru multiple threads.
Definition: ODManager.h:158
DEFINE_EVENT_TYPE(EVT_OPEN_AUDIO_FILE)
Custom events.
void RemoveWaveTrack(WaveTrack *track)
removes a wavetrack and notifies its associated tasks to stop using its reference.
Definition: ODManager.cpp:372
float GetOverallPercentComplete()
Gets the total percent complete for all tasks combined.
Definition: ODManager.cpp:539
static void Pause(bool pause=true)
Definition: ODManager.cpp:340
ODLock mCurrentThreadsMutex
Definition: ODManager.h:160
ODLock mTerminateMutex
Definition: ODManager.h:166
ODLock mTasksMutex
Definition: ODManager.h:149
static void UnmarkLoadedODFlag()
resets a flag that is set if we have loaded some OD blockfiles from PCM.
Definition: ODManager.cpp:515
void AddTask(movable_ptr< ODTask > &&mtask)
Add a task to the queue.
int GetTotalNumTasks()
Get Total Number of Tasks.
Definition: ODManager.cpp:555
#define safenew
Definition: Audacity.h:230
void MakeWaveTrackIndependent(WaveTrack *track)
if it shares a queue/task, creates a NEW queue/task for the track, and removes it from any previously...
Definition: ODManager.cpp:395
static ODManager * InstanceNormal()
Gets the singleton instance.
Definition: ODManager.cpp:200
static ODManager * InstanceFirstTime()
Gets the singleton instance.
Definition: ODManager.cpp:182
void FillTipForWaveTrack(const WaveTrack *t, wxString &tip)
fills in the status bar message for a given track
Definition: ODManager.cpp:528
AudacityProject provides the main window, with tools and tracks contained within it.
Definition: Project.h:176
void AddTask(ODTask *task)
Adds a task to the running queue. Threas-safe.
Definition: ODManager.cpp:95
A class representing a modular task to be used with the On-Demand structures.
Definition: ODTask.h:39
std::vector< movable_ptr< ODWaveTrackTaskQueue > > mQueues
Definition: ODManager.h:143
A thread that executes a part of the task specfied by an ODTask.
Definition: ODTaskThread.h:133
static ODLock gODInitedMutex
Definition: ODManager.cpp:29
static ODLock & AllProjectDeleteMutex()
Prevents DELETE from external thread - for e.g. use of GetActiveProject.
Definition: Project.cpp:183
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
void Init()
Launches a thread for the manager and starts accepting Tasks.
Definition: ODManager.cpp:215
static ODLock sLibSndFileMutex
Definition: ODManager.cpp:42
ODLock mQueueNotEmptyCondLock
Definition: ODManager.h:172
static void MarkLoadedODFlag()
sets a flag that is set if we have loaded some OD blockfiles from PCM.
Definition: ODManager.cpp:508
static bool gManagerCreated
Definition: ODManager.cpp:30
static bool HasLoadedODFlag()
returns a flag that is set if we have loaded some OD blockfiles from PCM.
Definition: ODManager.cpp:522
void RemoveTaskIfInQueue(ODTask *task)
removes a task from the active task queue
Definition: ODManager.cpp:125
bool MakeWaveTrackDependent(WaveTrack *dependentTrack, WaveTrack *masterTrack)
Definition: ODManager.cpp:418
ODLock mPauseLock
Definition: ODManager.h:153
ODLock mQueuesMutex
Definition: ODManager.h:144
std::vector< ODTask * > mTasks
Definition: ODManager.h:147
std::unique_ptr< ODCondition > mQueueNotEmptyCond
Definition: ODManager.h:173
AUDACITY_DLL_API AudacityProject * GetActiveProject()
Definition: Project.cpp:308
void MakeWaveTrackIndependent(WaveTrack *track)
int CompareNoCaseFileName(const wxString &first, const wxString &second)
wxstring compare function for sorting case, which is needed to load correctly.
Definition: ODManager.cpp:49
void UpdateQueues()
Remove references in our array to Tasks that have been completed/Schedule NEW ones.
Definition: ODManager.cpp:476
int mMaxThreads
Maximum number of threads allowed out.
Definition: ODManager.h:163
static std::unique_ptr< ODManager > pMan
Definition: ODManager.h:140
volatile bool mTerminate
Definition: ODManager.h:165
ODLock mTerminatedMutex
Definition: ODManager.h:169
void Start()
Start the main loop for the manager.
Definition: ODManager.cpp:242
A class representing a modular task to be used with the On-Demand structures.
volatile int mNeedsDraw
Definition: ODManager.h:155
volatile bool mTerminated
Definition: ODManager.h:168
bool CanMergeWith(ODWaveTrackTaskQueue *otherQueue)
returns whether or not this queue's task list and another's can merge together, as when we make two m...
static bool gPause
Definition: ODManager.cpp:31
ODManager *(* pfodman)()
Definition: ODManager.cpp:38
static bool sHasLoadedOD
a flag that is set if we have loaded some OD blockfiles from PCM.
Definition: ODManager.cpp:33
volatile bool mPause
Definition: ODManager.h:152