prioritized_task_runner_unittest.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383
  1. // Copyright (c) 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/base/prioritized_task_runner.h"
  5. #include <algorithm>
  6. #include <limits>
  7. #include <string>
  8. #include <vector>
  9. #include "base/bind.h"
  10. #include "base/callback_helpers.h"
  11. #include "base/location.h"
  12. #include "base/rand_util.h"
  13. #include "base/run_loop.h"
  14. #include "base/strings/string_number_conversions.h"
  15. #include "base/strings/string_util.h"
  16. #include "base/synchronization/lock.h"
  17. #include "base/synchronization/waitable_event.h"
  18. #include "base/task/sequenced_task_runner.h"
  19. #include "base/task/thread_pool.h"
  20. #include "base/test/task_environment.h"
  21. #include "base/threading/thread_restrictions.h"
  22. #include "testing/gtest/include/gtest/gtest.h"
  23. namespace net {
  24. namespace {
  25. class PrioritizedTaskRunnerTest : public testing::Test {
  26. public:
  27. PrioritizedTaskRunnerTest() = default;
  28. PrioritizedTaskRunnerTest(const PrioritizedTaskRunnerTest&) = delete;
  29. PrioritizedTaskRunnerTest& operator=(const PrioritizedTaskRunnerTest&) =
  30. delete;
  31. void PushName(const std::string& task_name) {
  32. base::AutoLock auto_lock(callback_names_lock_);
  33. callback_names_.push_back(task_name);
  34. }
  35. std::string PushNameWithResult(const std::string& task_name) {
  36. PushName(task_name);
  37. std::string reply_name = task_name;
  38. base::ReplaceSubstringsAfterOffset(&reply_name, 0, "Task", "Reply");
  39. return reply_name;
  40. }
  41. std::vector<std::string> TaskOrder() {
  42. std::vector<std::string> out;
  43. for (const std::string& name : callback_names_) {
  44. if (base::StartsWith(name, "Task", base::CompareCase::SENSITIVE))
  45. out.push_back(name);
  46. }
  47. return out;
  48. }
  49. std::vector<std::string> ReplyOrder() {
  50. std::vector<std::string> out;
  51. for (const std::string& name : callback_names_) {
  52. if (base::StartsWith(name, "Reply", base::CompareCase::SENSITIVE))
  53. out.push_back(name);
  54. }
  55. return out;
  56. }
  57. // Adds a task to the task runner and waits for it to execute.
  58. void ProcessTaskRunner(base::TaskRunner* task_runner) {
  59. // Use a waitable event instead of a run loop as we need to be careful not
  60. // to run any tasks on this task runner while waiting.
  61. base::WaitableEvent waitable_event;
  62. task_runner->PostTask(FROM_HERE,
  63. base::BindOnce(
  64. [](base::WaitableEvent* waitable_event) {
  65. waitable_event->Signal();
  66. },
  67. &waitable_event));
  68. base::ScopedAllowBaseSyncPrimitivesForTesting sync;
  69. waitable_event.Wait();
  70. }
  71. // Adds a task to the |task_runner|, forcing it to wait for a conditional.
  72. // Call ReleaseTaskRunner to continue.
  73. void BlockTaskRunner(base::TaskRunner* task_runner) {
  74. waitable_event_.Reset();
  75. auto wait_function = [](base::WaitableEvent* waitable_event) {
  76. base::ScopedAllowBaseSyncPrimitivesForTesting sync;
  77. waitable_event->Wait();
  78. };
  79. task_runner->PostTask(FROM_HERE,
  80. base::BindOnce(wait_function, &waitable_event_));
  81. }
  82. // Signals the task runner's conditional so that it can continue after calling
  83. // BlockTaskRunner.
  84. void ReleaseTaskRunner() { waitable_event_.Signal(); }
  85. protected:
  86. base::test::TaskEnvironment task_environment_;
  87. std::vector<std::string> callback_names_;
  88. base::Lock callback_names_lock_;
  89. base::WaitableEvent waitable_event_;
  90. };
  91. TEST_F(PrioritizedTaskRunnerTest, PostTaskAndReplyThreadCheck) {
  92. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
  93. auto prioritized_task_runner =
  94. base::MakeRefCounted<PrioritizedTaskRunner>(base::TaskTraits());
  95. prioritized_task_runner->SetTaskRunnerForTesting(task_runner);
  96. base::RunLoop run_loop;
  97. auto thread_check =
  98. [](scoped_refptr<base::SequencedTaskRunner> expected_task_runner,
  99. base::OnceClosure callback) {
  100. EXPECT_TRUE(expected_task_runner->RunsTasksInCurrentSequence());
  101. std::move(callback).Run();
  102. };
  103. prioritized_task_runner->PostTaskAndReply(
  104. FROM_HERE, base::BindOnce(thread_check, task_runner, base::DoNothing()),
  105. base::BindOnce(thread_check, task_environment_.GetMainThreadTaskRunner(),
  106. run_loop.QuitClosure()),
  107. 0);
  108. run_loop.Run();
  109. }
  110. TEST_F(PrioritizedTaskRunnerTest, PostTaskAndReplyRunsBothTasks) {
  111. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
  112. auto prioritized_task_runner =
  113. base::MakeRefCounted<PrioritizedTaskRunner>(base::TaskTraits());
  114. prioritized_task_runner->SetTaskRunnerForTesting(task_runner);
  115. prioritized_task_runner->PostTaskAndReply(
  116. FROM_HERE,
  117. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  118. base::Unretained(this), "Task"),
  119. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  120. base::Unretained(this), "Reply"),
  121. 0);
  122. // Run the TaskRunner and both the Task and Reply should run.
  123. task_environment_.RunUntilIdle();
  124. EXPECT_EQ((std::vector<std::string>{"Task", "Reply"}), callback_names_);
  125. }
  126. TEST_F(PrioritizedTaskRunnerTest, PostTaskAndReplyTestPriority) {
  127. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
  128. auto prioritized_task_runner =
  129. base::MakeRefCounted<PrioritizedTaskRunner>(base::TaskTraits());
  130. prioritized_task_runner->SetTaskRunnerForTesting(task_runner);
  131. BlockTaskRunner(task_runner.get());
  132. prioritized_task_runner->PostTaskAndReply(
  133. FROM_HERE,
  134. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  135. base::Unretained(this), "Task5"),
  136. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  137. base::Unretained(this), "Reply5"),
  138. 5);
  139. prioritized_task_runner->PostTaskAndReply(
  140. FROM_HERE,
  141. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  142. base::Unretained(this), "Task0"),
  143. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  144. base::Unretained(this), "Reply0"),
  145. 0);
  146. prioritized_task_runner->PostTaskAndReply(
  147. FROM_HERE,
  148. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  149. base::Unretained(this), "Task7"),
  150. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  151. base::Unretained(this), "Reply7"),
  152. 7);
  153. ReleaseTaskRunner();
  154. // Run the TaskRunner and all of the tasks and replies should have run, in
  155. // priority order.
  156. task_environment_.RunUntilIdle();
  157. EXPECT_EQ((std::vector<std::string>{"Task0", "Task5", "Task7"}), TaskOrder());
  158. EXPECT_EQ((std::vector<std::string>{"Reply0", "Reply5", "Reply7"}),
  159. ReplyOrder());
  160. }
  161. // Ensure that replies are run in priority order.
  162. TEST_F(PrioritizedTaskRunnerTest, PostTaskAndReplyTestReplyPriority) {
  163. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
  164. auto prioritized_task_runner =
  165. base::MakeRefCounted<PrioritizedTaskRunner>(base::TaskTraits());
  166. prioritized_task_runner->SetTaskRunnerForTesting(task_runner);
  167. // Add a couple of tasks to run right away, but don't run their replies yet.
  168. BlockTaskRunner(task_runner.get());
  169. prioritized_task_runner->PostTaskAndReply(
  170. FROM_HERE,
  171. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  172. base::Unretained(this), "Task2"),
  173. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  174. base::Unretained(this), "Reply2"),
  175. 2);
  176. prioritized_task_runner->PostTaskAndReply(
  177. FROM_HERE,
  178. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  179. base::Unretained(this), "Task1"),
  180. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  181. base::Unretained(this), "Reply1"),
  182. 1);
  183. ReleaseTaskRunner();
  184. // Run the current tasks (but not their replies).
  185. ProcessTaskRunner(task_runner.get());
  186. // Now post task 0 (highest priority) and run it. None of the replies have
  187. // been processed yet, so its reply should skip to the head of the queue.
  188. prioritized_task_runner->PostTaskAndReply(
  189. FROM_HERE,
  190. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  191. base::Unretained(this), "Task0"),
  192. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  193. base::Unretained(this), "Reply0"),
  194. 0);
  195. ProcessTaskRunner(task_runner.get());
  196. // Run the replies.
  197. task_environment_.RunUntilIdle();
  198. EXPECT_EQ((std::vector<std::string>{"Task1", "Task2", "Task0"}), TaskOrder());
  199. EXPECT_EQ((std::vector<std::string>{"Reply0", "Reply1", "Reply2"}),
  200. ReplyOrder());
  201. }
  202. TEST_F(PrioritizedTaskRunnerTest, PriorityOverflow) {
  203. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
  204. auto prioritized_task_runner =
  205. base::MakeRefCounted<PrioritizedTaskRunner>(base::TaskTraits());
  206. prioritized_task_runner->SetTaskRunnerForTesting(task_runner);
  207. const uint32_t kMaxPriority = std::numeric_limits<uint32_t>::max();
  208. BlockTaskRunner(task_runner.get());
  209. prioritized_task_runner->PostTaskAndReply(
  210. FROM_HERE,
  211. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  212. base::Unretained(this), "TaskMinus1"),
  213. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  214. base::Unretained(this), "ReplyMinus1"),
  215. kMaxPriority - 1);
  216. prioritized_task_runner->PostTaskAndReply(
  217. FROM_HERE,
  218. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  219. base::Unretained(this), "TaskMax"),
  220. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  221. base::Unretained(this), "ReplyMax"),
  222. kMaxPriority);
  223. prioritized_task_runner->PostTaskAndReply(
  224. FROM_HERE,
  225. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  226. base::Unretained(this), "TaskMaxPlus1"),
  227. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  228. base::Unretained(this), "ReplyMaxPlus1"),
  229. kMaxPriority + 1);
  230. ReleaseTaskRunner();
  231. // Run the TaskRunner and all of the tasks and replies should have run, in
  232. // priority order.
  233. task_environment_.RunUntilIdle();
  234. EXPECT_EQ((std::vector<std::string>{"TaskMaxPlus1", "TaskMinus1", "TaskMax"}),
  235. TaskOrder());
  236. EXPECT_EQ(
  237. (std::vector<std::string>{"ReplyMaxPlus1", "ReplyMinus1", "ReplyMax"}),
  238. ReplyOrder());
  239. }
  240. TEST_F(PrioritizedTaskRunnerTest, PostTaskAndReplyWithResultRunsBothTasks) {
  241. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
  242. auto prioritized_task_runner =
  243. base::MakeRefCounted<PrioritizedTaskRunner>(base::TaskTraits());
  244. prioritized_task_runner->SetTaskRunnerForTesting(task_runner);
  245. prioritized_task_runner->PostTaskAndReplyWithResult(
  246. FROM_HERE,
  247. base::BindOnce(&PrioritizedTaskRunnerTest::PushNameWithResult,
  248. base::Unretained(this), "Task"),
  249. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  250. base::Unretained(this)),
  251. 0);
  252. // Run the TaskRunner and both the Task and Reply should run.
  253. task_environment_.RunUntilIdle();
  254. EXPECT_EQ((std::vector<std::string>{"Task", "Reply"}), callback_names_);
  255. }
  256. TEST_F(PrioritizedTaskRunnerTest, PostTaskAndReplyWithResultTestPriority) {
  257. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
  258. auto prioritized_task_runner =
  259. base::MakeRefCounted<PrioritizedTaskRunner>(base::TaskTraits());
  260. prioritized_task_runner->SetTaskRunnerForTesting(task_runner);
  261. BlockTaskRunner(task_runner.get());
  262. prioritized_task_runner->PostTaskAndReplyWithResult(
  263. FROM_HERE,
  264. base::BindOnce(&PrioritizedTaskRunnerTest::PushNameWithResult,
  265. base::Unretained(this), "Task0"),
  266. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  267. base::Unretained(this)),
  268. 0);
  269. prioritized_task_runner->PostTaskAndReplyWithResult(
  270. FROM_HERE,
  271. base::BindOnce(&PrioritizedTaskRunnerTest::PushNameWithResult,
  272. base::Unretained(this), "Task7"),
  273. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  274. base::Unretained(this)),
  275. 7);
  276. prioritized_task_runner->PostTaskAndReplyWithResult(
  277. FROM_HERE,
  278. base::BindOnce(&PrioritizedTaskRunnerTest::PushNameWithResult,
  279. base::Unretained(this), "Task3"),
  280. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  281. base::Unretained(this)),
  282. 3);
  283. ReleaseTaskRunner();
  284. // Run the TaskRunner and both the Task and Reply should run.
  285. task_environment_.RunUntilIdle();
  286. EXPECT_EQ((std::vector<std::string>{"Task0", "Task3", "Task7"}), TaskOrder());
  287. EXPECT_EQ((std::vector<std::string>{"Reply0", "Reply3", "Reply7"}),
  288. ReplyOrder());
  289. }
  290. TEST_F(PrioritizedTaskRunnerTest, OrderSamePriorityByPostOrder) {
  291. auto task_runner = base::ThreadPool::CreateSequencedTaskRunner({});
  292. auto prioritized_task_runner =
  293. base::MakeRefCounted<PrioritizedTaskRunner>(base::TaskTraits());
  294. prioritized_task_runner->SetTaskRunnerForTesting(task_runner);
  295. std::vector<int> expected;
  296. // Create 1000 tasks with random priorities between 1 and 3. Those that have
  297. // the same priorities should run in posting order.
  298. BlockTaskRunner(task_runner.get());
  299. for (int i = 0; i < 1000; i++) {
  300. int priority = base::RandInt(0, 2);
  301. int id = (priority * 1000) + i;
  302. expected.push_back(id);
  303. prioritized_task_runner->PostTaskAndReply(
  304. FROM_HERE,
  305. base::BindOnce(&PrioritizedTaskRunnerTest::PushName,
  306. base::Unretained(this), base::NumberToString(id)),
  307. base::DoNothing(), priority);
  308. }
  309. ReleaseTaskRunner();
  310. // This is the order the tasks should run on the queue.
  311. std::sort(expected.begin(), expected.end());
  312. task_environment_.RunUntilIdle();
  313. // This is the order that the tasks ran on the queue.
  314. std::vector<int> results;
  315. for (const std::string& result : callback_names_) {
  316. int result_id;
  317. EXPECT_TRUE(base::StringToInt(result, &result_id));
  318. results.push_back(result_id);
  319. }
  320. EXPECT_EQ(expected, results);
  321. }
  322. } // namespace
  323. } // namespace net