diff options
Diffstat (limited to 'linden/indra/llcommon/llworkerthread.cpp')
-rw-r--r-- | linden/indra/llcommon/llworkerthread.cpp | 303 |
1 files changed, 303 insertions, 0 deletions
diff --git a/linden/indra/llcommon/llworkerthread.cpp b/linden/indra/llcommon/llworkerthread.cpp new file mode 100644 index 0000000..3190046 --- /dev/null +++ b/linden/indra/llcommon/llworkerthread.cpp | |||
@@ -0,0 +1,303 @@ | |||
1 | /** | ||
2 | * @file llworkerthread.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 "llworkerthread.h" | ||
29 | #include "llstl.h" | ||
30 | |||
31 | #if USE_FRAME_CALLBACK_MANAGER | ||
32 | #include "llframecallbackmanager.h" | ||
33 | #endif | ||
34 | |||
35 | //============================================================================ | ||
36 | |||
37 | /*static*/ LLWorkerThread* LLWorkerThread::sLocal = NULL; | ||
38 | /*static*/ std::set<LLWorkerThread*> LLWorkerThread::sThreadList; | ||
39 | |||
40 | //============================================================================ | ||
41 | // Run on MAIN thread | ||
42 | |||
43 | //static | ||
44 | void LLWorkerThread::initClass(bool local_is_threaded, bool local_run_always) | ||
45 | { | ||
46 | if (!sLocal) | ||
47 | { | ||
48 | sLocal = new LLWorkerThread(local_is_threaded, local_run_always); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | //static | ||
53 | void LLWorkerThread::cleanupClass() | ||
54 | { | ||
55 | if (sLocal) | ||
56 | { | ||
57 | while (sLocal->getPending()) | ||
58 | { | ||
59 | sLocal->update(0); | ||
60 | } | ||
61 | delete sLocal; | ||
62 | sLocal = NULL; | ||
63 | llassert(sThreadList.size() == 0); | ||
64 | } | ||
65 | } | ||
66 | |||
67 | //static | ||
68 | S32 LLWorkerThread::updateClass(U32 ms_elapsed) | ||
69 | { | ||
70 | for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++) | ||
71 | { | ||
72 | (*iter)->update(ms_elapsed); | ||
73 | } | ||
74 | return getAllPending(); | ||
75 | } | ||
76 | |||
77 | //static | ||
78 | S32 LLWorkerThread::getAllPending() | ||
79 | { | ||
80 | S32 res = 0; | ||
81 | for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++) | ||
82 | { | ||
83 | res += (*iter)->getPending(); | ||
84 | } | ||
85 | return res; | ||
86 | } | ||
87 | |||
88 | //static | ||
89 | void LLWorkerThread::pauseAll() | ||
90 | { | ||
91 | for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++) | ||
92 | { | ||
93 | (*iter)->pause(); | ||
94 | } | ||
95 | } | ||
96 | |||
97 | //static | ||
98 | void LLWorkerThread::waitOnAllPending() | ||
99 | { | ||
100 | for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++) | ||
101 | { | ||
102 | (*iter)->waitOnPending(); | ||
103 | } | ||
104 | } | ||
105 | |||
106 | //---------------------------------------------------------------------------- | ||
107 | |||
108 | LLWorkerThread::LLWorkerThread(bool threaded, bool runalways) : | ||
109 | LLQueuedThread("Worker", threaded, runalways) | ||
110 | { | ||
111 | sThreadList.insert(this); | ||
112 | } | ||
113 | |||
114 | LLWorkerThread::~LLWorkerThread() | ||
115 | { | ||
116 | llverify(sThreadList.erase(this) == 1); | ||
117 | // ~LLQueuedThread() will be called here | ||
118 | } | ||
119 | |||
120 | //---------------------------------------------------------------------------- | ||
121 | |||
122 | |||
123 | LLWorkerThread::handle_t LLWorkerThread::add(LLWorkerClass* workerclass, S32 param, U32 priority) | ||
124 | { | ||
125 | handle_t handle = generateHandle(); | ||
126 | |||
127 | Request* req = new Request(handle, priority, workerclass, param); | ||
128 | |||
129 | bool res = addRequest(req); | ||
130 | if (!res) | ||
131 | { | ||
132 | llerrs << "add called after LLWorkerThread::cleanupClass()" << llendl; | ||
133 | req->deleteRequest(); | ||
134 | handle = nullHandle(); | ||
135 | } | ||
136 | |||
137 | return handle; | ||
138 | } | ||
139 | |||
140 | //============================================================================ | ||
141 | // Runs on its OWN thread | ||
142 | |||
143 | bool LLWorkerThread::processRequest(QueuedRequest* qreq) | ||
144 | { | ||
145 | Request *req = (Request*)qreq; | ||
146 | |||
147 | req->getWorkerClass()->setWorking(true); | ||
148 | |||
149 | bool complete = req->getWorkerClass()->doWork(req->getParam()); | ||
150 | |||
151 | req->getWorkerClass()->setWorking(false); | ||
152 | |||
153 | LLThread::yield(); // worker thread should yield after each request | ||
154 | |||
155 | return complete; | ||
156 | } | ||
157 | |||
158 | //============================================================================ | ||
159 | |||
160 | LLWorkerThread::Request::Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) : | ||
161 | LLQueuedThread::QueuedRequest(handle, priority), | ||
162 | mWorkerClass(workerclass), | ||
163 | mParam(param) | ||
164 | { | ||
165 | } | ||
166 | |||
167 | void LLWorkerThread::Request::deleteRequest() | ||
168 | { | ||
169 | LLQueuedThread::QueuedRequest::deleteRequest(); | ||
170 | } | ||
171 | |||
172 | //============================================================================ | ||
173 | // LLWorkerClass:: operates in main thread | ||
174 | |||
175 | LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& name) | ||
176 | : mWorkerThread(workerthread), | ||
177 | mWorkerClassName(name), | ||
178 | mWorkHandle(LLWorkerThread::nullHandle()), | ||
179 | mWorkFlags(0) | ||
180 | { | ||
181 | if (!mWorkerThread) | ||
182 | { | ||
183 | mWorkerThread = LLWorkerThread::sLocal; | ||
184 | } | ||
185 | } | ||
186 | LLWorkerClass::~LLWorkerClass() | ||
187 | { | ||
188 | if (mWorkHandle != LLWorkerThread::nullHandle()) | ||
189 | { | ||
190 | LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle); | ||
191 | if (!workreq) | ||
192 | { | ||
193 | llerrs << "LLWorkerClass destroyed with stale work handle" << llendl; | ||
194 | } | ||
195 | if (workreq->getStatus() != LLWorkerThread::STATUS_ABORT && | ||
196 | workreq->getStatus() != LLWorkerThread::STATUS_ABORTED && | ||
197 | workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE) | ||
198 | { | ||
199 | llerrs << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << llendl; | ||
200 | } | ||
201 | } | ||
202 | } | ||
203 | |||
204 | void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread) | ||
205 | { | ||
206 | if (mWorkHandle != LLWorkerThread::nullHandle()) | ||
207 | { | ||
208 | llerrs << "LLWorkerClass attempt to change WorkerThread with active worker!" << llendl; | ||
209 | } | ||
210 | mWorkerThread = workerthread; | ||
211 | } | ||
212 | |||
213 | //---------------------------------------------------------------------------- | ||
214 | |||
215 | bool LLWorkerClass::yield() | ||
216 | { | ||
217 | llassert(mWorkFlags & WCF_WORKING); | ||
218 | LLThread::yield(); | ||
219 | mWorkerThread->checkPause(); | ||
220 | return (getFlags() & WCF_ABORT_REQUESTED) ? true : false; | ||
221 | } | ||
222 | |||
223 | //---------------------------------------------------------------------------- | ||
224 | |||
225 | // calls startWork, adds doWork() to queue | ||
226 | void LLWorkerClass::addWork(S32 param, U32 priority) | ||
227 | { | ||
228 | if (mWorkHandle != LLWorkerThread::nullHandle()) | ||
229 | { | ||
230 | llerrs << "LLWorkerClass attempt to add work with active worker!" << llendl; | ||
231 | } | ||
232 | #if _DEBUG | ||
233 | // llinfos << "addWork: " << mWorkerClassName << " Param: " << param << llendl; | ||
234 | #endif | ||
235 | startWork(param); | ||
236 | mWorkHandle = mWorkerThread->add(this, param, priority); | ||
237 | } | ||
238 | |||
239 | void LLWorkerClass::abortWork() | ||
240 | { | ||
241 | #if _DEBUG | ||
242 | // LLWorkerThread::Request* workreq = mWorkerThread->getRequest(mWorkHandle); | ||
243 | // if (workreq) | ||
244 | // llinfos << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl; | ||
245 | #endif | ||
246 | mWorkerThread->abortRequest(mWorkHandle); | ||
247 | setFlags(WCF_ABORT_REQUESTED); | ||
248 | } | ||
249 | |||
250 | // if doWork is complete or aborted, call endWork() and return true | ||
251 | bool LLWorkerClass::checkWork() | ||
252 | { | ||
253 | bool complete = false, abort = false; | ||
254 | LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle); | ||
255 | llassert(workreq); | ||
256 | if (getFlags(WCF_ABORT_REQUESTED) || workreq->getStatus() == LLWorkerThread::STATUS_ABORTED) | ||
257 | { | ||
258 | complete = true; | ||
259 | abort = true; | ||
260 | } | ||
261 | else if (workreq->getStatus() == LLWorkerThread::STATUS_COMPLETE) | ||
262 | { | ||
263 | complete = true; | ||
264 | } | ||
265 | if (complete) | ||
266 | { | ||
267 | #if _DEBUG | ||
268 | // llinfos << "endWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl; | ||
269 | #endif | ||
270 | endWork(workreq->getParam(), abort); | ||
271 | mWorkerThread->completeRequest(mWorkHandle); | ||
272 | mWorkHandle = LLWorkerThread::nullHandle(); | ||
273 | } | ||
274 | return complete; | ||
275 | } | ||
276 | |||
277 | void LLWorkerClass::killWork() | ||
278 | { | ||
279 | if (haveWork()) | ||
280 | { | ||
281 | abortWork(); | ||
282 | bool paused = mWorkerThread->isPaused(); | ||
283 | while (!checkWork()) | ||
284 | { | ||
285 | mWorkerThread->updateQueue(0); | ||
286 | } | ||
287 | if (paused) | ||
288 | { | ||
289 | mWorkerThread->pause(); | ||
290 | } | ||
291 | } | ||
292 | } | ||
293 | |||
294 | void LLWorkerClass::setPriority(U32 priority) | ||
295 | { | ||
296 | if (haveWork()) | ||
297 | { | ||
298 | mWorkerThread->setPriority(mWorkHandle, priority); | ||
299 | } | ||
300 | } | ||
301 | |||
302 | //============================================================================ | ||
303 | |||