1// Copyright 2013 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
5// NOTE(vtl): Some of these tests are inherently flaky (e.g., if run on a
6// heavily-loaded system). Sorry. |test::EpsilonTimeout()| may be increased to
7// increase tolerance and reduce observed flakiness (though doing so reduces the
8// meaningfulness of the test).
9
10#include "mojo/system/waiter.h"
11
12#include <stdint.h>
13
14#include "base/macros.h"
15#include "base/synchronization/lock.h"
16#include "base/threading/platform_thread.h"  // For |Sleep()|.
17#include "base/threading/simple_thread.h"
18#include "base/time/time.h"
19#include "mojo/system/test_utils.h"
20#include "testing/gtest/include/gtest/gtest.h"
21
22namespace mojo {
23namespace system {
24namespace {
25
26const int64_t kMicrosPerMs = 1000;
27const int64_t kPollTimeMicros = 10 * kMicrosPerMs;  // 10 ms.
28
29class WaitingThread : public base::SimpleThread {
30 public:
31  explicit WaitingThread(MojoDeadline deadline)
32      : base::SimpleThread("waiting_thread"),
33        deadline_(deadline),
34        done_(false),
35        result_(MOJO_RESULT_UNKNOWN),
36        context_(static_cast<uint32_t>(-1)) {
37    waiter_.Init();
38  }
39
40  virtual ~WaitingThread() { Join(); }
41
42  void WaitUntilDone(MojoResult* result,
43                     uint32_t* context,
44                     base::TimeDelta* elapsed) {
45    for (;;) {
46      {
47        base::AutoLock locker(lock_);
48        if (done_) {
49          *result = result_;
50          *context = context_;
51          *elapsed = elapsed_;
52          break;
53        }
54      }
55
56      base::PlatformThread::Sleep(
57          base::TimeDelta::FromMicroseconds(kPollTimeMicros));
58    }
59  }
60
61  Waiter* waiter() { return &waiter_; }
62
63 private:
64  virtual void Run() OVERRIDE {
65    test::Stopwatch stopwatch;
66    MojoResult result;
67    uint32_t context = static_cast<uint32_t>(-1);
68    base::TimeDelta elapsed;
69
70    stopwatch.Start();
71    result = waiter_.Wait(deadline_, &context);
72    elapsed = stopwatch.Elapsed();
73
74    {
75      base::AutoLock locker(lock_);
76      done_ = true;
77      result_ = result;
78      context_ = context;
79      elapsed_ = elapsed;
80    }
81  }
82
83  const MojoDeadline deadline_;
84  Waiter waiter_;  // Thread-safe.
85
86  base::Lock lock_;  // Protects the following members.
87  bool done_;
88  MojoResult result_;
89  uint32_t context_;
90  base::TimeDelta elapsed_;
91
92  DISALLOW_COPY_AND_ASSIGN(WaitingThread);
93};
94
95TEST(WaiterTest, Basic) {
96  MojoResult result;
97  uint32_t context;
98  base::TimeDelta elapsed;
99
100  // Finite deadline.
101
102  // Awake immediately after thread start.
103  {
104    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
105    thread.Start();
106    thread.waiter()->Awake(MOJO_RESULT_OK, 1);
107    thread.WaitUntilDone(&result, &context, &elapsed);
108    EXPECT_EQ(MOJO_RESULT_OK, result);
109    EXPECT_EQ(1u, context);
110    EXPECT_LT(elapsed, test::EpsilonTimeout());
111  }
112
113  // Awake before after thread start.
114  {
115    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
116    thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 2);
117    thread.Start();
118    thread.WaitUntilDone(&result, &context, &elapsed);
119    EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
120    EXPECT_EQ(2u, context);
121    EXPECT_LT(elapsed, test::EpsilonTimeout());
122  }
123
124  // Awake some time after thread start.
125  {
126    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
127    thread.Start();
128    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
129    thread.waiter()->Awake(1, 3);
130    thread.WaitUntilDone(&result, &context, &elapsed);
131    EXPECT_EQ(1, result);
132    EXPECT_EQ(3u, context);
133    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
134    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
135  }
136
137  // Awake some longer time after thread start.
138  {
139    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
140    thread.Start();
141    base::PlatformThread::Sleep(5 * test::EpsilonTimeout());
142    thread.waiter()->Awake(2, 4);
143    thread.WaitUntilDone(&result, &context, &elapsed);
144    EXPECT_EQ(2, result);
145    EXPECT_EQ(4u, context);
146    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
147    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
148  }
149
150  // Don't awake -- time out (on another thread).
151  {
152    WaitingThread thread(2 * test::EpsilonTimeout().InMicroseconds());
153    thread.Start();
154    thread.WaitUntilDone(&result, &context, &elapsed);
155    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
156    EXPECT_EQ(static_cast<uint32_t>(-1), context);
157    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
158    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
159  }
160
161  // No (indefinite) deadline.
162
163  // Awake immediately after thread start.
164  {
165    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
166    thread.Start();
167    thread.waiter()->Awake(MOJO_RESULT_OK, 5);
168    thread.WaitUntilDone(&result, &context, &elapsed);
169    EXPECT_EQ(MOJO_RESULT_OK, result);
170    EXPECT_EQ(5u, context);
171    EXPECT_LT(elapsed, test::EpsilonTimeout());
172  }
173
174  // Awake before after thread start.
175  {
176    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
177    thread.waiter()->Awake(MOJO_RESULT_CANCELLED, 6);
178    thread.Start();
179    thread.WaitUntilDone(&result, &context, &elapsed);
180    EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
181    EXPECT_EQ(6u, context);
182    EXPECT_LT(elapsed, test::EpsilonTimeout());
183  }
184
185  // Awake some time after thread start.
186  {
187    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
188    thread.Start();
189    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
190    thread.waiter()->Awake(1, 7);
191    thread.WaitUntilDone(&result, &context, &elapsed);
192    EXPECT_EQ(1, result);
193    EXPECT_EQ(7u, context);
194    EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
195    EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
196  }
197
198  // Awake some longer time after thread start.
199  {
200    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
201    thread.Start();
202    base::PlatformThread::Sleep(5 * test::EpsilonTimeout());
203    thread.waiter()->Awake(2, 8);
204    thread.WaitUntilDone(&result, &context, &elapsed);
205    EXPECT_EQ(2, result);
206    EXPECT_EQ(8u, context);
207    EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
208    EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
209  }
210}
211
212TEST(WaiterTest, TimeOut) {
213  test::Stopwatch stopwatch;
214  base::TimeDelta elapsed;
215
216  Waiter waiter;
217  uint32_t context = 123;
218
219  waiter.Init();
220  stopwatch.Start();
221  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0, &context));
222  elapsed = stopwatch.Elapsed();
223  EXPECT_LT(elapsed, test::EpsilonTimeout());
224  EXPECT_EQ(123u, context);
225
226  waiter.Init();
227  stopwatch.Start();
228  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
229            waiter.Wait(2 * test::EpsilonTimeout().InMicroseconds(), &context));
230  elapsed = stopwatch.Elapsed();
231  EXPECT_GT(elapsed, (2 - 1) * test::EpsilonTimeout());
232  EXPECT_LT(elapsed, (2 + 1) * test::EpsilonTimeout());
233  EXPECT_EQ(123u, context);
234
235  waiter.Init();
236  stopwatch.Start();
237  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
238            waiter.Wait(5 * test::EpsilonTimeout().InMicroseconds(), &context));
239  elapsed = stopwatch.Elapsed();
240  EXPECT_GT(elapsed, (5 - 1) * test::EpsilonTimeout());
241  EXPECT_LT(elapsed, (5 + 1) * test::EpsilonTimeout());
242  EXPECT_EQ(123u, context);
243}
244
245// The first |Awake()| should always win.
246TEST(WaiterTest, MultipleAwakes) {
247  MojoResult result;
248  uint32_t context;
249  base::TimeDelta elapsed;
250
251  {
252    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
253    thread.Start();
254    thread.waiter()->Awake(MOJO_RESULT_OK, 1);
255    thread.waiter()->Awake(1, 2);
256    thread.WaitUntilDone(&result, &context, &elapsed);
257    EXPECT_EQ(MOJO_RESULT_OK, result);
258    EXPECT_EQ(1u, context);
259    EXPECT_LT(elapsed, test::EpsilonTimeout());
260  }
261
262  {
263    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
264    thread.waiter()->Awake(1, 3);
265    thread.Start();
266    thread.waiter()->Awake(MOJO_RESULT_OK, 4);
267    thread.WaitUntilDone(&result, &context, &elapsed);
268    EXPECT_EQ(1, result);
269    EXPECT_EQ(3u, context);
270    EXPECT_LT(elapsed, test::EpsilonTimeout());
271  }
272
273  {
274    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
275    thread.Start();
276    thread.waiter()->Awake(10, 5);
277    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
278    thread.waiter()->Awake(20, 6);
279    thread.WaitUntilDone(&result, &context, &elapsed);
280    EXPECT_EQ(10, result);
281    EXPECT_EQ(5u, context);
282    EXPECT_LT(elapsed, test::EpsilonTimeout());
283  }
284
285  {
286    WaitingThread thread(10 * test::EpsilonTimeout().InMicroseconds());
287    thread.Start();
288    base::PlatformThread::Sleep(1 * test::EpsilonTimeout());
289    thread.waiter()->Awake(MOJO_RESULT_FAILED_PRECONDITION, 7);
290    base::PlatformThread::Sleep(2 * test::EpsilonTimeout());
291    thread.waiter()->Awake(MOJO_RESULT_OK, 8);
292    thread.WaitUntilDone(&result, &context, &elapsed);
293    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
294    EXPECT_EQ(7u, context);
295    EXPECT_GT(elapsed, (1 - 1) * test::EpsilonTimeout());
296    EXPECT_LT(elapsed, (1 + 1) * test::EpsilonTimeout());
297  }
298}
299
300}  // namespace
301}  // namespace system
302}  // namespace mojo
303