// 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): Some of these tests are inherently flaky (e.g., if run on a // heavily-loaded system). Sorry. |test::EpsilonDeadline()| may be increased to // increase tolerance and reduce observed flakiness (though doing so reduces the // meaningfulness of the test). #include "mojo/edk/system/awakable_list.h" #include "mojo/edk/system/handle_signals_state.h" #include "mojo/edk/system/test_utils.h" #include "mojo/edk/system/waiter.h" #include "mojo/edk/system/waiter_test_utils.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { namespace edk { namespace { TEST(AwakableListTest, BasicCancel) { MojoResult result; uintptr_t context; // Cancel immediately after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1); thread.Start(); awakable_list.CancelAll(); // Double-remove okay: awakable_list.Remove(thread.waiter()); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_CANCELLED, result); EXPECT_EQ(1u, context); // Cancel before after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2); awakable_list.CancelAll(); thread.Start(); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_CANCELLED, result); EXPECT_EQ(2u, context); // Cancel some time after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3); thread.Start(); test::Sleep(2 * test::EpsilonDeadline()); awakable_list.CancelAll(); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_CANCELLED, result); EXPECT_EQ(3u, context); } TEST(AwakableListTest, BasicAwakeSatisfied) { MojoResult result; uintptr_t context; // Awake immediately after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1); thread.Start(); awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); awakable_list.Remove(thread.waiter()); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(1u, context); // Awake before after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2); awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); awakable_list.Remove(thread.waiter()); // Double-remove okay: awakable_list.Remove(thread.waiter()); thread.Start(); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(2u, context); // Awake some time after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3); thread.Start(); test::Sleep(2 * test::EpsilonDeadline()); awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); awakable_list.Remove(thread.waiter()); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(3u, context); } TEST(AwakableListTest, BasicAwakeUnsatisfiable) { MojoResult result; uintptr_t context; // Awake (for unsatisfiability) immediately after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1); thread.Start(); awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE)); awakable_list.Remove(thread.waiter()); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); EXPECT_EQ(1u, context); // Awake (for unsatisfiability) before after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2); awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE)); awakable_list.Remove(thread.waiter()); thread.Start(); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); EXPECT_EQ(2u, context); // Awake (for unsatisfiability) some time after thread start. { AwakableList awakable_list; test::SimpleWaiterThread thread(&result, &context); awakable_list.Add(thread.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3); thread.Start(); test::Sleep(2 * test::EpsilonDeadline()); awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_WRITABLE)); awakable_list.Remove(thread.waiter()); // Double-remove okay: awakable_list.Remove(thread.waiter()); } // Join |thread|. EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); EXPECT_EQ(3u, context); } TEST(AwakableListTest, MultipleAwakables) { MojoResult result1; MojoResult result2; MojoResult result3; MojoResult result4; uintptr_t context1; uintptr_t context2; uintptr_t context3; uintptr_t context4; // Cancel two awakables. { AwakableList awakable_list; test::SimpleWaiterThread thread1(&result1, &context1); awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 1); thread1.Start(); test::SimpleWaiterThread thread2(&result2, &context2); awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 2); thread2.Start(); test::Sleep(2 * test::EpsilonDeadline()); awakable_list.CancelAll(); } // Join threads. EXPECT_EQ(MOJO_RESULT_CANCELLED, result1); EXPECT_EQ(1u, context1); EXPECT_EQ(MOJO_RESULT_CANCELLED, result2); EXPECT_EQ(2u, context2); // Awake one awakable, cancel other. { AwakableList awakable_list; test::SimpleWaiterThread thread1(&result1, &context1); awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 3); thread1.Start(); test::SimpleWaiterThread thread2(&result2, &context2); awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 4); thread2.Start(); test::Sleep(2 * test::EpsilonDeadline()); awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); awakable_list.Remove(thread1.waiter()); awakable_list.CancelAll(); } // Join threads. EXPECT_EQ(MOJO_RESULT_OK, result1); EXPECT_EQ(3u, context1); EXPECT_EQ(MOJO_RESULT_CANCELLED, result2); EXPECT_EQ(4u, context2); // Cancel one awakable, awake other for unsatisfiability. { AwakableList awakable_list; test::SimpleWaiterThread thread1(&result1, &context1); awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 5); thread1.Start(); test::SimpleWaiterThread thread2(&result2, &context2); awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 6); thread2.Start(); test::Sleep(2 * test::EpsilonDeadline()); awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE)); awakable_list.Remove(thread2.waiter()); awakable_list.CancelAll(); } // Join threads. EXPECT_EQ(MOJO_RESULT_CANCELLED, result1); EXPECT_EQ(5u, context1); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2); EXPECT_EQ(6u, context2); // Cancel one awakable, awake other for unsatisfiability. { AwakableList awakable_list; test::SimpleWaiterThread thread1(&result1, &context1); awakable_list.Add(thread1.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 7); thread1.Start(); test::Sleep(1 * test::EpsilonDeadline()); // Should do nothing. awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); test::SimpleWaiterThread thread2(&result2, &context2); awakable_list.Add(thread2.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 8); thread2.Start(); test::Sleep(1 * test::EpsilonDeadline()); // Awake #1. awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_READABLE, MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_WRITABLE)); awakable_list.Remove(thread1.waiter()); test::Sleep(1 * test::EpsilonDeadline()); test::SimpleWaiterThread thread3(&result3, &context3); awakable_list.Add(thread3.waiter(), MOJO_HANDLE_SIGNAL_WRITABLE, 9); thread3.Start(); test::SimpleWaiterThread thread4(&result4, &context4); awakable_list.Add(thread4.waiter(), MOJO_HANDLE_SIGNAL_READABLE, 10); thread4.Start(); test::Sleep(1 * test::EpsilonDeadline()); // Awake #2 and #3 for unsatisfiability. awakable_list.AwakeForStateChange(HandleSignalsState( MOJO_HANDLE_SIGNAL_NONE, MOJO_HANDLE_SIGNAL_READABLE)); awakable_list.Remove(thread2.waiter()); awakable_list.Remove(thread3.waiter()); // Cancel #4. awakable_list.CancelAll(); } // Join threads. EXPECT_EQ(MOJO_RESULT_OK, result1); EXPECT_EQ(7u, context1); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result2); EXPECT_EQ(8u, context2); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result3); EXPECT_EQ(9u, context3); EXPECT_EQ(MOJO_RESULT_CANCELLED, result4); EXPECT_EQ(10u, context4); } class KeepAwakable : public Awakable { public: KeepAwakable() : awake_count(0) {} bool Awake(MojoResult result, uintptr_t context) override { awake_count++; return true; } int awake_count; DISALLOW_COPY_AND_ASSIGN(KeepAwakable); }; class RemoveAwakable : public Awakable { public: RemoveAwakable() : awake_count(0) {} bool Awake(MojoResult result, uintptr_t context) override { awake_count++; return false; } int awake_count; DISALLOW_COPY_AND_ASSIGN(RemoveAwakable); }; TEST(AwakableListTest, KeepAwakablesReturningTrue) { KeepAwakable keep0; KeepAwakable keep1; RemoveAwakable remove0; RemoveAwakable remove1; RemoveAwakable remove2; HandleSignalsState hss(MOJO_HANDLE_SIGNAL_WRITABLE, MOJO_HANDLE_SIGNAL_WRITABLE); AwakableList remove_all; remove_all.Add(&remove0, MOJO_HANDLE_SIGNAL_WRITABLE, 0); remove_all.Add(&remove1, MOJO_HANDLE_SIGNAL_WRITABLE, 0); remove_all.AwakeForStateChange(hss); EXPECT_EQ(remove0.awake_count, 1); EXPECT_EQ(remove1.awake_count, 1); remove_all.AwakeForStateChange(hss); EXPECT_EQ(remove0.awake_count, 1); EXPECT_EQ(remove1.awake_count, 1); AwakableList remove_first; remove_first.Add(&remove2, MOJO_HANDLE_SIGNAL_WRITABLE, 0); remove_first.Add(&keep0, MOJO_HANDLE_SIGNAL_WRITABLE, 0); remove_first.Add(&keep1, MOJO_HANDLE_SIGNAL_WRITABLE, 0); remove_first.AwakeForStateChange(hss); EXPECT_EQ(keep0.awake_count, 1); EXPECT_EQ(keep1.awake_count, 1); EXPECT_EQ(remove2.awake_count, 1); remove_first.AwakeForStateChange(hss); EXPECT_EQ(keep0.awake_count, 2); EXPECT_EQ(keep1.awake_count, 2); EXPECT_EQ(remove2.awake_count, 1); remove_first.Remove(&keep0); remove_first.Remove(&keep1); } } // namespace } // namespace edk } // namespace mojo