Spinning Topp Logo BlackTopp Studios
inc
framescheduler.h
Go to the documentation of this file.
1 // The DAGFrameScheduler is a Multi-Threaded lock free and wait free scheduling library.
2 // © Copyright 2010 - 2016 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_h
42 #define _framescheduler_h
43 
44 #include "datatypes.h"
45 
46 #if !defined(SWIG) || defined(SWIG_THREADING) // Do not read when in swig and not in the threading module
47 #include "doublebufferedresource.h"
48 #include "thread.h"
49 #include "workunitkey.h"
50 #include "spinlock.h"
51 #include "systemcalls.h"
52 #include "rollingaverage.h"
53 #endif
54 
55 /// @file
56 /// @brief This file has the Declarations for the main FrameScheduler class.
57 
58 namespace Mezzanine
59 {
60  namespace Threading
61  {
62  class MonopolyWorkUnit;
63  class iWorkUnit;
64  class LogAggregator;
65  class FrameScheduler;
66  class WorkSorter;
67 
68  /// @brief This is central object in this algorithm, it is responsible for spawning threads and managing the order that work units are executed.
69  /// @details For a detailed description of the @ref algorithm_sec "Algorithm" this implements see the @ref algorithm_sec "Algorithm" section on
70  /// the Main page.
72  {
73  friend class LogAggregator;
74  friend class WorkSorter;
75 
76  #ifndef SWIG
77  friend void TerminateHandler();
78  #endif
79 
80  protected:
81  ////////////////////////////////////////////////////////////////////////////////
82  // Data Members
83 
84  /// @brief A collection of all the work units that are not Monopolies and do not have affinity for a given thread.
85  /// @details This stores a sorted listing(currently a vector) of @ref Mezzanine::Threading::WorkUnitKey "WorkUnitKey" instances.
86  /// These include just the metadata required for sorting @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s. Higher priority
87  /// @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s are higher/later in the collection. This list is sorted by calls
88  /// to @ref Mezzanine::Threading::FrameScheduler::SortWorkUnitsMain "SortWorkUnitsMain" or @ref Mezzanine::Threading::FrameScheduler::SortWorkUnitsAll "SortWorkUnitsAll".
89  std::vector<WorkUnitKey> WorkUnitsMain;
90 
91  /// @brief An iterator suitable for iterating over the main pool of work units.
92  typedef std::vector<WorkUnitKey>::iterator IteratorMain;
93 
94  /// @brief A const iterator suitable for iterating over the main pool of work units.
95  typedef std::vector<WorkUnitKey>::const_iterator ConstIteratorMain;
96 
97  /// @brief A collection of @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s that must be run on the main thread.
98  /// @details This is very similar to @ref WorkUnitsMain except that the @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s
99  /// are only run in the main thread and are sorted by calls to @ref SortWorkUnitsAll or @ref SortWorkUnitsAffinity .
100  std::vector<WorkUnitKey> WorkUnitsAffinity;
101 
102  /// @brief An iterator suitable for iterating over the main pool of work units.
103  typedef std::vector<WorkUnitKey>::iterator IteratorAffinity;
104 
105  /// @brief A const iterator suitable for iterating over the main pool of work units.
106  typedef std::vector<WorkUnitKey>::const_iterator ConstIteratorAffinity;
107 
108  /// @brief A structure designed to minimalistically represent Dependency and Reverse Dependency Graphs in work units.
109  typedef std::map<
110  iWorkUnit*,
111  std::set<iWorkUnit*>
113 
114  /// @brief This structure allows reverse lookup of dependencies.
115  /// @details This is is a key part of the workunit sorting algorithm. This is calculated during calls to generate
116  /// @ref Mezzanine::Threading::WorkUnitKey "WorkUnitKey"s,
117  /// @warning
118  /// @todo write this warning, it is important, but not easy to lay out.
120 
121  /// @brief A lock that protects the list frame schedulers, for use in the event of an error.
123  /// @brief A list frame schedulers, for use in the event of an error.
124  static std::vector<FrameScheduler*> FrameSchedulers;
125 
126  public:
127  /// @brief The kind of Resource the frame scheduler will use
129 
130  protected:
131  /// @brief This maintains ownership of all the thread specific resources.
132  /// @note There should be the same amount or more of these than entries in the Threads vector.
133  std::vector<Resource*> Resources;
134 
135  /// @brief A way to track an arbitrary number of threads.
136  /// @note There should never be more of these than Resources, and if there are more at the beginning of a frame the resources will be created in CreateThreads().
137  std::vector<Thread*> Threads;
138 
139  /// @brief A collection of all the monopolies this scheduler must run and keep ownership of.
140  std::vector<MonopolyWorkUnit*> WorkUnitsMonopolies;
141 
142  /// @brief An iterator suitable for iterating over the main pool of work units.
143  typedef std::vector<MonopolyWorkUnit*>::iterator IteratorMonoply;
144 
145  /// @brief A const iterator suitable for iterating over the main pool of work units.
146  typedef std::vector<MonopolyWorkUnit*>::const_iterator ConstIteratorMonopoly;
147 
148  /// @brief A rolling average of Frame times.
150 
151  /// @brief A rolling average of Frame Pause times.
153 
154  /// @brief What time did the current Frame Start at.
156 
157  /// @brief What time did the current Frame Start at.
159 
160  /// @brief When the logs are aggregated, this is where they are sent
161  std::ostream* LogDestination;
162 
163  /// @brief If this pointer is non-zero then the @ref WorkSorter it points at will be used to sort WorkUnits.
165 
166  /// @brief Protects DoubleBufferedResources during creation and error handling from being accessed by the LogAggregator.
168 
169  #ifdef MEZZ_USEATOMICSTODECACHECOMPLETEWORK
170  /// @brief Indicates the beginning of work that must be searched when starting a fresh search for work in WorkUnitsMain.
171  Int32 DecacheMain;
172 
173  /// @brief Indicates the beginning of work that must be searched when starting a fresh search for work in WorkUnitsAffinity.
174  Int32 DecacheAffinity;
175  #endif
176 
177  /// @brief How many threads will this try to execute with in the next frame.
179 
180  /// @brief Used to store a count of frames from the begining of game execution.
181  /// @warning At 60 Frames per second this loops in 2 years, 3 months, 6 days and around 18 hours, this may not be suitable for high uptime servers. Using a MaxInt fixes this.
183 
184  /// @brief The Maximum frame rate this algorithm should run at.
186 
187  /// @brief To prevent frame time drift this many microseconds is subtracted from the wait period to allow time for calculations.
189 
190  /// @brief For some task it is important to know the ID of the main thread.
192 
193  /// @brief Set based on which constructor is called, and only used during destruction.
195 
196  /// @brief Brief Do we have to log ependencies have they changed since last logged?
197  /// @details Since each workunit tracks its own dependencies this cannot easily be directly checked. There
198  /// is a function to set and since dependencies are likely to be between adding a working and runninf a
199  /// frame, adding a workunit sets this flag.
201 
202  ////////////////////////////////////////////////////////////////////////////////
203  // Protected Methods
204 
205  /// @brief Add this scheduler to the list that will log in the event of an unhand;ed exception
206  /// @param SchedulerToAdd The scheduler to add to the list list that will be cleaned up in the event of an error.
207  static void AddErrorScheduler(FrameScheduler* SchedulerToAdd);
208 
209  /// @brief Remove the passed scheduler from the list that will log in the event of an unhand;ed exception
210  /// @param SchedulerToRemove The scheduler to remove from the list list that will be cleaned up in the event of an error.
211  static void RemoveErrorScheduler(FrameScheduler* SchedulerToRemove);
212 
213  /// @brief Used in destruction to tear down threads.
214  void CleanUpThreads();
215 
216  /// @brief Simply iterates over and deletes everything in Threads.
217  void DeleteThreads();
218 
219  /// @brief Adds the dependencies of the @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s in the passed containter to the the internal reverse dependency graph.
220  /// @param Units The container to examine for dependency relationships.
221  void UpdateDependentGraph(const std::vector<WorkUnitKey> &Units);
222 
223  /// @brief Iterate over the passed container of @ref WorkUnitKey "WorkUnitKey"s and refresh them with the correct data from their respective @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s
224  /// @param Units The container to examine for @ref WorkUnitKey "WorkUnitKey" metadata.
225  void UpdateWorkUnitKeys(std::vector<WorkUnitKey> &Units);
226 
227  public:
228 
229  ////////////////////////////////////////////////////////////////////////////////
230  // Construction and Destruction
231 
232  /// @brief Create a Framescheduler that owns a filestream for logging.
233  /// @param _LogDestination An fstream that will be closed and deleted when this framescheduler is destroyed. Defaults to a new Filestream Logging to local file.
234  /// @param StartingThreadCount How many threads. Defaults to the value returned by @ref Mezzanine::GetCPUCount "GetCPUCount()".
235  /// @warning This must be constructed from the Main(only) thread for any features with thread affinity to work correctly.
237  std::fstream* _LogDestination = 0,
238  Whole StartingThreadCount = GetCPUCount()
239  );
240 
241  /// @brief Create a Framescheduler, that logs to an unowned stream.
242  /// @param _LogDestination Any stream, other than an fstream, and it will be closed (not deleted) when this frame scheduler is destroyed.
243  /// @param StartingThreadCount How many threads. Defaults to the value returned by @ref Mezzanine::GetCPUCount "GetCPUCount()".
244  /// @warning This must be constructed from the Main(only) thread for any features with thread affinity to work correctly.
246  std::ostream* _LogDestination,
247  Whole StartingThreadCount = GetCPUCount()
248  );
249 
250  /// @brief When Things crash the logs still needs to be flushed and other resources cleaned. This sets a function for when std::terminate is called.
251  void SetErrorHandler();
252 
253  /// @brief Destructor
254  /// @details Deletes all std::fstream, WorkUnit, MonopolyWorkUnit and ThreadSpecificStorage objects that this was passed or created during its lifetime.
255  virtual ~FrameScheduler();
256 
257  ////////////////////////////////////////////////////////////////////////////////
258  // WorkUnit management
259 
260  /// @brief Add a normal Mezzanine::Threading::iWorkUnit to this For fcheduling.
261  /// @param MoreWork A pointer the the WorkUnit, that the FrameScheduler will take ownership of, and schedule for work.
262  /// @param WorkUnitName A name to uniquely identify this work unit in the logs
263  virtual void AddWorkUnitMain(iWorkUnit* MoreWork, const String& WorkUnitName);
264 
265  /// @brief Add a normal Mezzanine::Threading::iWorkUnit to this For scheduling.
266  /// @param MoreWork A pointer the the WorkUnit, that the FrameScheduler will take ownership of, and schedule for work.
267  /// @param WorkUnitName A name to uniquely identify this work unit in the logs
268  virtual void AddWorkUnitAffinity(iWorkUnit* MoreWork, const String& WorkUnitName);
269 
270  /// @brief Add a @ref MonopolyWorkUnit for execution at the beginning of the frame.
271  /// @param MoreWork A pointer to the @ref MonopolyWorkUnit to add.
272  /// @param WorkUnitName A name to uniquely identify this work unit in the logs
273  virtual void AddWorkUnitMonopoly(MonopolyWorkUnit* MoreWork, const String& WorkUnitName);
274 
275  /// @brief Sort the the main pool of WorkUnits to allow them to be used more efficiently in the next frame executed.
276  /// @param UpdateDependentGraph_ Should the internal cache of reverse dependents be updated.
277  /// @details See @ref Mezzanine::Threading::FrameScheduler::DependentGraph "DependentGraph"
278  /// for the appropriate times to use this.
279  virtual void SortWorkUnitsMain(bool UpdateDependentGraph_ = true);
280 
281  /// @brief Sort the WorkUnits that must run on the main thread to allow them to be used more efficiently in the next frame executed.
282  /// @param UpdateDependentGraph_ Should the internal cache of reverse dependents be updated.
283  /// @details See @ref Mezzanine::Threading::FrameScheduler::DependentGraph "DependentGraph"
284  /// for the appropriate times to use this.
285  virtual void SortWorkUnitsAffinity(bool UpdateDependentGraph_ = true);
286 
287  /// @brief Sort all the WorkUnits that must run on the main thread to allow them to be used more efficiently in the next frame executed.
288  /// @param UpdateDependentGraph_ Should the internal cache of reverse dependents be updated.
289  /// @details See @ref Mezzanine::Threading::FrameScheduler::DependentGraph "DependentGraph"
290  /// for the appropriate times to use this.
291  virtual void SortWorkUnitsAll(bool UpdateDependentGraph_ = true);
292 
293  // @brief Remove a WorkUnit regardless of type, and caller regains ownership of it.
294  // @param LessWork A pointer to a WorkUnit that should no longer be scheduled.
295  // @details This is relative slow compared to adding or finding a working unit, this works in linear time relative to the number of WorkUnits in the scheduler.
296  //virtual void RemoveWorkUnit(iWorkUnit* LessWork);
297 
298  /// @brief Remove a WorkUnit from the main pool of WorkUnits (and not from the groups of Affinity or MonpolyWorkUnits).
299  /// @param LessWork A pointer to the workunit the calling coding will reclaim ownership of and will no longer be scheduled, and have its dependencies removed.
300  virtual void RemoveWorkUnitMain(iWorkUnit* LessWork);
301 
302  /// @brief Remove a WorkUnit from the Affinity pool of WorkUnits (and not from the Main group or MonpolyWorkUnits).
303  /// @param LessWork A pointer to the workunit the calling coding will reclaim ownership of and will no longer be scheduled, and have its dependencies removed.
304  virtual void RemoveWorkUnitAffinity(iWorkUnit* LessWork);
305 
306  /// @brief Remove a WorkUnit from the Monopoly pool of WorkUnits (and not from the Main or Affinity group).
307  /// @param LessWork A pointer to the MonopolyWorkUnit the calling coding will reclaim ownership of and will no longer be scheduled, and have its dependencies removed.
308  virtual void RemoveWorkUnitMonopoly(MonopolyWorkUnit* LessWork);
309 
310  ////////////////////////////////////////////////////////////////////////////////
311  // Algorithm essentials
312 
313  /// @brief How many other WorkUnit instances must wait on this one.
314  /// @param Work The WorkUnit to get the updated count of.
315  /// @param UsedCachedDepedentGraph If the cache is already up to date leaving this false, and not updating it can save significant time.
316  /// @return A Whole Number representing the amount of WorkUnit instances that cannot start until this finishes.
317  virtual Whole GetDependentCountOf(iWorkUnit *Work, bool UsedCachedDepedentGraph=false);
318 
319  /// @brief Gets the next available workunit for execution.
320  /// @details This finds the next available WorkUnit which has not started execution, has no dependencies that have
321  /// not complete, has the most WorkUnits that depend on it (has the highest runtime in the case of a tie).
322  /// @return A pointer to the WorkUnit that could be executed or a null pointer if that could not be acquired. This does not give ownership of that WorkUnit.
323  virtual iWorkUnit* GetNextWorkUnit();
324 
325  /// @brief Just like @ref GetNextWorkUnit except that it also searches through and prioritizes work units with affinity too.
326  /// @return A pointer to the WorkUnit that could be executed *in the main thread* or a null pointer if that could not be acquired. This does not give ownership of that WorkUnit.
327  virtual iWorkUnit* GetNextWorkUnitAffinity();
328 
329  /// @brief Is the work of the frame done?
330  /// @return This returns true if all the WorkUnit instances are complete, and false otherwise.
331  virtual bool AreAllWorkUnitsComplete();
332 
333  /// @brief Create a reverse depedent graph that can be used for sorting Mezzanine::Threading::iWorkUnit "iWorkUnit"s to optimize execution each frame.
334  /// @details This can be called automatically from any of several places that make sense by passing a boolean true value.
335  /// These place include create a @ref WorkUnitKey or Sorting the work units in a framescheduler.
336  virtual void UpdateDependentGraph();
337 
338  ////////////////////////////////////////////////////////////////////////////////
339  // Algorithm Configuration and Introspection
340 
341  /// @brief Get the current number of frames that have elapsed
342  /// @return A Whole containing the frame count.
343  virtual Whole GetFrameCount() const;
344 
345  /// @brief Get the desired length of a frame.
346  /// @return The desired frame length as a Whole in Microseconds
347  virtual Whole GetFrameLength() const;
348 
349  /// @brief Set the desired Frate rate.
350  /// @param FrameRate in frames per second
351  /// @details Defaults to 60, to maximize smoothmess of execution (no human can see that fast),
352  /// while not killing battery life. This is a maximum framerate, if WorkUnits take too long
353  /// to execute this will not make them finish faster. This controls a delay to prevent the
354  /// machine's resources from being completely tapped. @n @n Set this to 0 to never pause
355  /// and run as fast as possible.
356  /// @return A Whole containing the Target frame rate.
357  virtual void SetFrameRate(const Whole& FrameRate);
358 
359  /// @brief Set the Desired length of a frame in microseconds.
360  /// @param FrameLength The desired minimum length of the frame. Use 0 for no pause.
361  virtual void SetFrameLength(const Whole& FrameLength);
362 
363  /// @brief Get the amount of threads that will be used to execute WorkUnits a the start of the next frame.
364  /// @return A Whole with the current desired thread count.
365  virtual Whole GetThreadCount();
366 
367  /// @brief Set the amount of threads to use.
368  /// @param NewThreadCount The amount of threads to use starting at the begining of the next frame.
369  /// @note Currently the thread count cannot be reduced if Mezz_MinimizeThreadsEachFrame is selected in cmake configuration.
370  virtual void SetThreadCount(const Whole& NewThreadCount);
371 
372  /// @brief When did this frame start?
373  /// @return A MaxInt with the timestamp corresponding to when this frame started.
374  virtual MaxInt GetCurrentFrameStart() const;
375 
376  /// @brief Get The complete record of the past durations of the Pauses Each frame.
377  /// @return A Rolling average of the default type.
378  DefaultRollingAverage<Whole>::Type& GetPauseTimeRollingAverage();
379 
380  /// @brief How long was the pause, if any, last frame?
381  /// @return A whole containing the duration of the last pause.
382  Whole GetLastPauseTime() const;
383 
384  /// @brief Get The complete record of the durations of the last few frames.
385  /// @return A Rolling average of the default type.
386  DefaultRollingAverage<Whole>::Type& GetFrameTimeRollingAverage();
387 
388  /// @brief How long was the previous frame?
389  /// @return A Whole containing the duration of the last frame.
390  Whole GetLastFrameTime() const;
391 
392 
393  ////////////////////////////////////////////////////////////////////////////////
394  // Executing a Frame
395 
396  /// @brief Do one frame worth of work.
397  /// @details This just calls the following functions in the order presented:
398  /// @code
399  /// void FrameScheduler::DoOneFrame()
400  /// {
401  /// RunAllMonopolies();
402  /// CreateThreads();
403  /// RunMainThreadWork();
404  /// JoinAllThreads();
405  /// ResetAllWorkUnits();
406  /// WaitUntilNextFrame();
407  /// }
408  /// @endcode
409  /// This can be replaced calling these functions in this order. You can add any other calls you like between the various stages. This can be done to
410  /// allow maximum integration with existing projects. It can also be used to prevent a giant migration and replace it with a piecemeal upgrade.
411  /// @warning Do not call this on an unsorted set of WorkUnits. Use @ref FrameScheduler::SortWorkUnitsAll() and the @ref DependentGraph to sort WorkUnits after
412  /// they are inserted into the frame scheduler for the first time. This doesn't need to happen each frame, just after any new work units are added or removed
413  /// (except Monopolies) or you want to take the most recent performance number into account.
414  virtual void DoOneFrame();
415 
416  // Image that the next 6 functions should be call in sequence for ideal performance.
417  // Do preparation here.
418 
419  // Setup anything monopolies need, though it would be better if the monopolies could do it themselves in a multithreaded way.
420 
421  /// @brief This is the 1st step (of 6) in a frame.
422  /// @details This iterates over the listing of @ref MonopolyWorkUnit "MonopolyWorkUnit"s and executes each one in the order
423  /// it was added. This should be considered as consuming all available CPU time until it returns. This call blocks until execution
424  /// of monopolies is complete.
425  virtual void RunAllMonopolies();
426 
427  // If the monopolies need some cleanup, it could be done here. Not much should be done here, most work is better as a work unit.
428 
429  /// @brief This is the 2nd step (of 6) in a frame.
430  /// @details This starts all the threads on their work. Until @ref JoinAllThreads() is called some thread may still be working.
431  /// This thread starts the man @ref algorithm_sec "scheduling algorithm" working on every thread except the calling thread. This
432  /// call does not block and tends to return very quickly.
433  /// @n @n
434  /// This checks the amount of threads as set by @ref SetFrameLength "SetFrameLength". It creates any
435  /// @ref ThreadSpecificStorage instances required and creates threads if they are required. SwapAllBufferedResources() Is called on
436  /// each ThreadSpecificStorage before being passed into the thread. If extra double buffered resources are required per thread
437  /// and they need to be swapped each frame, SwapAllBufferedResources() should be inherited or adjusted to account for these new
438  /// resources.
439  /// @n @n
440  /// If the build option
441  virtual void CreateThreads();
442 
443  // If it must be run on the main thread and must be run first each frame it could go here, but I think you should
444  // just make a work unit out of it and make other work units depend on it. Right now all the other thread are working.
445 
446  /// @brief This is the 3rd step (of 6) in a frame.
447  /// @details This runs the main portion of the @ref algorithm_sec "scheduling algorithm" on the main thread. This call
448  /// blocks until the execution of all workunits with main thread affinity are complete and all other work units have at
449  /// least started. This could return and other threads could still be working.
450  /// @n @n
451  /// Before executing any work this checks a flag to determine if it should log the current work unit dependencies. After
452  /// this check or logging has occurred this then this releases the spinlock on the log, so there is a chance of brief
453  /// contention between a LogAggregator and this if depedencies have just changed or change frequently.
454  /// @warning This uses a Spinlock to prevent accesss to ThreadSpecificResources that the LogAggregator needs. This should
455  /// be called immediately after CreateThreads to minimize any possible contention.
456  virtual void RunMainThreadWork();
457 
458  // Alled the work units have at least been started, and quite possible most have finished. It is possible a few threads
459  // have finished. But if you must do something after all the workunits have started and most finished, while the threads
460  // remain in memory, then this is the place to do it.
461 
462  /// @brief This is the 4th step (of 6) in a frame.
463  /// @details Used when completing the work of a frame, to cleaning end the execution of the threads. This function will
464  /// only return when all the work started by @ref CreateThreads() and @ref RunMainThreadWork() have completed. This call
465  /// blocks until all threads executing. If a thread takes too long then this simply waits for it to finish. No attempt is
466  /// made to timeout or interupt a work unit before it finishes.
467  void JoinAllThreads();
468 
469  // All the threads have been cleaned up or paused. All the work units have finished. This is a reasonable place for heuristics
470  // and maybe other kinds of work. but maybe that stuff could go in a monopoly instead. This might be a good place to check
471  // statuses of work units, but all the interesting statuses kill the program.
472 
473  /// @brief This is the 5th step (of 6) in a frame.
474  /// @details Take any steps required to prepare all owned WorkUnits for execution next frame. This usually includes reseting
475  /// all the work units running state to @ref NotStarted "NotStarted". This can cause work units to be executed multiple times
476  /// if a thread is still executing.
477  virtual void ResetAllWorkUnits();
478 
479  // All the work units are ready for the next frame, but no real waiting has occurred yet.
480 
481  /// @brief This is the final step (of 6) in a frame.
482  /// @details Wait until this frame has consumed its fair share of a second. This uses the value passed in
483  /// @ref SetFrameRate "SetFrameRate" to determine what portion of a second each frame should
484  /// use. If a frame took too long to execute this calculates that and returns.
485  /// @n @n
486  /// Wait 1/TargetFrame seconds, minus time already run. This also starts the timer for the next frame so
487  /// any other logic that needs to run after the frame does not interfere with frame timing. Because
488  /// This is designed to wait fractions of a second any amount of waiting above 1 second fails automaticall.
489  void WaitUntilNextFrame();
490 
491  // This is the same place as before the monopolies, except for whatever one time setup code you might have.
492 
493  ////////////////////////////////////////////////////////////////////////////////
494  // Basic container features
495 
496  /// @brief Returns the amount of MonopolyWorkUnit ready to be scheduled
497  /// @return A Whole containing this amount
498  Whole GetWorkUnitMonopolyCount() const;
499 
500  /// @brief Returns the amount of iWorkUnit ready to be scheduled in the Affinity pool
501  /// @return A Whole containing this amount
502  Whole GetWorkUnitAffinityCount() const;
503 
504  /// @brief Returns the amount of iWorkUnit ready to be scheduled in the Main pool
505  /// @return A Whole containing this amount
506  Whole GetWorkUnitMainCount() const;
507 
508  ////////////////////////////////////////////////////////////////////////////////
509  // Logging Features
510 
511  /// @brief Get the Resource to go with a thread of a given ID.
512  /// @details This gets the Resource that goes with a given thread
513  /// by performing a linear search through the available threads.
514  /// @n @n
515  /// This is cheap computationly but will likely perform much slower
516  /// than other methods of getting the resource, because the ID is
517  /// unlikely be be cached and it may require a system call to
518  /// retrieve. If used consider wrapping it in \#ifdef MEZZ_DEBUG + \#endif
519  /// to disable for release builds.
520  /// @param ID This uses the current Threads ID by default but can search for any thread.
521  /// @return A pointer to ThreadSpecificResource or a null pointer on error.
522  /// @warning The thread that 'owns' this resource could do just about anything
523  /// with it while the frame is running, so this should only outside a frame
524  /// and carefully or inside a frame and only from the owning thread.
525  Resource* GetThreadResource(ThreadId ID = this_thread::get_id());
526 
527  /// @brief Get the logger safe to use this thread.
528  /// @warning This is written in terms of GetThreadResource and has all the
529  /// same limitations.
530  /// @param ID This uses the current Threads ID by default but can search for any thread.
531  /// @return A null pointer if there is an error or a pointer to the Logger that goes with the passed Thread::Id
532  Logger* GetThreadUsableLogger(ThreadId ID = this_thread::get_id());
533 
534  /// @brief Indicate to the framescheduler if dependencies need to be logged
535  /// @param Changed Defaults to true, and sets a flag that tells the framescheduler if it needs to log dependencies.
536  /// @details If false is passed, this prevents the FrameScheduler from logging until the next change.
537  void DependenciesChanged(bool Changed=true);
538 
539  /// @brief This sends the dependencies to the LogDestination (Skipping any thread specific resources)
540  /// @warning This should not be executed during the frame, unless the FrameScheduler is calling it.
541  void LogDependencies();
542 
543  /// @warning This is not thread safe at all. Any time during the frame using this can send gibberish to the log. Use GetThreadUsableLogger instead.
544  /// @brief Get the endpoint for the logs.
545  /// @return An std::ostream reference which can be streamed to commit log entries.
546  std::ostream& GetLog();
547 
548  void ChangeLogTarget(std::ostream* LogTarget);
549 
550  protected:
551  void InstallLog(std::ostream* LogTarget);
552  void RemoveLog();
553 
554  /// @warning This is not thread safe at all. Any time during the frame using this can break everything.
555  /// @brief Get the Log aggregation work unit if one exists for emergency loggin purposes only
556  /// @details Use by the termination handler in the even of an uncaught exception. This should also be avoided because
557  /// It is is implemented a linear search over every workunit and returns the first one derived from LogAggregator.
558  /// @return A null pointer or a pointer to a workunit that aggregates if it exists.
559  virtual LogAggregator* GetLogAggregator();
560 
561  public:
562  /// @warning This is not thread safe at all. Any time during the frame using this can break everything.
563  /// @brief Forces the FrameScheduler to find Its LogAggregator and make it flush the logs if it can
564  /// @return This return True if it flushed the logs and false otherwise.
565  virtual Boole ForceLogFlush();
566 
567  /// @warning This is not thread safe at all. Any time during the frame using this can break everything.
568  /// @brief This takes all active buffered resources offline (therfore available for async processing) and makes all the offline resources active for normal thread use.
569  void SwapBufferedResources();
570 
571  };//FrameScheduler
572  } // \Threading
573 }// \Mezanine
574 
575 
576 #endif
int32_t Int32
An 32-bit integer.
Definition: datatypes.h:124
Use this to get the default rolling average for a given type.
std::vector< WorkUnitKey >::const_iterator ConstIteratorMain
A const iterator suitable for iterating over the main pool of work units.
ThreadId MEZZ_LIB get_id()
Return the thread ID of the calling thread.
std::stringstream Logger
In case we ever replace the stringstream with another class, this will allow us to swap it out...
Definition: datatypes.h:180
bool Boole
Generally acts a single bit, true or false.
Definition: datatypes.h:173
std::vector< WorkUnitKey > WorkUnitsAffinity
A collection of iWorkUnits that must be run on the main thread.
std::vector< Resource * > Resources
This maintains ownership of all the thread specific resources.
Whole TargetFrameLength
The Maximum frame rate this algorithm should run at.
bool LoggingToAnOwnedFileStream
Set based on which constructor is called, and only used during destruction.
Integer TimingCostAllowance
To prevent frame time drift this many microseconds is subtracted from the wait period to allow time f...
Declares a Mutex, Mutex tools, and at least one MutexLike object.
All the definitions for datatypes as well as some basic conversion functions are defined here...
int Integer
A datatype used to represent any integer close to.
Definition: datatypes.h:154
DependentGraphType DependentGraph
This structure allows reverse lookup of dependencies.
Interface of a WorkUnit. This represents on piece of work through time.
Definition: workunit.h:66
std::ostream * LogDestination
When the logs are aggregated, this is where they are sent.
std::vector< WorkUnitKey >::iterator IteratorMain
An iterator suitable for iterating over the main pool of work units.
This file defines the template double buffered resources that can be attached to a thread...
std::vector< WorkUnitKey >::const_iterator ConstIteratorAffinity
A const iterator suitable for iterating over the main pool of work units.
This file defines a minimalistic cross-platform thread that the scheduler uses to schedule tasks...
A kind of workunit given exclusive runtime so it can consume time on multiple threads.
Definition: monopoly.h:62
bool NeedToLogDeps
Brief Do we have to log ependencies have they changed since last logged?
Whole FrameCount
Used to store a count of frames from the begining of game execution.
static SpinLock FrameSchedulersLock
A lock that protects the list frame schedulers, for use in the event of an error. ...
A thread specific collection of double-buffered and algorithm specific resources. ...
Whole MEZZ_LIB GetCPUCount()
Get the amount of logical processors, a reasononable amount to use for thread creation.
std::vector< WorkUnitKey > WorkUnitsMain
A collection of all the work units that are not Monopolies and do not have affinity for a given threa...
std::vector< MonopolyWorkUnit * >::iterator IteratorMonoply
An iterator suitable for iterating over the main pool of work units.
Sorts all of the WorkUnits in the FrameScheduler.
MaxInt CurrentPauseStart
What time did the current Frame Start at.
SpinLock LogResources
Protects DoubleBufferedResources during creation and error handling from being accessed by the LogAgg...
This is central object in this algorithm, it is responsible for spawning threads and managing the ord...
This file defines the metadata used to sort workunits.
std::vector< WorkUnitKey >::iterator IteratorAffinity
An iterator suitable for iterating over the main pool of work units.
std::map< iWorkUnit *, std::set< iWorkUnit * > > DependentGraphType
A structure designed to minimalistically represent Dependency and Reverse Dependency Graphs in work u...
This stores the implementation and the declaration of the RollingAverage, BufferedRollingAverage, WeightedRollingAverage and the DefaultRollingAverage.
static std::vector< FrameScheduler * > FrameSchedulers
A list frame schedulers, for use in the event of an error.
Gather all the thread specific logs and commit them to the main log.
A mutex like construct that never makes a system call and uses CPU instructions instead.
Definition: spinlock.h:83
std::vector< Thread * > Threads
A way to track an arbitrary number of threads.
MaxInt CurrentFrameStart
What time did the current Frame Start at.
std::vector< MonopolyWorkUnit * >::const_iterator ConstIteratorMonopoly
A const iterator suitable for iterating over the main pool of work units.
This file defines a minimalistic cross-platform thread that the scheduler uses to schedule tasks...
#define MEZZ_LIB
Some platforms require special decorations to denote what is exported/imported in a share library...
long long MaxInt
A large integer type suitable for compile time math and long term microsecond time keeping...
Definition: datatypes.h:190
std::vector< MonopolyWorkUnit * > WorkUnitsMonopolies
A collection of all the monopolies this scheduler must run and keep ownership of. ...
The bulk of the engine components go in this namspace.
Definition: actor.cpp:56
unsigned long Whole
Whole is an unsigned integer, it will be at least 32bits in size.
Definition: datatypes.h:151
WorkSorter * Sorter
If this pointer is non-zero then the WorkSorter it points at will be used to sort WorkUnits...
ThreadId MainThreadID
For some task it is important to know the ID of the main thread.
The thread ID is a unique identifier for each thread.
Definition: thread.h:209
DefaultRollingAverage< Whole >::Type FrameTimeLog
A rolling average of Frame times.
DefaultThreadSpecificStorage::Type Resource
The kind of Resource the frame scheduler will use.
std::string String
A datatype used to a series of characters.
Definition: datatypes.h:159
Whole CurrentThreadCount
How many threads will this try to execute with in the next frame.
DefaultRollingAverage< Whole >::Type PauseTimeLog
A rolling average of Frame Pause times.