普通文本  |  286行  |  8.21 KB

// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// NOTE(vtl): These tests are inherently flaky (e.g., if run on a heavily-loaded
// system). Sorry. |kEpsilonMicros| may be increased to increase tolerance and
// reduce observed flakiness.

#include "mojo/system/waiter.h"

#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/synchronization/lock.h"
#include "base/threading/platform_thread.h"  // For |Sleep()|.
#include "base/threading/simple_thread.h"
#include "base/time/time.h"
#include "mojo/system/test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace mojo {
namespace system {
namespace {

const int64_t kMicrosPerMs = 1000;
const int64_t kEpsilonMicros = 15 * kMicrosPerMs;  // 15 ms.
const int64_t kPollTimeMicros = 10 * kMicrosPerMs;  // 10 ms.

class WaitingThread : public base::SimpleThread {
 public:
  explicit WaitingThread(MojoDeadline deadline)
      : base::SimpleThread("waiting_thread"),
        deadline_(deadline),
        done_(false),
        result_(MOJO_RESULT_UNKNOWN),
        elapsed_micros_(-1) {
    waiter_.Init();
  }

  virtual ~WaitingThread() {
    Join();
  }

  void WaitUntilDone(MojoResult* result, int64_t* elapsed_micros) {
    for (;;) {
      {
        base::AutoLock locker(lock_);
        if (done_) {
          *result = result_;
          *elapsed_micros = elapsed_micros_;
          break;
        }
      }

      base::PlatformThread::Sleep(
          base::TimeDelta::FromMicroseconds(kPollTimeMicros));
    }
  }

  Waiter* waiter() { return &waiter_; }

 private:
  virtual void Run() OVERRIDE {
    test::Stopwatch stopwatch;
    MojoResult result;
    int64_t elapsed_micros;

    stopwatch.Start();
    result = waiter_.Wait(deadline_);
    elapsed_micros = stopwatch.Elapsed();

    {
      base::AutoLock locker(lock_);
      done_ = true;
      result_ = result;
      elapsed_micros_ = elapsed_micros;
    }
  }

  const MojoDeadline deadline_;
  Waiter waiter_;  // Thread-safe.

  base::Lock lock_;  // Protects the following members.
  bool done_;
  MojoResult result_;
  int64_t elapsed_micros_;

  DISALLOW_COPY_AND_ASSIGN(WaitingThread);
};

TEST(WaiterTest, Basic) {
  MojoResult result;
  int64_t elapsed_micros;

  // Finite deadline.

  // Awake immediately after thread start.
  {
    WaitingThread thread(static_cast<MojoDeadline>(10 * kEpsilonMicros));
    thread.Start();
    thread.waiter()->Awake(0);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(0, result);
    EXPECT_LT(elapsed_micros, kEpsilonMicros);
  }

  // Awake before after thread start.
  {
    WaitingThread thread(static_cast<MojoDeadline>(10 * kEpsilonMicros));
    thread.waiter()->Awake(MOJO_RESULT_CANCELLED);
    thread.Start();
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
    EXPECT_LT(elapsed_micros, kEpsilonMicros);
  }

  // Awake some time after thread start.
  {
    WaitingThread thread(static_cast<MojoDeadline>(10 * kEpsilonMicros));
    thread.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    thread.waiter()->Awake(1);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(1, result);
    EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros);
    EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros);
  }

  // Awake some longer time after thread start.
  {
    WaitingThread thread(static_cast<MojoDeadline>(10 * kEpsilonMicros));
    thread.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(5 * kEpsilonMicros));
    thread.waiter()->Awake(1);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(1, result);
    EXPECT_GT(elapsed_micros, (5-1) * kEpsilonMicros);
    EXPECT_LT(elapsed_micros, (5+1) * kEpsilonMicros);
  }

  // Don't awake -- time out (on another thread).
  {
    WaitingThread thread(static_cast<MojoDeadline>(2 * kEpsilonMicros));
    thread.Start();
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, result);
    EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros);
    EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros);
  }

  // No (indefinite) deadline.

  // Awake immediately after thread start.
  {
    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
    thread.Start();
    thread.waiter()->Awake(0);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(0, result);
    EXPECT_LT(elapsed_micros, kEpsilonMicros);
  }

  // Awake before after thread start.
  {
    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
    thread.waiter()->Awake(MOJO_RESULT_CANCELLED);
    thread.Start();
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
    EXPECT_LT(elapsed_micros, kEpsilonMicros);
  }

  // Awake some time after thread start.
  {
    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
    thread.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    thread.waiter()->Awake(1);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(1, result);
    EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros);
    EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros);
  }

  // Awake some longer time after thread start.
  {
    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
    thread.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(5 * kEpsilonMicros));
    thread.waiter()->Awake(1);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(1, result);
    EXPECT_GT(elapsed_micros, (5-1) * kEpsilonMicros);
    EXPECT_LT(elapsed_micros, (5+1) * kEpsilonMicros);
  }
}

TEST(WaiterTest, TimeOut) {
  test::Stopwatch stopwatch;
  int64_t elapsed_micros;

  Waiter waiter;

  waiter.Init();
  stopwatch.Start();
  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED, waiter.Wait(0));
  elapsed_micros = stopwatch.Elapsed();
  EXPECT_LT(elapsed_micros, kEpsilonMicros);

  waiter.Init();
  stopwatch.Start();
  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
            waiter.Wait(static_cast<MojoDeadline>(2 * kEpsilonMicros)));
  elapsed_micros = stopwatch.Elapsed();
  EXPECT_GT(elapsed_micros, (2-1) * kEpsilonMicros);
  EXPECT_LT(elapsed_micros, (2+1) * kEpsilonMicros);

  waiter.Init();
  stopwatch.Start();
  EXPECT_EQ(MOJO_RESULT_DEADLINE_EXCEEDED,
            waiter.Wait(static_cast<MojoDeadline>(5 * kEpsilonMicros)));
  elapsed_micros = stopwatch.Elapsed();
  EXPECT_GT(elapsed_micros, (5-1) * kEpsilonMicros);
  EXPECT_LT(elapsed_micros, (5+1) * kEpsilonMicros);
}

// The first |Awake()| should always win.
TEST(WaiterTest, MultipleAwakes) {
  MojoResult result;
  int64_t elapsed_micros;

  {
    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
    thread.Start();
    thread.waiter()->Awake(0);
    thread.waiter()->Awake(1);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(0, result);
    EXPECT_LT(elapsed_micros, kEpsilonMicros);
  }

  {
    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
    thread.waiter()->Awake(1);
    thread.Start();
    thread.waiter()->Awake(0);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(1, result);
    EXPECT_LT(elapsed_micros, kEpsilonMicros);
  }

  {
    WaitingThread thread(MOJO_DEADLINE_INDEFINITE);
    thread.Start();
    thread.waiter()->Awake(10);
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    thread.waiter()->Awake(20);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(10, result);
    EXPECT_LT(elapsed_micros, kEpsilonMicros);
  }

  {
    WaitingThread thread(static_cast<MojoDeadline>(10 * kEpsilonMicros));
    thread.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(1 * kEpsilonMicros));
    thread.waiter()->Awake(MOJO_RESULT_FAILED_PRECONDITION);
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    thread.waiter()->Awake(0);
    thread.WaitUntilDone(&result, &elapsed_micros);
    EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
    EXPECT_GT(elapsed_micros, (1-1) * kEpsilonMicros);
    EXPECT_LT(elapsed_micros, (1+1) * kEpsilonMicros);
  }
}

}  // namespace
}  // namespace system
}  // namespace mojo