root/mojo/system/waiter_list_unittest.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. TEST
  2. TEST
  3. TEST
  4. TEST

// 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_list.h"

#include "base/threading/platform_thread.h"  // For |Sleep()|.
#include "base/time/time.h"
#include "mojo/system/waiter.h"
#include "mojo/system/waiter_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace mojo {
namespace system {
namespace {

const int64_t kMicrosPerMs = 1000;
const int64_t kEpsilonMicros = 30 * kMicrosPerMs;  // 30 ms.

TEST(WaiterListTest, BasicCancel) {
  MojoResult result;

  // Cancel immediately after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_READABLE, 0);
    thread.Start();
    waiter_list.CancelAllWaiters();
    waiter_list.RemoveWaiter(thread.waiter());  // Double-remove okay.
  }  // Join |thread|.
  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);

  // Cancel before after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_WRITABLE, 1);
    waiter_list.CancelAllWaiters();
    thread.Start();
  }  // Join |thread|.
  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);

  // Cancel some time after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_READABLE, 2);
    thread.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    waiter_list.CancelAllWaiters();
  }  // Join |thread|.
  EXPECT_EQ(MOJO_RESULT_CANCELLED, result);
}

TEST(WaiterListTest, BasicAwakeSatisfied) {
  MojoResult result;

  // Awake immediately after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_READABLE, 0);
    thread.Start();
    waiter_list.AwakeWaitersForStateChange(MOJO_WAIT_FLAG_READABLE,
                                           MOJO_WAIT_FLAG_READABLE |
                                              MOJO_WAIT_FLAG_WRITABLE);
    waiter_list.RemoveWaiter(thread.waiter());
  }  // Join |thread|.
  EXPECT_EQ(0, result);

  // Awake before after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_WRITABLE, 1);
    waiter_list.AwakeWaitersForStateChange(MOJO_WAIT_FLAG_WRITABLE,
                                           MOJO_WAIT_FLAG_READABLE |
                                              MOJO_WAIT_FLAG_WRITABLE);
    waiter_list.RemoveWaiter(thread.waiter());
    waiter_list.RemoveWaiter(thread.waiter());  // Double-remove okay.
    thread.Start();
  }  // Join |thread|.
  EXPECT_EQ(1, result);

  // Awake some time after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_READABLE, 2);
    thread.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    waiter_list.AwakeWaitersForStateChange(MOJO_WAIT_FLAG_READABLE,
                                           MOJO_WAIT_FLAG_READABLE |
                                              MOJO_WAIT_FLAG_WRITABLE);
    waiter_list.RemoveWaiter(thread.waiter());
  }  // Join |thread|.
  EXPECT_EQ(2, result);
}

TEST(WaiterListTest, BasicAwakeUnsatisfiable) {
  MojoResult result;

  // Awake (for unsatisfiability) immediately after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_READABLE, 0);
    thread.Start();
    waiter_list.AwakeWaitersForStateChange(0, MOJO_WAIT_FLAG_WRITABLE);
    waiter_list.RemoveWaiter(thread.waiter());
  }  // Join |thread|.
  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);

  // Awake (for unsatisfiability) before after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_WRITABLE, 1);
    waiter_list.AwakeWaitersForStateChange(MOJO_WAIT_FLAG_READABLE,
                                           MOJO_WAIT_FLAG_READABLE);
    waiter_list.RemoveWaiter(thread.waiter());
    thread.Start();
  }  // Join |thread|.
  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);

  // Awake (for unsatisfiability) some time after thread start.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread(&result);
    waiter_list.AddWaiter(thread.waiter(), MOJO_WAIT_FLAG_READABLE, 2);
    thread.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    waiter_list.AwakeWaitersForStateChange(0, MOJO_WAIT_FLAG_WRITABLE);
    waiter_list.RemoveWaiter(thread.waiter());
    waiter_list.RemoveWaiter(thread.waiter());  // Double-remove okay.
  }  // Join |thread|.
  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result);
}

