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 <assert.h>
6#include <stdarg.h>
7#include <stdatomic.h>
8#include <stdbool.h>
9#include <stdio.h>
10#include <stdlib.h>
11#include <string.h>
12#include <threads.h>
13#include <unistd.h>
14
15#include <zircon/syscalls.h>
16#include <zircon/threads.h>
17#include <unittest/unittest.h>
18
19#include <zircon/compiler.h>
20
21#define ASSERT_NOT_REACHED() \
22    assert(0)
23
24// We have to poll a thread's state as there is no way to wait for it to
25// transition states. Wait this amount of time. Generally the thread won't
26// take very long so this is a compromise between polling too frequently and
27// waiting too long.
28#define THREAD_BLOCKED_WAIT_DURATION ZX_MSEC(1)
29
30enum message {
31    MSG_EXIT,
32    MSG_EXITED,
33    MSG_WAIT_EVENT,
34    MSG_WAIT_EVENT_SIGNALED,
35    MSG_WAIT_EVENT_CANCELLED,
36    MSG_PING,
37    MSG_PONG,
38    MSG_READ_CANCELLED,
39};
40
41enum wait_result {
42    WAIT_READABLE,
43    WAIT_SIGNALED,
44    WAIT_CLOSED,
45    WAIT_CANCELLED,
46};
47
48typedef struct thread_data {
49    int thread_num;
50    zx_handle_t channel;
51} thread_data_t;
52
53typedef struct wait_data {
54    zx_handle_t handle;
55    zx_handle_t signals;
56    zx_duration_t timeout;
57    zx_status_t status;
58} wait_data_t;
59
60// [0] is used by main thread
61// [1] is used by worker thread
62static zx_handle_t thread1_channel[2];
63static zx_handle_t thread2_channel[2];
64
65static atomic_int in_wait_event = ATOMIC_VAR_INIT(0);
66static zx_handle_t event_handle;
67
68// Wait until |handle| is readable or peer is closed (or wait is cancelled).
69
70static bool wait_readable(zx_handle_t handle, enum wait_result* result) {
71    zx_signals_t pending;
72    zx_signals_t signals = ZX_CHANNEL_READABLE | ZX_CHANNEL_PEER_CLOSED;
73    zx_time_t deadline = ZX_TIME_INFINITE;
74    zx_status_t status = zx_object_wait_one(handle, signals, deadline, &pending);
75    if (status == ZX_ERR_CANCELED) {
76        *result = WAIT_CANCELLED;
77        return true;
78    }
79    ASSERT_GE(status, 0, "handle wait one failed");
80    if ((pending & ZX_CHANNEL_READABLE) != 0) {
81        *result = WAIT_READABLE;
82        return true;
83    }
84    unittest_printf("wait_readable: peer closed\n");
85    *result = WAIT_CLOSED;
86    return true;
87}
88
89// N.B. This must use zx_object_wait_one.
90// See wait_thread_blocked_in_wait_event.
91static bool wait_event_worker(zx_handle_t handle, enum wait_result* result) {
92    zx_signals_t pending;
93    zx_signals_t signals = ZX_EVENT_SIGNALED;
94    zx_time_t deadline = ZX_TIME_INFINITE;
95    zx_status_t status = zx_object_wait_one(handle, signals, deadline, &pending);
96    if (status == ZX_ERR_CANCELED) {
97        *result = WAIT_CANCELLED;
98        return true;
99    }
100    ASSERT_GE(status, 0, "handle wait one failed");
101    ASSERT_NE(pending & ZX_EVENT_SIGNALED, 0u,
102              "unexpected return in wait_signaled");
103    *result = WAIT_SIGNALED;
104    return true;
105}
106
107static bool wait_event(enum wait_result* result) {
108    atomic_store(&in_wait_event, 1);
109    bool pass = wait_event_worker(event_handle, result);
110    atomic_store(&in_wait_event, 0);
111    return pass;
112}
113
114// Wait for |thread| to be blocked inside wait_event().
115// We wait forever and let Unittest's watchdog handle errors.
116// Returns true if |thread| successfully enters the blocked state,
117// false if there's an error somewhere.
118// N.B. We assume wait_event() uses zx_object_wait_one.
119static bool wait_thread_blocked_in_wait_event(zx_handle_t thread) {
120    while (true) {
121        if (atomic_load(&in_wait_event)) {
122            zx_info_thread_t info;
123            ASSERT_EQ(zx_object_get_info(thread, ZX_INFO_THREAD, &info, sizeof(info), NULL, NULL),
124                      ZX_OK, "");
125            if (info.state == ZX_THREAD_STATE_BLOCKED_WAIT_ONE)
126                break;
127        }
128        zx_nanosleep(zx_deadline_after(THREAD_BLOCKED_WAIT_DURATION));
129    }
130
131    return true;
132}
133
134static zx_status_t channel_create(zx_handle_t* handle0, zx_handle_t* handle1) {
135    return zx_channel_create(0, handle0, handle1);
136}
137
138static bool send_msg(zx_handle_t handle, enum message msg) {
139    uint64_t data = msg;
140    unittest_printf("sending message %d on handle %u\n", msg, handle);
141    zx_status_t status =
142        zx_channel_write(handle, 0, &data, sizeof(data), NULL, 0);
143    ASSERT_GE(status, 0, "message write failed");
144    return true;
145}
146
147static bool recv_msg(zx_handle_t handle, enum message* msg) {
148    uint64_t data;
149
150    unittest_printf("waiting for message on handle %u\n", handle);
151    enum wait_result result;
152    ASSERT_TRUE(wait_readable(handle, &result), "Error during waiting for read call");
153    ASSERT_NE(result, (enum wait_result)WAIT_CLOSED, "peer closed while trying to read message");
154    switch (result) {
155    case WAIT_READABLE:
156        break;
157    case WAIT_CANCELLED:
158        unittest_printf("read wait cancelled\n");
159        *msg = MSG_READ_CANCELLED;
160        return true;
161    default:
162        ASSERT_TRUE(false, "Invalid read-wait status");
163    }
164
165    uint32_t num_bytes = sizeof(data);
166
167    ASSERT_GE(zx_channel_read(handle, 0, &data, NULL, num_bytes, 0, &num_bytes, NULL), 0,
168              "Error while reading message");
169    EXPECT_EQ(num_bytes, sizeof(data), "unexpected message size");
170    if (num_bytes != sizeof(data)) {
171        zx_thread_exit();
172    }
173    *msg = (enum message)data;
174    unittest_printf("received message %d\n", *msg);
175    return true;
176}
177
178static bool msg_loop(zx_handle_t channel) {
179    bool my_done_tests = false;
180    while (!my_done_tests) {
181        enum message msg;
182        enum wait_result result;
183        ASSERT_TRUE(recv_msg(channel, &msg), "Error while receiving msg");
184        switch (msg) {
185        case MSG_EXIT:
186            my_done_tests = true;
187            break;
188        case MSG_PING:
189            send_msg(channel, MSG_PONG);
190            break;
191        case MSG_WAIT_EVENT:
192            ASSERT_TRUE(wait_event(&result), "Error during wait signal call");
193            switch (result) {
194            case WAIT_SIGNALED:
195                send_msg(channel, MSG_WAIT_EVENT_SIGNALED);
196                break;
197            case WAIT_CANCELLED:
198                send_msg(channel, MSG_WAIT_EVENT_CANCELLED);
199                break;
200            default:
201                ASSERT_TRUE(false, "Invalid wait signal");
202            }
203            break;
204        default:
205            unittest_printf("unknown message received: %d", msg);
206            break;
207        }
208    }
209    return true;
210}
211
212static int worker_thread_func(void* arg) {
213    thread_data_t* data = arg;
214    msg_loop(data->channel);
215    unittest_printf("thread %d exiting\n", data->thread_num);
216    send_msg(data->channel, MSG_EXITED);
217    return 0;
218}
219
220
221static int wait_thread_func(void* arg) {
222    wait_data_t* data = arg;
223    zx_signals_t observed;
224    data->status = zx_object_wait_one(data->handle, data->signals, zx_deadline_after(data->timeout),
225                                      &observed);
226    return 0;
227}
228
229bool handle_wait_test(void) {
230    BEGIN_TEST;
231
232    ASSERT_GE(channel_create(&thread1_channel[0], &thread1_channel[1]), 0, "channel creation failed");
233    ASSERT_GE(channel_create(&thread2_channel[0], &thread2_channel[1]), 0, "channel creation failed");
234
235    thread_data_t thread1_data = {1, thread1_channel[1]};
236    thread_data_t thread2_data = {2, thread2_channel[1]};
237
238    thrd_t thread1;
239    ASSERT_EQ(thrd_create(&thread1, worker_thread_func, &thread1_data), thrd_success,
240              "thread creation failed");
241    thrd_t thread2;
242    ASSERT_EQ(thrd_create(&thread2, worker_thread_func, &thread2_data), thrd_success,
243              "thread creation failed");
244    unittest_printf("threads started\n");
245
246    event_handle = ZX_HANDLE_INVALID;
247    ASSERT_EQ(zx_event_create(0u, &event_handle), 0, "");
248    ASSERT_NE(event_handle, ZX_HANDLE_INVALID, "event creation failed");
249
250    enum message msg;
251    send_msg(thread1_channel[0], MSG_PING);
252    ASSERT_TRUE(recv_msg(thread1_channel[0], &msg), "Error while receiving msg");
253    EXPECT_EQ(msg, (enum message)MSG_PONG, "unexpected reply to ping1");
254
255    send_msg(thread1_channel[0], MSG_WAIT_EVENT);
256
257    send_msg(thread2_channel[0], MSG_PING);
258    ASSERT_TRUE(recv_msg(thread2_channel[0], &msg), "Error while receiving msg");
259    EXPECT_EQ(msg, (enum message)MSG_PONG, "unexpected reply to ping2");
260
261    // Verify thread 1 is woken up when we close the handle it's waiting on
262    // when there exists a duplicate of the handle.
263    // But first make sure the thread is waiting on |event_handle| before we
264    // close it.
265    zx_handle_t thread1_handle = thrd_get_zx_handle(thread1);
266    ASSERT_TRUE(wait_thread_blocked_in_wait_event(thread1_handle), "");
267
268    zx_handle_t event_handle_dup = ZX_HANDLE_INVALID;
269    zx_status_t status = zx_handle_duplicate(event_handle, ZX_RIGHT_SAME_RIGHTS, &event_handle_dup);
270    ASSERT_EQ(status, ZX_OK, "");
271    ASSERT_NE(event_handle_dup, ZX_HANDLE_INVALID, "handle duplication failed");
272    ASSERT_EQ(zx_handle_close(event_handle), ZX_OK, "handle close failed");
273
274    ASSERT_TRUE(recv_msg(thread1_channel[0], &msg), "Error while receiving msg");
275    ASSERT_EQ(msg, (enum message)MSG_WAIT_EVENT_CANCELLED,
276              "unexpected reply from thread1 (wait for event)");
277
278    send_msg(thread1_channel[0], MSG_EXIT);
279    send_msg(thread2_channel[0], MSG_EXIT);
280    EXPECT_EQ(thrd_join(thread1, NULL), thrd_success, "failed to join thread");
281    EXPECT_EQ(thrd_join(thread2, NULL), thrd_success, "failed to join thread");
282    EXPECT_EQ(zx_handle_close(event_handle_dup), ZX_OK, "handle close failed");
283    END_TEST;
284}
285
286BEGIN_TEST_CASE(handle_wait_tests)
287RUN_TEST(handle_wait_test);
288END_TEST_CASE(handle_wait_tests)
289
290#ifndef BUILD_COMBINED_TESTS
291int main(int argc, char** argv) {
292    bool success = unittest_run_all_tests(argc, argv);
293    return success ? 0 : -1;
294}
295#endif
296