MezzanineEngine 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
framescheduler.cpp
Go to the documentation of this file.
1 // The DAGFrameScheduler is a Multi-Threaded lock free and wait free scheduling library.
2 // © Copyright 2010 - 2014 BlackTopp Studios Inc.
3 /* This file is part of The DAGFrameScheduler.
4 
5  The DAGFrameScheduler is free software: you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation, either version 3 of the License, or
8  (at your option) any later version.
9 
10  The DAGFrameScheduler is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  GNU General Public License for more details.
14 
15  You should have received a copy of the GNU General Public License
16  along with The DAGFrameScheduler. If not, see <http://www.gnu.org/licenses/>.
17 */
18 /* The original authors have included a copy of the license specified above in the
19  'doc' folder. See 'gpl.txt'
20 */
21 /* We welcome the use of the DAGFrameScheduler to anyone, including companies who wish to
22  Build professional software and charge for their product.
23 
24  However there are some practical restrictions, so if your project involves
25  any of the following you should contact us and we will try to work something
26  out:
27  - DRM or Copy Protection of any kind(except Copyrights)
28  - Software Patents You Do Not Wish to Freely License
29  - Any Kind of Linking to Non-GPL licensed Works
30  - Are Currently In Violation of Another Copyright Holder's GPL License
31  - If You want to change our code and not add a few hundred MB of stuff to
32  your distribution
33 
34  These and other limitations could cause serious legal problems if you ignore
35  them, so it is best to simply contact us or the Free Software Foundation, if
36  you have any questions.
37 
38  Joseph Toppi - toppij@gmail.com
39  John Blackwood - makoenergy02@gmail.com
40 */
41 #ifndef _framescheduler_cpp
42 #define _framescheduler_cpp
43 
44 #include "framescheduler.h"
45 #include "doublebufferedresource.h"
46 #include "monopoly.h"
48 #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
49  #include "atomicoperations.h"
50 #endif
51 
52 
53 #include <exception>
54 #include <iostream>
55 #include <algorithm>
56 
57 using namespace std;
58 
59 /// @file
60 /// @brief This is the core object implementation for this algorithm
61 
62 #ifdef _MEZZ_THREAD_WIN32_
63  #ifdef _MSC_VER
64  #pragma warning( disable : 4706) // Disable Legitimate assignment in a WorkUnit acquisition loops
65  #endif
66 #endif
67 
68 
69 namespace Mezzanine
70 {
71  namespace Threading
72  {
73 
74  /// @cond false
75 
76  /// @brief This is the function that all threads will run, except the main.
77  /// @param ThreadStorage A pointer to a ThreadSpecificStorage that has the required data for a thread after it launches.
78  void ThreadWork(void* ThreadStorage)
79  {
80  DefaultThreadSpecificStorage::Type& Storage = *((DefaultThreadSpecificStorage::Type*)ThreadStorage);
81  FrameScheduler& FS = *(Storage.GetFrameScheduler());
82  iWorkUnit* CurrentUnit;
83 
84  #ifdef MEZZ_USEBARRIERSEACHFRAME
85  while(!FS.LastFrame)
86  {
87  FS.StartFrameSync.Wait(); // Syncs with Main thread in CreateThreads()
88  if(FS.LastFrame)
89  { break; }
90  #endif
91 
92  do
93  {
94  while( (CurrentUnit = FS.GetNextWorkUnit()) ) /// @todo needs to skip ahead a unit instead of spinning
95  {
96  if(Starting==CurrentUnit->TakeOwnerShip())
97  { CurrentUnit->operator()(Storage); }
98  }
99  } while(!FS.AreAllWorkUnitsComplete());
100 
101  #ifdef MEZZ_USEBARRIERSEACHFRAME
102  FS.EndFrameSync.Wait(); // Syncs with Main thread in JoinAllThreads()
103  }
104  #endif
105  }
106 
107  /// @brief This is the function that the main thread runs.
108  /// @param ThreadStorage A pointer to a ThreadSpecificStorage that has the required data for a thread after it launches.
109  void ThreadWorkAffinity(void* ThreadStorage)
110  {
111  DefaultThreadSpecificStorage::Type& Storage = *((DefaultThreadSpecificStorage::Type*)ThreadStorage);
112  FrameScheduler& FS = *(Storage.GetFrameScheduler());
113  iWorkUnit* CurrentUnit;
114  do
115  {
116  while( (CurrentUnit = FS.GetNextWorkUnitAffinity()) ) /// @todo needs to skip ahead a unit instead of spinning
117  {
118  if(Starting==CurrentUnit->TakeOwnerShip())
119  { CurrentUnit->operator()(Storage); }
120  }
121  }
122  while(!FS.AreAllWorkUnitsComplete());
123  }
124  /// @endcond
125 
126  ////////////////////////////////////////////////////////////////////////////////
127  // Protected Methods
128 
129  void FrameScheduler::CleanUpThreads()
130  {
131  #ifdef MEZZ_USEBARRIERSEACHFRAME
132  while(1!=AtomicCompareAndSwap32(&LastFrame,LastFrame,1));
133  StartFrameSync.SetThreadSyncCount(0);
134  EndFrameSync.SetThreadSyncCount(0); // Handle situations where Threads have not been created yet
135  #else
136  JoinAllThreads();
137  #endif
138  }
139 
140  void FrameScheduler::DeleteThreads()
141  {
142  for(std::vector<Thread*>::iterator Iter = Threads.begin(); Iter!=Threads.end(); ++Iter)
143  { delete *Iter; }
144  }
145 
146  void FrameScheduler::UpdateDependentGraph(const std::vector<WorkUnitKey>& Units)
147  {
148  // for each WorkUnit
149  for(std::vector<WorkUnitKey>::const_iterator Iter=Units.begin(); Iter!=Units.end(); ++Iter)
150  {
151  // For each Dependent of that Workunit
152  size_t Max = Iter->Unit->GetImmediateDependencyCount();
153  for(size_t Counter=0; Counter<Max; ++Counter)
154  { DependentGraph[Iter->Unit->GetDependency(Counter)].insert(Iter->Unit); } // Make a record of the reverse association
155  }
156  }
157 
158  void FrameScheduler::UpdateWorkUnitKeys(std::vector<WorkUnitKey> &Units)
159  {
160  for(std::vector<WorkUnitKey>::iterator Iter=Units.begin(); Iter!=Units.end(); ++Iter)
161  { *Iter = Iter->Unit->GetSortingKey(*this); }
162  }
163 
164  ////////////////////////////////////////////////////////////////////////////////
165  // Construction and Destruction
166  FrameScheduler::FrameScheduler(std::fstream *_LogDestination, Whole StartingThreadCount) :
167  FrameTimeLog(MEZZ_FRAMESTOTRACK),
168  PauseTimeLog(MEZZ_FRAMESTOTRACK),
169  CurrentFrameStart(GetTimeStamp()),
170  CurrentPauseStart(GetTimeStamp()),
171  LogDestination(_LogDestination ? _LogDestination : new std::fstream("Mezzanine.log", std::ios::out | std::ios::trunc)),
172  Sorter(0),
174  StartFrameSync(StartingThreadCount),
175  EndFrameSync(StartingThreadCount),
176  LastFrame(0),
177  #endif
179  DecacheMain(0),
180  DecacheAffinity(0),
181  #endif
182  CurrentThreadCount(StartingThreadCount),
183  FrameCount(0), TargetFrameLength(16666),
184  TimingCostAllowance(0),
185  MainThreadID(this_thread::get_id()),
186  LoggingToAnOwnedFileStream(true),
187  NeedToLogDeps(true)
188  {
189  Resources.push_back(new DefaultThreadSpecificStorage::Type(this));
190  GetLog() << "<MezzanineLog>" << std::endl;
191  }
192 
193  FrameScheduler::FrameScheduler(std::ostream *_LogDestination, Whole StartingThreadCount) :
194  FrameTimeLog(MEZZ_FRAMESTOTRACK),
195  PauseTimeLog(MEZZ_FRAMESTOTRACK),
196  CurrentFrameStart(GetTimeStamp()),
197  CurrentPauseStart(GetTimeStamp()),
198  LogDestination(_LogDestination),
199  Sorter(0),
201  StartFrameSync(StartingThreadCount),
202  EndFrameSync(StartingThreadCount),
203  LastFrame(0),
204  #endif
206  DecacheMain(0),
207  DecacheAffinity(0),
208  #endif
209  CurrentThreadCount(StartingThreadCount),
210  FrameCount(0), TargetFrameLength(16666),
211  TimingCostAllowance(0),
212  MainThreadID(this_thread::get_id()),
213  LoggingToAnOwnedFileStream(false),
214  NeedToLogDeps(true)
215  {
216  Resources.push_back(new DefaultThreadSpecificStorage::Type(this));
217  (*LogDestination) << "<MezzanineLog>" << std::endl;
218  LogDestination->flush();
219  }
220 
222  {
223  CleanUpThreads();
224 
225  (*LogDestination) << "</MezzanineLog>" << std::endl;
226  LogDestination->flush();
227 
229  {
230  ((std::fstream*)LogDestination)->close();
231  delete LogDestination;
232  }
233  for(std::vector<WorkUnitKey>::iterator Iter=WorkUnitsMain.begin(); Iter!=WorkUnitsMain.end(); ++Iter)
234  { delete Iter->Unit; }
235  for(std::vector<MonopolyWorkUnit*>::iterator Iter = WorkUnitsMonopolies.begin(); Iter!=WorkUnitsMonopolies.end(); ++Iter)
236  { delete *Iter; }
237  for(std::vector<DefaultThreadSpecificStorage::Type*>::iterator Iter = Resources.begin(); Iter!=Resources.end(); ++Iter)
238  { delete *Iter; }
239  DeleteThreads();
240  }
241 
242  ////////////////////////////////////////////////////////////////////////////////
243  // WorkUnit management
244  void FrameScheduler::AddWorkUnitMain(iWorkUnit* MoreWork, const String& WorkUnitName)
245  {
247  this->WorkUnitsMain.push_back(MoreWork->GetSortingKey(*this));
248  (*this->LogDestination) << "<WorkUnitMainInsertion ID=\"" << hex << MoreWork << "\" Name=\"" << WorkUnitName << "\" />" << endl;
249  }
250 
251  void FrameScheduler::AddWorkUnitAffinity(iWorkUnit* MoreWork, const String& WorkUnitName)
252  {
254  this->WorkUnitsAffinity.push_back(MoreWork->GetSortingKey(*this));
255  (*this->LogDestination) << "<WorkUnitAffinityInsertion ID=\"" << hex << MoreWork << "\" Name=\"" << WorkUnitName << "\" />" << endl;
256  }
257 
258  void FrameScheduler::AddWorkUnitMonopoly(MonopolyWorkUnit* MoreWork, const String& WorkUnitName)
259  {
261  this->WorkUnitsMonopolies.push_back(MoreWork);
262  (*this->LogDestination) << "<WorkUnitMonopolyInsertion ID=\"" << hex << MoreWork << "\" Name=\"" << WorkUnitName << "\" />" << endl;
263  }
264 
265  void FrameScheduler::SortWorkUnitsMain(bool UpdateDependentGraph_)
266  {
267  if(UpdateDependentGraph_)
268  { UpdateDependentGraph(); }
269  if(WorkUnitsMain.size())
270  {
272  std::sort(WorkUnitsMain.begin(),WorkUnitsMain.end(),std::less<WorkUnitKey>() );
273  }
274  }
275 
276  void FrameScheduler::SortWorkUnitsAffinity(bool UpdateDependentGraph_)
277  {
278  if(UpdateDependentGraph_)
279  { UpdateDependentGraph(); }
280  if(WorkUnitsAffinity.size())
281  {
283  std::sort(WorkUnitsAffinity.begin(),WorkUnitsAffinity.end(),std::less<WorkUnitKey>() );
284  }
285  }
286 
287  void FrameScheduler::SortWorkUnitsAll(bool UpdateDependentGraph_)
288  {
289  SortWorkUnitsAffinity(UpdateDependentGraph_);
290  SortWorkUnitsMain(false);
291  }
292 
294  {
295  if(WorkUnitsMain.size())
296  {
297  IteratorMain RemovalTarget = WorkUnitsMain.end();
298  for(IteratorMain Iter = WorkUnitsMain.begin(); Iter!=WorkUnitsMain.end(); Iter++)
299  {
300  if(Iter->Unit == LessWork)
301  {
302  if(Iter+1 == WorkUnitsMain.end()) // once we find it, push it to the back linearly.
303  { RemovalTarget = Iter;} // This way we can erase from the back where it is cheap
304  else // to do so and still make just on pass through the list
305  { std::swap (*Iter,*(Iter+1)); }
306  }
307  Iter->Unit->RemoveDependency(LessWork); // This has a ton of cache miss potential I am curious what it benchmarks like?!
308  }
309  if(RemovalTarget!=WorkUnitsMain.end())
310  { WorkUnitsMain.erase(RemovalTarget); }
311  }
312  if(WorkUnitsAffinity.size())
313  {
314  for(IteratorAffinity Iter = WorkUnitsAffinity.begin(); Iter!=WorkUnitsAffinity.end(); Iter++)
315  { Iter->Unit->RemoveDependency(LessWork); }
316  }
317  }
318 
320  {
321  if(WorkUnitsAffinity.size())
322  {
323  IteratorAffinity RemovalTarget = WorkUnitsMain.end();
324  for(IteratorAffinity Iter = WorkUnitsAffinity.begin(); Iter!=WorkUnitsAffinity.end(); Iter++)
325  {
326  if(Iter->Unit == LessWork)
327  {
328  if(Iter+1 == WorkUnitsAffinity.end())
329  { RemovalTarget = Iter;}
330  else
331  { std::swap (*Iter,*(Iter+1)); }
332  }
333  Iter->Unit->RemoveDependency(LessWork);
334  }
335  if(RemovalTarget!=WorkUnitsAffinity.end())
336  { WorkUnitsAffinity.erase(RemovalTarget); }
337  }
338  if(WorkUnitsMain.size())
339  {
340  for(IteratorMain Iter = WorkUnitsMain.begin(); Iter!=WorkUnitsMain.end(); Iter++)
341  { Iter->Unit->RemoveDependency(LessWork); }
342  }
343  }
344 
346  {
347  if(WorkUnitsMain.size())
348  {
349  for(IteratorMain Iter = WorkUnitsMain.begin(); Iter!=WorkUnitsMain.end(); Iter++)
350  { Iter->Unit->RemoveDependency(LessWork); }
351  }
352  if(WorkUnitsAffinity.size())
353  {
354  for(IteratorAffinity Iter = WorkUnitsAffinity.begin(); Iter!=WorkUnitsAffinity.end(); Iter++)
355  { Iter->Unit->RemoveDependency(LessWork); }
356  }
357  if(WorkUnitsMonopolies.size())
358  {
359  for(IteratorMonoply Iter = WorkUnitsMonopolies.begin(); Iter!=WorkUnitsMonopolies.end(); Iter++)
360  {
361  if(*Iter==LessWork)
362  {
363  WorkUnitsMonopolies.erase(Iter);
364  return;
365  }
366  }
367  }
368  }
369 
370  ////////////////////////////////////////////////////////////////////////////////
371  // Algorithm essentials
372 
373  Whole FrameScheduler::GetDependentCountOf(iWorkUnit* Work, bool UsedCachedDepedentGraph)
374  {
375  if(UsedCachedDepedentGraph)
376  { UpdateDependentGraph(); }
377  Whole Results = DependentGraph[Work].size();
378  for(std::set<iWorkUnit*>::iterator Iter=DependentGraph[Work].begin(); Iter!=DependentGraph[Work].end(); ++Iter)
379  {
380  Results+=GetDependentCountOf(*Iter);
381  }
382  return Results;
383  }
384  //DecacheMain(0),
385  //DecacheAffinity(0),
387  {
388  #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
389  bool CompleteSoFar = true;
390  Int32 CurrentRun = DecacheMain;
391  for(std::vector<WorkUnitKey>::reverse_iterator Iter = WorkUnitsMain.rbegin()+CurrentRun; Iter!=WorkUnitsMain.rend(); ++Iter)
392  {
393  if (NotStarted==Iter->Unit->GetRunningState())
394  {
395  if(Iter->Unit->IsEveryDependencyComplete())
396  { return Iter->Unit; }
397  CompleteSoFar=false;
398  }
399 
400  if(CompleteSoFar)
401  {
402  CurrentRun++;
403  if(Complete==Iter->Unit->GetRunningState())
404  { AtomicCompareAndSwap32(&DecacheMain,DecacheMain,CurrentRun); }
405  }
406  }
407  #else
408  for(std::vector<WorkUnitKey>::reverse_iterator Iter = WorkUnitsMain.rbegin(); Iter!=WorkUnitsMain.rend(); ++Iter)
409  {
410  if (NotStarted==Iter->Unit->GetRunningState())
411  {
412  if(Iter->Unit->IsEveryDependencyComplete())
413  { return Iter->Unit; }
414  }
415  }
416  #endif
417 
418  return 0;
419  }
420 
422  {
423  #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
424  bool CompleteSoFar = true;
425  Int32 CurrentRun = DecacheAffinity;
426  for(std::vector<WorkUnitKey>::reverse_iterator Iter = WorkUnitsAffinity.rbegin()+CurrentRun; Iter!=WorkUnitsAffinity.rend(); ++Iter)
427  {
428  if (NotStarted==Iter->Unit->GetRunningState())
429  {
430  if(Iter->Unit->IsEveryDependencyComplete())
431  { return Iter->Unit; }
432  CompleteSoFar=false;
433  }
434 
435  if(CompleteSoFar)
436  {
437  CurrentRun++;
438  if(Complete==Iter->Unit->GetRunningState())
439  { AtomicCompareAndSwap32(&DecacheAffinity,DecacheAffinity,CurrentRun); }
440  }
441  }
442  #else
443  for(std::vector<WorkUnitKey>::reverse_iterator Iter = WorkUnitsAffinity.rbegin(); Iter!=WorkUnitsAffinity.rend(); ++Iter)
444  {
445  if (NotStarted==Iter->Unit->GetRunningState())
446  {
447  if(Iter->Unit->IsEveryDependencyComplete())
448  { return Iter->Unit; }
449  }
450  }
451  #endif
452 
453  return GetNextWorkUnit();
454  }
455 
457  {
458  // start reading from units likely to be executed last.
459  for(IteratorMain Iter = WorkUnitsMain.begin(); Iter!=WorkUnitsMain.end(); ++Iter)
460  {
461  if(Complete!=Iter->Unit->GetRunningState())
462  { return false; }
463  }
464 
465  for(IteratorAffinity Iter = WorkUnitsAffinity.begin(); Iter!=WorkUnitsAffinity.end(); ++Iter)
466  {
467  if(Complete!=Iter->Unit->GetRunningState())
468  { return false; }
469  }
470 
471  return true;
472  }
473 
475  {
476  DependentGraph.clear();
479  }
480 
481  ////////////////////////////////////////////////////////////////////////////////
482  // Algorithm Configuration and Introspection
483 
485  { return FrameCount; }
486 
488  { return TargetFrameLength; }
489 
490  void FrameScheduler::SetFrameRate(const Whole& FrameRate)
491  {
492  if(FrameRate)
493  { TargetFrameLength = 1000000/FrameRate; }
494  else
495  { TargetFrameLength = 0; }
496  }
497 
498  void FrameScheduler::SetFrameLength(const Whole& FrameLength)
499  { TargetFrameLength = FrameLength; }
500 
502  { return CurrentThreadCount; }
503 
504  void FrameScheduler::SetThreadCount(const Whole& NewThreadCount)
505  { CurrentThreadCount = NewThreadCount; }
506 
508  { return CurrentFrameStart; }
509 
511  { return this->PauseTimeLog; }
512 
514  { return this->PauseTimeLog[PauseTimeLog.RecordCapacity()-1]; }
515 
517  { return this->FrameTimeLog; }
518 
520  { return this->FrameTimeLog[FrameTimeLog.RecordCapacity()-1]; }
521 
522  ////////////////////////////////////////////////////////////////////////////////
523  // Executing a Frame
524 
526  {
528  CreateThreads();
530  JoinAllThreads();
533  }
534 
536  {
537  for(IteratorMonoply Iter = WorkUnitsMonopolies.begin(); Iter!=WorkUnitsMonopolies.end(); ++Iter)
538  { (*Iter)->operator()(*(Resources.at(0))); }
539  }
540 
542  {
543  LogResources.Lock(); //Unlocks in FrameScheduler::RunMainThreadWork() after last resource is swapped
544  #ifdef MEZZ_USEBARRIERSEACHFRAME
545  StartFrameSync.SetThreadSyncCount(CurrentThreadCount);
546  EndFrameSync.SetThreadSyncCount(CurrentThreadCount);
547  for(Whole Count = 1; Count<CurrentThreadCount; ++Count)
548  {
549  if(Count+1>Resources.size())
550  {
551  Resources.push_back(new DefaultThreadSpecificStorage::Type(this));
552  Resources[Count]->SwapAllBufferedResources();
553  Threads.push_back(new Thread(ThreadWork, Resources[Count]));
554  }
555  }
556  StartFrameSync.Wait();
557  #else
558  for(Whole Count = 1; Count<CurrentThreadCount; ++Count)
559  {
560  if(Count+1>Resources.size())
561  { Resources.push_back(new DefaultThreadSpecificStorage::Type(this)); }
562  Resources[Count]->SwapAllBufferedResources();
563  Threads.push_back(new Thread(ThreadWork, Resources[Count]));
564  }
565  #endif
566  }
567 
569  {
570  Resources[0]->SwapAllBufferedResources();
571  LogDependencies();
573  ThreadWorkAffinity(Resources[0]); // Do work in this thread and get the units with affinity
574 
575  }
576 
578  {
579  #ifdef MEZZ_USEBARRIERSEACHFRAME
580  EndFrameSync.Wait();
581  #else
582  for(std::vector<Thread*>::iterator Iter=Threads.begin(); Iter!=Threads.end(); ++Iter)
583  {
584  (*Iter)->join();
585  delete *Iter;
586  }
587  Threads.clear();
588  Threads.reserve(CurrentThreadCount);
589  #endif
590 
591  if(Sorter)
592  {
595  Sorter=0;
596  }
597 
599  }
600 
602  {
603  /// @todo could be replace with a parallel for, or a monopoly
604  for(std::vector<WorkUnitKey>::reverse_iterator Iter = WorkUnitsMain.rbegin(); Iter!=WorkUnitsMain.rend(); ++Iter)
605  { Iter->Unit->PrepareForNextFrame(); }
606  for(std::vector<WorkUnitKey>::reverse_iterator Iter = WorkUnitsAffinity.rbegin(); Iter!=WorkUnitsAffinity.rend(); ++Iter)
607  { Iter->Unit->PrepareForNextFrame(); }
608  #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
609  DecacheMain=0;
610  DecacheAffinity=0;
611  #endif
612  }
613 
615  {
616  FrameCount++;
617  Whole TargetFrameEnd;
619  {
620  TargetFrameEnd = TargetFrameLength + CurrentFrameStart;
621  Whole WaitTime = Whole(TargetFrameEnd - GetTimeStamp()) + TimingCostAllowance;
622  if(WaitTime>1000000) /// @todo Replace hard-code timeout with compiler/define/cmake_option
623  { WaitTime = 0; }
625  }
626  MaxInt Now = GetTimeStamp();
627  FrameTimeLog.Insert(Now-CurrentFrameStart); //Track Frame Time for the past while
628  PauseTimeLog.Insert(Now-CurrentPauseStart); //Track Pause Time for the past while
629  (*LogDestination) << dec << "<FrameTimes Frame=\"" << (FrameCount-1) << "\" PauseTimeLog=\"" << (Now-CurrentPauseStart) << "\" FrameLength=\"" << (Now-CurrentFrameStart) << "\" />" << endl;
630  CurrentFrameStart=Now;
631  TimingCostAllowance -= (CurrentFrameStart-TargetFrameEnd);
632  }
633 
634  ////////////////////////////////////////////////////////////////////////////////
635  // Basic container features
636 
638  { return this->WorkUnitsMonopolies.size(); }
639 
641  { return this->WorkUnitsAffinity.size(); }
642 
644  { return this->WorkUnitsMain.size(); }
645 
646  ////////////////////////////////////////////////////////////////////////////////
647  // Other Utility Features
648 
650  {
651  std::vector<Resource*>::iterator Results = Resources.begin();
652  if(ID == MainThreadID)
653  { return *Results; } // Main Thread Resources are stored in slot 0
654 
655  for(std::vector<Thread*>::iterator Iter=Threads.begin(); Iter!=Threads.end(); ++Iter)
656  {
657  Results++;
658  if ( (*Iter)->get_id()==ID)
659  { return *Results; }
660  }
661  return NULL;
662  }
663 
665  {
666  Resource* AlmostResults = GetThreadResource(ID);
667  if(AlmostResults)
668  { return &AlmostResults->GetUsableLogger(); }
669  return 0;
670  }
671 
673  { this->NeedToLogDeps = Changed; }
674 
676  {
677  if(this->NeedToLogDeps)
678  {
679  this->NeedToLogDeps = false;
680  for(IteratorMain Iter = WorkUnitsMain.begin(); Iter!=WorkUnitsMain.end(); ++Iter)
681  {
682  Whole MainCount = Iter->Unit->GetImmediateDependencyCount();
683  for(Whole Counter = 0; Counter<MainCount; Counter++)
684  {
685  *(this->LogDestination) << "<WorkUnitDependency "
686  "Unit=\"" << hex << Iter->Unit
687  << "\" DependsOn=\"" << Iter->Unit->GetDependency(Counter) << "\" "
688  "/>" << endl;
689  }
690  }
691 
692  for(IteratorAffinity Iter = WorkUnitsAffinity.begin(); Iter!=WorkUnitsAffinity.end(); ++Iter)
693  {
694  Whole AffinityCount = Iter->Unit->GetImmediateDependencyCount();
695  for(Whole Counter = 0; Counter<AffinityCount; Counter++)
696  {
697  *(this->LogDestination) << "<WorkUnitDependency "
698  "Unit=\"" << hex << Iter->Unit
699  << "\" DependsOn=\"" << Iter->Unit->GetDependency(Counter) << "\" "
700  "/>" << endl;
701  }
702  }
703 
704  for(IteratorMonoply Iter = WorkUnitsMonopolies.begin(); Iter!=WorkUnitsMonopolies.end(); ++Iter)
705  {
706  Whole MonopolyCount = (*Iter)->GetImmediateDependencyCount();
707  for(Whole Counter = 0; Counter<MonopolyCount; Counter++)
708  {
709  *(this->LogDestination) << "<WorkUnitDependency "
710  "Unit=\"" << hex << (*Iter)
711  << "\" DependsOn=\"" << (*Iter)->GetDependency(Counter) << "\" "
712  "/>" << endl;
713  }
714  }
715  }
716  }
717 
718  std::ostream& FrameScheduler::GetLog()
719  { return *LogDestination; }
720 
721  } // \FrameScheduler
722 }// \Mezanine
723 
724 
725 #endif