TEST(WaiterListTest, MultipleWaiters) {
  MojoResult result1;
  MojoResult result2;
  MojoResult result3;
  MojoResult result4;

  // Cancel two waiters.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread1(&result1);
    waiter_list.AddWaiter(thread1.waiter(), MOJO_WAIT_FLAG_READABLE, 0);
    thread1.Start();
    test::SimpleWaiterThread thread2(&result2);
    waiter_list.AddWaiter(thread2.waiter(), MOJO_WAIT_FLAG_WRITABLE, 1);
    thread2.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    waiter_list.CancelAllWaiters();
  }  // Join threads.
  EXPECT_EQ(MOJO_RESULT_CANCELLED, result1);
  EXPECT_EQ(MOJO_RESULT_CANCELLED, result2);

  // Awake one waiter, cancel other.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread1(&result1);
    waiter_list.AddWaiter(thread1.waiter(), MOJO_WAIT_FLAG_READABLE, 2);
    thread1.Start();
    test::SimpleWaiterThread thread2(&result2);
    waiter_list.AddWaiter(thread2.waiter(), MOJO_WAIT_FLAG_WRITABLE, 3);
    thread2.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    waiter_list.AwakeWaitersForStateChange(MOJO_WAIT_FLAG_READABLE,
                                           MOJO_WAIT_FLAG_READABLE |
                                              MOJO_WAIT_FLAG_WRITABLE);
    waiter_list.RemoveWaiter(thread1.waiter());
    waiter_list.CancelAllWaiters();
  }  // Join threads.
  EXPECT_EQ(2, result1);
  EXPECT_EQ(MOJO_RESULT_CANCELLED, result2);

  // Cancel one waiter, awake other for unsatisfiability.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread1(&result1);
    waiter_list.AddWaiter(thread1.waiter(), MOJO_WAIT_FLAG_READABLE, 4);
    thread1.Start();
    test::SimpleWaiterThread thread2(&result2);
    waiter_list.AddWaiter(thread2.waiter(), MOJO_WAIT_FLAG_WRITABLE, 5);
    thread2.Start();
    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(2 * kEpsilonMicros));
    waiter_list.AwakeWaitersForStateChange(0, MOJO_WAIT_FLAG_READABLE);
    waiter_list.RemoveWaiter(thread2.waiter());
    waiter_list.CancelAllWaiters();
  }  // Join threads.
  EXPECT_EQ(MOJO_RESULT_CANCELLED, result1);
  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2);

  // Cancel one waiter, awake other for unsatisfiability.
  {
    WaiterList waiter_list;
    test::SimpleWaiterThread thread1(&result1);
    waiter_list.AddWaiter(thread1.waiter(), MOJO_WAIT_FLAG_READABLE, 6);
    thread1.Start();

    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(1 * kEpsilonMicros));

    // Should do nothing.
    waiter_list.AwakeWaitersForStateChange(0,
                                           MOJO_WAIT_FLAG_READABLE |
                                               MOJO_WAIT_FLAG_WRITABLE);

    test::SimpleWaiterThread thread2(&result2);
    waiter_list.AddWaiter(thread2.waiter(), MOJO_WAIT_FLAG_WRITABLE, 7);
    thread2.Start();

    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(1 * kEpsilonMicros));

    // Awake #1.
    waiter_list.AwakeWaitersForStateChange(MOJO_WAIT_FLAG_READABLE,
                                           MOJO_WAIT_FLAG_READABLE |
                                               MOJO_WAIT_FLAG_WRITABLE);
    waiter_list.RemoveWaiter(thread1.waiter());

    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(1 * kEpsilonMicros));

    test::SimpleWaiterThread thread3(&result3);
    waiter_list.AddWaiter(thread3.waiter(), MOJO_WAIT_FLAG_WRITABLE, 8);
    thread3.Start();

    test::SimpleWaiterThread thread4(&result4);
    waiter_list.AddWaiter(thread4.waiter(), MOJO_WAIT_FLAG_READABLE, 9);
    thread4.Start();

    base::PlatformThread::Sleep(
        base::TimeDelta::FromMicroseconds(1 * kEpsilonMicros));

    // Awake #2 and #3 for unsatisfiability.
    waiter_list.AwakeWaitersForStateChange(0, MOJO_WAIT_FLAG_READABLE);
    waiter_list.RemoveWaiter(thread2.waiter());
    waiter_list.RemoveWaiter(thread3.waiter());

    // Cancel #4.
    waiter_list.CancelAllWaiters();
  }  // Join threads.
  EXPECT_EQ(6, result1);
  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2);
  EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result3);
  EXPECT_EQ(MOJO_RESULT_CANCELLED, result4);
}

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

/* [<][>][^][v][top][bottom][index][help] */