llcorehttppolicy.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. /**
  2. * @file llcorehttppolicy.cpp
  3. * @brief Internal definitions of the Http policy thread
  4. *
  5. * $LicenseInfo:firstyear=2012&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2012-2013, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "llcorehttppolicy.h"
  28. #include "llcorehttplibcurl.h"
  29. #include "llcorehttpoprequest.h"
  30. #include "llcorehttpretryqueue.h"
  31. #include "llcorehttppolicyclass.h"
  32. #include "llcorehttpservice.h"
  33. #include "lltimer.h"
  34. namespace LLCore
  35. {
  36. // Per-policy-class data for a running system. Collection of queues, options
  37. // and other data for a single policy class.
  38. //
  39. // Threading: accessed only by worker thread
  40. struct HttpPolicy::ClassState
  41. {
  42. ClassState()
  43. : mThrottleEnd(0),
  44. mThrottleLeft(0L),
  45. mRequestCount(0L),
  46. mStallStaging(false)
  47. {
  48. }
  49. HttpReadyQueue mReadyQueue;
  50. HttpRetryQueue mRetryQueue;
  51. HttpPolicyClass mOptions;
  52. HttpTime mThrottleEnd;
  53. long mThrottleLeft;
  54. long mRequestCount;
  55. bool mStallStaging;
  56. };
  57. HttpPolicy::HttpPolicy(HttpService* service)
  58. : mService(service)
  59. {
  60. // Create default class
  61. mClasses.push_back(new ClassState());
  62. }
  63. HttpPolicy::~HttpPolicy()
  64. {
  65. shutdown();
  66. for (class_list_t::iterator it = mClasses.begin(), end = mClasses.end();
  67. it != end; ++it)
  68. {
  69. delete *it;
  70. }
  71. mClasses.clear();
  72. mService = NULL;
  73. }
  74. HttpRequest::policy_t HttpPolicy::createPolicyClass()
  75. {
  76. const HttpRequest::policy_t policy_class = mClasses.size();
  77. if (policy_class >= (HttpRequest::policy_t)HTTP_POLICY_CLASS_LIMIT)
  78. {
  79. return HttpRequest::INVALID_POLICY_ID;
  80. }
  81. mClasses.push_back(new ClassState());
  82. return policy_class;
  83. }
  84. void HttpPolicy::shutdown()
  85. {
  86. for (size_t policy_class = 0, count = mClasses.size();
  87. policy_class < count; ++policy_class)
  88. {
  89. ClassState& state = *mClasses[policy_class];
  90. HttpRetryQueue& retryq = state.mRetryQueue;
  91. while (!retryq.empty())
  92. {
  93. HttpOpRequest::ptr_t op(retryq.top());
  94. retryq.pop();
  95. op->cancel();
  96. }
  97. HttpReadyQueue& readyq = state.mReadyQueue;
  98. while (!readyq.empty())
  99. {
  100. HttpOpRequest::ptr_t op(readyq.top());
  101. readyq.pop();
  102. op->cancel();
  103. }
  104. }
  105. }
  106. void HttpPolicy::start()
  107. {
  108. }
  109. void HttpPolicy::addOp(const HttpOpRequest::ptr_t& op)
  110. {
  111. const int policy_class(op->mReqPolicy);
  112. op->mPolicyRetries = 0;
  113. op->mPolicy503Retries = 0;
  114. mClasses[policy_class]->mReadyQueue.push(op);
  115. }
  116. void HttpPolicy::retryOp(const HttpOpRequest::ptr_t& op)
  117. {
  118. static const HttpTime retry_deltas[] =
  119. {
  120. 250000, // 1st retry in 0.25s, etc...
  121. 500000,
  122. 1000000,
  123. 2000000,
  124. 5000000 // ... to every 5.0s.
  125. };
  126. constexpr int delta_max = LL_ARRAY_SIZE(retry_deltas) - 1;
  127. const HttpTime now = LLTimer::totalTime();
  128. const int policy_class = op->mReqPolicy;
  129. HttpTime delta = retry_deltas[llclamp(op->mPolicyRetries, 0, delta_max)];
  130. bool external_delta = false;
  131. if (op->mReplyRetryAfter > 0 && op->mReplyRetryAfter < 30)
  132. {
  133. delta = op->mReplyRetryAfter * U64L(1000000);
  134. external_delta = true;
  135. }
  136. op->mPolicyRetryAt = now + delta;
  137. ++op->mPolicyRetries;
  138. if (op->mStatus == gStatusUnavailable)
  139. {
  140. ++op->mPolicy503Retries;
  141. }
  142. LL_DEBUGS("CoreHttp") << "HTTP request " << op->getHandle() << " retry "
  143. << op->mPolicyRetries << " scheduled in "
  144. << delta / HttpTime(1000) << "ms ("
  145. << (external_delta ? "external" : "internal")
  146. << "). Status: " << op->mStatus.toTerseString()
  147. << LL_ENDL;
  148. if (op->mTracing > HTTP_TRACE_OFF)
  149. {
  150. llinfos << "TRACE, ToRetryQueue. Handle = " << op->getHandle()
  151. << " - Delta = " << delta / HttpTime(1000)
  152. << " - Retries = " << op->mPolicyRetries << llendl;
  153. }
  154. mClasses[policy_class]->mRetryQueue.push(op);
  155. }
  156. // Attempt to deliver requests to the transport layer.
  157. //
  158. // Tries to find HTTP requests for each policy class with available capacity.
  159. // Starts with the retry queue first looking for requests that have waited long
  160. // enough then moves on to the ready queue.
  161. //
  162. // If all queues are empty, will return an indication that the worker thread
  163. // may sleep hard otherwise will ask for normal polling frequency.
  164. //
  165. // Implements a client-side request rate throttle as well. This is intended to
  166. // mimic and predict throttling behavior of grid services but that is difficult
  167. // to do with different time bases. This also represents a rigid coupling
  168. // between viewer and server that makes it hard to change parameters and I hope
  169. // we can make this go away with pipelining.
  170. int HttpPolicy::processReadyQueue()
  171. {
  172. const HttpTime now = LLTimer::totalTime();
  173. int result = HttpService::REQUEST_SLEEP;
  174. HttpLibcurl& transport = mService->getTransport();
  175. for (int policy_class = 0, count = mClasses.size();
  176. policy_class < count; ++policy_class)
  177. {
  178. ClassState& state = *mClasses[policy_class];
  179. HttpRetryQueue& retryq = state.mRetryQueue;
  180. HttpReadyQueue& readyq = state.mReadyQueue;
  181. if (state.mStallStaging)
  182. {
  183. // Stalling but don't sleep. Need to complete operations and get
  184. // back to servicing queues. Do this test before the retryq/readyq
  185. // test or you'll get stalls until you click a setting or an asset
  186. // request comes in.
  187. result = HttpService::NORMAL;
  188. continue;
  189. }
  190. if (retryq.empty() && readyq.empty())
  191. {
  192. continue;
  193. }
  194. const bool throttle_enabled = state.mOptions.mThrottleRate > 0L;
  195. const bool throttle_current = throttle_enabled &&
  196. now < state.mThrottleEnd;
  197. if (throttle_current && state.mThrottleLeft <= 0)
  198. {
  199. // Throttled condition, don't serve this class but don't sleep hard
  200. result = HttpService::NORMAL;
  201. continue;
  202. }
  203. int active = transport.getActiveCountInClass(policy_class);
  204. // Expect negatives here
  205. int active_limit = state.mOptions.mPipelining > 1L ?
  206. state.mOptions.mPerHostConnectionLimit *
  207. state.mOptions.mPipelining :
  208. state.mOptions.mConnectionLimit;
  209. int needed = active_limit - active; // Expect negatives here
  210. if (needed > 0)
  211. {
  212. // First see if we have any retries...
  213. while (needed > 0 && !retryq.empty())
  214. {
  215. HttpOpRequest::ptr_t op(retryq.top());
  216. if (op->mPolicyRetryAt > now)
  217. {
  218. break;
  219. }
  220. retryq.pop();
  221. op->stageFromReady(mService);
  222. op.reset();
  223. ++state.mRequestCount;
  224. --needed;
  225. if (throttle_enabled)
  226. {
  227. if (now >= state.mThrottleEnd)
  228. {
  229. // Throttle expired, move to next window
  230. LL_DEBUGS("CoreHttp") << "Throttle expired with "
  231. << state.mThrottleLeft
  232. << " requests to go and "
  233. << state.mRequestCount
  234. << " requests issued."
  235. << LL_ENDL;
  236. state.mThrottleLeft = state.mOptions.mThrottleRate;
  237. state.mThrottleEnd = now + HttpTime(1000000);
  238. }
  239. if (--state.mThrottleLeft <= 0)
  240. {
  241. goto throttle_on;
  242. }
  243. }
  244. }
  245. // Now go on to the new requests...
  246. while (needed > 0 && !readyq.empty())
  247. {
  248. HttpOpRequest::ptr_t op(readyq.top());
  249. readyq.pop();
  250. op->stageFromReady(mService);
  251. op.reset();
  252. ++state.mRequestCount;
  253. --needed;
  254. if (throttle_enabled)
  255. {
  256. if (now >= state.mThrottleEnd)
  257. {
  258. // Throttle expired, move to next window
  259. LL_DEBUGS("CoreHttp") << "Throttle expired with "
  260. << state.mThrottleLeft
  261. << " requests to go and "
  262. << state.mRequestCount
  263. << " requests issued."
  264. << LL_ENDL;
  265. state.mThrottleLeft = state.mOptions.mThrottleRate;
  266. state.mThrottleEnd = now + HttpTime(1000000);
  267. }
  268. if (--state.mThrottleLeft <= 0)
  269. {
  270. goto throttle_on;
  271. }
  272. }
  273. }
  274. }
  275. throttle_on:
  276. if (!readyq.empty() || !retryq.empty())
  277. {
  278. // If anything is ready, continue looping...
  279. result = HttpService::NORMAL;
  280. }
  281. }
  282. return result;
  283. }
  284. bool HttpPolicy::cancel(HttpHandle handle)
  285. {
  286. for (int policy_class = 0, count = mClasses.size(); policy_class < count;
  287. ++policy_class)
  288. {
  289. ClassState& state = *mClasses[policy_class];
  290. // Scan retry queue
  291. HttpRetryQueue::container_type& c1 = state.mRetryQueue.get_container();
  292. for (HttpRetryQueue::container_type::iterator iter = c1.begin(),
  293. end = c1.end();
  294. iter != end; ++iter)
  295. {
  296. if ((*iter)->getHandle() == handle)
  297. {
  298. HttpOpRequest::ptr_t op(*iter);
  299. c1.erase(iter); // All iterators are invalidated
  300. op->cancel();
  301. return true;
  302. }
  303. }
  304. // Scan ready queue
  305. HttpReadyQueue::container_type& c2= state.mReadyQueue.get_container();
  306. for (HttpReadyQueue::container_type::iterator iter = c2.begin(),
  307. end = c2.end();
  308. iter != end; ++iter)
  309. {
  310. if ((*iter)->getHandle() == handle)
  311. {
  312. HttpOpRequest::ptr_t op(*iter);
  313. c2.erase(iter); // All iterators are invalidated
  314. op->cancel();
  315. return true;
  316. }
  317. }
  318. }
  319. return false;
  320. }
  321. bool HttpPolicy::stageAfterCompletion(const HttpOpRequest::ptr_t& op)
  322. {
  323. // Retry or finalize
  324. if (!op->mStatus)
  325. {
  326. #if 0 // *DEBUG: For "[curl:bugs] #1420" tests.
  327. if (op->mStatus == HttpStatus(HttpStatus::EXT_CURL_EASY,
  328. CURLE_OPERATION_TIMEDOUT))
  329. {
  330. llwarns << "HTTP request " << op->getHandle() << " timed out."
  331. << llendl;
  332. }
  333. #endif
  334. // If this failed, we might want to retry.
  335. if (op->mPolicyRetries < op->mPolicyRetryLimit &&
  336. op->mStatus.isRetryable())
  337. {
  338. // Okay, worth a retry.
  339. retryOp(op);
  340. return true; // still active/ready
  341. }
  342. }
  343. // This op is done, finalize it delivering it to the reply queue...
  344. if (op->mTracing > HTTP_TRACE_OFF)
  345. {
  346. if (!op->mStatus)
  347. {
  348. llwarns << "HTTP request " << op->getHandle()
  349. << " failed after " << op->mPolicyRetries
  350. << " retries. Reason: " << op->mStatus.toString()
  351. << " (" << op->mStatus.toTerseString() << ")"
  352. << llendl;
  353. }
  354. else if (op->mPolicyRetries)
  355. {
  356. llinfos << "HTTP request " << op->getHandle()
  357. << " succeeded on retry " << op->mPolicyRetries
  358. << llendl;
  359. }
  360. }
  361. op->stageFromActive(mService);
  362. return false; // not active
  363. }
  364. HttpPolicyClass& HttpPolicy::getClassOptions(HttpRequest::policy_t pclass)
  365. {
  366. llassert_always(pclass >= 0 && pclass < mClasses.size());
  367. return mClasses[pclass]->mOptions;
  368. }
  369. int HttpPolicy::getReadyCount(HttpRequest::policy_t policy_class) const
  370. {
  371. if (policy_class < mClasses.size())
  372. {
  373. return mClasses[policy_class]->mReadyQueue.size() +
  374. mClasses[policy_class]->mRetryQueue.size();
  375. }
  376. return 0;
  377. }
  378. bool HttpPolicy::stallPolicy(HttpRequest::policy_t policy_class, bool stall)
  379. {
  380. bool ret = false;
  381. if (policy_class < mClasses.size())
  382. {
  383. ret = mClasses[policy_class]->mStallStaging;
  384. mClasses[policy_class]->mStallStaging = stall;
  385. }
  386. return ret;
  387. }
  388. } // End namespace LLCore