| Index: mojo/edk/system/watcher_unittest.cc
|
| diff --git a/mojo/edk/system/watcher_unittest.cc b/mojo/edk/system/watcher_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a3f27424321d55a6e225da26dce44dff3ec9c1f3
|
| --- /dev/null
|
| +++ b/mojo/edk/system/watcher_unittest.cc
|
| @@ -0,0 +1,1590 @@
|
| +// 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 "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));
|
| +}
|
| +
|
| +} // namespace
|
| +} // namespace edk
|
| +} // namespace mojo
|
|
|