| 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-2006 Torus Knot Software Ltd |
|---|
| 8 | Also see acknowledgements in Readme.html |
|---|
| 9 | |
|---|
| 10 | This program is free software; you can redistribute it and/or modify it under |
|---|
| 11 | the terms of the GNU Lesser General Public License as published by the Free Software |
|---|
| 12 | Foundation; either version 2 of the License, or (at your option) any later |
|---|
| 13 | version. |
|---|
| 14 | |
|---|
| 15 | This program is distributed in the hope that it will be useful, but WITHOUT |
|---|
| 16 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|---|
| 17 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. |
|---|
| 18 | |
|---|
| 19 | You should have received a copy of the GNU Lesser General Public License along with |
|---|
| 20 | this program; if not, write to the Free Software Foundation, Inc., 59 Temple |
|---|
| 21 | Place - Suite 330, Boston, MA 02111-1307, USA, or go to |
|---|
| 22 | http://www.gnu.org/copyleft/lesser.txt. |
|---|
| 23 | |
|---|
| 24 | You may alternatively use this source under the terms of a specific version of |
|---|
| 25 | the OGRE Unrestricted License provided you have obtained such a license from |
|---|
| 26 | Torus Knot Software Ltd. |
|---|
| 27 | ----------------------------------------------------------------------------- |
|---|
| 28 | */ |
|---|
| 29 | #ifndef __ResourceBackgroundQueue_H__ |
|---|
| 30 | #define __ResourceBackgroundQueue_H__ |
|---|
| 31 | |
|---|
| 32 | |
|---|
| 33 | #include "OgrePrerequisites.h" |
|---|
| 34 | #include "OgreCommon.h" |
|---|
| 35 | #include "OgreSingleton.h" |
|---|
| 36 | #include "OgreResource.h" |
|---|
| 37 | |
|---|
| 38 | #if OGRE_THREAD_SUPPORT |
|---|
| 39 | # include <boost/thread/thread.hpp> |
|---|
| 40 | # include <boost/thread/condition.hpp> |
|---|
| 41 | #endif |
|---|
| 42 | |
|---|
| 43 | namespace Ogre { |
|---|
| 44 | |
|---|
| 45 | /// Identifier of a background process |
|---|
| 46 | typedef unsigned long BackgroundProcessTicket; |
|---|
| 47 | |
|---|
| 48 | /** This class is used to perform Resource operations in a |
|---|
| 49 | background thread. |
|---|
| 50 | @remarks |
|---|
| 51 | If threading is enabled, Ogre will create a single background thread |
|---|
| 52 | which can be used to load / unload resources in parallel. Only one |
|---|
| 53 | resource will be processed at once in this background thread, but it |
|---|
| 54 | will be in parallel with the main thread. |
|---|
| 55 | @par |
|---|
| 56 | The general approach here is that on requesting a background resource |
|---|
| 57 | process, your request is placed on a queue ready for the background |
|---|
| 58 | thread to be picked up, and you will get a 'ticket' back, identifying |
|---|
| 59 | the request. Your call will then return and your thread can |
|---|
| 60 | proceed, knowing that at some point in the background the operation wil |
|---|
| 61 | be performed. In it's own thread, the resource operation will be |
|---|
| 62 | performed, and once finished the ticket will be marked as complete. |
|---|
| 63 | You can check the status of tickets by calling isProcessComplete() |
|---|
| 64 | from your queueing thread. It is also possible to get immediate |
|---|
| 65 | callbacks on completion, but these callbacks happen in the background |
|---|
| 66 | loading thread (not your calling thread), so should only be used if you |
|---|
| 67 | really understand multithreading. |
|---|
| 68 | @par |
|---|
| 69 | By default, when threading is enabled this class will start its own |
|---|
| 70 | separate thread to perform the actual loading. However, if you would |
|---|
| 71 | prefer to use your own existing thread to perform the background load, |
|---|
| 72 | then be sure to call setStartBackgroundThread(false) before initialise() is |
|---|
| 73 | called by Root::initialise. Your own thread should call _initThread |
|---|
| 74 | immediately on startup, before any resources are loaded at all, and |
|---|
| 75 | _doNextQueuedBackgroundProcess to process background requests. |
|---|
| 76 | @note |
|---|
| 77 | This class will only perform tasks in a background thread if |
|---|
| 78 | OGRE_THREAD_SUPPORT is defined to be 1. Otherwise, all methods will |
|---|
| 79 | call their exact equivalents in ResourceGroupManager synchronously. |
|---|
| 80 | */ |
|---|
| 81 | class _OgreExport ResourceBackgroundQueue : public Singleton<ResourceBackgroundQueue> |
|---|
| 82 | { |
|---|
| 83 | public: |
|---|
| 84 | /** This abstract listener interface lets you get notifications of |
|---|
| 85 | completed background processes instead of having to poll ticket |
|---|
| 86 | statuses. |
|---|
| 87 | @note |
|---|
| 88 | For simplicity, these callbacks are not issued direct from the background |
|---|
| 89 | loading thread, they are queued themselves to be sent from the main thread |
|---|
| 90 | so that you don't have to be concerned about thread safety. |
|---|
| 91 | */ |
|---|
| 92 | class _OgreExport Listener |
|---|
| 93 | { |
|---|
| 94 | public: |
|---|
| 95 | /** Called when a requested operation completes, queued into main thread. |
|---|
| 96 | @note |
|---|
| 97 | For simplicity, this callback is not issued direct from the background |
|---|
| 98 | loading thread, it is queued to be sent from the main thread |
|---|
| 99 | so that you don't have to be concerned about thread safety. |
|---|
| 100 | */ |
|---|
| 101 | virtual void operationCompleted(BackgroundProcessTicket ticket) = 0; |
|---|
| 102 | /** Called when a requested operation completes, immediate in background thread. |
|---|
| 103 | @note |
|---|
| 104 | This is the advanced version of the background operation notification, |
|---|
| 105 | it happens immediately when the background operation is completed, and |
|---|
| 106 | your callback is executed in the <b>background thread</b>. Therefore if |
|---|
| 107 | you use this version, you have to be aware of thread safety issues |
|---|
| 108 | and what you can and cannot do in your callback implementation. |
|---|
| 109 | */ |
|---|
| 110 | virtual void operationCompletedInThread(BackgroundProcessTicket ticket) {} |
|---|
| 111 | /// Need virtual destructor in case subclasses use it |
|---|
| 112 | virtual ~Listener() {} |
|---|
| 113 | |
|---|
| 114 | }; |
|---|
| 115 | /// Init notification mutex (must lock before waiting on initCondition) |
|---|
| 116 | OGRE_MUTEX(initMutex) |
|---|
| 117 | /// Synchroniser token to wait / notify on thread init (public incase external thread) |
|---|
| 118 | OGRE_THREAD_SYNCHRONISER(initSync); |
|---|
| 119 | |
|---|
| 120 | protected: |
|---|
| 121 | /** Enumerates the type of requests */ |
|---|
| 122 | enum RequestType |
|---|
| 123 | { |
|---|
| 124 | RT_INITIALISE_GROUP, |
|---|
| 125 | RT_INITIALISE_ALL_GROUPS, |
|---|
| 126 | RT_LOAD_GROUP, |
|---|
| 127 | RT_LOAD_RESOURCE, |
|---|
| 128 | RT_UNLOAD_GROUP, |
|---|
| 129 | RT_UNLOAD_RESOURCE, |
|---|
| 130 | RT_SHUTDOWN |
|---|
| 131 | }; |
|---|
| 132 | /** Encapsulates a queued request for the background queue */ |
|---|
| 133 | struct Request |
|---|
| 134 | { |
|---|
| 135 | BackgroundProcessTicket ticketID; |
|---|
| 136 | RequestType type; |
|---|
| 137 | String resourceName; |
|---|
| 138 | ResourceHandle resourceHandle; |
|---|
| 139 | String resourceType; |
|---|
| 140 | String groupName; |
|---|
| 141 | bool isManual; |
|---|
| 142 | ManualResourceLoader* loader; |
|---|
| 143 | const NameValuePairList* loadParams; |
|---|
| 144 | Listener* listener; |
|---|
| 145 | }; |
|---|
| 146 | typedef std::list<Request> RequestQueue; |
|---|
| 147 | typedef std::map<BackgroundProcessTicket, Request*> RequestTicketMap; |
|---|
| 148 | |
|---|
| 149 | /// Queue of requests, used to store and order requests |
|---|
| 150 | RequestQueue mRequestQueue; |
|---|
| 151 | |
|---|
| 152 | /// Request lookup by ticket |
|---|
| 153 | RequestTicketMap mRequestTicketMap; |
|---|
| 154 | |
|---|
| 155 | /// Next ticket ID |
|---|
| 156 | unsigned long mNextTicketID; |
|---|
| 157 | |
|---|
| 158 | /// Struct that holds details of queued notifications |
|---|
| 159 | struct QueuedNotification |
|---|
| 160 | { |
|---|
| 161 | QueuedNotification(Resource::Listener* l, Resource* r) |
|---|
| 162 | : resourceListener(l), resource(r), opListener(0), ticket(0) |
|---|
| 163 | {} |
|---|
| 164 | |
|---|
| 165 | QueuedNotification(Listener* l, BackgroundProcessTicket t) |
|---|
| 166 | : resourceListener(0), resource(0), opListener(l), ticket(t) |
|---|
| 167 | {} |
|---|
| 168 | |
|---|
| 169 | // Type 1 - Resource::Listener kind |
|---|
| 170 | Resource::Listener* resourceListener; |
|---|
| 171 | Resource* resource; |
|---|
| 172 | // Type 2 - ResourceBackgroundQueue::Listener kind |
|---|
| 173 | Listener* opListener; |
|---|
| 174 | BackgroundProcessTicket ticket; |
|---|
| 175 | }; |
|---|
| 176 | typedef std::list<QueuedNotification> NotificationQueue; |
|---|
| 177 | /// Queued notifications of background loading being finished |
|---|
| 178 | NotificationQueue mNotificationQueue; |
|---|
| 179 | /// Mutex to protect the background event queue] |
|---|
| 180 | OGRE_MUTEX(mNotificationQueueMutex) |
|---|
| 181 | |
|---|
| 182 | /// Whether this class should start it's own thread or not |
|---|
| 183 | bool mStartThread; |
|---|
| 184 | |
|---|
| 185 | #if OGRE_THREAD_SUPPORT |
|---|
| 186 | /// The single background thread which will process loading requests |
|---|
| 187 | boost::thread* mThread; |
|---|
| 188 | /// Synchroniser token to wait / notify on queue |
|---|
| 189 | boost::condition mCondition; |
|---|
| 190 | /// Thread function |
|---|
| 191 | static void threadFunc(void); |
|---|
| 192 | /// Internal method for adding a request; also assigns a ticketID |
|---|
| 193 | BackgroundProcessTicket addRequest(Request& req); |
|---|
| 194 | /// Thread shutdown? |
|---|
| 195 | bool mShuttingDown; |
|---|
| 196 | #else |
|---|
| 197 | /// Dummy |
|---|
| 198 | void* mThread; |
|---|
| 199 | #endif |
|---|
| 200 | |
|---|
| 201 | /// Private mutex, not allowed to lock from outside |
|---|
| 202 | OGRE_AUTO_MUTEX |
|---|
| 203 | |
|---|
| 204 | /** Queue the firing of the 'background loading complete' event to |
|---|
| 205 | a Resource::Listener event. |
|---|
| 206 | @remarks |
|---|
| 207 | The purpose of this is to allow the background loading thread to |
|---|
| 208 | call this method to queue the notification to listeners waiting on |
|---|
| 209 | the background loading of a resource. Rather than allow the resource |
|---|
| 210 | background loading thread to directly call these listeners, which |
|---|
| 211 | would require all the listeners to be thread-safe, this method |
|---|
| 212 | implements a thread-safe queue which can be processed in the main |
|---|
| 213 | frame loop thread each frame to clear the events in a simpler |
|---|
| 214 | manner. |
|---|
| 215 | @param listener The listener to be notified |
|---|
| 216 | @param ticket The ticket for the operation that has completed |
|---|
| 217 | */ |
|---|
| 218 | virtual void queueFireBackgroundOperationComplete(Listener* listener, |
|---|
| 219 | BackgroundProcessTicket ticket); |
|---|
| 220 | |
|---|
| 221 | public: |
|---|
| 222 | ResourceBackgroundQueue(); |
|---|
| 223 | virtual ~ResourceBackgroundQueue(); |
|---|
| 224 | |
|---|
| 225 | /** Sets whether or not a thread should be created and started to handle |
|---|
| 226 | the background loading, or whether a user thread will call the |
|---|
| 227 | appropriate hooks. |
|---|
| 228 | @remarks |
|---|
| 229 | By default, a new thread will be started to handle the background |
|---|
| 230 | load requests. However, the application may well have some threads |
|---|
| 231 | of its own which is wishes to use to perform the background loading |
|---|
| 232 | as well as other tasks (for example on most platforms there will be |
|---|
| 233 | a fixed number of hardware threads which the application will wish |
|---|
| 234 | to work within). Use this method to turn off the creation of a separate |
|---|
| 235 | thread if you wish, and call the _doNextQueuedBackgroundProcess |
|---|
| 236 | method from your own thread to process background requests. |
|---|
| 237 | @note |
|---|
| 238 | You <b>must</b> call this method prior to initialisation. Initialisation |
|---|
| 239 | of this class is automatically done when Root::initialise is called. |
|---|
| 240 | */ |
|---|
| 241 | void setStartBackgroundThread(bool startThread) { mStartThread = startThread; } |
|---|
| 242 | |
|---|
| 243 | /** Gets whether or not a thread should be created and started to handle |
|---|
| 244 | the background loading, or whether a user thread will call the |
|---|
| 245 | appropriate hooks. |
|---|
| 246 | */ |
|---|
| 247 | bool getStartBackgroundThread(void) { return mStartThread; } |
|---|
| 248 | /** Initialise the background queue system. |
|---|
| 249 | @note Called automatically by Root::initialise. |
|---|
| 250 | */ |
|---|
| 251 | virtual void initialise(void); |
|---|
| 252 | |
|---|
| 253 | /** Shut down the background queue system. |
|---|
| 254 | @note Called automatically by Root::shutdown. |
|---|
| 255 | */ |
|---|
| 256 | virtual void shutdown(void); |
|---|
| 257 | |
|---|
| 258 | /** Initialise a resource group in the background. |
|---|
| 259 | @see ResourceGroupManager::initialiseResourceGroup |
|---|
| 260 | @param name The name of the resource group to initialise |
|---|
| 261 | @param listener Optional callback interface, take note of warnings in |
|---|
| 262 | the header and only use if you understand them. |
|---|
| 263 | @returns Ticket identifying the request, use isProcessComplete() to |
|---|
| 264 | determine if completed if not using listener |
|---|
| 265 | */ |
|---|
| 266 | virtual BackgroundProcessTicket initialiseResourceGroup( |
|---|
| 267 | const String& name, Listener* listener = 0); |
|---|
| 268 | |
|---|
| 269 | /** Initialise all resource groups which are yet to be initialised in |
|---|
| 270 | the background. |
|---|
| 271 | @see ResourceGroupManager::intialiseResourceGroup |
|---|
| 272 | @param listener Optional callback interface, take note of warnings in |
|---|
| 273 | the header and only use if you understand them. |
|---|
| 274 | @returns Ticket identifying the request, use isProcessComplete() to |
|---|
| 275 | determine if completed if not using listener |
|---|
| 276 | */ |
|---|
| 277 | virtual BackgroundProcessTicket initialiseAllResourceGroups( |
|---|
| 278 | Listener* listener = 0); |
|---|
| 279 | /** Loads a resource group in the background. |
|---|
| 280 | @see ResourceGroupManager::loadResourceGroup |
|---|
| 281 | @param name The name of the resource group to load |
|---|
| 282 | @param listener Optional callback interface, take note of warnings in |
|---|
| 283 | the header and only use if you understand them. |
|---|
| 284 | @returns Ticket identifying the request, use isProcessComplete() to |
|---|
| 285 | determine if completed if not using listener |
|---|
| 286 | */ |
|---|
| 287 | virtual BackgroundProcessTicket loadResourceGroup(const String& name, |
|---|
| 288 | Listener* listener = 0); |
|---|
| 289 | |
|---|
| 290 | |
|---|
| 291 | /** Unload a single resource in the background. |
|---|
| 292 | @see ResourceManager::unload |
|---|
| 293 | @param resType The type of the resource |
|---|
| 294 | (from ResourceManager::getResourceType()) |
|---|
| 295 | @param name The name of the Resource |
|---|
| 296 | */ |
|---|
| 297 | virtual BackgroundProcessTicket unload( |
|---|
| 298 | const String& resType, const String& name, |
|---|
| 299 | Listener* listener = 0); |
|---|
| 300 | |
|---|
| 301 | /** Unload a single resource in the background. |
|---|
| 302 | @see ResourceManager::unload |
|---|
| 303 | @param resType The type of the resource |
|---|
| 304 | (from ResourceManager::getResourceType()) |
|---|
| 305 | @param handle Handle to the resource |
|---|
| 306 | */ |
|---|
| 307 | virtual BackgroundProcessTicket unload( |
|---|
| 308 | const String& resType, ResourceHandle handle, |
|---|
| 309 | Listener* listener = 0); |
|---|
| 310 | |
|---|
| 311 | /** Unloads a resource group in the background. |
|---|
| 312 | @see ResourceGroupManager::unloadResourceGroup |
|---|
| 313 | @param name The name of the resource group to load |
|---|
| 314 | @returns Ticket identifying the request, use isProcessComplete() to |
|---|
| 315 | determine if completed if not using listener |
|---|
| 316 | */ |
|---|
| 317 | virtual BackgroundProcessTicket unloadResourceGroup(const String& name, |
|---|
| 318 | Listener* listener = 0); |
|---|
| 319 | |
|---|
| 320 | |
|---|
| 321 | /** Load a single resource in the background. |
|---|
| 322 | @see ResourceManager::load |
|---|
| 323 | @param resType The type of the resource |
|---|
| 324 | (from ResourceManager::getResourceType()) |
|---|
| 325 | @param name The name of the Resource |
|---|
| 326 | @param group The resource group to which this resource will belong |
|---|
| 327 | @param isManual Is the resource to be manually loaded? If so, you should |
|---|
| 328 | provide a value for the loader parameter |
|---|
| 329 | @param loader The manual loader which is to perform the required actions |
|---|
| 330 | when this resource is loaded; only applicable when you specify true |
|---|
| 331 | for the previous parameter. NOTE: must be thread safe!! |
|---|
| 332 | @param loadParams Optional pointer to a list of name/value pairs |
|---|
| 333 | containing loading parameters for this type of resource. Remember |
|---|
| 334 | that this must have a lifespan longer than the return of this call! |
|---|
| 335 | */ |
|---|
| 336 | virtual BackgroundProcessTicket load( |
|---|
| 337 | const String& resType, const String& name, |
|---|
| 338 | const String& group, bool isManual = false, |
|---|
| 339 | ManualResourceLoader* loader = 0, |
|---|
| 340 | const NameValuePairList* loadParams = 0, |
|---|
| 341 | Listener* listener = 0); |
|---|
| 342 | /** Returns whether a previously queued process has completed or not. |
|---|
| 343 | @remarks |
|---|
| 344 | This method of checking that a background process has completed is |
|---|
| 345 | the 'polling' approach. Each queued method takes an optional listener |
|---|
| 346 | parameter to allow you to register a callback instead, which is |
|---|
| 347 | arguably more efficient. |
|---|
| 348 | @param ticket The ticket which was returned when the process was queued |
|---|
| 349 | @returns true if process has completed (or if the ticket is |
|---|
| 350 | unrecognised), false otherwise |
|---|
| 351 | @note Tickets are not stored onced complete so do not accumulate over |
|---|
| 352 | time. |
|---|
| 353 | This is why a non-existent ticket will return 'true'. |
|---|
| 354 | */ |
|---|
| 355 | virtual bool isProcessComplete(BackgroundProcessTicket ticket); |
|---|
| 356 | |
|---|
| 357 | /** Process a single queued background operation. |
|---|
| 358 | @remarks |
|---|
| 359 | If you are using your own thread to perform background loading, calling |
|---|
| 360 | this method from that thread triggers the processing of a single |
|---|
| 361 | background loading request from the queue. This method will not |
|---|
| 362 | return until the request has been fully processed. It also returns |
|---|
| 363 | whether it did in fact process anything - if it returned false, there |
|---|
| 364 | was nothing more in the queue. |
|---|
| 365 | @note |
|---|
| 366 | <b>Do not</b> call this method unless you are using your own thread |
|---|
| 367 | to perform the background loading and called setStartBackgroundThread(false). |
|---|
| 368 | You must only have one background loading thread. |
|---|
| 369 | @returns true if a request was processed, false if the queue was empty. |
|---|
| 370 | */ |
|---|
| 371 | bool _doNextQueuedBackgroundProcess(); |
|---|
| 372 | |
|---|
| 373 | /** Initialise processing for a background thread. |
|---|
| 374 | @remarks |
|---|
| 375 | You must call this method if you use your own thread rather than |
|---|
| 376 | letting this class create its own. Moreover, you must call it after |
|---|
| 377 | initialise() and after you've started your own thread, but before |
|---|
| 378 | any resources have been loaded. There are some |
|---|
| 379 | per-thread tasks which have to be performed on some rendering APIs |
|---|
| 380 | and it's important that they are done before rendering resources are |
|---|
| 381 | created. |
|---|
| 382 | @par |
|---|
| 383 | You must call this method in your own background thread, not the main |
|---|
| 384 | thread. It's important to block the main thread whilst this initialisation |
|---|
| 385 | is happening, use an OGRE_THREAD_WAIT on the public initSync token |
|---|
| 386 | after locking the initMutex. |
|---|
| 387 | */ |
|---|
| 388 | void _initThread(); |
|---|
| 389 | |
|---|
| 390 | /** Queue the firing of the 'background loading complete' event to |
|---|
| 391 | a Resource::Listener event. |
|---|
| 392 | @remarks |
|---|
| 393 | The purpose of this is to allow the background loading thread to |
|---|
| 394 | call this method to queue the notification to listeners waiting on |
|---|
| 395 | the background loading of a resource. Rather than allow the resource |
|---|
| 396 | background loading thread to directly call these listeners, which |
|---|
| 397 | would require all the listeners to be thread-safe, this method |
|---|
| 398 | implements a thread-safe queue which can be processed in the main |
|---|
| 399 | frame loop thread each frame to clear the events in a simpler |
|---|
| 400 | manner. |
|---|
| 401 | @param listener The listener to be notified |
|---|
| 402 | @param res The resource listened on |
|---|
| 403 | */ |
|---|
| 404 | virtual void _queueFireBackgroundLoadingComplete(Resource::Listener* listener, |
|---|
| 405 | Resource* res); |
|---|
| 406 | |
|---|
| 407 | /** Fires all the queued events for background loaded resources. |
|---|
| 408 | @remarks |
|---|
| 409 | You should call this from the thread that runs the main frame loop |
|---|
| 410 | to avoid having to make the receivers of this event thread-safe. |
|---|
| 411 | If you use Ogre's built in frame loop you don't need to call this |
|---|
| 412 | yourself. |
|---|
| 413 | */ |
|---|
| 414 | virtual void _fireBackgroundLoadingComplete(void); |
|---|
| 415 | |
|---|
| 416 | /** Override standard Singleton retrieval. |
|---|
| 417 | @remarks |
|---|
| 418 | Why do we do this? Well, it's because the Singleton |
|---|
| 419 | implementation is in a .h file, which means it gets compiled |
|---|
| 420 | into anybody who includes it. This is needed for the |
|---|
| 421 | Singleton template to work, but we actually only want it |
|---|
| 422 | compiled into the implementation of the class based on the |
|---|
| 423 | Singleton, not all of them. If we don't change this, we get |
|---|
| 424 | link errors when trying to use the Singleton-based class from |
|---|
| 425 | an outside dll. |
|---|
| 426 | @par |
|---|
| 427 | This method just delegates to the template version anyway, |
|---|
| 428 | but the implementation stays in this single compilation unit, |
|---|
| 429 | preventing link errors. |
|---|
| 430 | */ |
|---|
| 431 | static ResourceBackgroundQueue& getSingleton(void); |
|---|
| 432 | /** Override standard Singleton retrieval. |
|---|
| 433 | @remarks |
|---|
| 434 | Why do we do this? Well, it's because the Singleton |
|---|
| 435 | implementation is in a .h file, which means it gets compiled |
|---|
| 436 | into anybody who includes it. This is needed for the |
|---|
| 437 | Singleton template to work, but we actually only want it |
|---|
| 438 | compiled into the implementation of the class based on the |
|---|
| 439 | Singleton, not all of them. If we don't change this, we get |
|---|
| 440 | link errors when trying to use the Singleton-based class from |
|---|
| 441 | an outside dll. |
|---|
| 442 | @par |
|---|
| 443 | This method just delegates to the template version anyway, |
|---|
| 444 | but the implementation stays in this single compilation unit, |
|---|
| 445 | preventing link errors. |
|---|
| 446 | */ |
|---|
| 447 | static ResourceBackgroundQueue* getSingletonPtr(void); |
|---|
| 448 | |
|---|
| 449 | |
|---|
| 450 | }; |
|---|
| 451 | |
|---|
| 452 | |
|---|
| 453 | } |
|---|
| 454 | |
|---|
| 455 | #endif |
|---|
| 456 | |
|---|