| // 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 <stdint.h> |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| |
| #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<void(MojoResult, MojoHandleSignalsState)>; |
| |
| 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<NotificationContext>(callback); |
| NotificationContext* raw_context = context.get(); |
| raw_context->SetCancelCallback(base::Bind( |
| [](std::unique_ptr<NotificationContext> context, |
| const base::Closure& cancel_callback) { |
| if (cancel_callback) |
| cancel_callback.Run(); |
| }, |
| base::Passed(&context), cancel_callback)); |
| return reinterpret_cast<uintptr_t>(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<NotificationContext*>(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<uintptr_t> 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 |