Patch 2793067: fix trunk with OGRE_THREAD_SUPPORT=1 on non-Windows platforms (don...
[ogre3d.git] / OgreMain / src / OgreResourceBackgroundQueue.cpp
blobf250fbd017baeff10d47686019cf6e081f909570
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/
7 Copyright (c) 2000-2006 Torus Knot Software Ltd
8 Also see acknowledgements in Readme.html
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.
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.
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.
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 -----------------------------------------------------------------------------
29 #include "OgreStableHeaders.h"
30 #include "OgreResourceBackgroundQueue.h"
31 #include "OgreLogManager.h"
32 #include "OgreException.h"
33 #include "OgreResourceGroupManager.h"
34 #include "OgreResourceManager.h"
35 #include "OgreRoot.h"
36 #include "OgreRenderSystem.h"
38 namespace Ogre {
40 //------------------------------------------------------------------------
41 //-----------------------------------------------------------------------
42 template<> ResourceBackgroundQueue* Singleton<ResourceBackgroundQueue>::ms_Singleton = 0;
43 ResourceBackgroundQueue* ResourceBackgroundQueue::getSingletonPtr(void)
45 return ms_Singleton;
47 ResourceBackgroundQueue& ResourceBackgroundQueue::getSingleton(void)
49 assert( ms_Singleton ); return ( *ms_Singleton );
51 //-----------------------------------------------------------------------
52 //------------------------------------------------------------------------
53 ResourceBackgroundQueue::ResourceBackgroundQueue()
54 :mNextTicketID(0), mStartThread(true), mThread(0)
55 #if OGRE_THREAD_SUPPORT
56 , mShuttingDown(false)
57 #endif
60 //------------------------------------------------------------------------
61 ResourceBackgroundQueue::~ResourceBackgroundQueue()
63 shutdown();
65 //------------------------------------------------------------------------
66 void ResourceBackgroundQueue::initialise(void)
68 #if OGRE_THREAD_SUPPORT
69 if (mStartThread)
72 OGRE_LOCK_AUTO_MUTEX
73 mShuttingDown = false;
76 #if OGRE_THREAD_SUPPORT == 1
77 RenderSystem* rs = Root::getSingleton().getRenderSystem();
78 #endif
80 LogManager::getSingleton().logMessage(
81 "ResourceBackgroundQueue - threading enabled, starting own thread");
83 OGRE_LOCK_MUTEX_NAMED(initMutex, initLock)
85 #if OGRE_THREAD_SUPPORT == 1
86 // Call thread creation pre-hook
87 rs->preExtraThreadsStarted();
88 #endif
90 mThread = OGRE_NEW_T(boost::thread, MEMCATEGORY_RESOURCE)(
91 boost::function0<void>(&ResourceBackgroundQueue::threadFunc));
92 // Wait for init to finish before allowing main thread to continue
93 // this releases the initMutex until notified
94 OGRE_THREAD_WAIT(initSync, initLock)
96 #if OGRE_THREAD_SUPPORT == 1
97 // Call thread creation post-hook
98 rs->postExtraThreadsStarted();
99 #endif
103 else
105 LogManager::getSingleton().logMessage(
106 "ResourceBackgroundQueue - threading enabled, user thread");
108 #else
109 LogManager::getSingleton().logMessage(
110 "ResourceBackgroundQueue - threading disabled");
111 #endif
113 //------------------------------------------------------------------------
114 void ResourceBackgroundQueue::shutdown(void)
116 #if OGRE_THREAD_SUPPORT
117 if (mThread)
119 // Put a shutdown request on the queue
120 Request req;
121 req.type = RT_SHUTDOWN;
122 addRequest(req);
123 // Wait for thread to finish
124 mThread->join();
125 OGRE_DELETE_T(mThread, thread, MEMCATEGORY_RESOURCE);
126 mThread = 0;
127 mRequestQueue.clear();
128 mRequestTicketMap.clear();
130 #endif
132 //------------------------------------------------------------------------
133 BackgroundProcessTicket ResourceBackgroundQueue::initialiseResourceGroup(
134 const String& name, ResourceBackgroundQueue::Listener* listener)
136 #if OGRE_THREAD_SUPPORT
137 if (!mThread && mStartThread)
139 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
140 "Thread not initialised",
141 "ResourceBackgroundQueue::initialiseResourceGroup");
143 // queue a request
144 Request req;
145 req.type = RT_INITIALISE_GROUP;
146 req.groupName = name;
147 req.listener = listener;
148 return addRequest(req);
149 #else
150 // synchronous
151 ResourceGroupManager::getSingleton().initialiseResourceGroup(name);
152 return 0;
153 #endif
155 //------------------------------------------------------------------------
156 BackgroundProcessTicket
157 ResourceBackgroundQueue::initialiseAllResourceGroups(
158 ResourceBackgroundQueue::Listener* listener)
160 #if OGRE_THREAD_SUPPORT
161 if (!mThread && mStartThread)
163 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
164 "Thread not initialised",
165 "ResourceBackgroundQueue::initialiseAllResourceGroups");
167 // queue a request
168 Request req;
169 req.type = RT_INITIALISE_ALL_GROUPS;
170 req.listener = listener;
171 return addRequest(req);
172 #else
173 // synchronous
174 ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
175 return 0;
176 #endif
178 //------------------------------------------------------------------------
179 BackgroundProcessTicket ResourceBackgroundQueue::prepareResourceGroup(
180 const String& name, ResourceBackgroundQueue::Listener* listener)
182 #if OGRE_THREAD_SUPPORT
183 if (!mThread && mStartThread)
185 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
186 "Thread not initialised",
187 "ResourceBackgroundQueue::prepareResourceGroup");
189 // queue a request
190 Request req;
191 req.type = RT_PREPARE_GROUP;
192 req.groupName = name;
193 req.listener = listener;
194 return addRequest(req);
195 #else
196 // synchronous
197 ResourceGroupManager::getSingleton().prepareResourceGroup(name);
198 return 0;
199 #endif
201 //------------------------------------------------------------------------
202 BackgroundProcessTicket ResourceBackgroundQueue::loadResourceGroup(
203 const String& name, ResourceBackgroundQueue::Listener* listener)
205 #if OGRE_THREAD_SUPPORT
206 if (!mThread && mStartThread)
208 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
209 "Thread not initialised",
210 "ResourceBackgroundQueue::loadResourceGroup");
212 // queue a request
213 Request req;
214 req.type = RT_LOAD_GROUP;
215 req.groupName = name;
216 req.listener = listener;
217 return addRequest(req);
218 #else
219 // synchronous
220 ResourceGroupManager::getSingleton().loadResourceGroup(name);
221 return 0;
222 #endif
224 //------------------------------------------------------------------------
225 BackgroundProcessTicket ResourceBackgroundQueue::prepare(
226 const String& resType, const String& name,
227 const String& group, bool isManual,
228 ManualResourceLoader* loader,
229 const NameValuePairList* loadParams,
230 ResourceBackgroundQueue::Listener* listener)
232 #if OGRE_THREAD_SUPPORT
233 if (!mThread && mStartThread)
235 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
236 "Thread not initialised",
237 "ResourceBackgroundQueue::prepare");
239 // queue a request
240 Request req;
241 req.type = RT_PREPARE_RESOURCE;
242 req.resourceType = resType;
243 req.resourceName = name;
244 req.groupName = group;
245 req.isManual = isManual;
246 req.loader = loader;
247 req.loadParams = loadParams;
248 req.listener = listener;
249 return addRequest(req);
250 #else
251 // synchronous
252 ResourceManager* rm =
253 ResourceGroupManager::getSingleton()._getResourceManager(resType);
254 rm->prepare(name, group, isManual, loader, loadParams);
255 return 0;
256 #endif
258 //------------------------------------------------------------------------
259 BackgroundProcessTicket ResourceBackgroundQueue::load(
260 const String& resType, const String& name,
261 const String& group, bool isManual,
262 ManualResourceLoader* loader,
263 const NameValuePairList* loadParams,
264 ResourceBackgroundQueue::Listener* listener)
266 #if OGRE_THREAD_SUPPORT
267 if (!mThread && mStartThread)
269 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
270 "Thread not initialised",
271 "ResourceBackgroundQueue::load");
273 // queue a request
274 Request req;
275 req.type = RT_LOAD_RESOURCE;
276 req.resourceType = resType;
277 req.resourceName = name;
278 req.groupName = group;
279 req.isManual = isManual;
280 req.loader = loader;
281 req.loadParams = loadParams;
282 req.listener = listener;
283 return addRequest(req);
284 #else
285 // synchronous
286 ResourceManager* rm =
287 ResourceGroupManager::getSingleton()._getResourceManager(resType);
288 rm->load(name, group, isManual, loader, loadParams);
289 return 0;
290 #endif
292 //---------------------------------------------------------------------
293 BackgroundProcessTicket ResourceBackgroundQueue::unload(
294 const String& resType, const String& name, Listener* listener)
296 #if OGRE_THREAD_SUPPORT
297 if (!mThread && mStartThread)
299 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
300 "Thread not initialised",
301 "ResourceBackgroundQueue::unload");
303 // queue a request
304 Request req;
305 req.type = RT_UNLOAD_RESOURCE;
306 req.resourceType = resType;
307 req.resourceName = name;
308 req.listener = listener;
309 return addRequest(req);
310 #else
311 // synchronous
312 ResourceManager* rm =
313 ResourceGroupManager::getSingleton()._getResourceManager(resType);
314 rm->unload(name);
315 return 0;
316 #endif
319 //---------------------------------------------------------------------
320 BackgroundProcessTicket ResourceBackgroundQueue::unload(
321 const String& resType, ResourceHandle handle, Listener* listener)
323 #if OGRE_THREAD_SUPPORT
324 if (!mThread && mStartThread)
326 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
327 "Thread not initialised",
328 "ResourceBackgroundQueue::unload");
330 // queue a request
331 Request req;
332 req.type = RT_UNLOAD_RESOURCE;
333 req.resourceType = resType;
334 req.resourceHandle = handle;
335 req.listener = listener;
336 return addRequest(req);
337 #else
338 // synchronous
339 ResourceManager* rm =
340 ResourceGroupManager::getSingleton()._getResourceManager(resType);
341 rm->unload(handle);
342 return 0;
343 #endif
346 //---------------------------------------------------------------------
347 BackgroundProcessTicket ResourceBackgroundQueue::unloadResourceGroup(
348 const String& name, Listener* listener)
350 #if OGRE_THREAD_SUPPORT
351 if (!mThread && mStartThread)
353 OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
354 "Thread not initialised",
355 "ResourceBackgroundQueue::unloadResourceGroup");
357 // queue a request
358 Request req;
359 req.type = RT_UNLOAD_GROUP;
360 req.groupName = name;
361 req.listener = listener;
362 return addRequest(req);
363 #else
364 // synchronous
365 ResourceGroupManager::getSingleton().unloadResourceGroup(name);
366 return 0;
367 #endif
370 //------------------------------------------------------------------------
371 bool ResourceBackgroundQueue::isProcessComplete(
372 BackgroundProcessTicket ticket)
374 // Lock
375 OGRE_LOCK_AUTO_MUTEX
377 return mRequestTicketMap.find(ticket) == mRequestTicketMap.end();
379 //------------------------------------------------------------------------
380 #if OGRE_THREAD_SUPPORT
381 BackgroundProcessTicket ResourceBackgroundQueue::addRequest(Request& req)
383 // Lock
384 OGRE_LOCK_AUTO_MUTEX
386 req.ticketID = ++mNextTicketID;
387 mRequestQueue.push_back(req);
388 Request* requestInList = &(mRequestQueue.back());
389 mRequestTicketMap[req.ticketID] = requestInList;
391 // Notify to wake up loading thread
392 OGRE_THREAD_NOTIFY_ONE(mCondition)
394 return req.ticketID;
396 //------------------------------------------------------------------------
397 void ResourceBackgroundQueue::threadFunc(void)
399 // Background thread implementation
400 // Static (since no params allowed), so get instance
401 ResourceBackgroundQueue& queueInstance =
402 ResourceBackgroundQueue::getSingleton();
404 LogManager::getSingleton().logMessage("ResourceBackgroundQueue - thread starting.");
406 // Initialise the thread
407 queueInstance._initThread();
409 // Spin forever until we're told to shut down
410 while (!queueInstance.mShuttingDown)
412 // Our thread will just wait when there is nothing on the queue
413 // _doNextQueuedBackgroundProcess won't do this since the thread
414 // may be shared
416 // Manual scope block just to define scope of lock
418 // Lock; note that 'mCondition.wait()' will free the lock
419 boost::recursive_mutex::scoped_lock queueLock(
420 queueInstance.OGRE_AUTO_MUTEX_NAME);
421 if (queueInstance.mRequestQueue.empty())
423 // frees lock and suspends the thread
424 queueInstance.mCondition.wait(queueLock);
426 // When we get back here, it's because we've been notified
427 // and thus the thread as been woken up. Lock has also been
428 // re-acquired.
429 } // release lock so queueing can be done while we process one request
431 queueInstance._doNextQueuedBackgroundProcess();
436 LogManager::getSingleton().logMessage("ResourceBackgroundQueue - thread stopped.");
441 #endif
442 //-----------------------------------------------------------------------
443 void ResourceBackgroundQueue::_initThread()
445 // Register the calling thread with RenderSystem
446 // Note how we assume only one thread is processing the queue
447 #if OGRE_THREAD_SUPPORT == 1
448 Root::getSingleton().getRenderSystem()->registerThread();
449 #endif
451 // notify waiting thread(s)
452 OGRE_LOCK_MUTEX(initMutex)
453 OGRE_THREAD_NOTIFY_ALL(initSync)
457 //-----------------------------------------------------------------------
458 bool ResourceBackgroundQueue::_doNextQueuedBackgroundProcess()
461 Request* req;
463 // Manual scope block just to define scope of lock
465 OGRE_LOCK_AUTO_MUTEX
466 // return false if nothing in the queue
467 if (mRequestQueue.empty())
468 return false;
470 // Process one request
471 req = &(mRequestQueue.front());
472 } // release lock so queueing can be done while we process one request
473 // use of std::list means that references guarateed to remain valid
474 // we only allow one background thread
476 ResourceManager* rm = 0;
480 switch (req->type)
482 case RT_INITIALISE_GROUP:
483 ResourceGroupManager::getSingleton().initialiseResourceGroup(
484 req->groupName);
485 break;
486 case RT_INITIALISE_ALL_GROUPS:
487 ResourceGroupManager::getSingleton().initialiseAllResourceGroups();
488 break;
489 case RT_PREPARE_GROUP:
490 ResourceGroupManager::getSingleton().prepareResourceGroup(
491 req->groupName);
492 break;
493 case RT_LOAD_GROUP:
494 #if OGRE_THREAD_SUPPORT == 2
495 ResourceGroupManager::getSingleton().prepareResourceGroup(
496 req->groupName);
497 #else
498 ResourceGroupManager::getSingleton().loadResourceGroup(
499 req->groupName);
500 #endif
501 break;
502 case RT_UNLOAD_GROUP:
503 ResourceGroupManager::getSingleton().unloadResourceGroup(
504 req->groupName);
505 break;
506 case RT_PREPARE_RESOURCE:
507 rm = ResourceGroupManager::getSingleton()._getResourceManager(
508 req->resourceType);
509 rm->prepare(req->resourceName, req->groupName, req->isManual,
510 req->loader, req->loadParams);
511 break;
512 case RT_LOAD_RESOURCE:
513 rm = ResourceGroupManager::getSingleton()._getResourceManager(
514 req->resourceType);
515 #if OGRE_THREAD_SUPPORT == 2
516 rm->prepare(req->resourceName, req->groupName, req->isManual,
517 req->loader, req->loadParams);
518 #else
519 rm->load(req->resourceName, req->groupName, req->isManual,
520 req->loader, req->loadParams);
521 #endif
522 break;
523 case RT_UNLOAD_RESOURCE:
524 rm = ResourceGroupManager::getSingleton()._getResourceManager(
525 req->resourceType);
526 if (req->resourceName.empty())
527 rm->unload(req->resourceHandle);
528 else
529 rm->unload(req->resourceName);
530 break;
531 case RT_SHUTDOWN:
532 // That's all folks
533 #if OGRE_THREAD_SUPPORT
534 mShuttingDown = true;
535 #if OGRE_THREAD_SUPPORT == 1
536 Root::getSingleton().getRenderSystem()->unregisterThread();
537 #endif
538 #endif
539 break;
542 catch (Exception& e)
544 req->result.error = true;
545 req->result.message = e.getFullDescription();
548 // Queue notification (don't do shutdown since not needed & listeners
549 // might be being destroyed too
550 if (req->listener && req->type != RT_SHUTDOWN)
552 // Fire in-thread notification first
553 req->listener->operationCompletedInThread(req->ticketID, req->result);
554 // Then queue main thread notification
555 queueFireBackgroundOperationComplete(req);
560 // re-lock to consume completed request
561 OGRE_LOCK_AUTO_MUTEX
563 // Consume the ticket
564 mRequestTicketMap.erase(req->ticketID);
565 mRequestQueue.pop_front();
568 return true;
571 //-----------------------------------------------------------------------
572 void ResourceBackgroundQueue::_queueFireBackgroundLoadingComplete(Resource* res)
574 OGRE_LOCK_MUTEX(mNotificationQueueMutex);
575 mNotificationQueue.push_back(QueuedNotification(res,true));
578 //-----------------------------------------------------------------------
579 void ResourceBackgroundQueue::_queueFireBackgroundPreparingComplete(Resource* res)
581 OGRE_LOCK_MUTEX(mNotificationQueueMutex);
582 mNotificationQueue.push_back(QueuedNotification(res,false));
585 //-----------------------------------------------------------------------
586 void ResourceBackgroundQueue::queueFireBackgroundOperationComplete(
587 ResourceBackgroundQueue::Request* req)
589 OGRE_LOCK_MUTEX(mNotificationQueueMutex);
590 mNotificationQueue.push_back(QueuedNotification(*req));
592 //------------------------------------------------------------------------
593 void ResourceBackgroundQueue::_fireOnFrameCallbacks()
595 OGRE_LOCK_MUTEX(mNotificationQueueMutex);
596 for (NotificationQueue::iterator i = mNotificationQueue.begin();
597 i != mNotificationQueue.end(); ++i)
599 if (i->resource) {
600 if (i->load) {
601 i->resource->_fireLoadingComplete();
602 } else {
603 i->resource->_firePreparingComplete();
605 } else {
606 const ResourceBackgroundQueue::Request &r = i->req;
607 #if OGRE_THREAD_SUPPORT == 2
608 if (r.type==RT_LOAD_RESOURCE) {
609 ResourceManager *rm = ResourceGroupManager::getSingleton()
610 ._getResourceManager(r.resourceType);
611 rm->load(r.resourceName, r.groupName, r.isManual, r.loader, r.loadParams);
612 } else if (r.type==RT_LOAD_GROUP) {
613 ResourceGroupManager::getSingleton().loadResourceGroup(r.groupName);
615 #endif
616 r.listener->operationCompleted(r.ticketID, r.result);
619 mNotificationQueue.clear();
622 //------------------------------------------------------------------------