aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llcommon/llqueuedthread.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--linden/indra/llcommon/llqueuedthread.cpp510
1 files changed, 510 insertions, 0 deletions
diff --git a/linden/indra/llcommon/llqueuedthread.cpp b/linden/indra/llcommon/llqueuedthread.cpp
new file mode 100644
index 0000000..565836b
--- /dev/null
+++ b/linden/indra/llcommon/llqueuedthread.cpp
@@ -0,0 +1,510 @@
1/**
2 * @file llqueuedthread.cpp
3 *
4 * Copyright (c) 2004-2007, Linden Research, Inc.
5 *
6 * The source code in this file ("Source Code") is provided by Linden Lab
7 * to you under the terms of the GNU General Public License, version 2.0
8 * ("GPL"), unless you have obtained a separate licensing agreement
9 * ("Other License"), formally executed by you and Linden Lab. Terms of
10 * the GPL can be found in doc/GPL-license.txt in this distribution, or
11 * online at http://secondlife.com/developers/opensource/gplv2
12 *
13 * There are special exceptions to the terms and conditions of the GPL as
14 * it is applied to this Source Code. View the full text of the exception
15 * in the file doc/FLOSS-exception.txt in this software distribution, or
16 * online at http://secondlife.com/developers/opensource/flossexception
17 *
18 * By copying, modifying or distributing this software, you acknowledge
19 * that you have read and understood your obligations described above,
20 * and agree to abide by those obligations.
21 *
22 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
23 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
24 * COMPLETENESS OR PERFORMANCE.
25 */
26
27#include "linden_common.h"
28#include "llqueuedthread.h"
29#include "llstl.h"
30
31//============================================================================
32
33// MAIN THREAD
34LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool runalways) :
35 LLThread(name),
36 mThreaded(threaded),
37 mRunAlways(runalways),
38 mIdleThread(TRUE),
39 mNextHandle(0)
40{
41 if (mThreaded)
42 {
43 start();
44 }
45}
46
47// MAIN THREAD
48LLQueuedThread::~LLQueuedThread()
49{
50 setQuitting();
51
52 unpause(); // MAIN THREAD
53 if (mThreaded)
54 {
55 S32 timeout = 100;
56 for ( ; timeout>0; timeout--)
57 {
58 if (isStopped())
59 {
60 break;
61 }
62 ms_sleep(100);
63 LLThread::yield();
64 }
65 if (timeout == 0)
66 {
67 llwarns << "~LLQueuedThread (" << mName << ") timed out!" << llendl;
68 }
69 }
70 else
71 {
72 mStatus = STOPPED;
73 }
74
75 QueuedRequest* req;
76 while ( (req = (QueuedRequest*)mRequestHash.pop_element()) )
77 {
78 req->deleteRequest();
79 }
80
81 // ~LLThread() will be called here
82}
83
84//----------------------------------------------------------------------------
85
86// MAIN THREAD
87void LLQueuedThread::update(U32 ms_elapsed)
88{
89 updateQueue(0);
90}
91
92void LLQueuedThread::updateQueue(S32 inc)
93{
94 // If mRunAlways == TRUE, unpause the thread whenever we put something into the queue.
95 // If mRunAlways == FALSE, we only unpause the thread when updateQueue() is called from the main loop (i.e. between rendered frames)
96
97 if (inc == 0) // Frame Update
98 {
99 if (mThreaded)
100 {
101 unpause();
102 wake(); // Wake the thread up if necessary.
103 }
104 else
105 {
106 while (processNextRequest() > 0)
107 ;
108 }
109 }
110 else
111 {
112 // Something has been added to the queue
113 if (mRunAlways)
114 {
115 if (mThreaded)
116 {
117 wake(); // Wake the thread up if necessary.
118 }
119 else
120 {
121 while(processNextRequest() > 0)
122 ;
123 }
124 }
125 }
126}
127
128//virtual
129// May be called from any thread
130S32 LLQueuedThread::getPending(bool child_thread)
131{
132 S32 res;
133 lockData();
134 res = mRequestQueue.size();
135 unlockData();
136 return res;
137}
138
139// MAIN thread
140void LLQueuedThread::waitOnPending()
141{
142 while(1)
143 {
144 updateQueue(0);
145
146 if (mIdleThread)
147 {
148 break;
149 }
150 if (mThreaded)
151 {
152 yield();
153 }
154 }
155 return;
156}
157
158// MAIN thread
159void LLQueuedThread::printQueueStats()
160{
161 lockData();
162 if (!mRequestQueue.empty())
163 {
164 QueuedRequest *req = *mRequestQueue.begin();
165 llinfos << llformat("Pending Requests:%d Current status:%d", mRequestQueue.size(), req->getStatus()) << llendl;
166 }
167 else
168 {
169 llinfos << "Queued Thread Idle" << llendl;
170 }
171 unlockData();
172}
173
174// MAIN thread
175LLQueuedThread::handle_t LLQueuedThread::generateHandle()
176{
177 lockData();
178 while ((mNextHandle == nullHandle()) || (mRequestHash.find(mNextHandle)))
179 {
180 mNextHandle++;
181 }
182 unlockData();
183 return mNextHandle++;
184}
185
186// MAIN thread
187bool LLQueuedThread::addRequest(QueuedRequest* req)
188{
189 if (mStatus == QUITTING)
190 {
191 return false;
192 }
193
194 lockData();
195 req->setStatus(STATUS_QUEUED);
196 mRequestQueue.insert(req);
197 mRequestHash.insert(req);
198#if _DEBUG
199// llinfos << llformat("LLQueuedThread::Added req [%08d]",handle) << llendl;
200#endif
201 unlockData();
202
203 updateQueue(1);
204
205 return true;
206}
207
208// MAIN thread
209bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_complete)
210{
211 llassert (handle != nullHandle())
212 bool res = false;
213 bool waspaused = isPaused();
214 bool done = false;
215 while(!done)
216 {
217 updateQueue(0); // unpauses
218 lockData();
219 QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
220 if (!req)
221 {
222 done = true; // request does not exist
223 }
224 else if (req->getStatus() == STATUS_COMPLETE)
225 {
226 res = true;
227 if (auto_complete)
228 {
229 mRequestHash.erase(handle);
230 req->deleteRequest();
231// check();
232 }
233 done = true;
234 }
235 unlockData();
236
237 if (!done && mThreaded)
238 {
239 yield();
240 }
241 }
242 if (waspaused)
243 {
244 pause();
245 }
246 return res;
247}
248
249// MAIN thread
250LLQueuedThread::QueuedRequest* LLQueuedThread::getRequest(handle_t handle)
251{
252 if (handle == nullHandle())
253 {
254 return 0;
255 }
256 lockData();
257 QueuedRequest* res = (QueuedRequest*)mRequestHash.find(handle);
258 unlockData();
259 return res;
260}
261
262LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle)
263{
264 status_t res = STATUS_EXPIRED;
265 lockData();
266 QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
267 if (req)
268 {
269 res = req->getStatus();
270 }
271 unlockData();
272 return res;
273}
274
275LLQueuedThread::status_t LLQueuedThread::abortRequest(handle_t handle, U32 flags)
276{
277 status_t res = STATUS_EXPIRED;
278 lockData();
279 QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
280 if (req)
281 {
282 res = req->abortRequest(flags);
283 if ((flags & AUTO_COMPLETE) && (res == STATUS_COMPLETE))
284 {
285 mRequestHash.erase(handle);
286 req->deleteRequest();
287// check();
288 }
289#if _DEBUG
290// llinfos << llformat("LLQueuedThread::Aborted req [%08d]",handle) << llendl;
291#endif
292 }
293 unlockData();
294 return res;
295}
296
297// MAIN thread
298LLQueuedThread::status_t LLQueuedThread::setFlags(handle_t handle, U32 flags)
299{
300 status_t res = STATUS_EXPIRED;
301 lockData();
302 QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
303 if (req)
304 {
305 res = req->setFlags(flags);
306 }
307 unlockData();
308 return res;
309}
310
311void LLQueuedThread::setPriority(handle_t handle, U32 priority)
312{
313 lockData();
314 QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
315 if (req && (req->getStatus() == STATUS_QUEUED))
316 {
317 llverify(mRequestQueue.erase(req) == 1);
318 req->setPriority(priority);
319 mRequestQueue.insert(req);
320 }
321 unlockData();
322}
323
324bool LLQueuedThread::completeRequest(handle_t handle)
325{
326 bool res = false;
327 lockData();
328 QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle);
329 if (req)
330 {
331 llassert(req->getStatus() != STATUS_QUEUED && req->getStatus() != STATUS_ABORT);
332#if _DEBUG
333// llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl;
334#endif
335 mRequestHash.erase(handle);
336 req->deleteRequest();
337// check();
338 res = true;
339 }
340 unlockData();
341 return res;
342}
343
344bool LLQueuedThread::check()
345{
346#if 0 // not a reliable check once mNextHandle wraps, just for quick and dirty debugging
347 for (int i=0; i<REQUEST_HASH_SIZE; i++)
348 {
349 LLSimpleHashEntry<handle_t>* entry = mRequestHash.get_element_at_index(i);
350 while (entry)
351 {
352 if (entry->getHashKey() > mNextHandle)
353 {
354 llerrs << "Hash Error" << llendl;
355 return false;
356 }
357 entry = entry->getNextEntry();
358 }
359 }
360#endif
361 return true;
362}
363
364//============================================================================
365// Runs on its OWN thread
366
367int LLQueuedThread::processNextRequest()
368{
369 QueuedRequest *req = 0;
370 // Get next request from pool
371 lockData();
372 while(1)
373 {
374 if (!mRequestQueue.empty())
375 {
376 req = *mRequestQueue.begin();
377 mRequestQueue.erase(mRequestQueue.begin());
378 }
379 if (req && req->getStatus() == STATUS_ABORT)
380 {
381 req->setStatus(STATUS_ABORTED);
382 req = 0;
383 }
384 else
385 {
386 llassert (!req || req->getStatus() == STATUS_QUEUED)
387 break;
388 }
389 }
390 if (req)
391 {
392 req->setStatus(STATUS_INPROGRESS);
393 }
394 unlockData();
395
396 // This is the only place we will cal req->setStatus() after
397 // it has initially been seet to STATUS_QUEUED, so it is
398 // safe to access req.
399 if (req)
400 {
401 // process request
402 bool complete = processRequest(req);
403
404 if (complete)
405 {
406 lockData();
407 req->setStatus(STATUS_COMPLETE);
408 req->finishRequest();
409 if (req->getFlags() & AUTO_COMPLETE)
410 {
411 llverify(mRequestHash.erase(req))
412 req->deleteRequest();
413// check();
414 }
415 unlockData();
416 }
417 else
418 {
419 lockData();
420 req->setStatus(STATUS_QUEUED);
421 mRequestQueue.insert(req);
422 unlockData();
423 }
424 }
425
426 int res;
427 if (getPending(true) == 0)
428 {
429 if (isQuitting())
430 {
431 res = -1; // exit thread
432 }
433 else
434 {
435 res = 0;
436 }
437 }
438 else
439 {
440 res = 1;
441 }
442 return res;
443}
444
445bool LLQueuedThread::runCondition()
446{
447 // mRunCondition must be locked here
448 return (mRequestQueue.empty() && mIdleThread) ? FALSE : TRUE;
449}
450
451void LLQueuedThread::run()
452{
453 llinfos << "QUEUED THREAD STARTING" << llendl;
454
455 while (1)
456 {
457 // this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state.
458 checkPause();
459
460 if(isQuitting())
461 break;
462
463 //llinfos << "QUEUED THREAD RUNNING, queue size = " << mRequestQueue.size() << llendl;
464
465 mIdleThread = FALSE;
466
467 int res = processNextRequest();
468 if (res == 0)
469 {
470 mIdleThread = TRUE;
471 }
472
473 if (res < 0) // finished working and want to exit
474 {
475 break;
476 }
477 }
478
479 llinfos << "QUEUED THREAD " << mName << " EXITING." << llendl;
480}
481
482//============================================================================
483
484LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U32 priority, U32 flags) :
485 LLSimpleHashEntry<LLQueuedThread::handle_t>(handle),
486 mStatus(STATUS_UNKNOWN),
487 mPriority(priority),
488 mFlags(flags)
489{
490}
491
492LLQueuedThread::QueuedRequest::~QueuedRequest()
493{
494 if (mStatus != STATUS_DELETE)
495 {
496 llerrs << "Attemt to directly delete a LLQueuedThread::QueuedRequest; use deleteRequest()" << llendl;
497 }
498}
499
500//virtual
501void LLQueuedThread::QueuedRequest::finishRequest()
502{
503}
504
505//virtual
506void LLQueuedThread::QueuedRequest::deleteRequest()
507{
508 setStatus(STATUS_DELETE);
509 delete this;
510}