/src/ogre/OgreMain/include/OgreWorkQueue.h
Line | Count | Source |
1 | | /* |
2 | | ----------------------------------------------------------------------------- |
3 | | This source file is part of OGRE |
4 | | (Object-oriented Graphics Rendering Engine) |
5 | | For the latest info, see http://www.ogre3d.org/ |
6 | | |
7 | | Copyright (c) 2000-2014 Torus Knot Software Ltd |
8 | | |
9 | | Permission is hereby granted, free of charge, to any person obtaining a copy |
10 | | of this software and associated documentation files (the "Software"), to deal |
11 | | in the Software without restriction, including without limitation the rights |
12 | | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
13 | | copies of the Software, and to permit persons to whom the Software is |
14 | | furnished to do so, subject to the following conditions: |
15 | | |
16 | | The above copyright notice and this permission notice shall be included in |
17 | | all copies or substantial portions of the Software. |
18 | | |
19 | | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 | | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 | | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
22 | | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 | | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 | | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
25 | | THE SOFTWARE. |
26 | | ----------------------------------------------------------------------------- |
27 | | */ |
28 | | #ifndef __OgreWorkQueue_H__ |
29 | | #define __OgreWorkQueue_H__ |
30 | | |
31 | | #include "OgrePrerequisites.h" |
32 | | #include "OgreAny.h" |
33 | | #include "OgreSharedPtr.h" |
34 | | #include "OgreCommon.h" |
35 | | #include "Threading/OgreThreadHeaders.h" |
36 | | #include "OgreHeaderPrefix.h" |
37 | | |
38 | | #include <deque> |
39 | | #include <functional> |
40 | | |
41 | | namespace Ogre |
42 | | { |
43 | | /** \addtogroup Core |
44 | | * @{ |
45 | | */ |
46 | | /** \addtogroup General |
47 | | * @{ |
48 | | */ |
49 | | |
50 | | /** Interface to a general purpose task-based background work queue. |
51 | | |
52 | | A work queue is a simple structure, where tasks of work are placed |
53 | | onto the queue, then removed by a worker for processing. |
54 | | The typical use for this is in a threaded environment, |
55 | | although any kind of deferred processing could use this approach to |
56 | | decouple and distribute work over a period of time even |
57 | | if it was single threaded. |
58 | | |
59 | | WorkQueues also incorporate thread pools. One or more background worker threads |
60 | | can wait on the queue and be notified when a request is waiting to be |
61 | | processed. For maximal thread usage, a WorkQueue instance should be shared |
62 | | among many sources of work, rather than many work queues being created. |
63 | | This way, you can share a small number of hardware threads among a large |
64 | | number of background tasks. This doesn't mean you have to implement all the |
65 | | request processing in one class, you can plug in many handlers in order to |
66 | | process the tasks. |
67 | | |
68 | | The WorkQueue distinguishes between two types of tasks: |
69 | | 1. Background tasks: Added via @ref addTask. These are processed by the worker threads. |
70 | | 2. Main thread tasks: Added via @ref addMainThreadTask. These are processed by the main thread |
71 | | when @ref processMainThreadTasks is called. This is useful for tasks that need to access |
72 | | resources that are not thread-safe or must be accessed from the main thread (e.g. GPU resources). |
73 | | |
74 | | @ref processMainThreadTasks is automatically called by @ref Root::renderOneFrame (via @ref Root::_fireFrameEnded) |
75 | | or can be called manually by the user in the main loop. |
76 | | |
77 | | This is an abstract interface definition; users can subclass this and |
78 | | provide their own implementation if required to centralise task management |
79 | | in their own subsystems. We also provide a default implementation in the |
80 | | form of DefaultWorkQueue. |
81 | | */ |
82 | | class _OgreExport WorkQueue : public UtilityAlloc |
83 | | { |
84 | | public: |
85 | | /// Numeric identifier for a request |
86 | | typedef unsigned long long int RequestID; |
87 | | |
88 | | /** General purpose request structure. |
89 | | */ |
90 | | class _OgreExport Request : public UtilityAlloc |
91 | | { |
92 | | friend class WorkQueue; |
93 | | protected: |
94 | | /// The request channel, as an integer |
95 | | uint16 mChannel; |
96 | | /// The request type, as an integer within the channel (user can define enumerations on this) |
97 | | uint16 mType; |
98 | | /// The details of the request (user defined) |
99 | | Any mData; |
100 | | /// Retry count - set this to non-zero to have the request try again on failure |
101 | | uint8 mRetryCount; |
102 | | /// Identifier (assigned by the system) |
103 | | RequestID mID; |
104 | | /// Abort Flag |
105 | | mutable bool mAborted; |
106 | | |
107 | | public: |
108 | | /// Constructor |
109 | | Request(uint16 channel, uint16 rtype, const Any& rData, uint8 retry, RequestID rid); |
110 | | ~Request(); |
111 | | /// Get the request channel (top level categorisation) |
112 | 0 | uint16 getChannel() const { return mChannel; } |
113 | | /// Get the type of this request within the given channel |
114 | 0 | uint16 getType() const { return mType; } |
115 | | /// Get the user details of this request |
116 | 0 | const Any& getData() const { return mData; } |
117 | | /// Get the remaining retry count |
118 | 0 | uint8 getRetryCount() const { return mRetryCount; } |
119 | | /// Get the identifier of this request |
120 | 0 | RequestID getID() const { return mID; } |
121 | | }; |
122 | | |
123 | | /** General purpose response structure. |
124 | | */ |
125 | | struct _OgreExport Response : public UtilityAlloc |
126 | | { |
127 | | /// Pointer to the request that this response is in relation to |
128 | | const Request* mRequest; |
129 | | /// Whether the work item succeeded or not |
130 | | bool mSuccess; |
131 | | /// Any diagnostic messages |
132 | | String mMessages; |
133 | | /// Data associated with the result of the process |
134 | | Any mData; |
135 | | |
136 | | public: |
137 | | Response(const Request* rq, bool success, const Any& data, const String& msg = BLANKSTRING); |
138 | | ~Response(); |
139 | | /// Get the request that this is a response to (NB destruction destroys this) |
140 | 0 | const Request* getRequest() const { return mRequest; } |
141 | | /// Return whether this is a successful response |
142 | 0 | bool succeeded() const { return mSuccess; } |
143 | | /// Get any diagnostic messages about the process |
144 | 0 | const String& getMessages() const { return mMessages; } |
145 | | /// Return the response data (user defined, only valid on success) |
146 | 0 | const Any& getData() const { return mData; } |
147 | | }; |
148 | 0 | WorkQueue() {} |
149 | 0 | virtual ~WorkQueue() {} |
150 | | |
151 | | /** Get the number of worker threads that this queue will start when |
152 | | startup() is called. |
153 | | */ |
154 | 0 | virtual size_t getWorkerThreadCount() const { return 1; } |
155 | | |
156 | | /** Set the number of worker threads that this queue will start |
157 | | when startup() is called (default 1). |
158 | | Calling this will have no effect unless the queue is shut down and |
159 | | restarted. |
160 | | */ |
161 | 0 | virtual void setWorkerThreadCount(size_t c) {} |
162 | | |
163 | | /** Start up the queue with the options that have been set. |
164 | | @param forceRestart If the queue is already running, whether to shut it |
165 | | down and restart. |
166 | | */ |
167 | | virtual void startup(bool forceRestart = true) = 0; |
168 | | |
169 | | /** Add a new task to the queue */ |
170 | | virtual void addTask(std::function<void()> task) = 0; |
171 | | |
172 | | /** Set whether to pause further processing of any requests. |
173 | | If true, any further requests will simply be queued and not processed until |
174 | | setPaused(false) is called. Any requests which are in the process of being |
175 | | worked on already will still continue. |
176 | | */ |
177 | | virtual void setPaused(bool pause) = 0; |
178 | | /// Return whether the queue is paused ie not sending more work to workers |
179 | | virtual bool isPaused() const = 0; |
180 | | |
181 | | /** Set whether to accept new requests or not. |
182 | | If true, requests are added to the queue as usual. If false, requests |
183 | | are silently ignored until setRequestsAccepted(true) is called. |
184 | | */ |
185 | | virtual void setRequestsAccepted(bool accept) = 0; |
186 | | /// Returns whether requests are being accepted right now |
187 | | virtual bool getRequestsAccepted() const = 0; |
188 | | |
189 | | /** Process the tasks in the main-thread queue. |
190 | | |
191 | | This method must be called from the main render |
192 | | thread to 'pump' tasks through the system. The method will usually |
193 | | try to clear all tasks before returning; however, you can specify |
194 | | a time limit on the tasks processing to limit the impact of |
195 | | spikes in demand by calling @ref setMainThreadProcessingTimeLimit. |
196 | | |
197 | | @note This is automatically called by @ref Root::renderOneFrame. |
198 | | */ |
199 | | virtual void processMainThreadTasks(); |
200 | | |
201 | | /// @deprecated use @ref processMainThreadTasks |
202 | 0 | OGRE_DEPRECATED virtual void processResponses() { } |
203 | | |
204 | | /** Get the time limit imposed on the processing of tasks in a |
205 | | single frame, in milliseconds (0 indicates no limit). |
206 | | */ |
207 | 0 | uint64 getMainThreadProcessingTimeLimit() const { return getResponseProcessingTimeLimit(); } |
208 | | |
209 | | /// @deprecated use @ref getMainThreadProcessingTimeLimit() |
210 | | virtual unsigned long getResponseProcessingTimeLimit() const = 0; |
211 | | |
212 | | /** Set the time limit imposed on the processing of tasks in a |
213 | | single frame, in milliseconds (0 indicates no limit). |
214 | | This sets the maximum time that will be spent in @ref processMainThreadTasks() in |
215 | | a single frame. The default is 10ms. |
216 | | */ |
217 | 0 | void setMainThreadProcessingTimeLimit(uint64 ms) { setResponseProcessingTimeLimit(static_cast<unsigned long>(ms)); } |
218 | | |
219 | | /// @deprecated use @ref setMainThreadProcessingTimeLimit |
220 | | virtual void setResponseProcessingTimeLimit(unsigned long ms) = 0; |
221 | | |
222 | | /** Add a deferred task that will be processed on the main render thread */ |
223 | | virtual void addMainThreadTask(std::function<void()> task) = 0; |
224 | | |
225 | | /** Shut down the queue. |
226 | | */ |
227 | | virtual void shutdown() = 0; |
228 | | }; |
229 | | |
230 | | /** Base for a general purpose task-based background work queue. |
231 | | */ |
232 | | class _OgreExport DefaultWorkQueueBase : public WorkQueue |
233 | | { |
234 | | public: |
235 | | |
236 | | /** Constructor. |
237 | | Call startup() to initialise. |
238 | | @param name Optional name, just helps to identify logging output |
239 | | */ |
240 | | DefaultWorkQueueBase(const String& name = BLANKSTRING); |
241 | | virtual ~DefaultWorkQueueBase(); |
242 | | /// Get the name of the work queue |
243 | | const String& getName() const; |
244 | | |
245 | | /** Get whether worker threads will be allowed to access render system |
246 | | resources. |
247 | | Accessing render system resources from a separate thread can require that |
248 | | a context is maintained for that thread. Also, it requires that the |
249 | | render system is running in threadsafe mode, which only happens |
250 | | when OGRE_THREAD_SUPPORT=1. This option defaults to false, which means |
251 | | that threads can not use GPU resources, and the render system can |
252 | | work in non-threadsafe mode, which is more efficient. |
253 | | */ |
254 | | virtual bool getWorkersCanAccessRenderSystem() const; |
255 | | |
256 | | |
257 | | /** Set whether worker threads will be allowed to access render system |
258 | | resources. |
259 | | Accessing render system resources from a separate thread can require that |
260 | | a context is maintained for that thread. Also, it requires that the |
261 | | render system is running in threadsafe mode, which only happens |
262 | | when OGRE_THREAD_SUPPORT=1. This option defaults to false, which means |
263 | | that threads can not use GPU resources, and the render system can |
264 | | work in non-threadsafe mode, which is more efficient. |
265 | | Calling this will have no effect unless the queue is shut down and |
266 | | restarted. |
267 | | */ |
268 | | virtual void setWorkersCanAccessRenderSystem(bool access); |
269 | | |
270 | | /** Process the next request on the queue. |
271 | | |
272 | | This method is public, but only intended for advanced users to call. |
273 | | The only reason you would call this, is if you were using your |
274 | | own thread to drive the worker processing. The thread calling this |
275 | | method will be the thread used to call the RequestHandler. |
276 | | */ |
277 | | virtual void _processNextRequest(); |
278 | | |
279 | | /// Main function for each thread spawned. |
280 | | virtual void _threadMain() = 0; |
281 | | |
282 | | /** Returns whether the queue is trying to shut down. */ |
283 | 0 | virtual bool isShuttingDown() const { return mShuttingDown; } |
284 | | |
285 | | /// @copydoc WorkQueue::setPaused |
286 | | void setPaused(bool pause) override; |
287 | | /// @copydoc WorkQueue::isPaused |
288 | | bool isPaused() const override; |
289 | | /// @copydoc WorkQueue::setRequestsAccepted |
290 | | void setRequestsAccepted(bool accept) override; |
291 | | /// @copydoc WorkQueue::getRequestsAccepted |
292 | | virtual bool getRequestsAccepted() const override; |
293 | | void processMainThreadTasks() override; |
294 | | /// @copydoc WorkQueue::getResponseProcessingTimeLimit |
295 | 0 | unsigned long getResponseProcessingTimeLimit() const override { return mResposeTimeLimitMS; } |
296 | | /// @copydoc WorkQueue::setResponseProcessingTimeLimit |
297 | 0 | void setResponseProcessingTimeLimit(unsigned long ms) override { mResposeTimeLimitMS = ms; } |
298 | 0 | size_t getWorkerThreadCount() const override { return mWorkerThreadCount; } |
299 | 0 | void setWorkerThreadCount(size_t c) override { mWorkerThreadCount = c; } |
300 | | void addMainThreadTask(std::function<void()> task) override; |
301 | | void addTask(std::function<void()> task) override; |
302 | | protected: |
303 | | String mName; |
304 | | size_t mWorkerThreadCount; |
305 | | bool mWorkerRenderSystemAccess; |
306 | | bool mIsRunning; |
307 | | unsigned long mResposeTimeLimitMS; |
308 | | |
309 | | std::deque<std::function<void()>> mTasks; |
310 | | std::deque<std::function<void()>> mMainThreadTasks; |
311 | | |
312 | | bool mPaused; |
313 | | bool mAcceptRequests; |
314 | | bool mShuttingDown; |
315 | | |
316 | | OGRE_WQ_MUTEX(mRequestMutex); |
317 | | OGRE_WQ_MUTEX(mResponseMutex); |
318 | | |
319 | | /// Notify workers about a new request. |
320 | | virtual void notifyWorkers() = 0; |
321 | | }; |
322 | | |
323 | | |
324 | | |
325 | | |
326 | | |
327 | | /** @} */ |
328 | | /** @} */ |
329 | | |
330 | | } |
331 | | |
332 | | #include "OgreHeaderSuffix.h" |
333 | | |
334 | | #endif |
335 | | |