/src/ogre/OgreMain/include/OgreWorkQueue.h
Line | Count | Source (jump to first uncovered line) |
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-basedbackground 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 | | This is an abstract interface definition; users can subclass this and |
69 | | provide their own implementation if required to centralise task management |
70 | | in their own subsystems. We also provide a default implementation in the |
71 | | form of DefaultWorkQueue. |
72 | | */ |
73 | | class _OgreExport WorkQueue : public UtilityAlloc |
74 | | { |
75 | | public: |
76 | | /// Numeric identifier for a request |
77 | | typedef unsigned long long int RequestID; |
78 | | |
79 | | /** General purpose request structure. |
80 | | */ |
81 | | class _OgreExport Request : public UtilityAlloc |
82 | | { |
83 | | friend class WorkQueue; |
84 | | protected: |
85 | | /// The request channel, as an integer |
86 | | uint16 mChannel; |
87 | | /// The request type, as an integer within the channel (user can define enumerations on this) |
88 | | uint16 mType; |
89 | | /// The details of the request (user defined) |
90 | | Any mData; |
91 | | /// Retry count - set this to non-zero to have the request try again on failure |
92 | | uint8 mRetryCount; |
93 | | /// Identifier (assigned by the system) |
94 | | RequestID mID; |
95 | | /// Abort Flag |
96 | | mutable bool mAborted; |
97 | | |
98 | | public: |
99 | | /// Constructor |
100 | | Request(uint16 channel, uint16 rtype, const Any& rData, uint8 retry, RequestID rid); |
101 | | ~Request(); |
102 | | /// Get the request channel (top level categorisation) |
103 | 0 | uint16 getChannel() const { return mChannel; } |
104 | | /// Get the type of this request within the given channel |
105 | 0 | uint16 getType() const { return mType; } |
106 | | /// Get the user details of this request |
107 | 0 | const Any& getData() const { return mData; } |
108 | | /// Get the remaining retry count |
109 | 0 | uint8 getRetryCount() const { return mRetryCount; } |
110 | | /// Get the identifier of this request |
111 | 0 | RequestID getID() const { return mID; } |
112 | | }; |
113 | | |
114 | | /** General purpose response structure. |
115 | | */ |
116 | | struct _OgreExport Response : public UtilityAlloc |
117 | | { |
118 | | /// Pointer to the request that this response is in relation to |
119 | | const Request* mRequest; |
120 | | /// Whether the work item succeeded or not |
121 | | bool mSuccess; |
122 | | /// Any diagnostic messages |
123 | | String mMessages; |
124 | | /// Data associated with the result of the process |
125 | | Any mData; |
126 | | |
127 | | public: |
128 | | Response(const Request* rq, bool success, const Any& data, const String& msg = BLANKSTRING); |
129 | | ~Response(); |
130 | | /// Get the request that this is a response to (NB destruction destroys this) |
131 | 0 | const Request* getRequest() const { return mRequest; } |
132 | | /// Return whether this is a successful response |
133 | 0 | bool succeeded() const { return mSuccess; } |
134 | | /// Get any diagnostic messages about the process |
135 | 0 | const String& getMessages() const { return mMessages; } |
136 | | /// Return the response data (user defined, only valid on success) |
137 | 0 | const Any& getData() const { return mData; } |
138 | | }; |
139 | | WorkQueue() {} |
140 | | virtual ~WorkQueue() {} |
141 | | |
142 | | /** Get the number of worker threads that this queue will start when |
143 | | startup() is called. |
144 | | */ |
145 | 0 | virtual size_t getWorkerThreadCount() const { return 1; } |
146 | | |
147 | | /** Set the number of worker threads that this queue will start |
148 | | when startup() is called (default 1). |
149 | | Calling this will have no effect unless the queue is shut down and |
150 | | restarted. |
151 | | */ |
152 | 0 | virtual void setWorkerThreadCount(size_t c) {} |
153 | | |
154 | | /** Start up the queue with the options that have been set. |
155 | | @param forceRestart If the queue is already running, whether to shut it |
156 | | down and restart. |
157 | | */ |
158 | | virtual void startup(bool forceRestart = true) = 0; |
159 | | |
160 | | /** Add a new task to the queue */ |
161 | | virtual void addTask(std::function<void()> task) = 0; |
162 | | |
163 | | /** Set whether to pause further processing of any requests. |
164 | | If true, any further requests will simply be queued and not processed until |
165 | | setPaused(false) is called. Any requests which are in the process of being |
166 | | worked on already will still continue. |
167 | | */ |
168 | | virtual void setPaused(bool pause) = 0; |
169 | | /// Return whether the queue is paused ie not sending more work to workers |
170 | | virtual bool isPaused() const = 0; |
171 | | |
172 | | /** Set whether to accept new requests or not. |
173 | | If true, requests are added to the queue as usual. If false, requests |
174 | | are silently ignored until setRequestsAccepted(true) is called. |
175 | | */ |
176 | | virtual void setRequestsAccepted(bool accept) = 0; |
177 | | /// Returns whether requests are being accepted right now |
178 | | virtual bool getRequestsAccepted() const = 0; |
179 | | |
180 | | /** Process the tasks in the main-thread queue. |
181 | | |
182 | | This method must be called from the main render |
183 | | thread to 'pump' tasks through the system. The method will usually |
184 | | try to clear all tasks before returning; however, you can specify |
185 | | a time limit on the tasks processing to limit the impact of |
186 | | spikes in demand by calling @ref setMainThreadProcessingTimeLimit. |
187 | | */ |
188 | | virtual void processMainThreadTasks(); |
189 | | |
190 | | /// @deprecated use @ref processMainThreadTasks |
191 | 0 | OGRE_DEPRECATED virtual void processResponses() { } |
192 | | |
193 | | /** Get the time limit imposed on the processing of tasks in a |
194 | | single frame, in milliseconds (0 indicates no limit). |
195 | | */ |
196 | 0 | uint64 getMainThreadProcessingTimeLimit() const { return getResponseProcessingTimeLimit(); } |
197 | | |
198 | | /// @deprecated use @ref getMainThreadProcessingTimeLimit() |
199 | | virtual unsigned long getResponseProcessingTimeLimit() const = 0; |
200 | | |
201 | | /** Set the time limit imposed on the processing of tasks in a |
202 | | single frame, in milliseconds (0 indicates no limit). |
203 | | This sets the maximum time that will be spent in @ref processMainThreadTasks() in |
204 | | a single frame. The default is 10ms. |
205 | | */ |
206 | 0 | void setMainThreadProcessingTimeLimit(uint64 ms) { setResponseProcessingTimeLimit(ms); } |
207 | | |
208 | | /// @deprecated use @ref setMainThreadProcessingTimeLimit |
209 | | virtual void setResponseProcessingTimeLimit(unsigned long ms) = 0; |
210 | | |
211 | | /** Add a deferred task that will be processed on the main render thread */ |
212 | | virtual void addMainThreadTask(std::function<void()> task) = 0; |
213 | | |
214 | | /** Shut down the queue. |
215 | | */ |
216 | | virtual void shutdown() = 0; |
217 | | }; |
218 | | |
219 | | /** Base for a general purpose task-based background work queue. |
220 | | */ |
221 | | class _OgreExport DefaultWorkQueueBase : public WorkQueue |
222 | | { |
223 | | public: |
224 | | |
225 | | /** Constructor. |
226 | | Call startup() to initialise. |
227 | | @param name Optional name, just helps to identify logging output |
228 | | */ |
229 | | DefaultWorkQueueBase(const String& name = BLANKSTRING); |
230 | | virtual ~DefaultWorkQueueBase(); |
231 | | /// Get the name of the work queue |
232 | | const String& getName() const; |
233 | | |
234 | | /** Get whether worker threads will be allowed to access render system |
235 | | resources. |
236 | | Accessing render system resources from a separate thread can require that |
237 | | a context is maintained for that thread. Also, it requires that the |
238 | | render system is running in threadsafe mode, which only happens |
239 | | when OGRE_THREAD_SUPPORT=1. This option defaults to false, which means |
240 | | that threads can not use GPU resources, and the render system can |
241 | | work in non-threadsafe mode, which is more efficient. |
242 | | */ |
243 | | virtual bool getWorkersCanAccessRenderSystem() const; |
244 | | |
245 | | |
246 | | /** Set whether worker threads will be allowed to access render system |
247 | | resources. |
248 | | Accessing render system resources from a separate thread can require that |
249 | | a context is maintained for that thread. Also, it requires that the |
250 | | render system is running in threadsafe mode, which only happens |
251 | | when OGRE_THREAD_SUPPORT=1. This option defaults to false, which means |
252 | | that threads can not use GPU resources, and the render system can |
253 | | work in non-threadsafe mode, which is more efficient. |
254 | | Calling this will have no effect unless the queue is shut down and |
255 | | restarted. |
256 | | */ |
257 | | virtual void setWorkersCanAccessRenderSystem(bool access); |
258 | | |
259 | | /** Process the next request on the queue. |
260 | | |
261 | | This method is public, but only intended for advanced users to call. |
262 | | The only reason you would call this, is if you were using your |
263 | | own thread to drive the worker processing. The thread calling this |
264 | | method will be the thread used to call the RequestHandler. |
265 | | */ |
266 | | virtual void _processNextRequest(); |
267 | | |
268 | | /// Main function for each thread spawned. |
269 | | virtual void _threadMain() = 0; |
270 | | |
271 | | /** Returns whether the queue is trying to shut down. */ |
272 | 0 | virtual bool isShuttingDown() const { return mShuttingDown; } |
273 | | |
274 | | /// @copydoc WorkQueue::setPaused |
275 | | void setPaused(bool pause) override; |
276 | | /// @copydoc WorkQueue::isPaused |
277 | | bool isPaused() const override; |
278 | | /// @copydoc WorkQueue::setRequestsAccepted |
279 | | void setRequestsAccepted(bool accept) override; |
280 | | /// @copydoc WorkQueue::getRequestsAccepted |
281 | | virtual bool getRequestsAccepted() const override; |
282 | | void processMainThreadTasks() override; |
283 | | /// @copydoc WorkQueue::getResponseProcessingTimeLimit |
284 | 0 | unsigned long getResponseProcessingTimeLimit() const override { return mResposeTimeLimitMS; } |
285 | | /// @copydoc WorkQueue::setResponseProcessingTimeLimit |
286 | 0 | void setResponseProcessingTimeLimit(unsigned long ms) override { mResposeTimeLimitMS = ms; } |
287 | 0 | size_t getWorkerThreadCount() const override { return mWorkerThreadCount; } |
288 | | void setWorkerThreadCount(size_t c) override { mWorkerThreadCount = c; } |
289 | | void addMainThreadTask(std::function<void()> task) override; |
290 | | void addTask(std::function<void()> task) override; |
291 | | protected: |
292 | | String mName; |
293 | | size_t mWorkerThreadCount; |
294 | | bool mWorkerRenderSystemAccess; |
295 | | bool mIsRunning; |
296 | | unsigned long mResposeTimeLimitMS; |
297 | | |
298 | | std::deque<std::function<void()>> mTasks; |
299 | | std::deque<std::function<void()>> mMainThreadTasks; |
300 | | |
301 | | bool mPaused; |
302 | | bool mAcceptRequests; |
303 | | bool mShuttingDown; |
304 | | |
305 | | OGRE_WQ_MUTEX(mRequestMutex); |
306 | | OGRE_WQ_MUTEX(mResponseMutex); |
307 | | |
308 | | /// Notify workers about a new request. |
309 | | virtual void notifyWorkers() = 0; |
310 | | }; |
311 | | |
312 | | |
313 | | |
314 | | |
315 | | |
316 | | /** @} */ |
317 | | /** @} */ |
318 | | |
319 | | } |
320 | | |
321 | | #include "OgreHeaderSuffix.h" |
322 | | |
323 | | #endif |
324 | | |