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