Coverage Report

Created: 2026-04-12 06:46

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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