1// Copyright 2016 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <stdio.h>
6#include <threads.h>
7#include <time.h>
8
9#include <zircon/syscalls.h>
10#include <unittest/unittest.h>
11
12// This example tests transferring channel handles through channels. To do so, it:
13//   Creates two channels, A and B, with handles A0-A1 and B0-B1, respectively
14//   Sends message "1" into A0
15//   Sends A1 to B0
16//   Sends message "2" into A0
17//   Reads H from B1 (should receive A1 again, possibly with a new value)
18//   Sends "3" into A0
19//   Reads from H until empty. Should read "1", "2", "3" in that order.
20bool handle_transfer_test(void) {
21    BEGIN_TEST;
22    zx_handle_t A[2];
23    zx_status_t status = zx_channel_create(0, A, A + 1);
24    char msg[512];
25    snprintf(msg, sizeof(msg), "failed to create channel A: %d\n", status);
26    EXPECT_EQ(status, 0, msg);
27
28    zx_handle_t B[2];
29    status = zx_channel_create(0, B, B + 1);
30    snprintf(msg, sizeof(msg), "failed to create channel B: %d\n", status);
31    EXPECT_EQ(status, 0, msg);
32
33    status = zx_channel_write(A[0], 0u, "1", 1u, NULL, 0u);
34    snprintf(msg, sizeof(msg), "failed to write message \"1\" into A0: %u\n", status);
35    EXPECT_EQ(status, ZX_OK, msg);
36
37    status = zx_channel_write(B[0], 0u, NULL, 0u, &A[1], 1u);
38    snprintf(msg, sizeof(msg), "failed to write message with handle A[1]: %u\n", status);
39    EXPECT_EQ(status, ZX_OK, msg);
40
41    A[1] = ZX_HANDLE_INVALID;
42    status = zx_channel_write(A[0], 0u, "2", 1u, NULL, 0u);
43    snprintf(msg, sizeof(msg), "failed to write message \"2\" into A0: %u\n", status);
44    EXPECT_EQ(status, ZX_OK, msg);
45
46    zx_handle_t H;
47    uint32_t num_bytes = 0u;
48    uint32_t num_handles = 1u;
49    status = zx_channel_read(B[1], 0u, NULL, &H, 0, num_handles, &num_bytes, &num_handles);
50    snprintf(msg, sizeof(msg), "failed to read message from B1: %u\n", status);
51    EXPECT_EQ(status, ZX_OK, msg);
52
53    snprintf(msg, sizeof(msg), "failed to read actual handle value from B1\n");
54    EXPECT_FALSE((num_handles != 1u || H == ZX_HANDLE_INVALID), msg);
55
56    status = zx_channel_write(A[0], 0u, "3", 1u, NULL, 0u);
57    snprintf(msg, sizeof(msg), "failed to write message \"3\" into A0: %u\n", status);
58    EXPECT_EQ(status, ZX_OK, msg);
59
60    for (int i = 0; i < 3; ++i) {
61        char buf[1];
62        num_bytes = 1u;
63        num_handles = 0u;
64        status = zx_channel_read(H, 0u, buf, NULL, num_bytes, 0, &num_bytes, &num_handles);
65        snprintf(msg, sizeof(msg), "failed to read message from H: %u\n", status);
66        EXPECT_EQ(status, ZX_OK, msg);
67        unittest_printf("read message: %c\n", buf[0]);
68    }
69
70    zx_handle_close(A[0]);
71    zx_handle_close(B[0]);
72    zx_handle_close(B[1]);
73    zx_handle_close(H);
74    END_TEST;
75}
76
77static int thread(void* arg) {
78    // sleep for 10ms
79    // this is race-prone, but until there's a way to wait for a thread to be
80    // blocked, there's no better way to determine that the other thread has
81    // entered handle_wait_one.
82    struct timespec t = (struct timespec){
83        .tv_sec = 0,
84        .tv_nsec = 10 * 1000 * 1000,
85    };
86    nanosleep(&t, NULL);
87
88    // Send A0 through B1 to B0.
89    zx_handle_t* A = (zx_handle_t*)arg;
90    zx_handle_t* B = A + 2;
91    zx_status_t status = zx_channel_write(B[1], 0, NULL, 0u, &A[0], 1);
92    if (status != ZX_OK) {
93        UNITTEST_FAIL_TRACEF("failed to write message with handle A0 to B1: %d\n", status);
94        goto thread_exit;
95    }
96
97    // Read from B0 into H, thus canceling any waits on A0.
98    zx_handle_t H;
99    uint32_t num_handles = 1;
100    status = zx_channel_read(B[0], 0, NULL, &H, 0, num_handles, NULL, &num_handles);
101    if (status != ZX_OK || num_handles < 1) {
102        UNITTEST_FAIL_TRACEF("failed to read message handle H from B0: %d\n", status);
103    }
104
105thread_exit:
106    return 0;
107}
108
109// This tests canceling a wait when a handle is transferred.
110//   There are two channels: A0-A1 and B0-B1.
111//   A thread is created that sends A0 from B1 to B0.
112//   main() waits on A0.
113//   The thread then reads from B0, which should cancel the wait in main().
114// See [ZX-103].
115bool handle_transfer_cancel_wait_test(void) {
116    BEGIN_TEST;
117    zx_handle_t A[4];
118    zx_handle_t* B = &A[2];
119    zx_status_t status = zx_channel_create(0, A, A + 1);
120    char msg[512];
121    snprintf(msg, sizeof(msg), "failed to create channel A[0,1]: %d\n", status);
122    EXPECT_EQ(status, 0, msg);
123    status = zx_channel_create(0, B, B + 1);
124    snprintf(msg, sizeof(msg), "failed to create channel B[0,1]: %d\n", status);
125    EXPECT_EQ(status, 0, msg);
126
127    thrd_t thr;
128    int ret = thrd_create_with_name(&thr, thread, A, "write thread");
129    EXPECT_EQ(ret, thrd_success, "failed to create write thread");
130
131    zx_signals_t signals = ZX_CHANNEL_PEER_CLOSED;
132    status = zx_object_wait_one(A[0], signals, zx_deadline_after(ZX_SEC(1)), NULL);
133    EXPECT_NE(ZX_ERR_TIMED_OUT, status, "failed to complete wait when handle transferred");
134
135    thrd_join(thr, NULL);
136    zx_handle_close(B[1]);
137    zx_handle_close(B[0]);
138    zx_handle_close(A[1]);
139    zx_handle_close(A[0]);
140    END_TEST;
141}
142
143BEGIN_TEST_CASE(handle_transfer_tests)
144RUN_TEST(handle_transfer_test)
145RUN_TEST(handle_transfer_cancel_wait_test)
146END_TEST_CASE(handle_transfer_tests)
147
148#ifndef BUILD_COMBINED_TESTS
149int main(int argc, char** argv) {
150    return unittest_run_all_tests(argc, argv) ? 0 : -1;
151}
152#endif
153