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