#include <stdint.h>
#include <map>
#include <memory>
#include <set>
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/memory/ptr_util.h"
#include "base/rand_util.h"
#include "base/synchronization/waitable_event.h"
#include "base/test/bind.h"
#include "base/threading/platform_thread.h"
#include "base/threading/simple_thread.h"
#include "base/time/time.h"
#include "mojo/core/embedder/embedder.h"
#include "mojo/core/test/mojo_test_base.h"
#include "mojo/public/c/system/data_pipe.h"
#include "mojo/public/c/system/trap.h"
#include "mojo/public/c/system/types.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace mojo {
namespace core {
namespace {
using TrapTest = test::MojoTestBase;
class TriggerHelper {
public:
using ContextCallback = base::RepeatingCallback<void(const MojoTrapEvent&)>;
TriggerHelper() = default;
TriggerHelper(const TriggerHelper&) = delete;
TriggerHelper& operator=(const TriggerHelper&) = delete;
~TriggerHelper() = default;
MojoResult CreateTrap(MojoHandle* handle) {
return MojoCreateTrap(&Notify, nullptr, handle);
}
template <typename Handler>
uintptr_t CreateContext(Handler handler) {
return CreateContextWithCancel(handler, [] {});
}
template <typename Handler, typename CancelHandler>
uintptr_t CreateContextWithCancel(Handler handler,
CancelHandler cancel_handler) {
auto* context =
new NotificationContext(base::BindLambdaForTesting(handler));
context->SetCancelCallback(
base::BindLambdaForTesting([cancel_handler, context] {
cancel_handler();
delete context;
}));
return reinterpret_cast<uintptr_t>(context);
}
private:
class NotificationContext {
public:
explicit NotificationContext(const ContextCallback& callback)
: callback_(callback) {}
NotificationContext(const NotificationContext&) = delete;
NotificationContext& operator=(const NotificationContext&) = delete;
~NotificationContext() = default;
void SetCancelCallback(base::OnceClosure cancel_callback) {
cancel_callback_ = std::move(cancel_callback);
}
void Notify(const MojoTrapEvent& event) {
if (event.result == MOJO_RESULT_CANCELLED && cancel_callback_)
std::move(cancel_callback_).Run();
else
callback_.Run(event);
}
private:
const ContextCallback callback_;
base::OnceClosure cancel_callback_;
};
static void Notify(const MojoTrapEvent* event) {
reinterpret_cast<NotificationContext*>(event->trigger_context)
->Notify(*event);
}
};
class ThreadedRunner : public base::SimpleThread {
public:
explicit ThreadedRunner(base::OnceClosure callback)
: SimpleThread("ThreadedRunner"), callback_(std::move(callback)) {}
ThreadedRunner(const ThreadedRunner&) = delete;
ThreadedRunner& operator=(const ThreadedRunner&) = delete;
~ThreadedRunner() override = default;
void Run() override { std::move(callback_).Run(); }
private:
base::OnceClosure callback_;
};
void ExpectNoNotification(const MojoTrapEvent* event) {
NOTREACHED();
}
void ExpectOnlyCancel(const MojoTrapEvent* event) {
EXPECT_EQ(event->result, MOJO_RESULT_CANCELLED);
}
TEST_F(TrapTest, InvalidArguments) {
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoCreateTrap(&ExpectNoNotification, nullptr, nullptr));
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectNoNotification, nullptr, &t));
EXPECT_EQ(
MOJO_RESULT_INVALID_ARGUMENT,
MojoAddTrigger(t, t, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr));
MojoHandle buffer_handle = CreateBuffer(42);
EXPECT_EQ(
MOJO_RESULT_INVALID_ARGUMENT,
MojoAddTrigger(t, buffer_handle, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoRemoveTrigger(buffer_handle, 0, nullptr));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoArmTrap(MOJO_HANDLE_INVALID, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoArmTrap(buffer_handle, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(buffer_handle));
uint32_t num_blocking_events = 1;
EXPECT_EQ(MOJO_RESULT_INVALID_ARGUMENT,
MojoArmTrap(t, nullptr, &num_blocking_events, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, TrapMessagePipeReadable) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
int num_expected_notifications = 1;
const uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_GT(num_expected_notifications, 0);
num_expected_notifications -= 1;
EXPECT_EQ(MOJO_RESULT_OK, event.result);
wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
const char kMessage1[] = "hey hey hey hey";
const char kMessage2[] = "i said hey";
const char kMessage3[] = "what's goin' on?";
WriteMessage(b, kMessage1);
WriteMessage(b, kMessage2);
wait.Wait();
WriteMessage(b, kMessage3);
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(readable_a_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_EQ(kMessage1, ReadMessage(a));
EXPECT_EQ(kMessage2, ReadMessage(a));
EXPECT_EQ(kMessage3, ReadMessage(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
}
TEST_F(TrapTest, CloseWatchedMessagePipeHandle) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
const uintptr_t readable_a_context = helper.CreateContextWithCancel(
[](const MojoTrapEvent&) {}, [&] { wait.Signal(); });
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, CloseWatchedMessagePipeHandlePeer) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
const uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, event.result);
wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
wait.Wait();
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(readable_a_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, blocking_events[0].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_PEER_CLOSED);
EXPECT_FALSE(blocking_events[0].signals_state.satisfiable_signals &
MOJO_HANDLE_SIGNAL_READABLE);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
}
TEST_F(TrapTest, TrapDataPipeConsumerReadable) {
constexpr size_t kTestPipeCapacity = 64;
MojoHandle producer, consumer;
CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
int num_expected_notifications = 1;
const uintptr_t readable_consumer_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_GT(num_expected_notifications, 0);
num_expected_notifications -= 1;
EXPECT_EQ(MOJO_RESULT_OK, event.result);
wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, consumer, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_consumer_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
const char kMessage1[] = "hey hey hey hey";
const char kMessage2[] = "i said hey";
const char kMessage3[] = "what's goin' on?";
WriteData(producer, kMessage1);
WriteData(producer, kMessage2);
wait.Wait();
WriteData(producer, kMessage3);
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(readable_consumer_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_EQ(kMessage1, ReadData(consumer, sizeof(kMessage1) - 1));
EXPECT_EQ(kMessage2, ReadData(consumer, sizeof(kMessage2) - 1));
EXPECT_EQ(kMessage3, ReadData(consumer, sizeof(kMessage3) - 1));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
}
TEST_F(TrapTest, TrapDataPipeConsumerNewDataReadable) {
constexpr size_t kTestPipeCapacity = 64;
MojoHandle producer, consumer;
CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
int num_new_data_notifications = 0;
const uintptr_t new_data_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
num_new_data_notifications += 1;
EXPECT_EQ(MOJO_RESULT_OK, event.result);
wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, consumer, MOJO_HANDLE_SIGNAL_NEW_DATA_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
new_data_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
const char kMessage1[] = "hey hey hey hey";
const char kMessage2[] = "i said hey";
const char kMessage3[] = "what's goin' on?";
WriteData(producer, kMessage1);
WriteData(producer, kMessage2);
wait.Wait();
WriteData(producer, kMessage3);
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(new_data_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
char large_buffer[512];
uint32_t large_read_size = 512;
MojoReadDataOptions options;
options.struct_size = sizeof(options);
options.flags = MOJO_READ_DATA_FLAG_ALL_OR_NONE;
EXPECT_EQ(MOJO_RESULT_OUT_OF_RANGE,
MojoReadData(consumer, &options, large_buffer, &large_read_size));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
wait.Reset();
WriteData(producer, kMessage1);
wait.Wait();
EXPECT_EQ(kMessage1, ReadData(consumer, sizeof(kMessage1) - 1));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(2, num_new_data_notifications);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
}
TEST_F(TrapTest, TrapDataPipeProducerWritable) {
constexpr size_t kTestPipeCapacity = 8;
MojoHandle producer, consumer;
CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
const char kTestData[] = "aaaa";
static_assert((sizeof(kTestData) - 1) * 2 == kTestPipeCapacity,
"Invalid test data for this test.");
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
int num_expected_notifications = 1;
const uintptr_t writable_producer_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_GT(num_expected_notifications, 0);
num_expected_notifications -= 1;
EXPECT_EQ(MOJO_RESULT_OK, event.result);
wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, producer, MOJO_HANDLE_SIGNAL_WRITABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
writable_producer_context, nullptr));
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(writable_producer_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
WriteData(producer, kTestData);
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(writable_producer_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
WriteData(producer, kTestData);
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(kTestData, ReadData(consumer, sizeof(kTestData) - 1));
wait.Wait();
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(writable_producer_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
WriteData(producer, kTestData);
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
}
TEST_F(TrapTest, CloseWatchedDataPipeConsumerHandle) {
constexpr size_t kTestPipeCapacity = 8;
MojoHandle producer, consumer;
CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
const uintptr_t readable_consumer_context = helper.CreateContextWithCancel(
[](const MojoTrapEvent&) {}, [&] { wait.Signal(); });
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, consumer, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_consumer_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, CloseWatchedDataPipeConsumerHandlePeer) {
constexpr size_t kTestPipeCapacity = 8;
MojoHandle producer, consumer;
CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
const uintptr_t readable_consumer_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, event.result);
wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, consumer, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_consumer_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
wait.Wait();
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(readable_consumer_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, blocking_events[0].result);
EXPECT_FALSE(blocking_events[0].signals_state.satisfiable_signals &
MOJO_HANDLE_SIGNAL_READABLE);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
}
TEST_F(TrapTest, CloseWatchedDataPipeProducerHandle) {
constexpr size_t kTestPipeCapacity = 8;
MojoHandle producer, consumer;
CreateDataPipe(&producer, &consumer, kTestPipeCapacity);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
const uintptr_t writable_producer_context = helper.CreateContextWithCancel(
[](const MojoTrapEvent&) {}, [&] { wait.Signal(); });
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, producer, MOJO_HANDLE_SIGNAL_WRITABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
writable_producer_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, 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.");
WriteData(producer, kTestMessageFullCapacity);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
const uintptr_t writable_producer_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, event.result);
wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, producer, MOJO_HANDLE_SIGNAL_WRITABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
writable_producer_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(consumer));
wait.Wait();
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(writable_producer_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, blocking_events[0].result);
EXPECT_FALSE(blocking_events[0].signals_state.satisfiable_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
}
TEST_F(TrapTest, ArmWithNoTriggers) {
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectNoNotification, nullptr, &t));
EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, DuplicateTriggerContext) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &t));
EXPECT_EQ(
MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr));
EXPECT_EQ(
MOJO_RESULT_ALREADY_EXISTS,
MojoAddTrigger(t, b, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
}
TEST_F(TrapTest, RemoveUnknownTrigger) {
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectNoNotification, nullptr, &t));
EXPECT_EQ(MOJO_RESULT_NOT_FOUND, MojoRemoveTrigger(t, 1234, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, ArmWithTriggerConditionAlreadySatisfied) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &t));
EXPECT_EQ(
MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_WRITABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr));
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(0u, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
}
TEST_F(TrapTest, ArmWithTriggerConditionAlreadyUnsatisfiable) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &t));
EXPECT_EQ(
MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, 0, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = kMaxBlockingEvents;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(0u, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION, blocking_events[0].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_PEER_CLOSED);
EXPECT_FALSE(blocking_events[0].signals_state.satisfiable_signals &
MOJO_HANDLE_SIGNAL_READABLE);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
}
TEST_F(TrapTest, MultipleTriggers) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
base::WaitableEvent a_wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
base::WaitableEvent b_wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
int num_a_notifications = 0;
int num_b_notifications = 0;
uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
num_a_notifications += 1;
EXPECT_EQ(MOJO_RESULT_OK, event.result);
a_wait.Signal();
});
uintptr_t readable_b_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
num_b_notifications += 1;
EXPECT_EQ(MOJO_RESULT_OK, event.result);
b_wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, b, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_b_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
const char kMessage1[] = "things are happening";
const char kMessage2[] = "ok. ok. ok. ok.";
const char kMessage3[] = "plz wake up";
WriteMessage(b, kMessage1);
a_wait.Wait();
a_wait.Reset();
WriteMessage(b, kMessage2);
WriteMessage(b, kMessage3);
WriteMessage(a, kMessage1);
WriteMessage(a, kMessage2);
WriteMessage(a, kMessage3);
constexpr size_t kMaxBlockingEvents = 3;
uint32_t num_blocking_events = 1;
MojoTrapEvent blocking_events[kMaxBlockingEvents] = {
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])},
{sizeof(blocking_events[0])}};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_TRUE(blocking_events[0].trigger_context == readable_a_context ||
blocking_events[0].trigger_context == readable_b_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
num_blocking_events = kMaxBlockingEvents;
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(2u, num_blocking_events);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[1].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
EXPECT_TRUE(blocking_events[1].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
EXPECT_TRUE((blocking_events[0].trigger_context == readable_a_context &&
blocking_events[1].trigger_context == readable_b_context) ||
(blocking_events[0].trigger_context == readable_b_context &&
blocking_events[1].trigger_context == readable_a_context));
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));
uintptr_t writable_c_context =
helper.CreateContext([](const MojoTrapEvent&) { NOTREACHED(); });
MojoHandle c, d;
CreateMessagePipe(&c, &d);
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, c, MOJO_HANDLE_SIGNAL_WRITABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
writable_c_context, nullptr));
num_blocking_events = kMaxBlockingEvents;
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_events[0]));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(writable_c_context, blocking_events[0].trigger_context);
EXPECT_EQ(MOJO_RESULT_OK, blocking_events[0].result);
EXPECT_TRUE(blocking_events[0].signals_state.satisfied_signals &
MOJO_HANDLE_SIGNAL_WRITABLE);
EXPECT_EQ(MOJO_RESULT_OK, MojoRemoveTrigger(t, writable_c_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
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(TrapTest, ActivateOtherTriggerFromEventHandler) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
static const char kTestMessageToA[] = "hello a";
static const char kTestMessageToB[] = "hello b";
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ("hello a", ReadMessage(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(a, kTestMessageToB);
});
uintptr_t readable_b_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToB, ReadMessage(b));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
wait.Signal();
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, b, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_b_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA);
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
}
TEST_F(TrapTest, ActivateSameTriggerFromEventHandler) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
static const char kTestMessageToA[] = "hello a";
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
int expected_notifications = 10;
uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ("hello a", ReadMessage(a));
EXPECT_GT(expected_notifications, 0);
expected_notifications -= 1;
if (expected_notifications == 0) {
wait.Signal();
return;
} else {
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA);
}
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA);
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, ImplicitRemoveOtherTriggerWithinEventHandler) {
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 wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
uintptr_t readable_a_context = helper.CreateContextWithCancel(
[](const MojoTrapEvent&) { NOTREACHED(); }, [&] { wait.Signal(); });
uintptr_t readable_c_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToC, ReadMessage(c));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, c, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_c_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(d, kTestMessageToC);
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
}
TEST_F(TrapTest, ExplicitRemoveOtherTriggerWithinEventHandler) {
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 wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
uintptr_t readable_a_context =
helper.CreateContext([](const MojoTrapEvent&) { NOTREACHED(); });
uintptr_t readable_c_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToC, ReadMessage(c));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA);
WriteMessage(b, kTestMessageToA);
EXPECT_EQ(MOJO_RESULT_OK,
MojoRemoveTrigger(t, readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
wait.Signal();
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, c, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_c_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(d, kTestMessageToC);
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d));
}
TEST_F(TrapTest, NestedCancellation) {
if (IsMojoIpczEnabled()) {
GTEST_SKIP() << "This test expects trap handlers to be reentrant in some "
<< "edge cases, which is an unsafe artifact of pre-ipcz Mojo "
<< "that is unsupported by MojoIpcz. This case should not be "
<< "relevant to any current production usage.";
}
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";
MojoHandle b_trap;
MojoHandle cd_trap;
TriggerHelper helper;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&b_trap));
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&cd_trap));
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
uintptr_t readable_d_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToD, ReadMessage(d));
wait.Signal();
});
int num_expected_c_notifications = 1;
uintptr_t readable_c_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_GT(num_expected_c_notifications--, 0);
WriteMessage(a, kTestMessageToA);
EXPECT_EQ(kTestMessageToC, ReadMessage(c));
EXPECT_EQ(MOJO_RESULT_OK,
MojoArmTrap(cd_trap, nullptr, nullptr, nullptr));
WriteMessage(d, kTestMessageToC);
});
uintptr_t readable_b_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK,
MojoRemoveTrigger(cd_trap, readable_c_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK,
MojoArmTrap(cd_trap, nullptr, nullptr, nullptr));
WriteMessage(c, kTestMessageToD);
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(b_trap, b, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_b_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(cd_trap, c, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_c_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(cd_trap, d, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_d_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(b_trap, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(cd_trap, nullptr, nullptr, nullptr));
WriteMessage(d, kTestMessageToC);
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(cd_trap));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_trap));
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(TrapTest, RemoveSelfWithinEventHandler) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
static const char kTestMessageToA[] = "hey a";
MojoHandle t;
TriggerHelper helper;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
static uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
MojoHandle a_handle = a;
MojoHandle trap = t;
uintptr_t context = readable_a_context;
auto& wait_event = wait;
EXPECT_EQ(MOJO_RESULT_OK, MojoRemoveTrigger(trap, context, nullptr));
EXPECT_EQ(kTestMessageToA, ReadMessage(a_handle));
EXPECT_EQ(MOJO_RESULT_NOT_FOUND,
MojoArmTrap(trap, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a_handle));
wait_event.Signal();
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA);
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, CloseTrapWithinEventHandler) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
static const char kTestMessageToA1[] = "hey a";
static const char kTestMessageToA2[] = "hey a again";
MojoHandle t;
TriggerHelper helper;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToA1, ReadMessage(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
MojoHandle a_handle = a;
MojoHandle b_handle = b;
base::WaitableEvent& wait_event = wait;
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
WriteMessage(b_handle, kTestMessageToA2);
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_handle));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a_handle));
wait_event.Signal();
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA1);
wait.Wait();
}
TEST_F(TrapTest, CloseTrapAfterImplicitTriggerRemoval) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
static const char kTestMessageToA[] = "hey a";
MojoHandle t;
TriggerHelper helper;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToA, ReadMessage(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
base::WaitableEvent& wait_event = wait;
MojoHandle trap = t;
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(trap));
wait_event.Signal();
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA);
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
}
TEST_F(TrapTest, OtherThreadRemovesTriggerDuringEventHandler) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
static const char kTestMessageToA[] = "hey a";
MojoHandle t;
TriggerHelper helper;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
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(
[&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToA, ReadMessage(a));
wait_for_notification.Signal();
base::PlatformThread::Sleep(base::Seconds(1));
callback_done = true;
},
[&] {
EXPECT_TRUE(callback_done);
wait_for_cancellation.Signal();
});
ThreadedRunner runner(base::BindLambdaForTesting([&] {
wait_for_notification.Wait();
EXPECT_EQ(MOJO_RESULT_OK,
MojoRemoveTrigger(t, readable_a_context, nullptr));
wait_for_cancellation.Wait();
EXPECT_TRUE(callback_done);
}));
runner.Start();
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
WriteMessage(b, kTestMessageToA);
runner.Join();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
}
TEST_F(TrapTest, TriggersRemoveEachOtherWithinEventHandlers) {
if (IsMojoIpczEnabled()) {
GTEST_SKIP() << "This test deadlocks with MojoIpcz, because it expects "
<< "trap handlers to be re-entrant in some edge cases. Not "
<< "relevant to any current production usage.";
}
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_trap;
MojoHandle b_trap;
TriggerHelper helper;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&a_trap));
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&b_trap));
bool a_cancelled = false;
bool b_cancelled = false;
static uintptr_t readable_b_context;
uintptr_t readable_a_context = helper.CreateContextWithCancel(
[&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToA, ReadMessage(a));
base::WaitableEvent& wait_for_b = wait_for_b_to_notify;
MojoHandle b_trap_handle = b_trap;
uintptr_t b_context = readable_b_context;
wait_for_a_to_notify.Signal();
wait_for_b.Wait();
EXPECT_EQ(MOJO_RESULT_OK,
MojoRemoveTrigger(b_trap_handle, b_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b_trap_handle));
},
[&] {
a_cancelled = true;
wait_for_a_to_cancel.Signal();
wait_for_b_to_cancel.Wait();
});
readable_b_context = helper.CreateContextWithCancel(
[&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
EXPECT_EQ(kTestMessageToB, ReadMessage(b));
base::WaitableEvent& wait_for_a = wait_for_a_to_notify;
MojoHandle a_trap_handle = a_trap;
uintptr_t a_context = readable_a_context;
wait_for_b_to_notify.Signal();
wait_for_a.Wait();
EXPECT_EQ(MOJO_RESULT_OK,
MojoRemoveTrigger(a_trap_handle, a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a_trap_handle));
},
[&] {
b_cancelled = true;
wait_for_b_to_cancel.Signal();
wait_for_a_to_cancel.Wait();
});
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(a_trap, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(a_trap, nullptr, nullptr, nullptr));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(b_trap, b, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_b_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(b_trap, nullptr, nullptr, nullptr));
ThreadedRunner runner(base::BindOnce(
[](MojoHandle b) { WriteMessage(b, kTestMessageToA); }, b));
runner.Start();
wait_for_a_to_notify.Wait();
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(TrapTest, AlwaysCancel) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
MojoHandle t;
TriggerHelper helper;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
auto ignore_event = [](const MojoTrapEvent&) {};
auto signal_wait = [&] { wait.Signal(); };
uintptr_t context = helper.CreateContextWithCancel(ignore_event, signal_wait);
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, context,
nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoRemoveTrigger(t, context, nullptr));
wait.Wait();
wait.Reset();
context = helper.CreateContextWithCancel(ignore_event, signal_wait);
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, context,
nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
wait.Wait();
wait.Reset();
context = helper.CreateContextWithCancel(ignore_event, signal_wait);
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, b, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, context,
nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
}
TEST_F(TrapTest, ArmFailureCirculation) {
constexpr size_t kNumTestPipes = 100;
constexpr size_t kNumTestHandles = kNumTestPipes * 2;
MojoHandle handles[kNumTestHandles];
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);
}
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, MojoCreateTrap(&ExpectOnlyCancel, nullptr, &t));
for (size_t i = 0; i < kNumTestHandles; ++i) {
EXPECT_EQ(
MOJO_RESULT_OK,
MojoAddTrigger(t, handles[i], MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED, i, nullptr));
}
std::set<uintptr_t> ready_contexts;
while (ready_contexts.size() < kNumTestHandles) {
uint32_t num_blocking_events = 1;
MojoTrapEvent blocking_event = {sizeof(blocking_event)};
EXPECT_EQ(MOJO_RESULT_FAILED_PRECONDITION,
MojoArmTrap(t, nullptr, &num_blocking_events, &blocking_event));
EXPECT_EQ(1u, num_blocking_events);
EXPECT_EQ(MOJO_RESULT_OK, blocking_event.result);
ready_contexts.insert(blocking_event.trigger_context);
}
for (size_t i = 0; i < kNumTestHandles; ++i)
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(handles[i]));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
}
TEST_F(TrapTest, TriggerOnUnsatisfiedSignals) {
if (IsMojoIpczEnabled()) {
GTEST_SKIP() << "Monitoring for unsatisfied signals is not supported by "
<< "MojoIpcz.";
}
MojoHandle a, b;
CreateMessagePipe(&a, &b);
base::WaitableEvent wait(base::WaitableEvent::ResetPolicy::MANUAL,
base::WaitableEvent::InitialState::NOT_SIGNALED);
TriggerHelper helper;
const uintptr_t readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
wait.Signal();
});
MojoHandle t;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
const char kMessage[] = "this is not a message";
WriteMessage(b, kMessage);
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
const uintptr_t not_readable_a_context =
helper.CreateContext([&](const MojoTrapEvent& event) {
EXPECT_EQ(MOJO_RESULT_OK, event.result);
wait.Signal();
});
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_UNSATISFIED,
not_readable_a_context, nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
wait.Reset();
EXPECT_EQ(kMessage, ReadMessage(a));
wait.Wait();
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(t));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(b));
EXPECT_EQ(MOJO_RESULT_OK, MojoClose(a));
}
TEST_F(TrapTest, TriggerDuringDestruction) {
MojoHandle a, b;
CreateMessagePipe(&a, &b);
auto drain_a = [&](const MojoTrapEvent& event) {
MojoMessageHandle m;
while (MojoReadMessage(a, nullptr, &m) == MOJO_RESULT_OK) {
MojoDestroyMessage(m);
}
};
constexpr size_t kNumIterations = 1000;
for (size_t i = 0; i < kNumIterations; ++i) {
MojoHandle t;
TriggerHelper helper;
EXPECT_EQ(MOJO_RESULT_OK, helper.CreateTrap(&t));
EXPECT_EQ(MOJO_RESULT_OK,
MojoAddTrigger(t, a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
helper.CreateContext(drain_a), nullptr));
drain_a(MojoTrapEvent{});
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(t, nullptr, nullptr, nullptr));
ThreadedRunner writer(
base::BindLambdaForTesting([&] { WriteMessage(b, "ping!"); }));
ThreadedRunner closer(base::BindLambdaForTesting([&] { MojoClose(t); }));
closer.Start();
writer.Start();
writer.Join();
closer.Join();
}
MojoClose(a);
MojoClose(b);
}
TEST_F(TrapTest, RaceDispatchAndBlockedCancel) {
struct State;
struct Pipe {
explicit Pipe(State* state) : state(state) { CreateMessagePipe(&a, &b); }
uintptr_t context() const { return reinterpret_cast<uintptr_t>(this); }
MojoHandle a;
MojoHandle b;
bool trigger_cancelled = false;
const raw_ptr<State> state;
};
struct State {
Pipe pipe0{this};
Pipe pipe1{this};
MojoHandle trap;
base::WaitableEvent event;
};
State state;
const MojoTrapEventHandler event_handler = +[](const MojoTrapEvent* event) {
auto& pipe = *reinterpret_cast<Pipe*>(event->trigger_context);
auto& state = *pipe.state;
EXPECT_FALSE(pipe.trigger_cancelled);
if (event->result == MOJO_RESULT_CANCELLED) {
pipe.trigger_cancelled = true;
if (&pipe == &state.pipe0) {
EXPECT_EQ(MOJO_RESULT_OK,
MojoArmTrap(state.trap, nullptr, nullptr, nullptr));
state.event.Signal();
base::PlatformThread::Sleep(base::Milliseconds(10));
MojoClose(state.pipe1.a);
}
}
};
EXPECT_EQ(MOJO_RESULT_OK,
MojoCreateTrap(event_handler, nullptr, &state.trap));
EXPECT_EQ(
MOJO_RESULT_OK,
MojoAddTrigger(state.trap, state.pipe0.a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
state.pipe0.context(), nullptr));
EXPECT_EQ(
MOJO_RESULT_OK,
MojoAddTrigger(state.trap, state.pipe1.a, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
state.pipe1.context(), nullptr));
EXPECT_EQ(MOJO_RESULT_OK, MojoArmTrap(state.trap, nullptr, nullptr, nullptr));
ThreadedRunner close_pipe1_b(base::BindLambdaForTesting([&] {
state.event.Wait();
MojoClose(state.pipe1.b);
}));
close_pipe1_b.Start();
MojoClose(state.pipe0.a);
close_pipe1_b.Join();
MojoClose(state.pipe0.b);
MojoClose(state.trap);
}
base::RepeatingClosure g_do_random_thing_callback;
void ReadAllMessages(const MojoTrapEvent* event) {
if (event->result == MOJO_RESULT_OK) {
MojoHandle handle = static_cast<MojoHandle>(event->trigger_context);
MojoMessageHandle message;
while (MojoReadMessage(handle, nullptr, &message) == MOJO_RESULT_OK)
MojoDestroyMessage(message);
}
constexpr size_t kNumRandomThingsToDoOnNotify = 5;
for (size_t i = 0; i < kNumRandomThingsToDoOnNotify; ++i)
g_do_random_thing_callback.Run();
}
MojoHandle RandomHandle(MojoHandle* handles, size_t size) {
return handles[base::RandInt(0, static_cast<int>(size) - 1)];
}
void DoRandomThing(MojoHandle* traps,
size_t num_traps,
MojoHandle* watched_handles,
size_t num_watched_handles) {
switch (base::RandInt(0, 10)) {
case 0:
MojoClose(RandomHandle(traps, num_traps));
break;
case 1:
MojoClose(RandomHandle(watched_handles, num_watched_handles));
break;
case 2:
case 3:
case 4: {
MojoMessageHandle message;
ASSERT_EQ(MOJO_RESULT_OK, MojoCreateMessage(nullptr, &message));
ASSERT_EQ(MOJO_RESULT_OK,
MojoSetMessageContext(message, 1, nullptr, nullptr, nullptr));
MojoWriteMessage(RandomHandle(watched_handles, num_watched_handles),
message, nullptr);
break;
}
case 5:
case 6: {
MojoHandle t = RandomHandle(traps, num_traps);
MojoHandle h = RandomHandle(watched_handles, num_watched_handles);
MojoAddTrigger(t, h, MOJO_HANDLE_SIGNAL_READABLE,
MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
static_cast<uintptr_t>(h), nullptr);
break;
}
case 7:
case 8: {
uint32_t num_blocking_events = 1;
MojoTrapEvent blocking_event = {sizeof(blocking_event)};
if (MojoArmTrap(RandomHandle(traps, num_traps), nullptr,
&num_blocking_events,
&blocking_event) == MOJO_RESULT_FAILED_PRECONDITION &&
blocking_event.result == MOJO_RESULT_OK) {
ReadAllMessages(&blocking_event);
}
break;
}
case 9:
case 10: {
MojoHandle t = RandomHandle(traps, num_traps);
MojoHandle h = RandomHandle(watched_handles, num_watched_handles);
MojoRemoveTrigger(t, static_cast<uintptr_t>(h), nullptr);
break;
}
default:
NOTREACHED();
break;
}
}
TEST_F(TrapTest, ConcurrencyStressTest) {
if (IsMojoIpczEnabled()) {
GTEST_SKIP() << "This test relies on implementation assumptions which are "
<< "invalid when MojoIpcz is enabled; namely that it's safe "
<< "to attempt operations on invalid handles.";
}
constexpr size_t kNumTraps = 50;
constexpr size_t kNumWatchedHandles = 50;
static_assert(kNumWatchedHandles % 2 == 0, "Invalid number of test handles.");
constexpr size_t kNumThreads = 10;
static constexpr size_t kNumOperationsPerThread = 400;
MojoHandle traps[kNumTraps];
MojoHandle watched_handles[kNumWatchedHandles];
g_do_random_thing_callback = base::BindRepeating(
&DoRandomThing, traps, kNumTraps, watched_handles, kNumWatchedHandles);
for (size_t i = 0; i < kNumTraps; ++i)
MojoCreateTrap(&ReadAllMessages, nullptr, &traps[i]);
for (size_t i = 0; i < kNumWatchedHandles; i += 2)
CreateMessagePipe(&watched_handles[i], &watched_handles[i + 1]);
std::unique_ptr<ThreadedRunner> threads[kNumThreads];
for (size_t i = 0; i < kNumThreads; ++i) {
threads[i] = std::make_unique<ThreadedRunner>(base::BindOnce([] {
for (size_t i = 0; i < kNumOperationsPerThread; ++i)
g_do_random_thing_callback.Run();
}));
threads[i]->Start();
}
for (size_t i = 0; i < kNumThreads; ++i)
threads[i]->Join();
for (size_t i = 0; i < kNumTraps; ++i)
MojoClose(traps[i]);
for (size_t i = 0; i < kNumWatchedHandles; ++i)
MojoClose(watched_handles[i]);
}
}
}
}