Spinning Topp Logo BlackTopp Studios
inc
dagframescheduler.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 _DAGFrameScheduler_h
42 #define _DAGFrameScheduler_h
43 
44 /// @file
45 /// @brief This is the file that code using this library should include. It includes all the required components.
46 
47 #include "datatypes.h"
48 
49 #ifndef MEZZANINE_CORE
50  #ifdef SWIG
51  // Tell SWIG to create a module that scripting languages can use called "mezzanine"
52  // and insert a minimum of documentation into the bindingfile
53  %{
54  // code to be inserted verbatim into the swig file goes here
55  #ifdef GetCurrentTime
56  #undef GetCurrentTime
57  #endif
58 
59  using namespace Mezzanine;
60  using namespace Mezzanine::Threading;
61  %}
62 
63  %include stl.i
64  %include stdint.i
65  %include std_except.i
66  %include std_common.i
67  //%include std_container.i
68  %include std_deque.i
69  %include std_except.i
70  //%include std_list.i
71  %include std_map.i
72  %include std_pair.i
73  %include std_string.i
74  %include std_vector.i
75  #endif
76 #else
77  #include "swig.h"
78 #endif
79 
80 
81 #ifdef SWIG_THREADING
82  #ifdef SWIG_UNSAFE
83  %module MezzanineThreading
84  #else
85  #define SWIG_SAFE
86  %module MezzanineThreadingSafe
87  #endif
88  #define SWIG_MODULE_SET
89 #endif
90 
91 #if !defined(SWIG) || defined(SWIG_THREADING) // Do not read when in swig and not in the threading module
93 #include "asynchronousworkunit.h"
94 #include "atomicoperations.h"
95 #include "barrier.h"
96 //#include "crossplatformincludes.h" // This is omitted because windows.h include a ton of macros that break clean code, so this vile file's scope must be minimized
97 #include "crossplatformexport.h"
98 #include "datatypes.h"
99 #include "doublebufferedresource.h"
100 #include "framescheduler.h"
101 #include "frameschedulerworkunits.h"
102 #include "lockguard.h"
103 #include "logtools.h"
104 #include "monopoly.h"
105 #include "mutex.h"
106 #include "readwritespinlock.h"
107 #include "rollingaverage.h"
108 #include "spinlock.h"
109 #include "systemcalls.h"
110 #include "thread.h"
111 #include "threadingenumerations.h"
112 #include "workunit.h"
113 #include "workunitkey.h"
114 #endif
115 
116 
117 
118 /// @def MEZZ_DAGFRAMESCHEDULER_MAJOR_VERSION
119 /// @brief The Major version number of the library. (The front/left number)
120 #define MEZZ_DAGFRAMESCHEDULER_MAJOR_VERSION 1
121 
122 /// @def MEZZ_DAGFRAMESCHEDULER_MINOR_VERSION
123 /// @brief The Minor version number of the library. (The middle number)
124 #define MEZZ_DAGFRAMESCHEDULER_MINOR_VERSION 11
125 
126 /// @def MEZZ_DAGFRAMESCHEDULER_REVISION_VERSION
127 /// @brief The revision version number of the library. (This right/back number)
128 #define MEZZ_DAGFRAMESCHEDULER_REVISION_VERSION 1
129 
130 
131 /// @page ThreadingManual Directed Acyclic Graph Frame Scheduler
132 /// @section dag_header The DAG Frame Scheduler Manual.
133 /// This page will explain what the DAG FrameScheduler than explain how to use it.
134 /// @section dag_contents Contents
135 /// - @ref dag_goal_sec - A description of this algorithm and how/why it is different from other threading algorithms.
136 /// - @ref dag_usage - Examples, code snippets, caveats and best practices with the scheduler. *Incomplete*
137 /// @subsection dag_goal_sec Goals
138 /// This library tries to make writing multithreaded software easier by changing the kinds of primitives that
139 /// multithreaded software is built upon. Several libraries before this have attempted this already.
140 /// This library is different becuse it focuses on a specific kind of workload and provides the kinds of
141 /// guarantees that the workload needs while sacrificing other guarantees that the workload does not need.
142 /// @n @n
143 /// This attempts to provide a multithreading solution for workloads that must be run in many iterations in
144 /// a given amount of realtime. Games are an ideal example. Every frame a video game must update physics
145 /// simulations, make AI decisions, accept/interpret user input, enforce game rules, perform dynamic I/O
146 /// and render it to the screen all while maintaining a smooth FrameRate and do that while minimizing drain
147 /// to batteries on portable devices (sometimes without even knowing if the device is portable).
148 /// @n @n
149 /// This library accomplishes those goals by removing the conventional mutlithreading primitives that so many
150 /// developers have come to fear, loathe, or misunderstand. Mutexes, threads, memory fences, thread_local
151 /// storage, atomic variables, and all the pitfalls that come with them are replaced by a small set of
152 /// of primitives that provide all the required sophistication a typical multi-threaded application
153 /// requires. It does this using a new kind of @ref Mezzanine::Threading::iWorkUnit "iWorkUnit",
154 /// @ref Mezzanine::Threading::DoubleBufferedResource "Double Buffering", a strong concept of
155 /// dependencies and a @ref Mezzanine::Threading::FrameScheduler "FrameScheduler" that uses heuristics
156 /// to decide how to run it all without exposing needless complexity to the application developer.
157 ///
158 /// @subsection overview_sec Algorithm Overview
159 /// The DAGFrameScheduler is a variation on a common multithreaded work queue. It seeks to avoid its pitfalls,
160 /// such as non-determinism, thread contention, and lackluster scalability while keeping its advantages
161 /// including simplicity, understandiblity, and low overhead.
162 /// @n @n
163 /// With this algorithm very few, if any,
164 /// calls will need to be made to the underlying system for synchronization of the actual work to be performed.
165 /// Instead, this library will provide limited (not always, but for constistent workloads)
166 /// deterministic ordering of @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" execution through a dependency
167 /// feature. Having the knowledge that one @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" will complete after
168 /// another allows for resources to be used without using expensive and complex synchronization mechansisms
169 /// like @ref Mezzanine::Threading::Mutex "mutexes", semaphores, or even an
170 /// @ref Mezzanine::Threading::AtomicCompareAndSwap32 "Atomic Compare And Swaps". These primitives are provided
171 /// to allow use of this library in advanced ways for developers who are already familiar with
172 /// multithreaded systems.
173 /// @n @n
174 /// The internal work queue is not changed while a frame is executing. Because it is only read, each
175 /// thread can pick its own work. Synchronization still needs to occur, but it has been moved onto each
176 /// @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" amd is managed with atomic CPU operations. Like this,
177 /// contention is less frequent occurring only when threads simultaneously attempt to start the same
178 /// @ref Mezzanine::Threading::iWorkUnit "iWorkUnit". This consumes far less time because atomic operations
179 /// are CPU instructions instead of Operating System calls. This is managed by the library, so individual
180 /// @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s do not need to worry synchronization beyond telling
181 /// each @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" about its data dependencies and making sure
182 /// all the @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s are added to a
183 /// @ref Mezzanine::Threading::FrameScheduler "FrameScheduler".
184 ///
185 /// @subsection broken_sec Broken Algorithms
186 /// To understand why a new multithreading system is needed, it is helpful to look at other methods
187 /// of threading that have been used in the past. This can give us an understanding of what they lack
188 /// or how they aren't ideal for the kinds of work this algorithm is intended for. This overview is
189 /// intentionally simplified. There are variations on many of these algorithms that can fix some of
190 /// the problems presented. Despite these workarounds there are fundamental limitations that prevent
191 /// these algorithms from being ideal for video games, simulations and similar tasks.
192 /// These threading models aren't necessarily broken, as some of these clearly have a place in software
193 /// development. Many of these require complex algorithms, require subtle knowledge, or simply aren't
194 /// performant enough for realtime environments.
195 /// @n @n
196 /// I will use charts that plot possible resource use of a computer across time. Generally time will
197 /// run across the top as resources, usually CPUs, will run down one side. Most of these algorithms have a
198 /// concept of tasks or workunits, which are just pieces of work with a distinct begining and end. The
199 /// width of a piece of work loosely represents the execution time (the names are just for show and not
200 /// related to anything real).
201 /// @subsubsection broken_Single Single Threaded
202 /// An application using this threading model is not actually multithreaded at all. However, It has been shown
203 /// that software can run in a single and get good perfomance. This is the benchmark all other threading models
204 /// get compared too.
205 /// @n @n
206 /// There is a term, Speedup ( http://en.wikipedia.org/wiki/Speedup ), which is simply a
207 /// comparison of the single threaded performance of an algorithm to the mutlithreaded performance. You simply
208 /// determine how many times more work the multithreaded algorithm does in the same time, or how many times
209 /// longer the single threaded algorithm takes to the same work. Ideally two threads will be twice as fast
210 /// (speedup of 2x), and three thread would be three times as fast (3x speedup), and so; this is called linear
211 /// speedup. In practice there is always some overhead in creating and synchronizing threads, so achieving
212 /// linear speedup is difficult.
213 /// @image html Single.png "Single Threaded Execution - Fig 1."
214 /// @image latex Single.png "Single Threaded Execution - Fig 1."
215 /// @image rtf Single.png "Single Threaded Execution - Fig 1."
216 /// @n @n
217 /// The DAGFrameScheduler library tries to tailor the threading model to a specific problem to minimize that
218 /// overhead. With a single threaded application one thread does all the work and always wastes every other
219 /// thread, but there is no overhead if the system only has one thread.
220 /// @n @n
221 /// @subsubsection broken_Unplanned Unplanned Thread
222 /// Sometimes someone means well and tries to increase the performance of a single threaded program and tries
223 /// to add extra threads to increase performance. Sometimes this works really well and sometimes there is a
224 /// marginal increase in performance or a significant increase in bugs. If that someone has a good plan
225 /// then they can usually achieve close to the best speedup possible in the given situation. This is not easy
226 /// and many cannot do this or do not want to invest the time it would take. If not carefully planned
227 /// bugs like deadlock ( http://en.wikipedia.org/wiki/Deadlock ) and race conditions
228 /// ( http://stackoverflow.com/questions/34510/what-is-a-race-condition )
229 /// can be introduced. Unfortunately no amount of testing can replace this careful planning. Without a
230 /// complete understanding of how multithreaded software is assembled (a plan) it is not possible to prove
231 /// that multithreaded software will not hang/freeze or that it will produce the correct results.
232 /// @n @n
233 /// Software with no multithreading plan could have just about any kind of execution behavior. Usually
234 /// unplanned software performs at least slightly better than single threaded versions of the software, but
235 /// frequently does not utilize all the available resources. Generally performance does not scale well as
236 /// unplanned software is run on more processors. Frequently, there is contention for a specific resource and
237 /// a thread will wait for that resource longer than is actually needed.
238 /// @image html Unplanned.png "Unplanned Threaded Execution - Fig 2."
239 /// @image latex Unplanned.png "Unplanned Threaded Execution - Fig 2."
240 /// @image rtf Unplanned.png "Unplanned Threaded Execution - Fig 2."
241 /// @n @n
242 /// The DAGFrameScheduler is carefully planned and completely avoids costly synchronization
243 /// mechanisms in favor of less costly minimalistic ones. Marking one @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"
244 /// as dependent on another allows the reordering of @ref Mezzanine::Threading::iWorkUnit "iWorkUnits" so that
245 /// some @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" can be executed with no thread waiting or blocking.
246 /// @n @n
247 /// @subsubsection broken_TaskPerThread One Task Per Thread
248 /// A common example of poor planning is the creation of one thread for each task in a game. Despite
249 /// being conceptually simple, performance of systems designed this way is poor due to synchronization
250 /// and complexities that synchronization requires.
251 /// @subsection broken_ConventionWorkQueue Conventional Work Queue/Thread Pools
252 /// Conventional work queues and thread pools are a well known and robust way to increase the throughput of
253 /// of an application. These are ideal solutions for many systems, but not games.
254 /// @n @n
255 /// In conventional workqueues all of the work is broken into a number of small thread-safe
256 /// units. As these units are created they are stuffed into a queue and threads pull out units of work
257 /// as it completes other units it has started. This simple plan has many advantages. If there is work
258 /// to do then at least one thread will be doing some, and usually more threads will be working. This is
259 /// good for games and the DAGFrameScheduler mimics it. If the kind of work is unknown when the software is
260 /// written heuristics and runtime decisions can create the kind of units of work that are required. This
261 /// is not the case with games and the others kinds of software this library caters to, so changes can
262 /// be made that remove the problems this causes. One such drawback is that a given unit of work never
263 /// knows if another is running or has completed, and must therefor make some pessimistic assumptions.
264 /// @image html Threadpool.png "Convention Work Queue/ThreadPools - Fig 3."
265 /// @image latex Threadpool.png "Convention Work Queue/ThreadPools - Fig 3."
266 /// @image rtf Threadpool.png "Convention Work Queue/ThreadPools - Fig 3."
267 /// @n @n
268 /// Common synchronization mechanisms like mutexes or semaphores block the thread for an unknown
269 /// amount of time, and are required by the design of workqueues. There are two times this is required.
270 /// The first time is whenever a work unit is acquired by a thread, a mutex (or similar) must be used
271 /// to prevent other threads from modifying the queue as well. This impacts scalability, but can be
272 /// circumvented to a point. Common ways to work around this try to split up the work queue
273 /// pre-emptively, or feed the threads work units from varying points in the queue. The
274 /// DAGFrameScheduler moves the synchronizantion onto each work unit to greatly reduce the contention as
275 /// more work units are added.
276 /// @n @n
277 /// The other, and less obvious, point of contention that has not be circumvented in a
278 /// satisfactory way for games is the large of amount of synchronization required between units of
279 /// work that must communicate. For example, there may be hundreds of thousands of pieces of data
280 /// that must be passed from a system into a 3d rendering system. Applying mutexes to each would slow
281 /// execution an impossibly long time (if it didn't introduce deadlock), while more coarse grained
282 /// lock would prevent large portions of physics and rendering from occurring at the time causing
283 /// one or both of them to wait/block. A simple solution would be to run physics before graphics,
284 /// but common work queues do not provide good guarantees in this regard.
285 /// @n @n
286 /// The DAGFrameScheduler was explicitly designed to provide exactly this guarantee. If the
287 /// physics @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" is added to the graphics
288 /// @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" with
289 /// @ref Mezzanine::Threading::iWorkUnit::AddDependency() "AddDependency(WorkUnit*)" then it will
290 /// always be run before the graphics workunit in a given frame. The drawback of this is that it
291 /// is more difficult to make runtime creation of workunits (It is possible but it cannot be done
292 /// during any frame execution), but completely removes the locking
293 /// mechanisms of conventional work queues. The DAGFrameScheduler has traded one useless feature
294 /// for a useful guarantee.
295 ///
296 /// @subsection algorithm_sec The Algorithm
297 /// When first creating the DAGFrameScheduler it was called it "Dagma-CP". When describing it the
298 /// phrase "Directed Acyclic Graph Minimal Assembly of Critical Path" was used. If you are lucky
299 /// enough to knows what all those terms mean when assembled this way they are very descriptive. For
300 /// rest of us the algorithm tries to determine what is the shortest way to execute the work in a
301 /// minimalistic way using a mathematical graph. The graph is based on what work must done
302 /// before other work each frame and executing it. All the work in this graph will have a
303 /// location somewhere between the beginning and end, and will never circle around back so it can
304 /// be called acyclic.
305 /// @n @n
306 /// This algorithm was designed with practicality as the first priority. To accomodate and integrate
307 /// with a variety of other algorithms and system a variety of Work Units are provided. New classes
308 /// can be created that inherit from these to allow them to be in the scheduler where they will work
309 /// best.
310 /// @li @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" - The interface for a common workunit.
311 /// These work units will be executed once each frame after all their dependencies have completed.
312 /// These are also expected to complete execution in a relatively brief period of time compared to
313 /// the length of a frame, and create no threads while doing so.
314 /// @li @ref Mezzanine::Threading::DefaultWorkUnit "DefaultWorkUnit" - A simple implementation
315 /// of an @ref Mezzanine::Threading::iWorkUnit "iWorkUnit". This may not be suitable for every
316 /// use case, but it should be suitable for most. Just one function for derived classes to
317 /// implement, the one that does actual work.
318 /// @li @ref Mezzanine::Threading::iAsynchronousWorkUnit "iAsynchronousWorkUnit" - Intended to
319 /// allow loading of files and streams even after the framescheduler has paused. Work units are
320 /// to spawn one thread and manage it without interfering with other execution. DMA, and other
321 /// hardware coprocessors are expected to be utilized to their fullest to help accomplish this.
322 /// @li @ref Mezzanine::Threading::MonopolyWorkUnit "MonopolyWorkUnit" - These are expected
323 /// to monopolize cpu resources at the beginning of each frame. This is ideal when working with
324 /// other systems. For example a phsyics system like Bullet3D. If the calls to a physics system are
325 /// wrapped in a @ref Mezzanine::Threading::MonopolyWorkUnit "MonopolyWorkUnit" then it will be
326 /// given full opportunity to run before other work units.
327 ///
328 /// Once all the @ref Mezzanine::Threading::MonopolyWorkUnit "MonopolyWorkUnit"s are done then the
329 /// @ref Mezzanine::Threading::FrameScheduler "FrameScheduler" class instance spawns or activates
330 /// a number of threads based on a simple heuristic. This heuristic is the way work units are sorted
331 /// in preparation for execution. To understand how these are sorted, the dependency system needs to
332 /// be understood.
333 /// @n @n
334 /// Most other work queues do not provide any guarantee about the order work will be executed in.
335 /// This means that each piece of work must ensure its own data integrity using synchronization
336 /// primitives like mutexes and semaphores to protect from being corrupted by multithread access. In
337 /// most cases these should be removed and one of any two work units that must read/write the data
338 /// must depend on the other. This allows the code in the workunits to be very simple even if it
339 /// needs to use a great deal of data other work units may also consume or produce.
340 /// @n @n
341 /// Once all the dependencies are in place for any synchronization that has been removed, a
342 /// @ref Mezzanine::Threading::FrameScheduler "FrameScheduler" can be created and started. At
343 /// runtime this creates a reverse dependency graph, a
344 /// @ref Mezzanine::Threading::FrameScheduler::DependentGraph "DependentGraph". This is used to determine
345 /// which work units are the most depended upon. For each work unit a simple count of how many work
346 /// units cannot start until has been completed is generated. The higher this number the earlier
347 /// the work unit will be executed in a frame. Additionally workunits that take longer to execute
348 /// will be prioritized ahead of work units that are faster.
349 /// @n @n
350 /// Here is a chart that provides an example of this re-factoring and the runtime sorting process:
351 /// @image html DAGSorting.png "DAG WorkSorting - Fig 4."
352 /// @image latex DAGSorting.png "DAG WorkSorting - Fig 4."
353 /// @image rtf DAGSorting.png "DAG WorkSorting - Fig 4."
354 /// @n @n
355 /// There are several advantages this sorting provides that are not immediately obvious. It separates
356 /// the scheduling from the execution allowing the relatively costly sorting process to be executed
357 /// only when work units are added, removed, or changed. Prioritizing Workunits that
358 /// take longer to run should help insure the shortest critical path is found by minimizing how often
359 /// dependencies cause threads to wait for more work.
360 /// @n @n
361 /// Sorting the work can be done by a manual call to
362 /// @ref Mezzanine::Threading::FrameScheduler::SortWorkUnitsAll "FrameScheduler::SortWorkUnitsAll()",
363 /// @ref Mezzanine::Threading::FrameScheduler::SortWorkUnitsMain "FrameScheduler::SortWorkUnitsMain()",
364 /// @ref Mezzanine::Threading::FrameScheduler::SortWorkUnitsAffinity "FrameScheduler::SortWorkUnitsAffinity()"
365 /// or by adding a @ref Mezzanine::Threading::WorkSorter "WorkSorter" WorkUnit to the
366 /// @ref Mezzanine::Threading::FrameScheduler "FrameScheduler". This only needs to be done when work
367 /// units have been added, removed, or their times are likely to have changed.
368 /// @n @n
369 /// Each thread queries the @ref Mezzanine::Threading::FrameScheduler "FrameScheduler" for the next
370 /// piece of work. The @ref Mezzanine::Threading::FrameScheduler "FrameScheduler" maintains the
371 /// list of work units and the next available piece of work can be retrieved with
372 /// @ref Mezzanine::Threading::FrameScheduler::GetNextWorkUnit "FrameScheduler::GetNextWorkUnit()" or
373 /// @ref Mezzanine::Threading::FrameScheduler::GetNextWorkUnitAffinity "FrameScheduler::GetNextWorkUnitAffinity()".
374 /// This by itself is not the atomic operation that allows the thread to execute the workunit, instead
375 /// @ref Mezzanine::Threading::iWorkUnit::TakeOwnerShip "iWorkUnit::TakeOwnerShip()" can grant that.
376 /// Internally this uses an @ref Mezzanine::Threading::AtomicCompareAndSwap32 "Atomic Compare And Swap"
377 /// operation to maximize performance.
378 /// By having the workunit manage the right to execute it removes the work queue as the primary source
379 /// of contention that would prevent scaling. This does add another potential point of slowdowns though;
380 /// threads must iterate over each other workunit until they reach the work to be executed. If atomic
381 /// operations are used to maintain an iterator that keeps track of where to start searching for work,
382 /// in a waitfree way, then we can trade the cost of this iteration for a number of atomic operations.
383 /// On some systems this is a great idea, on others a terrible idea, so it is a
384 /// @ref MEZZ_USEATOMICSTODECACHECOMPLETEWORK "CMake option called DecachingWork". Because this update
385 /// can be skipped if it work incur a wait, it does not recreate a central workqueue's primary point of
386 /// contention while providing all the benefits.
387 /// @image html DAGThreads.gif "DAG threads - Fig 5."
388 /// @n @n
389 /// Some work must be run on specific threads, such as calls to underlying devices (for example,
390 /// graphics cards using Directx or OpenGL). These @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s
391 /// are put into a different listing where only the main thread will attempt to execute them. Other
392 /// than running these, and running these first, the behavior of the main thread is very similar to
393 /// other threads.
394 /// @n @n
395 /// Even much of the @ref Mezzanine::Threading::FrameScheduler "FrameScheduler"'s work is performed
396 /// in @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s, such as log aggregation and
397 /// @ref Mezzanine::Threading::WorkSorter "Sorting the work listing".
398 /// @ref Mezzanine::Threading::iAsynchronousWorkUnit "iAsynchronousWorkUnit"s continue to run in a thread
399 /// beyond normal scheduling and are intended to consume fewer CPU resources and more IO resources.
400 /// For example loading a large file or listening for network traffic. These will be normal
401 /// @ref Mezzanine::Threading::iWorkUnit "iWorkUnit"s in most regards and will check on the asynchronous
402 /// tasks they manage each frame when they run as a normally scheduled.
403 /// @n @n
404 /// If a thread runs out of work because all the work is completed the frame will pause until it
405 /// should start again the next frame. This pause length is calulated using a runtime configurable value
406 /// on the @ref Mezzanine::Threading::FrameScheduler "FrameScheduler". If a thread has checked every
407 /// @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" and some are still not executing, but could not
408 /// be started because of incomplete dependencies the thread will simply iterate over every
409 /// @ref Mezzanine::Threading::iWorkUnit "iWorkUnit" in the
410 /// @ref Mezzanine::Threading::FrameScheduler "FrameScheduler" until the dependencies of one are
411 /// met and allows one to be executed. This implicitly guarantees that at least one thread will
412 /// always do work, and if dependencies chains are kept short then it is more likely that several
413 /// threads will advance.
414 /// @subsection algorithmintegrate_sec Integrating with the Algorithm
415 /// When
416 /// @ref Mezzanine::Threading::FrameScheduler::DoOneFrame() "FrameScheduler::DoOneFrame()"
417 /// is called several things happen. All work units are executed, all threads are paused until the
418 /// this frame has consumed the amount of time it should, and the timer is restarted for the next
419 /// frame.
420 /// @n @n
421 /// This process is actually divided into six steps. The function
422 /// @ref Mezzanine::Threading::FrameScheduler::DoOneFrame() "FrameScheduler::DoOneFrame()"
423 /// simply calls the following functions:
424 /// @subsubsection integrate1 Step 1 - Run All the Monopolies
425 /// The function
426 /// @ref Mezzanine::Threading::FrameScheduler::RunAllMonopolies() "FrameScheduler::RunAllMonopolies()"
427 /// simply iterates through all the @ref Mezzanine::Threading::MonopolyWorkUnit "MonopolyWorkUnit"
428 /// that have been added with
429 /// @ref Mezzanine::Threading::FrameScheduler::AddWorkUnitMonopoly() "FrameScheduler::AddWorkUnitMonopoly()".
430 /// It does this in no specific order.
431 /// @n @n
432 /// In general @ref Mezzanine::Threading::MonopolyWorkUnit "MonopolyWorkUnit"s can be expected to use
433 /// all available CPU resources. Other threads should not be executed in general.
434 /// @subsubsection integrate2 Step 2 - Create and Start Threads
435 /// The function
436 /// @ref Mezzanine::Threading::FrameScheduler::CreateThreads() "FrameScheduler::CreateThreads()"
437 /// Creates enough threads to get to the amount set by
438 /// @ref Mezzanine::Threading::FrameScheduler::SetThreadCount() "FrameScheduler::SetThreadCount()"
439 /// . Depending on how this library is configured this could mean either creating that many threads each
440 /// frame or creating additional threads only if this changed.
441 /// @n @n
442 /// Regardless of the amount of threads created, all but one of them will start executing work units as
443 /// described in the section @ref algorithm_sec "The Algorithm". This will execute as much work as possible
444 /// (work units with affinity can affect how much work can be done with out waiting) that was added by
445 /// @ref Mezzanine::Threading::FrameScheduler::AddWorkUnitMain(iWorkUnit *, const String&) "FrameScheduler::AddWorkUnitMain".
446 /// If there is only one thread, the main thread, then this will return immediately and no work will be done.
447 /// @subsubsection integrate3 Step 3 - Main Thread Work
448 /// The call to
449 /// @ref Mezzanine::Threading::FrameScheduler::RunMainThreadWork() "FrameScheduler::RunMainThreadWork()"
450 /// will start the main thread executing work units. This is the call that executes work units added with
451 /// @ref Mezzanine::Threading::FrameScheduler::AddWorkUnitAffinity(iWorkUnit *, const String&) "FrameScheduler::AddWorkUnitAffinity".
452 /// @n @n
453 /// If you have single thread work that is not part of a work unit and will not interfere with and work
454 /// units execution then you can run it before calling this. Be careful when doing this, if there are any
455 /// work units that depend on work units with affinity then they will not be able to start until some
456 /// point after this is called.
457 /// @n @n
458 /// Once every work unit has started this call can return. This does not mean every work unit is complete,
459 /// though every work unit with affinity will be complete. There could be work in other threads still
460 /// executing. This is another good point to run work that is single threaded and won't interfere with
461 /// workunits that could be executing.
462 /// @subsubsection integrate4 Step 4 - Clean Up Threads
463 /// If you must execute something that could interfere (write to anything they could read or write)
464 /// with work units, you should do that after
465 /// @ref Mezzanine::Threading::FrameScheduler::JoinAllThreads() "FrameScheduler::JoinAllThreads()" is
466 /// called. This joins, destroys, or otherwise cleans up the threads the scheduler has used depending
467 /// on how this library is configured.
468 /// @subsubsection integrate5 Step 5 - Prepare for the next frame.
469 /// All the work units are marked as complete and need to be reset with
470 /// @ref Mezzanine::Threading::FrameScheduler::ResetAllWorkUnits() "FrameScheduler::ResetAllWorkUnits()"
471 /// to be used by the next frame. This simply iterates over each work unit resetting their status. A
472 /// potential future optimization could run this as a multithreaded monopoly instead.
473 /// @subsubsection integrate6 Step 6 - Wait for next frame.
474 /// The final step is to wait until the next frame should begin. To do this tracking the begining of
475 /// of each frame is required. The value in
476 /// @ref Mezzanine::Threading::FrameScheduler::CurrentFrameStart "FrameScheduler::CurrentFrameStart"
477 /// is on @ref Mezzanine::Threading::FrameScheduler "FrameScheduler" construction to the current time,
478 /// and reset every to the current time when
479 /// @ref Mezzanine::Threading::FrameScheduler::WaitUntilNextFrame() "FrameScheduler::WaitUntilNextFrame()"
480 /// is called. The value set by
481 /// @ref Mezzanine::Threading::FrameScheduler::SetFrameRate() "FrameScheduler::SetFrameRate()"
482 /// is used to calculate the desired length of a frame in microseconds. If the begining of the next
483 /// frame has not been reached, then this function will sleep the scheduler until the next frame should
484 /// begin. To compensate for systems with an imprecise sleep mechanism (or timing mechanism) an internal
485 /// @ref Mezzanine::Threading::FrameScheduler::TimingCostAllowance "FrameScheduler::TimingCostAllowance"
486 /// is tracked that averages the effects of imprecision across multiple frames to prevent roundings
487 /// errors from consistently lengthening of shortening frames.
488 /// @section dag_usage Using the DAG FrameScheduler
489 /// Still in progress. In the meantime please peruse the unit test source directory for examples.
490 /// @section dag_maintanence Code Maintenance Tasks
491 /// @subsection dag_maintanence_codestandards Coding Standards Diverge from Mezzanine
492 /// There are some fundamental differences in the DAGFrameSchedulers coded when compared to the code
493 /// in the Mezzanine.
494 /// @n @n
495 /// The most obvious is the usage of exceptions. The DAGFrameScheduler does not use
496 /// Exceptions in any way. There are a few reasons for this.
497 /// 1. Exception Thread Safety.
498 /// 2. No need to copy Exception
499 /// 3. Performance.
500 /// @n @n
501 /// The team at BlackTopp Studios Inc will be some of the first to extol the virtues of exceptions
502 /// and downplay their cost. In most situations the cost is negligible. The case of the tighest and
503 /// innermost loop in a simulation seemed to us to be one of those situations where the cost matters.
504 /// @n @n
505 /// There is no use of a number of other Mezzanine foundational classes. The CountedPtr is completely
506 /// missing. For the same reasons as exceptions. Raw pointers using RAII and references are used
507 /// instead.
508 /// @n @n
509 /// Most of the Datatypes that are used are in a trimmed down version of the Mezzanine's datatypes.h.
510 /// @subsection dag_maintanence_merging Getting changes from the Mezzanine into DAGFrameScheduler
511 /// There are 3 directories with code that overlap in the Mezzanine and in the DAGFrameScheduler. Code
512 /// That lies outside these directories is independent of the other project. The entire FrameScheduler
513 /// library loosely correlates with the Mezzanine::Threading namespace. Then there is the tests and
514 /// test framework source code. Presuming you have used git to clone both repositories to
515 /// ~/Code/Mezzanine/UnitTests/src <=> ~/Code/DAGFrameScheduler/testframework @n
516 /// ~/Code/Mezzanine/Mezzanine/src/Threading <=> ~/Code/DAGFrameScheduler/src @n
517 /// ~/Code/Mezzanine/UnitTests/tests <=> ~/Code/DAGFrameScheduler/tests @n
518 /// @n @n
519 /// For simplicity when BlackTopp Studios Inc merge changes a graphical merge tool capable of directory
520 /// merges is used to manually move changes between corresponding directories. Winmerge and Meld (Quite
521 /// possibly the prettiest merge tool ever created) are two great examples of tools for doing this.
522 
523 
524 /// @brief All of the Mezzanine game library components reside in this namespace.
525 /// @details The DAG Frame Scheduler is just one part of many in the Mezzanine. The Mezzanine as a
526 /// whole is intended to tie a complex collection of libraries into one cohesive library.
527 namespace Mezzanine
528 {
529  /// @brief This is where game specific threading algorithms and a minimalistic subset of the std threading library a held
530  /// @details This implements all of the multithreaded algorithms from BlackTopp Studios, parts of std::thread,
531  /// std::this_thread, std:: mutex, and maybe a few others. In general only the specialized gaming algorithms store here are
532  /// intended to be used in game code.
533  namespace Threading
534  {
535 
536  }//Threading
537 }//Mezzanine
538 
539 #endif
Declares a Mutex, Mutex tools, and at least one MutexLike object.
Any enumerations the threading library requires are all declared here.
This file is used on some platforms to determine what data should be read and written to and from a s...
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...
The declaration of the AsynchronousFileLoadWorkUnit a workunit that loads a listing of files asynchro...
This file defines the template double buffered resources that can be attached to a thread...
Contains an interface for a kind of WorkUnit that loads or does other work even when the frame schedu...
This file defines a minimalistic cross-platform thread that the scheduler uses to schedule tasks...
This is where game specific threading algorithms and a minimalistic subset of the std threading libra...
Declares a tool for automatically unlocking a mutex in an exception safe way.
This file has the Declarations for the main FrameScheduler class.
This file defines the metadata used to sort workunits.
This file declares and defines a mutex that is a partial implementation.
This defines a number of workunits that are required for doing some tasks that the Framescheduler req...
Contains an interface for a kind of WorkUnit that consumes time on multiple thread.
This stores the implementation and the declaration of the RollingAverage, BufferedRollingAverage, WeightedRollingAverage and the DefaultRollingAverage.
The definitions of the logging tools.
This file defines a minimalistic cross-platform thread that the scheduler uses to schedule tasks...
Simple thread safe ways to check and change a specified variable atomically.
The bulk of the engine components go in this namspace.
Definition: actor.cpp:56
Used to give commands specifically to the SWIG preprocessor.
The declaration of the Mezzanine::Threading::Barrier Barrier synchronization primitive.
This file has the definition of the workunit.