blob: 688d808678607b1d60135bf128443fca0c49e4b1 [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 <stdio.h>
#include <threads.h>
#include <time.h>
#include <zircon/syscalls.h>
#include <unittest/unittest.h>
// This example tests transferring channel handles through channels. To do so, it:
// Creates two channels, A and B, with handles A0-A1 and B0-B1, respectively
// Sends message "1" into A0
// Sends A1 to B0
// Sends message "2" into A0
// Reads H from B1 (should receive A1 again, possibly with a new value)
// Sends "3" into A0
// Reads from H until empty. Should read "1", "2", "3" in that order.
bool handle_transfer_test(void) {
BEGIN_TEST;
zx_handle_t A[2];
zx_status_t status = zx_channel_create(0, A, A + 1);
char msg[512];
snprintf(msg, sizeof(msg), "failed to create channel A: %d\n", status);
EXPECT_EQ(status, 0, msg);
zx_handle_t B[2];
status = zx_channel_create(0, B, B + 1);
snprintf(msg, sizeof(msg), "failed to create channel B: %d\n", status);
EXPECT_EQ(status, 0, msg);
status = zx_channel_write(A[0], 0u, "1", 1u, NULL, 0u);
snprintf(msg, sizeof(msg), "failed to write message \"1\" into A0: %u\n", status);
EXPECT_EQ(status, ZX_OK, msg);
status = zx_channel_write(B[0], 0u, NULL, 0u, &A[1], 1u);
snprintf(msg, sizeof(msg), "failed to write message with handle A[1]: %u\n", status);
EXPECT_EQ(status, ZX_OK, msg);
A[1] = ZX_HANDLE_INVALID;
status = zx_channel_write(A[0], 0u, "2", 1u, NULL, 0u);
snprintf(msg, sizeof(msg), "failed to write message \"2\" into A0: %u\n", status);
EXPECT_EQ(status, ZX_OK, msg);
zx_handle_t H;
uint32_t num_bytes = 0u;
uint32_t num_handles = 1u;
status = zx_channel_read(B[1], 0u, NULL, &H, 0, num_handles, &num_bytes, &num_handles);
snprintf(msg, sizeof(msg), "failed to read message from B1: %u\n", status);
EXPECT_EQ(status, ZX_OK, msg);
snprintf(msg, sizeof(msg), "failed to read actual handle value from B1\n");
EXPECT_FALSE((num_handles != 1u || H == ZX_HANDLE_INVALID), msg);
status = zx_channel_write(A[0], 0u, "3", 1u, NULL, 0u);
snprintf(msg, sizeof(msg), "failed to write message \"3\" into A0: %u\n", status);
EXPECT_EQ(status, ZX_OK, msg);
for (int i = 0; i < 3; ++i) {
char buf[1];
num_bytes = 1u;
num_handles = 0u;
status = zx_channel_read(H, 0u, buf, NULL, num_bytes, 0, &num_bytes, &num_handles);
snprintf(msg, sizeof(msg), "failed to read message from H: %u\n", status);
EXPECT_EQ(status, ZX_OK, msg);
unittest_printf("read message: %c\n", buf[0]);
}
zx_handle_close(A[0]);
zx_handle_close(B[0]);
zx_handle_close(B[1]);
zx_handle_close(H);
END_TEST;
}
static int thread(void* arg) {
// sleep for 10ms
// this is race-prone, but until there's a way to wait for a thread to be
// blocked, there's no better way to determine that the other thread has
// entered handle_wait_one.
struct timespec t = (struct timespec){
.tv_sec = 0,
.tv_nsec = 10 * 1000 * 1000,
};
nanosleep(&t, NULL);
// Send A0 through B1 to B0.
zx_handle_t* A = (zx_handle_t*)arg;
zx_handle_t* B = A + 2;
zx_status_t status = zx_channel_write(B[1], 0, NULL, 0u, &A[0], 1);
if (status != ZX_OK) {
UNITTEST_TRACEF("failed to write message with handle A0 to B1: %d\n", status);
goto thread_exit;
}
// Read from B0 into H, thus canceling any waits on A0.
zx_handle_t H;
uint32_t num_handles = 1;
status = zx_channel_read(B[0], 0, NULL, &H, 0, num_handles, NULL, &num_handles);
if (status != ZX_OK || num_handles < 1) {
UNITTEST_TRACEF("failed to read message handle H from B0: %d\n", status);
}
thread_exit:
return 0;
}
// This tests canceling a wait when a handle is transferred.
// There are two channels: A0-A1 and B0-B1.
// A thread is created that sends A0 from B1 to B0.
// main() waits on A0.
// The thread then reads from B0, which should cancel the wait in main().
// See [MG-103].
bool handle_transfer_cancel_wait_test(void) {
BEGIN_TEST;
zx_handle_t A[4];
zx_handle_t* B = &A[2];
zx_status_t status = zx_channel_create(0, A, A + 1);
char msg[512];
snprintf(msg, sizeof(msg), "failed to create channel A[0,1]: %d\n", status);
EXPECT_EQ(status, 0, msg);
status = zx_channel_create(0, B, B + 1);
snprintf(msg, sizeof(msg), "failed to create channel B[0,1]: %d\n", status);
EXPECT_EQ(status, 0, msg);
thrd_t thr;
int ret = thrd_create_with_name(&thr, thread, A, "write thread");
EXPECT_EQ(ret, thrd_success, "failed to create write thread");
zx_signals_t signals = ZX_CHANNEL_PEER_CLOSED;
status = zx_object_wait_one(A[0], signals, zx_deadline_after(ZX_SEC(1)), NULL);
EXPECT_NE(ZX_ERR_TIMED_OUT, status, "failed to complete wait when handle transferred");
thrd_join(thr, NULL);
zx_handle_close(B[1]);
zx_handle_close(B[0]);
zx_handle_close(A[1]);
zx_handle_close(A[0]);
END_TEST;
}
BEGIN_TEST_CASE(handle_transfer_tests)
RUN_TEST(handle_transfer_test)
RUN_TEST(handle_transfer_cancel_wait_test)
END_TEST_CASE(handle_transfer_tests)
#ifndef BUILD_COMBINED_TESTS
int main(int argc, char** argv) {
return unittest_run_all_tests(argc, argv) ? 0 : -1;
}
#endif