~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/libjingle/talk/base/task_unittest.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * libjingle
 
3
 * Copyright 2004--2011, Google Inc.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions are met:
 
7
 *
 
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.
 
15
 *
 
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.
 
26
 */
 
27
 
 
28
#ifdef POSIX
 
29
#include <sys/time.h>
 
30
#endif  // POSIX
 
31
 
 
32
// TODO: Remove this once the cause of sporadic failures in these
 
33
// tests is tracked down.
 
34
#include <iostream>
 
35
 
 
36
#ifdef WIN32
 
37
#include "talk/base/win32.h"
 
38
#endif  // WIN32
 
39
 
 
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"
 
47
 
 
48
namespace talk_base {
 
49
 
 
50
static int64 GetCurrentTime() {
 
51
  return static_cast<int64>(Time()) * 10000;
 
52
}
 
53
 
 
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
 
57
 
 
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)
 
62
 
 
63
class IdTimeoutTask : public Task, public sigslot::has_slots<> {
 
64
 public:
 
65
  explicit IdTimeoutTask(TaskParent *parent) : Task(parent) {
 
66
    SignalTimeout.connect(this, &IdTimeoutTask::OnLocalTimeout);
 
67
  }
 
68
 
 
69
  sigslot::signal1<const int> SignalTimeoutId;
 
70
  sigslot::signal1<const int> SignalDoneId;
 
71
 
 
72
  virtual int ProcessStart() {
 
73
    return STATE_RESPONSE;
 
74
  }
 
75
 
 
76
  void OnLocalTimeout() {
 
77
    SignalTimeoutId(unique_id());
 
78
  }
 
79
 
 
80
 protected:
 
81
  virtual void Stop() {
 
82
    SignalDoneId(unique_id());
 
83
    Task::Stop();
 
84
  }
 
85
};
 
86
 
 
87
class StuckTask : public IdTimeoutTask {
 
88
 public:
 
89
  explicit StuckTask(TaskParent *parent) : IdTimeoutTask(parent) {}
 
90
  virtual int ProcessStart() {
 
91
    return STATE_BLOCKED;
 
92
  }
 
93
};
 
94
 
 
95
class HappyTask : public IdTimeoutTask {
 
96
 public:
 
97
  explicit HappyTask(TaskParent *parent) : IdTimeoutTask(parent) {
 
98
    time_to_perform_ = rand() % (STUCK_TASK_COUNT / 2);
 
99
  }
 
100
  virtual int ProcessStart() {
 
101
    if (ElapsedTime() > (time_to_perform_ * 1000 * 10000))
 
102
      return STATE_RESPONSE;
 
103
    else
 
104
      return STATE_BLOCKED;
 
105
  }
 
106
 
 
107
 private:
 
108
  int time_to_perform_;
 
109
};
 
110
 
 
111
// simple implementation of a task runner which uses Windows'
 
112
// GetSystemTimeAsFileTime() to get the current clock ticks
 
113
 
 
114
class MyTaskRunner : public TaskRunner {
 
115
 public:
 
116
  virtual void WakeTasks() { RunTasks(); }
 
117
  virtual int64 CurrentTime() {
 
118
    return GetCurrentTime();
 
119
  }
 
120
 
 
121
  bool timeout_change() const {
 
122
    return timeout_change_;
 
123
  }
 
124
 
 
125
  void clear_timeout_change() {
 
126
    timeout_change_ = false;
 
127
  }
 
128
 protected:
 
129
  virtual void OnTimeoutChange() {
 
130
    timeout_change_ = true;
 
131
  }
 
132
  bool timeout_change_;
 
133
};
 
134
 
 
135
//
 
136
// this unit test is primarily concerned (for now) with the timeout
 
137
// functionality in tasks.  It works as follows:
 
138
//
 
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
 
145
 
 
146
class TaskTest : public sigslot::has_slots<> {
 
147
 public:
 
148
  TaskTest() {}
 
149
 
 
150
  // no need to delete any tasks; the task runner owns them
 
151
  ~TaskTest() {}
 
152
 
 
153
  void Start() {
 
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();
 
164
    }
 
165
 
 
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();
 
174
    }
 
175
 
 
176
    // start all the tasks in random order
 
177
    int stuck_index = 0;
 
178
    int happy_index = 0;
 
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();
 
184
        } else {
 
185
          happy_[happy_index++].task_->Start();
 
186
        }
 
187
      } else if (stuck_index < STUCK_TASK_COUNT) {
 
188
        stuck_[stuck_index++].task_->Start();
 
189
      } else {
 
190
        happy_[happy_index++].task_->Start();
 
191
      }
 
192
    }
 
193
 
 
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;
 
198
    }
 
199
 
 
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);
 
203
 
 
204
    // run the unblocked tasks
 
