| Index: mojo/edk/system/data_pipe_unittest.cc
|
| diff --git a/mojo/edk/system/data_pipe_unittest.cc b/mojo/edk/system/data_pipe_unittest.cc
|
| index af2cf3e6e815170e5c929df195fc2fd8c46a65dc..36d793a20fd523b992f549b092045fc79882dc8f 100644
|
| --- a/mojo/edk/system/data_pipe_unittest.cc
|
| +++ b/mojo/edk/system/data_pipe_unittest.cc
|
| @@ -15,7 +15,7 @@
|
| #include "mojo/edk/embedder/simple_platform_support.h"
|
| #include "mojo/edk/system/test_utils.h"
|
| #include "mojo/edk/system/waiter.h"
|
| -#include "mojo/edk/test/multiprocess_test_helper.h"
|
| +#include "mojo/edk/test/mojo_test_base.h"
|
| #include "mojo/public/c/system/data_pipe.h"
|
| #include "mojo/public/c/system/functions.h"
|
| #include "mojo/public/c/system/message_pipe.h"
|
| @@ -38,9 +38,9 @@ const size_t kMaxPoll = 100;
|
| // Used in Multiprocess test.
|
| const size_t kMultiprocessCapacity = 37;
|
| const char kMultiprocessTestData[] = "hello i'm a string that is 36 bytes";
|
| -const int kMultiprocessMaxIter = 513;
|
| +const int kMultiprocessMaxIter = 5;
|
|
|
| -class DataPipeTest : public testing::Test {
|
| +class DataPipeTest : public test::MojoTestBase {
|
| public:
|
| DataPipeTest() : producer_(MOJO_HANDLE_INVALID),
|
| consumer_(MOJO_HANDLE_INVALID) {}
|
| @@ -391,10 +391,8 @@ TEST_F(DataPipeTest, BasicProducerWaiting) {
|
| ASSERT_EQ(MOJO_RESULT_OK,
|
| BeginReadData(&read_buffer, &num_bytes, false));
|
| EXPECT_TRUE(read_buffer);
|
| - // Since we only read one element (after having written three in all), the
|
| - // two-phase read should only allow us to read one. This checks an
|
| - // implementation detail!
|
| - ASSERT_EQ(static_cast<uint32_t>(1u * sizeof(elements[0])), num_bytes);
|
| + // The two-phase read should be able to read at least one element.
|
| + ASSERT_GE(num_bytes, static_cast<uint32_t>(1u * sizeof(elements[0])));
|
| ASSERT_EQ(456, static_cast<const int32_t*>(read_buffer)[0]);
|
| ASSERT_EQ(MOJO_RESULT_OK, EndReadData(static_cast<uint32_t>(
|
| 1u * sizeof(elements[0]))));
|
| @@ -560,7 +558,7 @@ TEST_F(DataPipeTest, BasicConsumerWaiting) {
|
| ASSERT_EQ(MOJO_RESULT_OK,
|
| MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
|
| MOJO_DEADLINE_INDEFINITE, &hss));
|
| - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
|
| + ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) != 0);
|
| ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
|
| hss.satisfiable_signals);
|
|
|
| @@ -952,14 +950,11 @@ TEST_F(DataPipeTest, AllOrNone) {
|
| ASSERT_EQ(0u, num_bytes);
|
| }
|
|
|
| -/*
|
| -jam: this is testing that the implementation uses a circular buffer, which we
|
| -don't use currently.
|
| // Tests that |ProducerWriteData()| and |ConsumerReadData()| writes and reads,
|
| // respectively, as much as possible, even if it may have to "wrap around" the
|
| // internal circular buffer. (Note that the two-phase write and read need not do
|
| // this.)
|
| -TYPED_TEST(DataPipeImplTest, WrapAround) {
|
| +TEST_F(DataPipeTest, WrapAround) {
|
| unsigned char test_data[1000];
|
| for (size_t i = 0; i < MOJO_ARRAYSIZE(test_data); i++)
|
| test_data[i] = static_cast<unsigned char>(i);
|
| @@ -970,119 +965,80 @@ TYPED_TEST(DataPipeImplTest, WrapAround) {
|
| 1u, // |element_num_bytes|.
|
| 100u // |capacity_num_bytes|.
|
| };
|
| - MojoCreateDataPipeOptions validated_options = {};
|
| - // This test won't be valid if |ValidateCreateOptions()| decides to give the
|
| - // pipe more space.
|
| - ASSERT_EQ(MOJO_RESULT_OK, DataPipe::ValidateCreateOptions(
|
| - &options, &validated_options));
|
| - ASSERT_EQ(100u, validated_options.capacity_num_bytes);
|
| - this->Create(options);
|
| - this->DoTransfer();
|
| -
|
| - Waiter waiter;
|
| - HandleSignalsState hss;
|
| -
|
| - // Add waiter.
|
| - waiter.Init();
|
| - ASSERT_EQ(MOJO_RESULT_OK,
|
| - this->ConsumerAddAwakable(&waiter, MOJO_HANDLE_SIGNAL_READABLE, 1,
|
| - nullptr));
|
| +
|
| + ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
|
| + MojoHandleSignalsState hss;
|
|
|
| // Write 20 bytes.
|
| uint32_t num_bytes = 20u;
|
| - ASSERT_EQ(MOJO_RESULT_OK,
|
| - this->ProducerWriteData(&test_data[0], &num_bytes, false));
|
| + ASSERT_EQ(MOJO_RESULT_OK, WriteData(&test_data[0], &num_bytes, true));
|
| ASSERT_EQ(20u, num_bytes);
|
|
|
| // Wait for data.
|
| - // TODO(vtl): (See corresponding TODO in AllOrNone.)
|
| - ASSERT_EQ(MOJO_RESULT_OK, waiter.Wait(test::TinyDeadline(), nullptr));
|
| - hss = HandleSignalsState();
|
| - this->ConsumerRemoveAwakable(&waiter, &hss);
|
| - ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE, hss.satisfied_signals);
|
| + ASSERT_EQ(MOJO_RESULT_OK,
|
| + MojoWait(consumer_, MOJO_HANDLE_SIGNAL_READABLE,
|
| + MOJO_DEADLINE_INDEFINITE, &hss));
|
| + ASSERT_TRUE((hss.satisfied_signals & MOJO_HANDLE_SIGNAL_READABLE) != 0);
|
| ASSERT_EQ(MOJO_HANDLE_SIGNAL_READABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED,
|
| hss.satisfiable_signals);
|
|
|
| // Read 10 bytes.
|
| unsigned char read_buffer[1000] = {0};
|
| num_bytes = 10u;
|
| - ASSERT_EQ(MOJO_RESULT_OK,
|
| - this->ConsumerReadData(read_buffer, &num_bytes, false, false));
|
| + ASSERT_EQ(MOJO_RESULT_OK, ReadData(read_buffer, &num_bytes, true));
|
| ASSERT_EQ(10u, num_bytes);
|
| ASSERT_EQ(0, memcmp(read_buffer, &test_data[0], 10u));
|
|
|
| - if (this->IsStrictCircularBuffer()) {
|
| - // Check that a two-phase write can now only write (at most) 80 bytes. (This
|
| - // checks an implementation detail; this behavior is not guaranteed.)
|
| - void* write_buffer_ptr = nullptr;
|
| - num_bytes = 0u;
|
| - ASSERT_EQ(MOJO_RESULT_OK,
|
| - this->ProducerBeginWriteData(&write_buffer_ptr, &num_bytes,
|
| - false));
|
| - EXPECT_TRUE(write_buffer_ptr);
|
| - ASSERT_EQ(80u, num_bytes);
|
| - ASSERT_EQ(MOJO_RESULT_OK, this->ProducerEndWriteData(0u));
|
| - }
|
| + // Check that a two-phase write can now only write (at most) 80 bytes. (This
|
| + // checks an implementation detail; this behavior is not guaranteed.)
|
| + void* write_buffer_ptr = nullptr;
|
| + num_bytes = 0u;
|
| + ASSERT_EQ(MOJO_RESULT_OK,
|
| + BeginWriteData(&write_buffer_ptr, &num_bytes, false));
|
| + EXPECT_TRUE(write_buffer_ptr);
|
| + ASSERT_EQ(80u, num_bytes);
|
| + ASSERT_EQ(MOJO_RESULT_OK, EndWriteData(0));
|
|
|
| - // TODO(vtl): (See corresponding TODO in TwoPhaseAllOrNone.)
|
| size_t total_num_bytes = 0;
|
| - for (size_t i = 0; i < kMaxPoll; i++) {
|
| - // Write as much data as we can (using |ProducerWriteData()|). We should
|
| - // write 90 bytes (eventually).
|
| - num_bytes = 200u;
|
| - MojoResult result = this->ProducerWriteData(
|
| - &test_data[20 + total_num_bytes], &num_bytes, false);
|
| - if (result == MOJO_RESULT_OK) {
|
| - total_num_bytes += num_bytes;
|
| - if (total_num_bytes >= 90u)
|
| - break;
|
| - } else {
|
| - ASSERT_EQ(MOJO_RESULT_OUT_OF_RANGE, result);
|
| - }
|
| -
|
| - test::Sleep(test::EpsilonDeadline());
|
| + while (total_num_bytes < 90) {
|
| + // Wait to write.
|
| + ASSERT_EQ(MOJO_RESULT_OK,
|
| + MojoWait(producer_, MOJO_HANDLE_SIGNAL_WRITABLE,
|
| + MOJO_DEADLINE_INDEFINITE, &hss));
|
| + ASSERT_EQ(hss.satisfied_signals, MOJO_HANDLE_SIGNAL_WRITABLE);
|
| + ASSERT_EQ(hss.satisfiable_signals,
|
| + MOJO_HANDLE_SIGNAL_WRITABLE | MOJO_HANDLE_SIGNAL_PEER_CLOSED);
|
| +
|
| + // Write as much as we can.
|
| + num_bytes = 100;
|
| + ASSERT_EQ(MOJO_RESULT_OK,
|
| + WriteData(&test_data[20 + total_num_bytes], &num_bytes, false));
|
| + total_num_bytes += num_bytes;
|
| }
|
| - ASSERT_EQ(90u, total_num_bytes);
|
|
|
| - // TODO(vtl): (See corresponding TODO in TwoPhaseAllOrNone.)
|
| - for (size_t i = 0; i < kMaxPoll; i++) {
|
| - // We have 100.
|
| - num_bytes = 0u;
|
| - ASSERT_EQ(MOJO_RESULT_OK,
|
| - this->ConsumerQueryData(&num_bytes));
|
| - if (num_bytes >= 100u)
|
| - break;
|
| + ASSERT_EQ(90u, total_num_bytes);
|
|
|
| - test::Sleep(test::EpsilonDeadline());
|
| - }
|
| + num_bytes = 0;
|
| + ASSERT_EQ(MOJO_RESULT_OK, QueryData(&num_bytes));
|
| ASSERT_EQ(100u, num_bytes);
|
|
|
| - if (this->IsStrictCircularBuffer()) {
|
| - // Check that a two-phase read can now only read (at most) 90 bytes. (This
|
| - // checks an implementation detail; this behavior is not guaranteed.)
|
| - const void* read_buffer_ptr = nullptr;
|
| - num_bytes = 0u;
|
| - ASSERT_EQ(MOJO_RESULT_OK,
|
| - this->ConsumerBeginReadData(&read_buffer_ptr, &num_bytes, false));
|
| - EXPECT_TRUE(read_buffer_ptr);
|
| - ASSERT_EQ(90u, num_bytes);
|
| - ASSERT_EQ(MOJO_RESULT_OK, this->ConsumerEndReadData(0u));
|
| - }
|
| + // Check that a two-phase read can now only read (at most) 90 bytes. (This
|
| + // checks an implementation detail; this behavior is not guaranteed.)
|
| + const void* read_buffer_ptr = nullptr;
|
| + num_bytes = 0;
|
| + ASSERT_EQ(MOJO_RESULT_OK, BeginReadData(&read_buffer_ptr, &num_bytes, false));
|
| + EXPECT_TRUE(read_buffer_ptr);
|
| + ASSERT_EQ(90u, num_bytes);
|
| + ASSERT_EQ(MOJO_RESULT_OK, EndReadData(0));
|
|
|
| - // Read as much as possible (using |ConsumerReadData()|). We should read 100
|
| - // bytes.
|
| + // Read as much as possible. We should read 100 bytes.
|
| num_bytes = static_cast<uint32_t>(MOJO_ARRAYSIZE(read_buffer) *
|
| sizeof(read_buffer[0]));
|
| memset(read_buffer, 0, num_bytes);
|
| - ASSERT_EQ(MOJO_RESULT_OK,
|
| - this->ConsumerReadData(read_buffer, &num_bytes, false, false));
|
| + ASSERT_EQ(MOJO_RESULT_OK, ReadData(read_buffer, &num_bytes));
|
| ASSERT_EQ(100u, num_bytes);
|
| ASSERT_EQ(0, memcmp(read_buffer, &test_data[10], 100u));
|
| -
|
| - this->ProducerClose();
|
| - this->ConsumerClose();
|
| }
|
| -*/
|
|
|
| // Tests the behavior of writing (simple and two-phase), closing the producer,
|
| // then reading (simple and two-phase).
|
| @@ -1717,79 +1673,68 @@ TEST_F(DataPipeTest, MAYBE_Multiprocess) {
|
| };
|
| ASSERT_EQ(MOJO_RESULT_OK, Create(&options));
|
|
|
| - test::MultiprocessTestHelper multiprocess_test_helper;
|
| - multiprocess_test_helper.StartChild("MultiprocessClient");
|
| + RUN_CHILD_ON_PIPE(MultiprocessClient, server_mp)
|
| + // Send some data before serialising and sending the data pipe over.
|
| + // This is the first write so we don't need to use WriteAllData.
|
| + uint32_t num_bytes = kTestDataSize;
|
| + ASSERT_EQ(MOJO_RESULT_OK, WriteData(kMultiprocessTestData, &num_bytes,
|
| + MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
|
| + ASSERT_EQ(kTestDataSize, num_bytes);
|
|
|
| - // Send some data before serialising and sending the data pipe over.
|
| - // This is the first write so we don't need to use WriteAllData.
|
| - uint32_t num_bytes = kTestDataSize;
|
| - ASSERT_EQ(MOJO_RESULT_OK, WriteData(kMultiprocessTestData, &num_bytes,
|
| - MOJO_WRITE_DATA_FLAG_ALL_OR_NONE));
|
| - ASSERT_EQ(kTestDataSize, num_bytes);
|
| -
|
| - MojoHandle server_mp =
|
| - CreateMessagePipe(
|
| - std::move(multiprocess_test_helper.server_platform_handle))
|
| - .release()
|
| - .value();
|
| -
|
| - // Send child process the data pipe.
|
| - ASSERT_EQ(MOJO_RESULT_OK, MojoWriteMessage(server_mp, nullptr, 0, &consumer_,
|
| - 1, MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| -
|
| - // Send a bunch of data of varying sizes.
|
| - uint8_t buffer[100];
|
| - int seq = 0;
|
| - for (int i = 0; i < kMultiprocessMaxIter; ++i) {
|
| - for (uint32_t size = 1; size <= kMultiprocessCapacity; size++) {
|
| - for (unsigned int j = 0; j < size; ++j)
|
| - buffer[j] = seq + j;
|
| - EXPECT_TRUE(WriteAllData(producer_, buffer, size));
|
| - seq += size;
|
| + // Send child process the data pipe.
|
| + ASSERT_EQ(MOJO_RESULT_OK,
|
| + MojoWriteMessage(server_mp, nullptr, 0, &consumer_, 1,
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| +
|
| + // Send a bunch of data of varying sizes.
|
| + uint8_t buffer[100];
|
| + int seq = 0;
|
| + for (int i = 0; i < kMultiprocessMaxIter; ++i) {
|
| + for (uint32_t size = 1; size <= kMultiprocessCapacity; size++) {
|
| + for (unsigned int j = 0; j < size; ++j)
|
| + buffer[j] = seq + j;
|
| + EXPECT_TRUE(WriteAllData(producer_, buffer, size));
|
| + seq += size;
|
| + }
|
| }
|
| - }
|
|
|
| - // Write the test string in again.
|
| - EXPECT_TRUE(WriteAllData(producer_, kMultiprocessTestData, kTestDataSize));
|
| + // Write the test string in again.
|
| + ASSERT_TRUE(WriteAllData(producer_, kMultiprocessTestData, kTestDataSize));
|
|
|
| - // Swap ends.
|
| - ASSERT_EQ(MOJO_RESULT_OK, MojoWriteMessage(server_mp, nullptr, 0, &producer_,
|
| - 1, MOJO_WRITE_MESSAGE_FLAG_NONE));
|
| + // Swap ends.
|
| + ASSERT_EQ(MOJO_RESULT_OK,
|
| + MojoWriteMessage(server_mp, nullptr, 0, &producer_, 1,
|
| + MOJO_WRITE_MESSAGE_FLAG_NONE));
|
|
|
| - // Receive the consumer from the other side.
|
| - producer_ = MOJO_HANDLE_INVALID;
|
| - MojoHandleSignalsState hss = MojoHandleSignalsState();
|
| - ASSERT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_READABLE,
|
| - MOJO_DEADLINE_INDEFINITE, &hss));
|
| - MojoHandle handles[2];
|
| - uint32_t num_handles = MOJO_ARRAYSIZE(handles);
|
| - ASSERT_EQ(MOJO_RESULT_OK,
|
| - MojoReadMessage(server_mp, nullptr, 0, handles, &num_handles,
|
| - MOJO_READ_MESSAGE_FLAG_NONE));
|
| - ASSERT_EQ(1u, num_handles);
|
| - consumer_ = handles[0];
|
| + // Receive the consumer from the other side.
|
| + producer_ = MOJO_HANDLE_INVALID;
|
| + MojoHandleSignalsState hss = MojoHandleSignalsState();
|
| + ASSERT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_READABLE,
|
| + MOJO_DEADLINE_INDEFINITE, &hss));
|
| + MojoHandle handles[2];
|
| + uint32_t num_handles = MOJO_ARRAYSIZE(handles);
|
| + ASSERT_EQ(MOJO_RESULT_OK,
|
| + MojoReadMessage(server_mp, nullptr, 0, handles, &num_handles,
|
| + MOJO_READ_MESSAGE_FLAG_NONE));
|
| + ASSERT_EQ(1u, num_handles);
|
| + consumer_ = handles[0];
|
| +
|
| + // Read the test string twice. Once for when we sent it, and once for the
|
| + // other end sending it.
|
| + for (int i = 0; i < 2; ++i) {
|
| + EXPECT_TRUE(ReadAllData(consumer_, buffer, kTestDataSize, i == 1));
|
| + EXPECT_EQ(0, memcmp(buffer, kMultiprocessTestData, kTestDataSize));
|
| + }
|
|
|
| - // Read the test string twice. Once for when we sent it, and once for the
|
| - // other end sending it.
|
| - for (int i = 0; i < 2; ++i) {
|
| - EXPECT_TRUE(ReadAllData(consumer_, buffer, kTestDataSize, i == 1));
|
| - EXPECT_EQ(0, memcmp(buffer, kMultiprocessTestData, kTestDataSize));
|
| - }
|
| + WriteMessage(server_mp, "quit");
|
|
|
| - // Don't have to close the consumer here because it will be done for us.
|
| - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(server_mp));
|
| - EXPECT_TRUE(multiprocess_test_helper.WaitForChildTestShutdown());
|
| + // Don't have to close the consumer here because it will be done for us.
|
| + END_CHILD()
|
| }
|
|
|
| -MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessClient) {
|
| - ScopedPlatformHandle client_platform_handle =
|
| - std::move(test::MultiprocessTestHelper::client_platform_handle);
|
| +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessClient, DataPipeTest, client_mp) {
|
| const uint32_t kTestDataSize =
|
| static_cast<uint32_t>(sizeof(kMultiprocessTestData));
|
| - EXPECT_TRUE(client_platform_handle.is_valid());
|
| -
|
| - MojoHandle client_mp =
|
| - CreateMessagePipe(std::move(client_platform_handle)).release().value();
|
|
|
| // Receive the data pipe from the other side.
|
| MojoHandle consumer = MOJO_HANDLE_INVALID;
|
| @@ -1844,7 +1789,68 @@ MOJO_MULTIPROCESS_TEST_CHILD_TEST(MultiprocessClient) {
|
|
|
| // We swapped ends, so close the producer.
|
| EXPECT_EQ(MOJO_RESULT_OK, MojoClose(producer));
|
| - EXPECT_EQ(MOJO_RESULT_OK, MojoClose(client_mp));
|
| +
|
| + // Wait to receive a "quit" message before exiting.
|
| + EXPECT_EQ("quit", ReadMessage(client_mp));
|
| +}
|
| +
|
| +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(WriteAndCloseProducer, DataPipeTest, h) {
|
| + MojoHandle p;
|
| + std::string message = ReadMessageWithHandles(h, &p, 1);
|
| +
|
| + // Write some data to the producer and close it.
|
| + uint32_t num_bytes = static_cast<uint32_t>(message.size());
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoWriteData(p, message.data(), &num_bytes,
|
| + MOJO_WRITE_DATA_FLAG_NONE));
|
| + EXPECT_EQ(num_bytes, static_cast<uint32_t>(message.size()));
|
| +
|
| + // Close the producer before quitting.
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(p));
|
| +
|
| + // Wait for a quit message.
|
| + EXPECT_EQ("quit", ReadMessage(h));
|
| +}
|
| +
|
| +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ReadAndCloseConsumer, DataPipeTest, h) {
|
| + MojoHandle c;
|
| + std::string expected_message = ReadMessageWithHandles(h, &c, 1);
|
| +
|
| + // Wait for the consumer to become readable.
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoWait(c, MOJO_HANDLE_SIGNAL_READABLE,
|
| + MOJO_DEADLINE_INDEFINITE, nullptr));
|
| +
|
| + // Drain the consumer and expect to find the given message.
|
| + uint32_t num_bytes = static_cast<uint32_t>(expected_message.size());
|
| + std::vector<char> bytes(expected_message.size());
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoReadData(c, bytes.data(), &num_bytes,
|
| + MOJO_READ_DATA_FLAG_NONE));
|
| + EXPECT_EQ(num_bytes, static_cast<uint32_t>(bytes.size()));
|
| +
|
| + std::string message(bytes.data(), bytes.size());
|
| + EXPECT_EQ(expected_message, message);
|
| +
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoClose(c));
|
| +
|
| + // Wait for a quit message.
|
| + EXPECT_EQ("quit", ReadMessage(h));
|
| +}
|
| +
|
| +TEST_F(DataPipeTest, SendConsumerAndCloseProducer) {
|
| + // Create a new data pipe.
|
| + MojoHandle p, c;
|
| + EXPECT_EQ(MOJO_RESULT_OK, MojoCreateDataPipe(nullptr, &p ,&c));
|
| +
|
| + RUN_CHILD_ON_PIPE(WriteAndCloseProducer, producer_client)
|
| + RUN_CHILD_ON_PIPE(ReadAndCloseConsumer, consumer_client)
|
| + const std::string kMessage = "Hello, world!";
|
| + WriteMessageWithHandles(producer_client, kMessage, &p, 1);
|
| + WriteMessageWithHandles(consumer_client, kMessage, &c, 1);
|
| +
|
| + WriteMessage(consumer_client, "quit");
|
| + END_CHILD()
|
| +
|
| + WriteMessage(producer_client, "quit");
|
| + END_CHILD()
|
| }
|
|
|
| } // namespace
|
|
|