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