205
    LOG(LS_INFO) << "Running tasks";
 
206
    task_runner_.RunTasks();
 
207
 
 
208
    std::cout << "Start time is " << GetCurrentTime() << std::endl;
 
209
 
 
210
    // give all the stuck tasks time to timeout
 
211
    for (int i = 0; !task_runner_.AllChildrenDone() && i < STUCK_TASK_COUNT;
 
212
         ++i) {
 
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();
 
217
        }
 
218
      }
 
219
      LOG(LS_INFO) << "Polling tasks";
 
220
      task_runner_.PollTasks();
 
221
    }
 
222
 
 
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;
 
228
  }
 
229
 
 
230
  void OnTimeoutStuck(const int id) {
 
231
    LOG(LS_INFO) << "Timed out task " << id;
 
232
 
 
233
    int i;
 
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;
 
238
        break;
 
239
      }
 
240
    }
 
241
 
 
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);
 
245
  }
 
246
 
 
247
  void OnTimeoutHappy(const int id) {
 
248
    int i;
 
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;
 
253
        break;
 
254
      }
 
255
    }
 
256
 
 
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);
 
260
  }
 
261
 
 
262
  void OnDoneHappy(const int id) {
 
263
    int i;
 
264
    for (i = 0; i < HAPPY_TASK_COUNT; ++i) {
 
265
      if (happy_[i].xlat_ == id) {
 
266
        happy_[i].task_ = NULL;
 
267
        break;
 
268
      }
 
269
    }
 
270
 
 
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);
 
274
  }
 
275
 
 
276
  void check_passed() {
 
277
    EXPECT_TRUE(task_runner_.AllChildrenDone());
 
278
 
 
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_);
 
282
    }
 
283
 
 
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;        
 
290
      }
 
291
    }
 
292
 
 
293
    std::cout.flush();
 
294
  }
 
295
 
 
296
 private:
 
297
  struct TaskInfo {
 
298
    IdTimeoutTask *task_;
 
299
    bool timed_out_;
 
300
    int xlat_;
 
301
  };
 
302
 
 
303
  MyTaskRunner task_runner_;
 
304
  TaskInfo stuck_[STUCK_TASK_COUNT];
 
305
  TaskInfo happy_[HAPPY_TASK_COUNT];
 
306
};
 
307
 
 
308
TEST(start_task_test, Timeout) {
 
309
  TaskTest task_test;
 
310
  task_test.Start();
 
311
  task_test.check_passed();
 
312
}
 
313
 
 
314
// Test for aborting the task while it is running
 
315
 
 
316
class AbortTask : public Task {
 
317
 public:
 
318
  explicit AbortTask(TaskParent *parent) : Task(parent) {
 
319
    set_timeout_seconds(1);
 
320
  }
 
321
 
 
322
  virtual int ProcessStart() {
 
323
    Abort();
 
324
    return STATE_NEXT;
 
325
  }
 
326
 private:
 
327
  DISALLOW_EVIL_CONSTRUCTORS(AbortTask);
 
328
};
 
329
 
 
330
class TaskAbortTest : public sigslot::has_slots<> {
 
331
 public:
 
332
  TaskAbortTest() {}
 
333
 
 
334
  // no need to delete any tasks; the task runner owns them
 
335
  ~TaskAbortTest() {}
 
336
 
 
337
  void Start() {
 
338
    Task *abort_task = new AbortTask(&task_runner_);
 
339
    abort_task->SignalTimeout.connect(this, &TaskAbortTest::OnTimeout);
 
340
    abort_task->Start();
 
341
 
 
342
    // run the task
 
343
    task_runner_.RunTasks();
 
344
  }
 
345
 
 
346
 private:
 
347
  void OnTimeout() {
 
348
    FAIL() << "Task timed out instead of aborting.";
 
349
  }
 
350
 
 
351
  MyTaskRunner task_runner_;
 
352
  DISALLOW_EVIL_CONSTRUCTORS(TaskAbortTest);
 
353
};
 
354
 
 
355
TEST(start_task_test, Abort) {
 
356
  TaskAbortTest abort_test;
 
357
  abort_test.Start();
 
358
}
 
359
 
 
360
// Test for aborting a task to verify that it does the Wake operation
 
361
// which gets it deleted.
 
362
 
 
363
class SetBoolOnDeleteTask : public Task {
 
364
 public:
 
365
  SetBoolOnDeleteTask(TaskParent *parent, bool *set_when_deleted)
 
366
    : Task(parent),
 
367
      set_when_deleted_(set_when_deleted) {
 
368
    EXPECT_TRUE(NULL != set_when_deleted);
 
369
    EXPECT_FALSE(*set_when_deleted);
 
370
  }
 
371
 
 
372
  virtual ~SetBoolOnDeleteTask() {
 
373
    *set_when_deleted_ = true;
 
374
  }
 
375
 
 
376
  virtual int ProcessStart() {
 
377
    return STATE_BLOCKED;
 
378
  }
 
379
 
 
380
 private:
 
381
  bool* set_when_deleted_;
 
382
  DISALLOW_EVIL_CONSTRUCTORS(SetBoolOnDeleteTask);
 
383
};
 
