PageRenderTime 49ms CodeModel.GetById 15ms app.highlight 29ms RepoModel.GetById 1ms app.codeStats 0ms

/indra/llcommon/llworkerthread.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 410 lines | 310 code | 43 blank | 57 comment | 49 complexity | 879e0baf83bf1954d8190bb768e9bc9c MD5 | raw file
  1/** 
  2 * @file llworkerthread.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 "llworkerthread.h"
 28#include "llstl.h"
 29
 30#if USE_FRAME_CALLBACK_MANAGER
 31#include "llframecallbackmanager.h"
 32#endif
 33
 34//============================================================================
 35// Run on MAIN thread
 36
 37LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded, bool should_pause) :
 38	LLQueuedThread(name, threaded, should_pause)
 39{
 40	mDeleteMutex = new LLMutex(NULL);
 41
 42	if(!mLocalAPRFilePoolp)
 43	{
 44		mLocalAPRFilePoolp = new LLVolatileAPRPool() ;
 45	}
 46}
 47
 48LLWorkerThread::~LLWorkerThread()
 49{
 50	// Delete any workers in the delete queue (should be safe - had better be!)
 51	if (!mDeleteList.empty())
 52	{
 53		llwarns << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
 54				<< " entries in delete list." << llendl;
 55	}
 56
 57	delete mDeleteMutex;
 58	
 59	// ~LLQueuedThread() will be called here
 60}
 61
 62//called only in destructor.
 63void LLWorkerThread::clearDeleteList()
 64{
 65	// Delete any workers in the delete queue (should be safe - had better be!)
 66	if (!mDeleteList.empty())
 67	{
 68		llwarns << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size()
 69				<< " entries in delete list." << llendl;
 70
 71		mDeleteMutex->lock();
 72		for (delete_list_t::iterator iter = mDeleteList.begin(); iter != mDeleteList.end(); ++iter)
 73		{
 74			(*iter)->mRequestHandle = LLWorkerThread::nullHandle();
 75			(*iter)->clearFlags(LLWorkerClass::WCF_HAVE_WORK);
 76			delete *iter ;
 77		}
 78		mDeleteList.clear() ;
 79		mDeleteMutex->unlock() ;
 80	}
 81}
 82
 83// virtual
 84S32 LLWorkerThread::update(F32 max_time_ms)
 85{
 86	S32 res = LLQueuedThread::update(max_time_ms);
 87	// Delete scheduled workers
 88	std::vector<LLWorkerClass*> delete_list;
 89	std::vector<LLWorkerClass*> abort_list;
 90	mDeleteMutex->lock();
 91	for (delete_list_t::iterator iter = mDeleteList.begin();
 92		 iter != mDeleteList.end(); )
 93	{
 94		delete_list_t::iterator curiter = iter++;
 95		LLWorkerClass* worker = *curiter;
 96		if (worker->deleteOK())
 97		{
 98			if (worker->getFlags(LLWorkerClass::WCF_WORK_FINISHED))
 99			{
100				delete_list.push_back(worker);
101				mDeleteList.erase(curiter);
102			}
103			else if (!worker->getFlags(LLWorkerClass::WCF_ABORT_REQUESTED))
104			{
105				abort_list.push_back(worker);
106			}
107		}
108	}
109	mDeleteMutex->unlock();	
110	// abort and delete after releasing mutex
111	for (std::vector<LLWorkerClass*>::iterator iter = abort_list.begin();
112		 iter != abort_list.end(); ++iter)
113	{
114		(*iter)->abortWork(false);
115	}
116	for (std::vector<LLWorkerClass*>::iterator iter = delete_list.begin();
117		 iter != delete_list.end(); ++iter)
118	{
119		LLWorkerClass* worker = *iter;
120		if (worker->mRequestHandle)
121		{
122			// Finished but not completed
123			completeRequest(worker->mRequestHandle);
124			worker->mRequestHandle = LLWorkerThread::nullHandle();
125			worker->clearFlags(LLWorkerClass::WCF_HAVE_WORK);
126		}
127		delete *iter;
128	}
129	// delete and aborted entries mean there's still work to do
130	res += delete_list.size() + abort_list.size();
131	return res;
132}
133
134//----------------------------------------------------------------------------
135
136LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority)
137{
138	handle_t handle = generateHandle();
139	
140	WorkRequest* req = new WorkRequest(handle, priority, workerclass, param);
141
142	bool res = addRequest(req);
143	if (!res)
144	{
145		llerrs << "add called after LLWorkerThread::cleanupClass()" << llendl;
146		req->deleteRequest();
147		handle = nullHandle();
148	}
149	
150	return handle;
151}
152
153void LLWorkerThread::deleteWorker(LLWorkerClass* workerclass)
154{
155	mDeleteMutex->lock();
156	mDeleteList.push_back(workerclass);
157	mDeleteMutex->unlock();
158}
159
160//============================================================================
161// Runs on its OWN thread
162
163LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) :
164	LLQueuedThread::QueuedRequest(handle, priority),
165	mWorkerClass(workerclass),
166	mParam(param)
167{
168}
169
170LLWorkerThread::WorkRequest::~WorkRequest()
171{
172}
173
174// virtual (required for access by LLWorkerThread)
175void LLWorkerThread::WorkRequest::deleteRequest()
176{
177	LLQueuedThread::QueuedRequest::deleteRequest();
178}	
179
180// virtual
181bool LLWorkerThread::WorkRequest::processRequest()
182{
183	LLWorkerClass* workerclass = getWorkerClass();
184	workerclass->setWorking(true);
185	bool complete = workerclass->doWork(getParam());
186	workerclass->setWorking(false);
187	return complete;
188}
189
190// virtual
191void LLWorkerThread::WorkRequest::finishRequest(bool completed)
192{
193	LLWorkerClass* workerclass = getWorkerClass();
194	workerclass->finishWork(getParam(), completed);
195	U32 flags = LLWorkerClass::WCF_WORK_FINISHED | (completed ? 0 : LLWorkerClass::WCF_WORK_ABORTED);
196	workerclass->setFlags(flags);
197}
198
199//============================================================================
200// LLWorkerClass:: operates in main thread
201
202LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& name)
203	: mWorkerThread(workerthread),
204	  mWorkerClassName(name),
205	  mRequestHandle(LLWorkerThread::nullHandle()),
206	  mRequestPriority(LLWorkerThread::PRIORITY_NORMAL),
207	  mMutex(NULL),
208	  mWorkFlags(0)
209{
210	if (!mWorkerThread)
211	{
212		llerrs << "LLWorkerClass() called with NULL workerthread: " << name << llendl;
213	}
214}
215
216LLWorkerClass::~LLWorkerClass()
217{
218	llassert_always(!(mWorkFlags & WCF_WORKING));
219	llassert_always(mWorkFlags & WCF_DELETE_REQUESTED);
220	llassert_always(!mMutex.isLocked());
221	if (mRequestHandle != LLWorkerThread::nullHandle())
222	{
223		LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle);
224		if (!workreq)
225		{
226			llerrs << "LLWorkerClass destroyed with stale work handle" << llendl;
227		}
228		if (workreq->getStatus() != LLWorkerThread::STATUS_ABORTED &&
229			workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE)
230		{
231			llerrs << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << llendl;
232		}
233	}
234}
235
236void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread)
237{
238	mMutex.lock();
239	if (mRequestHandle != LLWorkerThread::nullHandle())
240	{
241		llerrs << "LLWorkerClass attempt to change WorkerThread with active worker!" << llendl;
242	}
243	mWorkerThread = workerthread;
244	mMutex.unlock();
245}
246
247//----------------------------------------------------------------------------
248
249//virtual
250void LLWorkerClass::finishWork(S32 param, bool success)
251{
252}
253
254//virtual
255bool LLWorkerClass::deleteOK()
256{
257	return true; // default always OK
258}
259
260//----------------------------------------------------------------------------
261
262// Called from worker thread
263void LLWorkerClass::setWorking(bool working)
264{
265	mMutex.lock();
266	if (working)
267	{
268		llassert_always(!(mWorkFlags & WCF_WORKING));
269		setFlags(WCF_WORKING);
270	}
271	else
272	{
273		llassert_always((mWorkFlags & WCF_WORKING));
274		clearFlags(WCF_WORKING);
275	}
276	mMutex.unlock();
277}
278
279//----------------------------------------------------------------------------
280
281bool LLWorkerClass::yield()
282{
283	LLThread::yield();
284	mWorkerThread->checkPause();
285	bool res;
286	mMutex.lock();
287	res = (getFlags() & WCF_ABORT_REQUESTED) ? true : false;
288	mMutex.unlock();
289	return res;
290}
291
292//----------------------------------------------------------------------------
293
294// calls startWork, adds doWork() to queue
295void LLWorkerClass::addWork(S32 param, U32 priority)
296{
297	mMutex.lock();
298	llassert_always(!(mWorkFlags & (WCF_WORKING|WCF_HAVE_WORK)));
299	if (mRequestHandle != LLWorkerThread::nullHandle())
300	{
301		llerrs << "LLWorkerClass attempt to add work with active worker!" << llendl;
302	}
303#if _DEBUG
304// 	llinfos << "addWork: " << mWorkerClassName << " Param: " << param << llendl;
305#endif
306	startWork(param);
307	clearFlags(WCF_WORK_FINISHED|WCF_WORK_ABORTED);
308	setFlags(WCF_HAVE_WORK);
309	mRequestHandle = mWorkerThread->addWorkRequest(this, param, priority);
310	mMutex.unlock();
311}
312
313void LLWorkerClass::abortWork(bool autocomplete)
314{
315	mMutex.lock();
316#if _DEBUG
317// 	LLWorkerThread::WorkRequest* workreq = mWorkerThread->getRequest(mRequestHandle);
318// 	if (workreq)
319// 		llinfos << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl;
320#endif
321	if (mRequestHandle != LLWorkerThread::nullHandle())
322	{
323		mWorkerThread->abortRequest(mRequestHandle, autocomplete);
324		mWorkerThread->setPriority(mRequestHandle, LLQueuedThread::PRIORITY_IMMEDIATE);
325		setFlags(WCF_ABORT_REQUESTED);
326	}
327	mMutex.unlock();
328}
329
330// if doWork is complete or aborted, call endWork() and return true
331bool LLWorkerClass::checkWork(bool aborting)
332{
333	LLMutexLock lock(&mMutex);
334	bool complete = false, abort = false;
335	if (mRequestHandle != LLWorkerThread::nullHandle())
336	{
337		LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle);
338		if(!workreq)
339		{
340			if(mWorkerThread->isQuitting() || mWorkerThread->isStopped()) //the mWorkerThread is not running
341			{
342				mRequestHandle = LLWorkerThread::nullHandle();
343				clearFlags(WCF_HAVE_WORK);
344			}
345			else
346			{
347				llassert_always(workreq);
348			}
349			return true ;
350		}
351
352		LLQueuedThread::status_t status = workreq->getStatus();
353		if (status == LLWorkerThread::STATUS_ABORTED)
354		{
355			complete = true;
356			abort = true;
357		}
358		else if (status == LLWorkerThread::STATUS_COMPLETE)
359		{
360			complete = true;
361		}
362		else
363		{
364			llassert_always(!aborting || (workreq->getFlags() & LLQueuedThread::FLAG_ABORT));
365		}
366		if (complete)
367		{
368			llassert_always(!(getFlags(WCF_WORKING)));
369			endWork(workreq->getParam(), abort);
370			mWorkerThread->completeRequest(mRequestHandle);
371			mRequestHandle = LLWorkerThread::nullHandle();
372			clearFlags(WCF_HAVE_WORK);
373		}
374	}
375	else
376	{
377		complete = true;
378	}
379	return complete;
380}
381
382void LLWorkerClass::scheduleDelete()
383{
384	bool do_delete = false;
385	mMutex.lock();
386	if (!(getFlags(WCF_DELETE_REQUESTED)))
387	{
388		setFlags(WCF_DELETE_REQUESTED);
389		do_delete = true;
390	}
391	mMutex.unlock();
392	if (do_delete)
393	{
394		mWorkerThread->deleteWorker(this);
395	}
396}
397
398void LLWorkerClass::setPriority(U32 priority)
399{
400	mMutex.lock();
401	if (mRequestHandle != LLWorkerThread::nullHandle() && mRequestPriority != priority)
402	{
403		mRequestPriority = priority;
404		mWorkerThread->setPriority(mRequestHandle, priority);
405	}
406	mMutex.unlock();
407}
408
409//============================================================================
410