3
* Copyright 2004--2011, Google Inc.
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions are met:
8
* 1. Redistributions of source code must retain the above copyright notice,
9
* this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright notice,
11
* this list of conditions and the following disclaimer in the documentation
12
* and/or other materials provided with the distribution.
13
* 3. The name of the author may not be used to endorse or promote products
14
* derived from this software without specific prior written permission.
16
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32
// TODO: Remove this once the cause of sporadic failures in these
33
// tests is tracked down.
37
#include "talk/base/win32.h"
40
#include "talk/base/common.h"
41
#include "talk/base/gunit.h"
42
#include "talk/base/logging.h"
43
#include "talk/base/task.h"
44
#include "talk/base/taskrunner.h"
45
#include "talk/base/thread.h"
46
#include "talk/base/timeutils.h"
50
static int64 GetCurrentTime() {
51
return static_cast<int64>(Time()) * 10000;
54
// feel free to change these numbers. Note that '0' won't work, though
55
#define STUCK_TASK_COUNT 5
56
#define HAPPY_TASK_COUNT 20
58
// this is a generic timeout task which, when it signals timeout, will
59
// include the unique ID of the task in the signal (we don't use this
60
// in production code because we haven't yet had occasion to generate
61
// an array of the same types of task)
63
class IdTimeoutTask : public Task, public sigslot::has_slots<> {
65
explicit IdTimeoutTask(TaskParent *parent) : Task(parent) {
66
SignalTimeout.connect(this, &IdTimeoutTask::OnLocalTimeout);
69
sigslot::signal1<const int> SignalTimeoutId;
70
sigslot::signal1<const int> SignalDoneId;
72
virtual int ProcessStart() {
73
return STATE_RESPONSE;
76
void OnLocalTimeout() {
77
SignalTimeoutId(unique_id());
82
SignalDoneId(unique_id());
87
class StuckTask : public IdTimeoutTask {
89
explicit StuckTask(TaskParent *parent) : IdTimeoutTask(parent) {}
90
virtual int ProcessStart() {
95
class HappyTask : public IdTimeoutTask {
97
explicit HappyTask(TaskParent *parent) : IdTimeoutTask(parent) {
98
time_to_perform_ = rand() % (STUCK_TASK_COUNT / 2);
100
virtual int ProcessStart() {
101
if (ElapsedTime() > (time_to_perform_ * 1000 * 10000))
102
return STATE_RESPONSE;
104
return STATE_BLOCKED;
108
int time_to_perform_;
111
// simple implementation of a task runner which uses Windows'
112
// GetSystemTimeAsFileTime() to get the current clock ticks
114
class MyTaskRunner : public TaskRunner {
116
virtual void WakeTasks() { RunTasks(); }
117
virtual int64 CurrentTime() {
118
return GetCurrentTime();
121
bool timeout_change() const {
122
return timeout_change_;
125
void clear_timeout_change() {
126
timeout_change_ = false;
129
virtual void OnTimeoutChange() {
130
timeout_change_ = true;
132
bool timeout_change_;
136
// this unit test is primarily concerned (for now) with the timeout
137
// functionality in tasks. It works as follows:
139
// * Create a bunch of tasks, some "stuck" (ie., guaranteed to timeout)
140
// and some "happy" (will immediately finish).
141
// * Set the timeout on the "stuck" tasks to some number of seconds between
142
// 1 and the number of stuck tasks
143
// * Start all the stuck & happy tasks in random order
144
// * Wait "number of stuck tasks" seconds and make sure everything timed out
146
class TaskTest : public sigslot::has_slots<> {
150
// no need to delete any tasks; the task runner owns them
154
// create and configure tasks
155
for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
156
stuck_[i].task_ = new StuckTask(&task_runner_);
157
stuck_[i].task_->SignalTimeoutId.connect(this,
158
&TaskTest::OnTimeoutStuck);
159
stuck_[i].timed_out_ = false;
160
stuck_[i].xlat_ = stuck_[i].task_->unique_id();
161
stuck_[i].task_->set_timeout_seconds(i + 1);
162
LOG(LS_INFO) << "Task " << stuck_[i].xlat_ << " created with timeout "
163
<< stuck_[i].task_->timeout_seconds();
166
for (int i = 0; i < HAPPY_TASK_COUNT; ++i) {
167
happy_[i].task_ = new HappyTask(&task_runner_);
168
happy_[i].task_->SignalTimeoutId.connect(this,
169
&TaskTest::OnTimeoutHappy);
170
happy_[i].task_->SignalDoneId.connect(this,
171
&TaskTest::OnDoneHappy);
172
happy_[i].timed_out_ = false;
173
happy_[i].xlat_ = happy_[i].task_->unique_id();
176
// start all the tasks in random order
179
for (int i = 0; i < STUCK_TASK_COUNT + HAPPY_TASK_COUNT; ++i) {
180
if ((stuck_index < STUCK_TASK_COUNT) &&
181
(happy_index < HAPPY_TASK_COUNT)) {
182
if (rand() % 2 == 1) {
183
stuck_[stuck_index++].task_->Start();
185
happy_[happy_index++].task_->Start();
187
} else if (stuck_index < STUCK_TASK_COUNT) {
188
stuck_[stuck_index++].task_->Start();
190
happy_[happy_index++].task_->Start();
194
for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
195
std::cout << "Stuck task #" << i << " timeout is " <<
196
stuck_[i].task_->timeout_seconds() << " at " <<
197
stuck_[i].task_->timeout_time() << std::endl;
200
// just a little self-check to make sure we started all the tasks
201
ASSERT_EQ(STUCK_TASK_COUNT, stuck_index);
202
ASSERT_EQ(HAPPY_TASK_COUNT, happy_index);
204
// run the unblocked tasks
205
LOG(LS_INFO) << "Running tasks";
206
task_runner_.RunTasks();
208
std::cout << "Start time is " << GetCurrentTime() << std::endl;
210
// give all the stuck tasks time to timeout
211
for (int i = 0; !task_runner_.AllChildrenDone() && i < STUCK_TASK_COUNT;
213
Thread::Current()->ProcessMessages(1000);
214
for (int j = 0; j < HAPPY_TASK_COUNT; ++j) {
215
if (happy_[j].task_) {
216
happy_[j].task_->Wake();
219
LOG(LS_INFO) << "Polling tasks";
220
task_runner_.PollTasks();
223
// We see occasional test failures here due to the stuck tasks not having
224
// timed-out yet, which seems like it should be impossible. To help track
225
// this down we have added logging of the timing information, which we send
226
// directly to stdout so that we get it in opt builds too.
227
std::cout << "End time is " << GetCurrentTime() << std::endl;
230
void OnTimeoutStuck(const int id) {
231
LOG(LS_INFO) << "Timed out task " << id;
234
for (i = 0; i < STUCK_TASK_COUNT; ++i) {
235
if (stuck_[i].xlat_ == id) {
236
stuck_[i].timed_out_ = true;
237
stuck_[i].task_ = NULL;
242
// getting a bad ID here is a failure, but let's continue
243
// running to see what else might go wrong
244
EXPECT_LT(i, STUCK_TASK_COUNT);
247
void OnTimeoutHappy(const int id) {
249
for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
250
if (happy_[i].xlat_ == id) {
251
happy_[i].timed_out_ = true;
252
happy_[i].task_ = NULL;
257
// getting a bad ID here is a failure, but let's continue
258
// running to see what else might go wrong
259
EXPECT_LT(i, HAPPY_TASK_COUNT);
262
void OnDoneHappy(const int id) {
264
for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
265
if (happy_[i].xlat_ == id) {
266
happy_[i].task_ = NULL;
271
// getting a bad ID here is a failure, but let's continue
272
// running to see what else might go wrong
273
EXPECT_LT(i, HAPPY_TASK_COUNT);
276
void check_passed() {
277
EXPECT_TRUE(task_runner_.AllChildrenDone());
279
// make sure none of our happy tasks timed out
280
for (int i = 0; i < HAPPY_TASK_COUNT; ++i) {
281
EXPECT_FALSE(happy_[i].timed_out_);
284
// make sure all of our stuck tasks timed out
285
for (int i = 0; i < STUCK_TASK_COUNT; ++i) {
286
EXPECT_TRUE(stuck_[i].timed_out_);
287
if (!stuck_[i].timed_out_) {
288
std::cout << "Stuck task #" << i << " timeout is at "
289
<< stuck_[i].task_->timeout_time() << std::endl;
298
IdTimeoutTask *task_;
303
MyTaskRunner task_runner_;
304
TaskInfo stuck_[STUCK_TASK_COUNT];
305
TaskInfo happy_[HAPPY_TASK_COUNT];
308
TEST(start_task_test, Timeout) {
311
task_test.check_passed();
314
// Test for aborting the task while it is running
316
class AbortTask : public Task {
318
explicit AbortTask(TaskParent *parent) : Task(parent) {
319
set_timeout_seconds(1);
322
virtual int ProcessStart() {
327
DISALLOW_EVIL_CONSTRUCTORS(AbortTask);
330
class TaskAbortTest : public sigslot::has_slots<> {
334
// no need to delete any tasks; the task runner owns them
338
Task *abort_task = new AbortTask(&task_runner_);
339
abort_task->SignalTimeout.connect(this, &TaskAbortTest::OnTimeout);
343
task_runner_.RunTasks();
348
FAIL() << "Task timed out instead of aborting.";
351
MyTaskRunner task_runner_;
352
DISALLOW_EVIL_CONSTRUCTORS(TaskAbortTest);
355
TEST(start_task_test, Abort) {
356
TaskAbortTest abort_test;
360
// Test for aborting a task to verify that it does the Wake operation
361
// which gets it deleted.
363
class SetBoolOnDeleteTask : public Task {
365
SetBoolOnDeleteTask(TaskParent *parent, bool *set_when_deleted)
367
set_when_deleted_(set_when_deleted) {
368
EXPECT_TRUE(NULL != set_when_deleted);
369
EXPECT_FALSE(*set_when_deleted);
372
virtual ~SetBoolOnDeleteTask() {
373
*set_when_deleted_ = true;
376
virtual int ProcessStart() {
377
return STATE_BLOCKED;
381
bool* set_when_deleted_;
382
DISALLOW_EVIL_CONSTRUCTORS(SetBoolOnDeleteTask);
385
class AbortShouldWakeTest : public sigslot::has_slots<> {
387
AbortShouldWakeTest() {}
389
// no need to delete any tasks; the task runner owns them
390
~AbortShouldWakeTest() {}
393
bool task_deleted = false;
394
Task *task_to_abort = new SetBoolOnDeleteTask(&task_runner_, &task_deleted);
395
task_to_abort->Start();
397
// Task::Abort() should call TaskRunner::WakeTasks(). WakeTasks calls
398
// TaskRunner::RunTasks() immediately which should delete the task.
399
task_to_abort->Abort();
400
EXPECT_TRUE(task_deleted);
403
// avoid a crash (due to referencing a local variable)
404
// if the test fails.
405
task_runner_.RunTasks();
411
FAIL() << "Task timed out instead of aborting.";
414
MyTaskRunner task_runner_;
415
DISALLOW_EVIL_CONSTRUCTORS(AbortShouldWakeTest);
418
TEST(start_task_test, AbortShouldWake) {
419
AbortShouldWakeTest abort_should_wake_test;
420
abort_should_wake_test.Start();
423
// Validate that TaskRunner's OnTimeoutChange gets called appropriately
424
// * When a task calls UpdateTaskTimeout
425
// * When the next timeout task time, times out
426
class TimeoutChangeTest : public sigslot::has_slots<> {
429
: task_count_(ARRAY_SIZE(stuck_tasks_)) {}
431
// no need to delete any tasks; the task runner owns them
432
~TimeoutChangeTest() {}
435
for (int i = 0; i < task_count_; ++i) {
436
stuck_tasks_[i] = new StuckTask(&task_runner_);
437
stuck_tasks_[i]->set_timeout_seconds(i + 2);
438
stuck_tasks_[i]->SignalTimeoutId.connect(this,
439
&TimeoutChangeTest::OnTimeoutId);
442
for (int i = task_count_ - 1; i >= 0; --i) {
443
stuck_tasks_[i]->Start();
445
task_runner_.clear_timeout_change();
447
// At this point, our timeouts are set as follows
448
// task[0] is 2 seconds, task[1] at 3 seconds, etc.
450
stuck_tasks_[0]->set_timeout_seconds(2);
451
// Now, task[0] is 2 seconds, task[1] at 3 seconds...
452
// so timeout change shouldn't be called.
453
EXPECT_FALSE(task_runner_.timeout_change());
454
task_runner_.clear_timeout_change();
456
stuck_tasks_[0]->set_timeout_seconds(1);
457
// task[0] is 1 seconds, task[1] at 3 seconds...
458
// The smallest timeout got smaller so timeout change be called.
459
EXPECT_TRUE(task_runner_.timeout_change());
460
task_runner_.clear_timeout_change();
462
stuck_tasks_[1]->set_timeout_seconds(2);
463
// task[0] is 1 seconds, task[1] at 2 seconds...
464
// The smallest timeout is still 1 second so no timeout change.
465
EXPECT_FALSE(task_runner_.timeout_change());
466
task_runner_.clear_timeout_change();
468
while (task_count_ > 0) {
469
int previous_count = task_count_;
470
task_runner_.PollTasks();
471
if (previous_count != task_count_) {
472
// We only get here when a task times out. When that
473
// happens, the timeout change should get called because
474
// the smallest timeout is now in the past.
475
EXPECT_TRUE(task_runner_.timeout_change());
476
task_runner_.clear_timeout_change();
478
Thread::Current()->socketserver()->Wait(500, false);
483
void OnTimeoutId(const int id) {
484
for (int i = 0; i < ARRAY_SIZE(stuck_tasks_); ++i) {
485
if (stuck_tasks_[i] && stuck_tasks_[i]->unique_id() == id) {
487
stuck_tasks_[i] = NULL;
493
MyTaskRunner task_runner_;
494
StuckTask* (stuck_tasks_[3]);
496
DISALLOW_EVIL_CONSTRUCTORS(TimeoutChangeTest);
499
TEST(start_task_test, TimeoutChange) {
500
TimeoutChangeTest timeout_change_test;
501
timeout_change_test.Start();
504
class DeleteTestTaskRunner : public TaskRunner {
506
DeleteTestTaskRunner() {
508
virtual void WakeTasks() { }
509
virtual int64 CurrentTime() {
510
return GetCurrentTime();
513
DISALLOW_EVIL_CONSTRUCTORS(DeleteTestTaskRunner);
516
TEST(unstarted_task_test, DeleteTask) {
517
// This test ensures that we don't
518
// crash if a task is deleted without running it.
519
DeleteTestTaskRunner task_runner;
520
HappyTask* happy_task = new HappyTask(&task_runner);
523
// try deleting the task directly
524
HappyTask* child_happy_task = new HappyTask(happy_task);
525
delete child_happy_task;
527
// run the unblocked tasks
528
task_runner.RunTasks();
531
TEST(unstarted_task_test, DoNotDeleteTask1) {
532
// This test ensures that we don't
533
// crash if a task runner is deleted without
534
// running a certain task.
535
DeleteTestTaskRunner task_runner;
536
HappyTask* happy_task = new HappyTask(&task_runner);
539
HappyTask* child_happy_task = new HappyTask(happy_task);
540
child_happy_task->Start();
542
// Never run the tasks
545
TEST(unstarted_task_test, DoNotDeleteTask2) {
546
// This test ensures that we don't
547
// crash if a taskrunner is delete with a
548
// task that has never been started.
549
DeleteTestTaskRunner task_runner;
550
HappyTask* happy_task = new HappyTask(&task_runner);
553
// Do not start the task.
554
// Note: this leaks memory, so don't do this.
555
// Instead, always run your tasks or delete them.
556
new HappyTask(happy_task);
558
// run the unblocked tasks
559
task_runner.RunTasks();
562
} // namespace talk_base