384
 
 
385
class AbortShouldWakeTest : public sigslot::has_slots<> {
 
386
 public:
 
387
  AbortShouldWakeTest() {}
 
388
 
 
389
  // no need to delete any tasks; the task runner owns them
 
390
  ~AbortShouldWakeTest() {}
 
391
 
 
392
  void Start() {
 
393
    bool task_deleted = false;
 
394
    Task *task_to_abort = new SetBoolOnDeleteTask(&task_runner_, &task_deleted);
 
395
    task_to_abort->Start();
 
396
 
 
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);
 
401
 
 
402
    if (!task_deleted) {
 
403
      // avoid a crash (due to referencing a local variable)
 
404
      // if the test fails.
 
405
      task_runner_.RunTasks();
 
406
    }
 
407
  }
 
408
 
 
409
 private:
 
410
  void OnTimeout() {
 
411
    FAIL() << "Task timed out instead of aborting.";
 
412
  }
 
413
 
 
414
  MyTaskRunner task_runner_;
 
415
  DISALLOW_EVIL_CONSTRUCTORS(AbortShouldWakeTest);
 
416
};
 
417
 
 
418
TEST(start_task_test, AbortShouldWake) {
 
419
  AbortShouldWakeTest abort_should_wake_test;
 
420
  abort_should_wake_test.Start();
 
421
}
 
422
 
 
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<> {
 
427
 public:
 
428
  TimeoutChangeTest()
 
429
    : task_count_(ARRAY_SIZE(stuck_tasks_)) {}
 
430
 
 
431
  // no need to delete any tasks; the task runner owns them
 
432
  ~TimeoutChangeTest() {}
 
433
 
 
434
  void Start() {
 
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);
 
440
    }
 
441
 
 
442
    for (int i = task_count_ - 1; i >= 0; --i) {
 
443
      stuck_tasks_[i]->Start();
 
444
    }
 
445
    task_runner_.clear_timeout_change();
 
446
 
 
447
    // At this point, our timeouts are set as follows
 
448
    // task[0] is 2 seconds, task[1] at 3 seconds, etc.
 
449
 
 
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();
 
455
 
 
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();
 
461
 
 
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();
 
467
 
 
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();
 
477
      }
 
478
      Thread::Current()->socketserver()->Wait(500, false);
 
479
    }
 
480
  }
 
481
 
 
482
 private:
 
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) {
 
486
        task_count_--;
 
487
        stuck_tasks_[i] = NULL;
 
488
        break;
 
489
      }
 
490
    }
 
491
  }
 
492
 
 
493
  MyTaskRunner task_runner_;
 
494
  StuckTask* (stuck_tasks_[3]);
 
495
  int task_count_;
 
496
  DISALLOW_EVIL_CONSTRUCTORS(TimeoutChangeTest);
 
497
};
 
498
 
 
499
TEST(start_task_test, TimeoutChange) {
 
500
  TimeoutChangeTest timeout_change_test;
 
501
  timeout_change_test.Start();
 
502
}
 
503
 
 
504
class DeleteTestTaskRunner : public TaskRunner {
 
505
 public:
 
506
  DeleteTestTaskRunner() {
 
507
  }
 
508
  virtual void WakeTasks() { }
 
509
  virtual int64 CurrentTime() {
 
510
    return GetCurrentTime();
 
511
  }
 
512
 private:
 
513
  DISALLOW_EVIL_CONSTRUCTORS(DeleteTestTaskRunner);
 
514
};
 
515
 
 
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);
 
521
  happy_task->Start();
 
522
 
 
523
  // try deleting the task directly
 
524
  HappyTask* child_happy_task = new HappyTask(happy_task);
 
525
  delete child_happy_task;
 
526
 
 
527
  // run the unblocked tasks
 
528
  task_runner.RunTasks();
 
529
}
 
530
 
 
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);
 
537
  happy_task->Start();
 
538
 
 
539
  HappyTask* child_happy_task = new HappyTask(happy_task);
 
540
  child_happy_task->Start();
 
541
 
 
542
  // Never run the tasks
 
543
}
 
544
 
 
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);
 
551
  happy_task->Start();
 
552
 
 
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);
 
557
 
 
558
  // run the unblocked tasks
 
559
  task_runner.RunTasks();
 
560
}
 
561
 
 
562
}  // namespace talk_base