PageRenderTime 92ms CodeModel.GetById 28ms app.highlight 46ms RepoModel.GetById 13ms app.codeStats 0ms

/indra/llcommon/llqueuedthread.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 562 lines | 440 code | 53 blank | 69 comment | 78 complexity | afa7c3597a8c05fb5290e5e1e457a862 MD5 | raw file
  1/** 
  2 * @file llqueuedthread.cpp
  3 *
  4 * $LicenseInfo:firstyear=2004&license=viewerlgpl$
  5 * Second Life Viewer Source Code
  6 * Copyright (C) 2010, Linden Research, Inc.
  7 * 
  8 * This library is free software; you can redistribute it and/or
  9 * modify it under the terms of the GNU Lesser General Public
 10 * License as published by the Free Software Foundation;
 11 * version 2.1 of the License only.
 12 * 
 13 * This library is distributed in the hope that it will be useful,
 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 16 * Lesser General Public License for more details.
 17 * 
 18 * You should have received a copy of the GNU Lesser General Public
 19 * License along with this library; if not, write to the Free Software
 20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 21 * 
 22 * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 23 * $/LicenseInfo$
 24 */
 25
 26#include "linden_common.h"
 27#include "llqueuedthread.h"
 28
 29#include "llstl.h"
 30#include "lltimer.h"	// ms_sleep()
 31
 32//============================================================================
 33
 34// MAIN THREAD
 35LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool should_pause) :
 36	LLThread(name),
 37	mThreaded(threaded),
 38	mIdleThread(TRUE),
 39	mNextHandle(0),
 40	mStarted(FALSE)
 41{
 42	if (mThreaded)
 43	{
 44		if(should_pause)
 45		{
 46			pause() ; //call this before start the thread.
 47		}
 48
 49		start();
 50	}
 51}
 52
 53// MAIN THREAD
 54LLQueuedThread::~LLQueuedThread()
 55{
 56	if (!mThreaded)
 57	{
 58		endThread();
 59	}
 60	shutdown();
 61	// ~LLThread() will be called here
 62}
 63
 64void LLQueuedThread::shutdown()
 65{
 66	setQuitting();
 67
 68	unpause(); // MAIN THREAD
 69	if (mThreaded)
 70	{
 71		S32 timeout = 100;
 72		for ( ; timeout>0; timeout--)
 73		{
 74			if (isStopped())
 75			{
 76				break;
 77			}
 78			ms_sleep(100);
 79			LLThread::yield();
 80		}
 81		if (timeout == 0)
 82		{
 83			llwarns << "~LLQueuedThread (" << mName << ") timed out!" << llendl;
 84		}
 85	}
 86	else
 87	{
 88		mStatus = STOPPED;
 89	}
 90
 91	QueuedRequest* req;
 92	S32 active_count = 0;
 93	while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
 94	{
 95		if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS)
 96		{
 97			++active_count;
 98			req->setStatus(STATUS_ABORTED); // avoid assert in deleteRequest
 99		}
100		req->deleteRequest();
101	}
102	if (active_count)
103	{
104		llwarns << "~LLQueuedThread() called with active requests: " << active_count << llendl;
105	}
106}
107
108//----------------------------------------------------------------------------
109
110// MAIN THREAD
111// virtual
112S32 LLQueuedThread::update(F32 max_time_ms)
113{
114	if (!mStarted)
115	{
116		if (!mThreaded)
117		{
118			startThread();
119			mStarted = TRUE;
120		}
121	}
122	return updateQueue(max_time_ms);
123}
124
125S32 LLQueuedThread::updateQueue(F32 max_time_ms)
126{
127	F64 max_time = (F64)max_time_ms * .001;
128	LLTimer timer;
129	S32 pending = 1;
130
131	// Frame Update
132	if (mThreaded)
133	{
134		pending = getPending();
135		if(pending > 0)
136		{
137		unpause();
138	}
139	}
140	else
141	{
142		while (pending > 0)
143		{
144			pending = processNextRequest();
145			if (max_time && timer.getElapsedTimeF64() > max_time)
146				break;
147		}
148	}
149	return pending;
150}
151
152void LLQueuedThread::incQueue()
153{
154	// Something has been added to the queue
155	if (!isPaused())
156	{
157		if (mThreaded)
158		{
159			wake(); // Wake the thread up if necessary.
160		}
161	}
162}
163
164//virtual
165// May be called from any thread
166S32 LLQueuedThread::getPending()
167{
168	S32 res;
169	lockData();
170	res = mRequestQueue.size();
171	unlockData();
172	return res;
173}
174
175// MAIN thread
176void LLQueuedThread::waitOnPending()
177{
178	while(1)
179	{
180		update(0);
181
182		if (mIdleThread)
183		{
184			break;
185		}
186		if (mThreaded)
187		{
188			yield();
189		}
190	}
191	return;
192}
193
194// MAIN thread
195void LLQueuedThread::printQueueStats()
196{
197	lockData();
198	if (!mRequestQueue.empty())
199	{
200		QueuedRequest *req = *mRequestQueue.begin();
201		llinfos << llformat("Pending Requests:%d Current status:%d", mRequestQueue.size(), req->getStatus()) << llendl;
202	}
203	else
204	{
205		llinfos << "Queued Thread Idle" << llendl;
206	}
207	unlockData();
208}
209
210// MAIN thread
211LLQueuedThread::handle_t LLQueuedThread::generateHandle()
212{
213	lockData();
214	while ((mNextHandle == nullHandle()) || (mRequestHash.find(mNextHandle)))
215	{
216		mNextHandle++;
217	}
218	const LLQueuedThread::handle_t res = mNextHandle++;
219	unlockData();
220	return res;
221}
222
223// MAIN thread
224bool LLQueuedThread::addRequest(QueuedRequest* req)
225{
226	if (mStatus == QUITTING)
227	{
228		return false;
229	}
230	
231	lockData();
232	req->setStatus(STATUS_QUEUED);
233	mRequestQueue.insert(req);
234	mRequestHash.insert(req);
235#if _DEBUG
236// 	llinfos << llformat("LLQueuedThread::Added req [%08d]",handle) << llendl;
237#endif
238	unlockData();
239
240	incQueue();
241
242	return true;
243}
244
245// MAIN thread
246bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
247{
248	llassert (handle != nullHandle())
249	bool res = false;
250	bool waspaused = isPaused();
251	bool done = false;
252	while(!done)
253	{
254		update(0); // unpauses
255		lockData();
256		QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
257		if (!req)
258		{
259			done = true; // request does not exist
260		}
261		else if (req->getStatus() == STATUS_COMPLETE)
262		{
263			res = true;
264			if (auto_complete)
265			{
266				mRequestHash.erase(handle);
267				req->deleteRequest();
268// 				check();
269			}
270			done = true;
271		}
272		unlockData();
273		
274		if (!done && mThreaded)
275		{
276			yield();
277		}
278	}
279	if (waspaused)
280	{
281		pause();
282	}
283	return res;
284}
285
286// MAIN thread
287LLQueuedThread::QueuedRequest* LLQueuedThread::getRequest(handle_t handle)
288{
289	if (handle == nullHandle())
290	{
291		return 0;
292	}
293	lockData();
294	QueuedRequest* res = (QueuedRequest*)mRequestHash.find(handle);
295	unlockData();
296	return res;
297}
298
299LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
300{
301	status_t res = STATUS_EXPIRED;
302	lockData();
303	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
304	if (req)
305	{
306		res = req->getStatus();
307	}
308	unlockData();
309	return res;
310}
311
312void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete)
313{
314	lockData();
315	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
316	if (req)
317	{
318		req->setFlags(FLAG_ABORT | (autocomplete ? FLAG_AUTO_COMPLETE : 0));
319	}
320	unlockData();
321}
322
323// MAIN thread
324void LLQueuedThread::setFlags(handle_t handle, U32 flags)
325{
326	lockData();
327	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
328	if (req)
329	{
330		req->setFlags(flags);
331	}
332	unlockData();
333}
334
335void LLQueuedThread::setPriority(handle_t handle, U32 priority)
336{
337	lockData();
338	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
339	if (req)
340	{
341		if(req->getStatus() == STATUS_INPROGRESS)
342		{
343			// not in list
344			req->setPriority(priority);
345		}
346		else if(req->getStatus() == STATUS_QUEUED)
347		{
348			// remove from list then re-insert
349			llverify(mRequestQueue.erase(req) == 1);
350			req->setPriority(priority);
351			mRequestQueue.insert(req);
352		}
353	}
354	unlockData();
355}
356
357bool LLQueuedThread::completeRequest(handle_t handle)
358{
359	bool res = false;
360	lockData();
361	QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
362	if (req)
363	{
364		llassert_always(req->getStatus() != STATUS_QUEUED);
365		llassert_always(req->getStatus() != STATUS_INPROGRESS);
366#if _DEBUG
367// 		llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl;
368#endif
369		mRequestHash.erase(handle);
370		req->deleteRequest();
371// 		check();
372		res = true;
373	}
374	unlockData();
375	return res;
376}
377
378bool LLQueuedThread::check()
379{
380#if 0 // not a reliable check once mNextHandle wraps, just for quick and dirty debugging
381	for (int i=0; i<REQUEST_HASH_SIZE; i++)
382	{
383		LLSimpleHashEntry<handle_t>* entry = mRequestHash.get_element_at_index(i);
384		while (entry)
385		{
386			if (entry->getHashKey() > mNextHandle)
387			{
388				llerrs << "Hash Error" << llendl;
389				return false;
390			}
391			entry = entry->getNextEntry();
392		}
393	}
394#endif
395	return true;
396}		
397	
398//============================================================================
399// Runs on its OWN thread
400
401S32 LLQueuedThread::processNextRequest()
402{
403	QueuedRequest *req;
404	// Get next request from pool
405	lockData();
406	while(1)
407	{
408		req = NULL;
409		if (mRequestQueue.empty())
410		{
411			break;
412		}
413		req = *mRequestQueue.begin();
414		mRequestQueue.erase(mRequestQueue.begin());
415		if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING))
416		{
417			req->setStatus(STATUS_ABORTED);
418			req->finishRequest(false);
419			if (req->getFlags() & FLAG_AUTO_COMPLETE)
420			{
421				mRequestHash.erase(req);
422				req->deleteRequest();
423// 				check();
424			}
425			continue;
426		}
427		llassert_always(req->getStatus() == STATUS_QUEUED);
428		break;
429	}
430	U32 start_priority = 0 ;
431	if (req)
432	{
433		req->setStatus(STATUS_INPROGRESS);
434		start_priority = req->getPriority();
435	}
436	unlockData();
437
438	// This is the only place we will call req->setStatus() after
439	// it has initially been seet to STATUS_QUEUED, so it is
440	// safe to access req.
441	if (req)
442	{
443		// process request		
444		bool complete = req->processRequest();
445
446		if (complete)
447		{
448			lockData();
449			req->setStatus(STATUS_COMPLETE);
450			req->finishRequest(true);
451			if (req->getFlags() & FLAG_AUTO_COMPLETE)
452			{
453				mRequestHash.erase(req);
454				req->deleteRequest();
455// 				check();
456			}
457			unlockData();
458		}
459		else
460		{
461			lockData();
462			req->setStatus(STATUS_QUEUED);
463			mRequestQueue.insert(req);
464			unlockData();
465			if (mThreaded && start_priority < PRIORITY_NORMAL)
466			{
467				ms_sleep(1); // sleep the thread a little
468			}
469		}
470	}
471
472	S32 pending = getPending();
473
474	return pending;
475}
476
477// virtual
478bool LLQueuedThread::runCondition()
479{
480	// mRunCondition must be locked here
481	if (mRequestQueue.empty() && mIdleThread)
482		return false;
483	else
484		return true;
485}
486
487// virtual
488void LLQueuedThread::run()
489{
490	// call checPause() immediately so we don't try to do anything before the class is fully constructed
491	checkPause();
492	startThread();
493	mStarted = TRUE;
494	
495	while (1)
496	{
497		// this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
498		checkPause();
499		
500		if (isQuitting())
501		{
502			endThread();
503			break;
504		}
505
506		mIdleThread = FALSE;
507
508		threadedUpdate();
509		
510		int res = processNextRequest();
511		if (res == 0)
512		{
513			mIdleThread = TRUE;
514			ms_sleep(1);
515		}
516		//LLThread::yield(); // thread should yield after each request		
517	}
518	llinfos << "LLQueuedThread " << mName << " EXITING." << llendl;
519}
520
521// virtual
522void LLQueuedThread::startThread()
523{
524}
525
526// virtual
527void LLQueuedThread::endThread()
528{
529}
530
531// virtual
532void LLQueuedThread::threadedUpdate()
533{
534}
535
536//============================================================================
537
538LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 priority, U32 flags) :
539	LLSimpleHashEntry<LLQueuedThread::handle_t>(handle),
540	mStatus(STATUS_UNKNOWN),
541	mPriority(priority),
542	mFlags(flags)
543{
544}
545
546LLQueuedThread::QueuedRequest::~QueuedRequest()
547{
548	llassert_always(mStatus == STATUS_DELETE);
549}
550
551//virtual
552void LLQueuedThread::QueuedRequest::finishRequest(bool completed)
553{
554}
555
556//virtual
557void LLQueuedThread::QueuedRequest::deleteRequest()
558{
559	llassert_always(mStatus != STATUS_INPROGRESS);
560	setStatus(STATUS_DELETE);
561	delete this;
562}