diff options
Diffstat (limited to '')
-rw-r--r-- | linden/indra/llcommon/llqueuedthread.cpp | 510 |
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 | ||
34 | LLQueuedThread::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 | ||
48 | LLQueuedThread::~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 | ||
87 | void LLQueuedThread::update(U32 ms_elapsed) | ||
88 | { | ||
89 | updateQueue(0); | ||
90 | } | ||
91 | |||
92 | void 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 | ||
130 | S32 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 | ||
140 | void 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 | ||
159 | void 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 | ||
175 | LLQueuedThread::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 | ||
187 | bool 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 | ||
209 | bool 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 | ||
250 | LLQueuedThread::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 | |||
262 | LLQueuedThread::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 | |||
275 | LLQueuedThread::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 | ||
298 | LLQueuedThread::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 | |||
311 | void 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 | |||
324 | bool 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 | |||
344 | bool 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 | |||
367 | int 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 | |||
445 | bool LLQueuedThread::runCondition() | ||
446 | { | ||
447 | // mRunCondition must be locked here | ||
448 | return (mRequestQueue.empty() && mIdleThread) ? FALSE : TRUE; | ||
449 | } | ||
450 | |||
451 | void 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 | |||
484 | LLQueuedThread::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 | |||
492 | LLQueuedThread::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 | ||
501 | void LLQueuedThread::QueuedRequest::finishRequest() | ||
502 | { | ||
503 | } | ||
504 | |||
505 | //virtual | ||
506 | void LLQueuedThread::QueuedRequest::deleteRequest() | ||
507 | { | ||
508 | setStatus(STATUS_DELETE); | ||
509 | delete this; | ||
510 | } | ||