// Copyright 2017 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. #include #include #include #include #include "base/bind.h" #include "base/callback.h" #include "base/macros.h" #include "base/memory/ptr_util.h" #include "base/synchronization/waitable_event.h" #include "base/threading/platform_thread.h" #include "base/threading/simple_thread.h" #include "base/time/time.h" #include "mojo/edk/test/mojo_test_base.h" #include "mojo/public/c/system/data_pipe.h" #include "mojo/public/c/system/types.h" #include "mojo/public/c/system/watcher.h" #include "testing/gtest/include/gtest/gtest.h" namespace mojo { namespace edk { namespace { using WatcherTest = test::MojoTestBase; class WatchHelper { public: using ContextCallback = base::Callback; WatchHelper() {} ~WatchHelper() {} MojoResult CreateWatcher(MojoHandle* handle) { return MojoCreateWatcher(&Notify, handle); } uintptr_t CreateContext(const ContextCallback& callback) { return CreateContextWithCancel(callback, base::Closure()); } uintptr_t CreateContextWithCancel(const ContextCallback& callback, const base::Closure& cancel_callback) { auto context = base::MakeUnique(callback); NotificationContext* raw_context = context.get(); raw_context->SetCancelCallback(base::Bind( [](std::unique_ptr context, const base::Closure& cancel_callback) { if (cancel_callback) cancel_callback.Run(); }, base::Passed(&context), cancel_callback)); return reinterpret_cast(raw_context); } private: class NotificationContext { public: explicit NotificationContext(const ContextCallback& callback) : callback_(callback) {} ~NotificationContext() {} void SetCancelCallback(const base::Closure& cancel_callback) { cancel_callback_ = cancel_callback; } void Notify(MojoResult result, MojoHandleSignalsState state) { if (result == MOJO_RESULT_CANCELLED) cancel_callback_.Run(); else callback_.Run(result, state); } private: const ContextCallback callback_; base::Closure cancel_callback_; DISALLOW_COPY_AND_ASSIGN(NotificationContext); }; static void Notify(uintptr_t context, MojoResult result, MojoHandleSignalsState state, MojoWatcherNotificationFlags flags) { reinterpret_cast(context)->Notify(result, state); } DISALLOW_COPY_AND_ASSIGN(WatchHelper); }; class ThreadedRunner : public base::SimpleThread { public: explicit ThreadedRunner(const base::Closure& callback) : SimpleThread("ThreadedRunner"), callback_(callback) {} ~ThreadedRunner() override {} void Run() override { callback_.Run(); } private: const base::Closure callback_; DISALLOW_COPY_AND_ASSIGN(ThreadedRunner); }; void ExpectNoNotification(uintptr_t context, MojoResult result, MojoHandleSignalsState state, MojoWatcherNotificationFlags flags) { NOTREACHED(); } void ExpectOnlyCancel(uintptr_t context, MojoResult result, MojoHandleSignalsState state, MojoWatcherNotificationFlags flags) { EXPECT_EQ(result, MOJO_RESULT_CANCELLED); } TEST_F(WatcherTest, InvalidArguments) { EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCreateWatcher(&ExpectNoNotification, nullptr)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w)); // Try to watch unwatchable handles. EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoWatch(w, w, MOJO_HANDLE_SIGNAL_READABLE, 0)); MojoHandle buffer_handle = CreateBuffer(42); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoWatch(w, buffer_handle, MOJO_HANDLE_SIGNAL_READABLE, 0)); // Try to cancel a watch on an invalid watcher handle. EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoCancelWatch(buffer_handle, 0)); // Try to arm an invalid handle. EXPECT_EQ( MOJO_RESULT_INVALID_ARGUMENT, MojoArmWatcher(MOJO_HANDLE_INVALID, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoArmWatcher(buffer_handle, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(buffer_handle)); // Try to arm with a non-null count but at least one null output buffer. uint32_t num_ready_contexts = 1; uintptr_t ready_context; MojoResult ready_result; MojoHandleSignalsState ready_state; EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoArmWatcher(w, &num_ready_contexts, nullptr, &ready_result, &ready_state)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoArmWatcher(w, &num_ready_contexts, &ready_context, nullptr, &ready_state)); EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT, MojoArmWatcher(w, &num_ready_contexts, &ready_context, &ready_result, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); } TEST_F(WatcherTest, WatchMessagePipeReadable) { MojoHandle a, b; CreateMessagePipe(&a, &b); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; int num_expected_notifications = 1; const uintptr_t readable_a_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, int* expected_count, MojoResult result, MojoHandleSignalsState state) { EXPECT_GT(*expected_count, 0); *expected_count -= 1; EXPECT_EQ(MOJO_RESULT_OK, result); event->Signal(); }, &event, &num_expected_notifications)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); const char kMessage1[] = "hey hey hey hey"; const char kMessage2[] = "i said hey"; const char kMessage3[] = "what's goin' on?"; // Writing to |b| multiple times should notify exactly once. WriteMessage(b, kMessage1); WriteMessage(b, kMessage2); event.Wait(); // This also shouldn't fire a notification; the watcher is still disarmed. WriteMessage(b, kMessage3); // Arming should fail with relevant information. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(readable_a_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); // Flush the three messages from above. EXPECT_EQ(kMessage1, ReadMessage(a)); EXPECT_EQ(kMessage2, ReadMessage(a)); EXPECT_EQ(kMessage3, ReadMessage(a)); // Now we can rearm the watcher. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); } TEST_F(WatcherTest, CloseWatchedMessagePipeHandle) { MojoHandle a, b; CreateMessagePipe(&a, &b); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; const uintptr_t readable_a_context = helper.CreateContextWithCancel( WatchHelper::ContextCallback(), base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); // Test that closing a watched handle fires an appropriate notification, even // when the watcher is unarmed. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); } TEST_F(WatcherTest, CloseWatchedMessagePipeHandlePeer) { MojoHandle a, b; CreateMessagePipe(&a, &b); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; const uintptr_t readable_a_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); event->Signal(); }, &event)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); // Test that closing a watched handle's peer with an armed watcher fires an // appropriate notification. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); event.Wait(); // And now arming should fail with correct information about |a|'s state. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(readable_a_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED); EXPECT_FALSE(ready_states[0].satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); } TEST_F(WatcherTest, WatchDataPipeConsumerReadable) { constexpr size_t kTestPipeCapacity = 64; MojoHandle producer, consumer; CreateDataPipe(&producer, &consumer, kTestPipeCapacity); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; int num_expected_notifications = 1; const uintptr_t readable_consumer_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, int* expected_count, MojoResult result, MojoHandleSignalsState state) { EXPECT_GT(*expected_count, 0); *expected_count -= 1; EXPECT_EQ(MOJO_RESULT_OK, result); event->Signal(); }, &event, &num_expected_notifications)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE, readable_consumer_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); const char kMessage1[] = "hey hey hey hey"; const char kMessage2[] = "i said hey"; const char kMessage3[] = "what's goin' on?"; // Writing to |producer| multiple times should notify exactly once. WriteData(producer, kMessage1); WriteData(producer, kMessage2); event.Wait(); // This also shouldn't fire a notification; the watcher is still disarmed. WriteData(producer, kMessage3); // Arming should fail with relevant information. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(readable_consumer_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); // Flush the three messages from above. EXPECT_EQ(kMessage1, ReadData(consumer, sizeof(kMessage1) - 1)); EXPECT_EQ(kMessage2, ReadData(consumer, sizeof(kMessage2) - 1)); EXPECT_EQ(kMessage3, ReadData(consumer, sizeof(kMessage3) - 1)); // Now we can rearm the watcher. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); } TEST_F(WatcherTest, WatchDataPipeConsumerNewDataReadable) { constexpr size_t kTestPipeCapacity = 64; MojoHandle producer, consumer; CreateDataPipe(&producer, &consumer, kTestPipeCapacity); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; int num_new_data_notifications = 0; const uintptr_t new_data_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, int* notification_count, MojoResult result, MojoHandleSignalsState state) { *notification_count += 1; EXPECT_EQ(MOJO_RESULT_OK, result); event->Signal(); }, &event, &num_new_data_notifications)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE, new_data_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); const char kMessage1[] = "hey hey hey hey"; const char kMessage2[] = "i said hey"; const char kMessage3[] = "what's goin' on?"; // Writing to |producer| multiple times should notify exactly once. WriteData(producer, kMessage1); WriteData(producer, kMessage2); event.Wait(); // This also shouldn't fire a notification; the watcher is still disarmed. WriteData(producer, kMessage3); // Arming should fail with relevant information. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(new_data_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); // Attempt to read more data than is available. Should fail but clear the // NEW_DATA_READABLE signal. char large_buffer[512]; uint32_t large_read_size = 512; EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE, MojoReadData(consumer, large_buffer, &large_read_size, MOJO_READ_DATA_FLAG_ALL_OR_NONE)); // Attempt to arm again. Should succeed. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Write more data. Should notify. event.Reset(); WriteData(producer, kMessage1); event.Wait(); // Reading some data should clear NEW_DATA_READABLE again so we can rearm. EXPECT_EQ(kMessage1, ReadData(consumer, sizeof(kMessage1) - 1)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(2, num_new_data_notifications); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); } TEST_F(WatcherTest, WatchDataPipeProducerWritable) { constexpr size_t kTestPipeCapacity = 8; MojoHandle producer, consumer; CreateDataPipe(&producer, &consumer, kTestPipeCapacity); // Half the capacity of the data pipe. const char kTestData[] = "aaaa"; static_assert((sizeof(kTestData) - 1) * 2 == kTestPipeCapacity, "Invalid test data for this test."); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; int num_expected_notifications = 1; const uintptr_t writable_producer_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, int* expected_count, MojoResult result, MojoHandleSignalsState state) { EXPECT_GT(*expected_count, 0); *expected_count -= 1; EXPECT_EQ(MOJO_RESULT_OK, result); event->Signal(); }, &event, &num_expected_notifications)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE, writable_producer_context)); // The producer is already writable, so arming should fail with relevant // information. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(writable_producer_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); // Write some data, but don't fill the pipe yet. Arming should fail again. WriteData(producer, kTestData); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(writable_producer_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); // Write more data, filling the pipe to capacity. Arming should succeed now. WriteData(producer, kTestData); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Now read from the pipe, making the producer writable again. Should notify. EXPECT_EQ(kTestData, ReadData(consumer, sizeof(kTestData) - 1)); event.Wait(); // Arming should fail again. EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(writable_producer_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); // Fill the pipe once more and arm the watcher. Should succeed. WriteData(producer, kTestData); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); }; TEST_F(WatcherTest, CloseWatchedDataPipeConsumerHandle) { constexpr size_t kTestPipeCapacity = 8; MojoHandle producer, consumer; CreateDataPipe(&producer, &consumer, kTestPipeCapacity); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; const uintptr_t readable_consumer_context = helper.CreateContextWithCancel( WatchHelper::ContextCallback(), base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE, readable_consumer_context)); // Closing the consumer should fire a cancellation notification. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); } TEST_F(WatcherTest, CloseWatcherDataPipeConsumerHandlePeer) { constexpr size_t kTestPipeCapacity = 8; MojoHandle producer, consumer; CreateDataPipe(&producer, &consumer, kTestPipeCapacity); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; const uintptr_t readable_consumer_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); event->Signal(); }, &event)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, consumer, MOJO_HANDLE_SIGNAL_READABLE, readable_consumer_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Closing the producer should fire a notification for an unsatisfiable watch. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); event.Wait(); // Now attempt to rearm and expect appropriate error feedback. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(readable_consumer_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]); EXPECT_FALSE(ready_states[0].satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); } TEST_F(WatcherTest, CloseWatchedDataPipeProducerHandle) { constexpr size_t kTestPipeCapacity = 8; MojoHandle producer, consumer; CreateDataPipe(&producer, &consumer, kTestPipeCapacity); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; const uintptr_t writable_producer_context = helper.CreateContextWithCancel( WatchHelper::ContextCallback(), base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE, writable_producer_context)); // Closing the consumer should fire a cancellation notification. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); } TEST_F(WatcherTest, CloseWatchedDataPipeProducerHandlePeer) { constexpr size_t kTestPipeCapacity = 8; MojoHandle producer, consumer; CreateDataPipe(&producer, &consumer, kTestPipeCapacity); const char kTestMessageFullCapacity[] = "xxxxxxxx"; static_assert(sizeof(kTestMessageFullCapacity) - 1 == kTestPipeCapacity, "Invalid test message size for this test."); // Make the pipe unwritable initially. WriteData(producer, kTestMessageFullCapacity); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; const uintptr_t writable_producer_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, result); event->Signal(); }, &event)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, producer, MOJO_HANDLE_SIGNAL_WRITABLE, writable_producer_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Closing the consumer should fire a notification for an unsatisfiable watch, // as the full data pipe can never be read from again and is therefore // permanently full and unwritable. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer)); event.Wait(); // Now attempt to rearm and expect appropriate error feedback. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(writable_producer_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]); EXPECT_FALSE(ready_states[0].satisfiable_signals & MOJO_HANDLE_SIGNAL_WRITABLE); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer)); } TEST_F(WatcherTest, ArmWithNoWatches) { MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w)); EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); } TEST_F(WatcherTest, WatchDuplicateContext) { MojoHandle a, b; CreateMessagePipe(&a, &b); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, 0)); EXPECT_EQ(MOJO_RESULT_ALREADY_EXISTS, MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, 0)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); } TEST_F(WatcherTest, CancelUnknownWatch) { MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectNoNotification, &w)); EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoCancelWatch(w, 1234)); } TEST_F(WatcherTest, ArmWithWatchAlreadySatisfied) { MojoHandle a, b; CreateMessagePipe(&a, &b); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_WRITABLE, 0)); // |a| is always writable, so we can never arm this watcher. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(0u, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); } TEST_F(WatcherTest, ArmWithWatchAlreadyUnsatisfiable) { MojoHandle a, b; CreateMessagePipe(&a, &b); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, 0)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); // |b| is closed and never wrote any messages, so |a| won't be readable again. // MojoArmWatcher() should fail, incidcating as much. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = kMaxReadyContexts; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(0u, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, ready_results[0]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_PEER_CLOSED); EXPECT_FALSE(ready_states[0].satisfiable_signals & MOJO_HANDLE_SIGNAL_READABLE); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); } TEST_F(WatcherTest, MultipleWatches) { MojoHandle a, b; CreateMessagePipe(&a, &b); base::WaitableEvent a_event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent b_event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; int num_a_notifications = 0; int num_b_notifications = 0; auto notify_callback = base::Bind([](base::WaitableEvent* event, int* notification_count, MojoResult result, MojoHandleSignalsState state) { *notification_count += 1; EXPECT_EQ(MOJO_RESULT_OK, result); event->Signal(); }); uintptr_t readable_a_context = helper.CreateContext( base::Bind(notify_callback, &a_event, &num_a_notifications)); uintptr_t readable_b_context = helper.CreateContext( base::Bind(notify_callback, &b_event, &num_b_notifications)); MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); // Add two independent watch contexts to watch for |a| or |b| readability. EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, readable_b_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); const char kMessage1[] = "things are happening"; const char kMessage2[] = "ok. ok. ok. ok."; const char kMessage3[] = "plz wake up"; // Writing to |b| should signal |a|'s watch. WriteMessage(b, kMessage1); a_event.Wait(); a_event.Reset(); // Subsequent messages on |b| should not trigger another notification. WriteMessage(b, kMessage2); WriteMessage(b, kMessage3); // Messages on |a| also shouldn't trigger |b|'s notification, since the // watcher should be disarmed by now. WriteMessage(a, kMessage1); WriteMessage(a, kMessage2); WriteMessage(a, kMessage3); // Arming should fail. Since we only ask for at most one context's information // that's all we should get back. Which one we get is unspecified. constexpr size_t kMaxReadyContexts = 10; uint32_t num_ready_contexts = 1; uintptr_t ready_contexts[kMaxReadyContexts]; MojoResult ready_results[kMaxReadyContexts]; MojoHandleSignalsState ready_states[kMaxReadyContexts]; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_TRUE(ready_contexts[0] == readable_a_context || ready_contexts[0] == readable_b_context); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); // Now try arming again, verifying that both contexts are returned. num_ready_contexts = kMaxReadyContexts; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(2u, num_ready_contexts); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[1]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); EXPECT_TRUE(ready_states[1].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); EXPECT_TRUE((ready_contexts[0] == readable_a_context && ready_contexts[1] == readable_b_context) || (ready_contexts[0] == readable_b_context && ready_contexts[1] == readable_a_context)); // Flush out the test messages so we should be able to successfully rearm. EXPECT_EQ(kMessage1, ReadMessage(a)); EXPECT_EQ(kMessage2, ReadMessage(a)); EXPECT_EQ(kMessage3, ReadMessage(a)); EXPECT_EQ(kMessage1, ReadMessage(b)); EXPECT_EQ(kMessage2, ReadMessage(b)); EXPECT_EQ(kMessage3, ReadMessage(b)); // Add a watch which is always satisfied, so we can't arm. Arming should fail // with only this new watch's information. uintptr_t writable_c_context = helper.CreateContext(base::Bind( [](MojoResult result, MojoHandleSignalsState state) { NOTREACHED(); })); MojoHandle c, d; CreateMessagePipe(&c, &d); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, c, MOJO_HANDLE_SIGNAL_WRITABLE, writable_c_context)); num_ready_contexts = kMaxReadyContexts; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, ready_contexts, ready_results, ready_states)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(writable_c_context, ready_contexts[0]); EXPECT_EQ(MOJO_RESULT_OK, ready_results[0]); EXPECT_TRUE(ready_states[0].satisfied_signals & MOJO_HANDLE_SIGNAL_WRITABLE); // Cancel the new watch and arming should succeed once again. EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, writable_c_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); } TEST_F(WatcherTest, NotifyOtherFromNotificationCallback) { MojoHandle a, b; CreateMessagePipe(&a, &b); static const char kTestMessageToA[] = "hello a"; static const char kTestMessageToB[] = "hello b"; base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); uintptr_t readable_a_context = helper.CreateContext(base::Bind( [](MojoHandle w, MojoHandle a, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ("hello a", ReadMessage(a)); // Re-arm the watcher and signal |b|. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); WriteMessage(a, kTestMessageToB); }, w, a)); uintptr_t readable_b_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, MojoHandle w, MojoHandle b, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToB, ReadMessage(b)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); event->Signal(); }, &event, w, b)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, readable_b_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Send a message to |a|. The relevant watch context should be notified, and // should in turn send a message to |b|, waking up the other context. The // second context signals |event|. WriteMessage(b, kTestMessageToA); event.Wait(); } TEST_F(WatcherTest, NotifySelfFromNotificationCallback) { MojoHandle a, b; CreateMessagePipe(&a, &b); static const char kTestMessageToA[] = "hello a"; base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); int expected_notifications = 10; uintptr_t readable_a_context = helper.CreateContext(base::Bind( [](int* expected_count, MojoHandle w, MojoHandle a, MojoHandle b, base::WaitableEvent* event, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ("hello a", ReadMessage(a)); EXPECT_GT(*expected_count, 0); *expected_count -= 1; if (*expected_count == 0) { event->Signal(); return; } else { // Re-arm the watcher and signal |a| again. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); WriteMessage(b, kTestMessageToA); } }, &expected_notifications, w, a, b, &event)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Send a message to |a|. When the watch above is notified, it will rearm and // send another message to |a|. This will happen until // |expected_notifications| reaches 0. WriteMessage(b, kTestMessageToA); event.Wait(); } TEST_F(WatcherTest, ImplicitCancelOtherFromNotificationCallback) { MojoHandle a, b; CreateMessagePipe(&a, &b); MojoHandle c, d; CreateMessagePipe(&c, &d); static const char kTestMessageToA[] = "hi a"; static const char kTestMessageToC[] = "hi c"; base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); uintptr_t readable_a_context = helper.CreateContextWithCancel( base::Bind([](MojoResult result, MojoHandleSignalsState state) { NOTREACHED(); }), base::Bind([](base::WaitableEvent* event) { event->Signal(); }, &event)); uintptr_t readable_c_context = helper.CreateContext(base::Bind( [](MojoHandle w, MojoHandle a, MojoHandle b, MojoHandle c, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToC, ReadMessage(c)); // Now rearm the watcher. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Must result in exactly ONE notification on the above context, for // CANCELLED only. Because we cannot dispatch notifications until the // stack unwinds, and because we must never dispatch non-cancellation // notifications for a handle once it's been closed, we must be certain // that cancellation due to closure preemptively invalidates any // pending non-cancellation notifications queued on the current // RequestContext, such as the one resulting from the WriteMessage here. WriteMessage(b, kTestMessageToA); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); // Rearming should be fine since |a|'s watch should already be // implicitly cancelled (even though the notification will not have // been invoked yet.) EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Nothing interesting should happen as a result of this. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); }, w, a, b, c)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, c, MOJO_HANDLE_SIGNAL_READABLE, readable_c_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); WriteMessage(d, kTestMessageToC); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); } TEST_F(WatcherTest, ExplicitCancelOtherFromNotificationCallback) { MojoHandle a, b; CreateMessagePipe(&a, &b); MojoHandle c, d; CreateMessagePipe(&c, &d); static const char kTestMessageToA[] = "hi a"; static const char kTestMessageToC[] = "hi c"; base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); WatchHelper helper; MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); uintptr_t readable_a_context = helper.CreateContext(base::Bind( [](MojoResult result, MojoHandleSignalsState state) { NOTREACHED(); })); uintptr_t readable_c_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, uintptr_t readable_a_context, MojoHandle w, MojoHandle a, MojoHandle b, MojoHandle c, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToC, ReadMessage(c)); // Now rearm the watcher. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Should result in no notifications on the above context, because the // watch will have been cancelled by the time the notification callback // can execute. WriteMessage(b, kTestMessageToA); WriteMessage(b, kTestMessageToA); EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context)); // Rearming should be fine now. EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // Nothing interesting should happen as a result of these. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); event->Signal(); }, &event, readable_a_context, w, a, b, c)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, c, MOJO_HANDLE_SIGNAL_READABLE, readable_c_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); WriteMessage(d, kTestMessageToC); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); } TEST_F(WatcherTest, NestedCancellation) { MojoHandle a, b; CreateMessagePipe(&a, &b); MojoHandle c, d; CreateMessagePipe(&c, &d); static const char kTestMessageToA[] = "hey a"; static const char kTestMessageToC[] = "hey c"; static const char kTestMessageToD[] = "hey d"; // This is a tricky test. It establishes a watch on |b| using one watcher and // watches on |c| and |d| using another watcher. // // A message is written to |d| to wake up |c|'s watch, and the notification // handler for that event does the following: // 1. Writes to |a| to eventually wake up |b|'s watcher. // 2. Rearms |c|'s watcher. // 3. Writes to |d| to eventually wake up |c|'s watcher again. // // Meanwhile, |b|'s watch notification handler cancels |c|'s watch altogether // before writing to |c| to wake up |d|. // // The net result should be that |c|'s context only gets notified once (from // the first write to |d| above) and everyone else gets notified as expected. MojoHandle b_watcher; MojoHandle cd_watcher; WatchHelper helper; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&b_watcher)); EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&cd_watcher)); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); uintptr_t readable_d_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, MojoHandle d, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToD, ReadMessage(d)); event->Signal(); }, &event, d)); static int num_expected_c_notifications = 1; uintptr_t readable_c_context = helper.CreateContext(base::Bind( [](MojoHandle cd_watcher, MojoHandle a, MojoHandle c, MojoHandle d, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_GT(num_expected_c_notifications--, 0); // Trigger an eventual |readable_b_context| notification. WriteMessage(a, kTestMessageToA); EXPECT_EQ(kTestMessageToC, ReadMessage(c)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(cd_watcher, nullptr, nullptr, nullptr, nullptr)); // Trigger another eventual |readable_c_context| notification. WriteMessage(d, kTestMessageToC); }, cd_watcher, a, c, d)); uintptr_t readable_b_context = helper.CreateContext(base::Bind( [](MojoHandle cd_watcher, uintptr_t readable_c_context, MojoHandle c, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(cd_watcher, readable_c_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(cd_watcher, nullptr, nullptr, nullptr, nullptr)); WriteMessage(c, kTestMessageToD); }, cd_watcher, readable_c_context, c)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(b_watcher, b, MOJO_HANDLE_SIGNAL_READABLE, readable_b_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(cd_watcher, c, MOJO_HANDLE_SIGNAL_READABLE, readable_c_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(cd_watcher, d, MOJO_HANDLE_SIGNAL_READABLE, readable_d_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(b_watcher, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(cd_watcher, nullptr, nullptr, nullptr, nullptr)); WriteMessage(d, kTestMessageToC); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(cd_watcher)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_watcher)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); } TEST_F(WatcherTest, CancelSelfInNotificationCallback) { MojoHandle a, b; CreateMessagePipe(&a, &b); static const char kTestMessageToA[] = "hey a"; MojoHandle w; WatchHelper helper; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); static uintptr_t readable_a_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, MojoHandle w, MojoHandle a, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); // There should be no problem cancelling this watch from its own // notification invocation. EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context)); EXPECT_EQ(kTestMessageToA, ReadMessage(a)); // Arming should fail because there are no longer any registered // watches on the watcher. EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // And closing |a| should be fine (and should not invoke this // notification with MOJO_RESULT_CANCELLED) for the same reason. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); event->Signal(); }, &event, w, a)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); WriteMessage(b, kTestMessageToA); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); } TEST_F(WatcherTest, CloseWatcherInNotificationCallback) { MojoHandle a, b; CreateMessagePipe(&a, &b); static const char kTestMessageToA1[] = "hey a"; static const char kTestMessageToA2[] = "hey a again"; MojoHandle w; WatchHelper helper; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); uintptr_t readable_a_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, MojoHandle w, MojoHandle a, MojoHandle b, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToA1, ReadMessage(a)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // There should be no problem closing this watcher from its own // notification callback. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); // And these should not trigger more notifications, because |w| has been // closed already. WriteMessage(b, kTestMessageToA2); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); event->Signal(); }, &event, w, a, b)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); WriteMessage(b, kTestMessageToA1); event.Wait(); } TEST_F(WatcherTest, CloseWatcherAfterImplicitCancel) { MojoHandle a, b; CreateMessagePipe(&a, &b); static const char kTestMessageToA[] = "hey a"; MojoHandle w; WatchHelper helper; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); uintptr_t readable_a_context = helper.CreateContext(base::Bind( [](base::WaitableEvent* event, MojoHandle w, MojoHandle a, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToA, ReadMessage(a)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); // This will cue up a notification for |MOJO_RESULT_CANCELLED|... EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); // ...but it should never fire because we close the watcher here. EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); event->Signal(); }, &event, w, a)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); WriteMessage(b, kTestMessageToA); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); } TEST_F(WatcherTest, OtherThreadCancelDuringNotification) { MojoHandle a, b; CreateMessagePipe(&a, &b); static const char kTestMessageToA[] = "hey a"; MojoHandle w; WatchHelper helper; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); base::WaitableEvent wait_for_notification( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent wait_for_cancellation( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); static bool callback_done = false; uintptr_t readable_a_context = helper.CreateContextWithCancel( base::Bind( [](base::WaitableEvent* wait_for_notification, MojoHandle w, MojoHandle a, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToA, ReadMessage(a)); wait_for_notification->Signal(); // Give the other thread sufficient time to race with the completion // of this callback. There should be no race, since the cancellation // notification must be mutually exclusive to this notification. base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1)); callback_done = true; }, &wait_for_notification, w, a), base::Bind( [](base::WaitableEvent* wait_for_cancellation) { EXPECT_TRUE(callback_done); wait_for_cancellation->Signal(); }, &wait_for_cancellation)); ThreadedRunner runner(base::Bind( [](base::WaitableEvent* wait_for_notification, base::WaitableEvent* wait_for_cancellation, MojoHandle w, uintptr_t readable_a_context) { wait_for_notification->Wait(); // Cancel the watch while the notification is still running. EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, readable_a_context)); wait_for_cancellation->Wait(); EXPECT_TRUE(callback_done); }, &wait_for_notification, &wait_for_cancellation, w, readable_a_context)); runner.Start(); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(w, nullptr, nullptr, nullptr, nullptr)); WriteMessage(b, kTestMessageToA); runner.Join(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); } TEST_F(WatcherTest, WatchesCancelEachOtherFromNotifications) { MojoHandle a, b; CreateMessagePipe(&a, &b); static const char kTestMessageToA[] = "hey a"; static const char kTestMessageToB[] = "hey b"; base::WaitableEvent wait_for_a_to_notify( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent wait_for_b_to_notify( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent wait_for_a_to_cancel( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); base::WaitableEvent wait_for_b_to_cancel( base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); MojoHandle a_watcher; MojoHandle b_watcher; WatchHelper helper; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&a_watcher)); EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&b_watcher)); // We set up two watchers, one on |a| and one on |b|. They cancel each other // from within their respective watch notifications. This should be safe, // i.e., it should not deadlock, in spite of the fact that we also guarantee // mutually exclusive notification execution (including cancellations) on any // given watch. bool a_cancelled = false; bool b_cancelled = false; static uintptr_t readable_b_context; uintptr_t readable_a_context = helper.CreateContextWithCancel( base::Bind( [](base::WaitableEvent* wait_for_a_to_notify, base::WaitableEvent* wait_for_b_to_notify, MojoHandle b_watcher, MojoHandle a, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToA, ReadMessage(a)); wait_for_a_to_notify->Signal(); wait_for_b_to_notify->Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(b_watcher, readable_b_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_watcher)); }, &wait_for_a_to_notify, &wait_for_b_to_notify, b_watcher, a), base::Bind( [](base::WaitableEvent* wait_for_a_to_cancel, base::WaitableEvent* wait_for_b_to_cancel, bool* a_cancelled) { *a_cancelled = true; wait_for_a_to_cancel->Signal(); wait_for_b_to_cancel->Wait(); }, &wait_for_a_to_cancel, &wait_for_b_to_cancel, &a_cancelled)); readable_b_context = helper.CreateContextWithCancel( base::Bind( [](base::WaitableEvent* wait_for_a_to_notify, base::WaitableEvent* wait_for_b_to_notify, uintptr_t readable_a_context, MojoHandle a_watcher, MojoHandle b, MojoResult result, MojoHandleSignalsState state) { EXPECT_EQ(MOJO_RESULT_OK, result); EXPECT_EQ(kTestMessageToB, ReadMessage(b)); wait_for_b_to_notify->Signal(); wait_for_a_to_notify->Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(a_watcher, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a_watcher)); }, &wait_for_a_to_notify, &wait_for_b_to_notify, readable_a_context, a_watcher, b), base::Bind( [](base::WaitableEvent* wait_for_a_to_cancel, base::WaitableEvent* wait_for_b_to_cancel, bool* b_cancelled) { *b_cancelled = true; wait_for_b_to_cancel->Signal(); wait_for_a_to_cancel->Wait(); }, &wait_for_a_to_cancel, &wait_for_b_to_cancel, &b_cancelled)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(a_watcher, a, MOJO_HANDLE_SIGNAL_READABLE, readable_a_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(a_watcher, nullptr, nullptr, nullptr, nullptr)); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(b_watcher, b, MOJO_HANDLE_SIGNAL_READABLE, readable_b_context)); EXPECT_EQ(MOJO_RESULT_OK, MojoArmWatcher(b_watcher, nullptr, nullptr, nullptr, nullptr)); ThreadedRunner runner( base::Bind([](MojoHandle b) { WriteMessage(b, kTestMessageToA); }, b)); runner.Start(); WriteMessage(a, kTestMessageToB); wait_for_a_to_cancel.Wait(); wait_for_b_to_cancel.Wait(); runner.Join(); EXPECT_TRUE(a_cancelled); EXPECT_TRUE(b_cancelled); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); } TEST_F(WatcherTest, AlwaysCancel) { // Basic sanity check to ensure that all possible ways to cancel a watch // result in a final MOJO_RESULT_CANCELLED notification. MojoHandle a, b; CreateMessagePipe(&a, &b); MojoHandle w; WatchHelper helper; EXPECT_EQ(MOJO_RESULT_OK, helper.CreateWatcher(&w)); base::WaitableEvent event(base::WaitableEvent::ResetPolicy::MANUAL, base::WaitableEvent::InitialState::NOT_SIGNALED); const base::Closure signal_event = base::Bind(&base::WaitableEvent::Signal, base::Unretained(&event)); // Cancel via |MojoCancelWatch()|. uintptr_t context = helper.CreateContextWithCancel( WatchHelper::ContextCallback(), signal_event); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, context)); EXPECT_EQ(MOJO_RESULT_OK, MojoCancelWatch(w, context)); event.Wait(); event.Reset(); // Cancel by closing the watched handle. context = helper.CreateContextWithCancel(WatchHelper::ContextCallback(), signal_event); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, a, MOJO_HANDLE_SIGNAL_READABLE, context)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a)); event.Wait(); event.Reset(); // Cancel by closing the watcher handle. context = helper.CreateContextWithCancel(WatchHelper::ContextCallback(), signal_event); EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, b, MOJO_HANDLE_SIGNAL_READABLE, context)); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); event.Wait(); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b)); } TEST_F(WatcherTest, ArmFailureCirculation) { // Sanity check to ensure that all ready handles will eventually be returned // over a finite number of calls to MojoArmWatcher(). constexpr size_t kNumTestPipes = 100; constexpr size_t kNumTestHandles = kNumTestPipes * 2; MojoHandle handles[kNumTestHandles]; // Create a bunch of pipes and make sure they're all readable. for (size_t i = 0; i < kNumTestPipes; ++i) { CreateMessagePipe(&handles[i], &handles[i + kNumTestPipes]); WriteMessage(handles[i], "hey"); WriteMessage(handles[i + kNumTestPipes], "hay"); WaitForSignals(handles[i], MOJO_HANDLE_SIGNAL_READABLE); WaitForSignals(handles[i + kNumTestPipes], MOJO_HANDLE_SIGNAL_READABLE); } // Create a watcher and watch all of them. MojoHandle w; EXPECT_EQ(MOJO_RESULT_OK, MojoCreateWatcher(&ExpectOnlyCancel, &w)); for (size_t i = 0; i < kNumTestHandles; ++i) { EXPECT_EQ(MOJO_RESULT_OK, MojoWatch(w, handles[i], MOJO_HANDLE_SIGNAL_READABLE, i)); } // Keep trying to arm |w| until every watch gets an entry in |ready_contexts|. // If MojoArmWatcher() is well-behaved, this should terminate eventually. std::set ready_contexts; while (ready_contexts.size() < kNumTestHandles) { uint32_t num_ready_contexts = 1; uintptr_t ready_context; MojoResult ready_result; MojoHandleSignalsState ready_state; EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, MojoArmWatcher(w, &num_ready_contexts, &ready_context, &ready_result, &ready_state)); EXPECT_EQ(1u, num_ready_contexts); EXPECT_EQ(MOJO_RESULT_OK, ready_result); ready_contexts.insert(ready_context); } for (size_t i = 0; i < kNumTestHandles; ++i) EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[i])); EXPECT_EQ(MOJO_RESULT_OK, MojoClose(w)); } } // namespace } // namespace edk } // namespace mojo