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 <stdbool.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <threads.h>
10#include <unistd.h>
11
12#include <zircon/syscalls.h>
13#include <unittest/unittest.h>
14
15static bool wait(zx_handle_t event, zx_handle_t quit_event) {
16    zx_status_t ms;
17    zx_wait_item_t items[2];
18    items[0].waitfor = ZX_EVENT_SIGNALED;
19    items[0].handle = event;
20    items[1].waitfor = ZX_EVENT_SIGNALED;
21    items[1].handle = quit_event;
22
23    ms = zx_object_wait_many(items, 2, ZX_TIME_INFINITE);
24    if (ms < 0)
25        return false;
26
27    return (items[1].pending & ZX_EVENT_SIGNALED);
28}
29
30static bool wait_user(zx_handle_t event, zx_handle_t quit_event, zx_signals_t user_signal) {
31    zx_status_t ms;
32
33    zx_wait_item_t items[2];
34    items[0].waitfor = user_signal;
35    items[0].handle = event;
36    items[1].waitfor = ZX_EVENT_SIGNALED;
37    items[1].handle = quit_event;
38
39    ms = zx_object_wait_many(items, 2, ZX_TIME_INFINITE);
40    if (ms < 0)
41        return false;
42
43    return (items[1].pending & ZX_EVENT_SIGNALED);
44}
45
46static int thread_fn_1(void* arg) {
47    zx_handle_t* events = (zx_handle_t*)(arg);
48
49    do {
50        zx_nanosleep(zx_deadline_after(ZX_MSEC(200)));
51        zx_status_t status = zx_object_signal(events[1], 0u, ZX_EVENT_SIGNALED);
52        assert(status == ZX_OK);
53    } while (!wait(events[2], events[0]));
54
55    return 0;
56}
57
58static int thread_fn_2(void* arg) {
59    zx_handle_t* events = (zx_handle_t*)(arg);
60
61    while (!wait(events[1], events[0])) {
62        zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
63        zx_status_t status = zx_object_signal(events[2], 0u, ZX_EVENT_SIGNALED);
64        assert(status == ZX_OK);
65    }
66
67    return 0;
68}
69
70static bool basic_test(void) {
71    BEGIN_TEST;
72
73    zx_handle_t events[3];
74    ASSERT_EQ(zx_event_create(0u, &events[0]), 0, "Error during event create");
75    ASSERT_EQ(zx_event_create(0u, &events[1]), 0, "Error during event create");
76    ASSERT_EQ(zx_event_create(0u, &events[2]), 0, "Error during event create");
77
78    thrd_t threads[4];
79    int ret = thrd_create_with_name(&threads[3], thread_fn_1, events, "master");
80    ASSERT_EQ(ret, thrd_success, "Error during thread creation");
81
82    for (int ix = 0; ix != 3; ++ix) {
83        ret = thrd_create_with_name(&threads[ix], thread_fn_2, events, "worker");
84        ASSERT_EQ(ret, thrd_success, "Error during thread creation");
85    }
86
87    zx_nanosleep(zx_deadline_after(ZX_MSEC(400)));
88    zx_object_signal(events[0], 0u, ZX_EVENT_SIGNALED);
89
90    for (int ix = 0; ix != 4; ++ix) {
91        ASSERT_EQ(thrd_join(threads[ix], NULL), thrd_success, "Error during wait");
92    }
93
94    ASSERT_GE(zx_handle_close(events[0]), 0, "Error during event-0 close");
95    ASSERT_GE(zx_handle_close(events[1]), 0, "Error during event-1 close");
96    ASSERT_GE(zx_handle_close(events[2]), 0, "Error during event-2 close");
97    END_TEST;
98}
99
100static int thread_fn_3(void* arg) {
101    zx_handle_t* events = (zx_handle_t*)(arg);
102
103    do {
104        zx_nanosleep(zx_deadline_after(ZX_MSEC(200)));
105        zx_object_signal(events[1], ZX_USER_SIGNAL_ALL, ZX_USER_SIGNAL_1);
106    } while (!wait_user(events[2], events[0], ZX_USER_SIGNAL_2));
107
108    return 0;
109}
110
111static int thread_fn_4(void* arg) {
112    zx_handle_t* events = (zx_handle_t*)(arg);
113
114    while (!wait_user(events[1], events[0], ZX_USER_SIGNAL_1)) {
115        zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
116        zx_object_signal(events[2], ZX_USER_SIGNAL_ALL, ZX_USER_SIGNAL_2);
117    }
118
119    return 0;
120}
121
122static bool user_signals_test(void) {
123    BEGIN_TEST;
124
125    zx_handle_t events[3];
126    ASSERT_GE(zx_event_create(0U, &events[0]), 0, "Error during event create");
127    ASSERT_GE(zx_event_create(0U, &events[1]), 0, "Error during event create");
128    ASSERT_GE(zx_event_create(0U, &events[2]), 0, "Error during event create");
129
130    thrd_t threads[4];
131    int ret = thrd_create_with_name(&threads[3], thread_fn_3, events, "master");
132    ASSERT_EQ(ret, thrd_success, "Error during thread creation");
133
134    for (int ix = 0; ix != 3; ++ix) {
135        ret = thrd_create_with_name(&threads[ix], thread_fn_4, events, "workers");
136        ASSERT_EQ(ret, thrd_success, "Error during thread creation");
137    }
138
139    zx_nanosleep(zx_deadline_after(ZX_MSEC(400)));
140    zx_object_signal(events[0], 0u, ZX_EVENT_SIGNALED);
141
142    for (int ix = 0; ix != 4; ++ix) {
143        ASSERT_EQ(thrd_join(threads[ix], NULL), thrd_success, "Error during wait");
144    }
145
146    ASSERT_GE(zx_handle_close(events[0]), 0, "Error during event-0 close");
147    ASSERT_GE(zx_handle_close(events[1]), 0, "Error during event-1 close");
148    ASSERT_GE(zx_handle_close(events[2]), 0, "Error during event-2 close");
149    END_TEST;
150}
151
152static int thread_fn_closer(void* arg) {
153    zx_nanosleep(zx_deadline_after(ZX_MSEC(200)));
154
155    zx_handle_t handle = *((zx_handle_t*)arg);
156    int rc = (int)zx_handle_close(handle);
157
158    return rc;
159}
160
161static bool wait_signals_test(void) {
162    BEGIN_TEST;
163
164    zx_handle_t events[3];
165    ASSERT_EQ(zx_event_create(0U, &events[0]), 0, "Error during event create");
166    ASSERT_EQ(zx_event_create(0U, &events[1]), 0, "Error during event create");
167    ASSERT_EQ(zx_event_create(0U, &events[2]), 0, "Error during event create");
168
169    zx_status_t status;
170    zx_signals_t pending;
171
172    zx_wait_item_t items[3];
173    items[0].waitfor = ZX_EVENT_SIGNALED;
174    items[0].handle = events[0];
175    items[1].waitfor = ZX_EVENT_SIGNALED;
176    items[1].handle = events[1];
177    items[2].waitfor = ZX_EVENT_SIGNALED;
178    items[2].handle = events[2];
179
180    status = zx_object_wait_one(events[0], ZX_EVENT_SIGNALED, zx_deadline_after(1u), &pending);
181    ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
182    ASSERT_EQ(pending, 0u, "");
183
184    status = zx_object_wait_many(items, 3, zx_deadline_after(1));
185    ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
186    ASSERT_EQ(items[0].pending, 0u, "");
187    ASSERT_EQ(items[1].pending, 0u, "");
188    ASSERT_EQ(items[2].pending, 0u, "");
189
190    status = zx_object_wait_one(events[0], ZX_EVENT_SIGNALED, 0u, &pending);
191    ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
192    ASSERT_EQ(pending, 0u, "");
193
194    status = zx_object_wait_many(items, 3, 0);
195    ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
196    ASSERT_EQ(items[0].pending, 0u, "");
197    ASSERT_EQ(items[1].pending, 0u, "");
198    ASSERT_EQ(items[2].pending, 0u, "");
199
200    ASSERT_GE(zx_object_signal(events[0], 0u, ZX_EVENT_SIGNALED), 0, "Error during event signal");
201
202    status = zx_object_wait_one(events[0], ZX_EVENT_SIGNALED, zx_deadline_after(1u), &pending);
203    ASSERT_EQ(status, 0, "wait failed");
204    ASSERT_EQ(pending, ZX_EVENT_SIGNALED, "Error during wait call");
205
206    status = zx_object_wait_many(items, 3, zx_deadline_after(1));
207    ASSERT_EQ(status, 0, "wait failed");
208    ASSERT_EQ(items[0].pending,
209        ZX_EVENT_SIGNALED, "Error during wait call");
210
211    status = zx_object_wait_one(events[0], ZX_EVENT_SIGNALED, 0u, &pending);
212    ASSERT_EQ(status, ZX_OK, "wait failed");
213    ASSERT_EQ(pending, ZX_EVENT_SIGNALED, "Error during wait call");
214
215    ASSERT_GE(zx_object_signal(events[0], ZX_EVENT_SIGNALED, 0u), 0, "Error during event reset");
216    ASSERT_GE(zx_object_signal(events[2], 0u, ZX_EVENT_SIGNALED), 0, "Error during event signal");
217    status = zx_object_wait_many(items, 3, zx_deadline_after(1));
218    ASSERT_EQ(status, 0, "wait failed");
219    ASSERT_EQ(items[2].pending,
220        ZX_EVENT_SIGNALED, "Error during wait call");
221
222    thrd_t thread;
223    int ret = thrd_create_with_name(&thread, thread_fn_closer, &events[1], "closer");
224    ASSERT_EQ(ret, thrd_success, "Error during thread creation");
225
226    status = zx_object_wait_one(events[1], ZX_EVENT_SIGNALED, ZX_TIME_INFINITE, NULL);
227    ASSERT_EQ(status, ZX_ERR_CANCELED, "Error during wait");
228
229    ASSERT_EQ(thrd_join(thread, NULL), thrd_success, "Error during thread close");
230
231    ASSERT_GE(zx_handle_close(events[0]), 0, "Error during event-0 close");
232    ASSERT_GE(zx_handle_close(events[2]), 0, "Error during event-2 close");
233
234    END_TEST;
235}
236
237static bool reset_test(void) {
238    BEGIN_TEST;
239    zx_handle_t event;
240    ASSERT_EQ(zx_event_create(0U, &event), 0, "Error during event creation");
241    ASSERT_GE(zx_object_signal(event, 0u, ZX_EVENT_SIGNALED), 0, "Error during event signal");
242    ASSERT_GE(zx_object_signal(event, ZX_EVENT_SIGNALED, 0u), 0, "Error during event reset");
243
244    zx_status_t status;
245    status = zx_object_wait_one(event, ZX_EVENT_SIGNALED, zx_deadline_after(1u), NULL);
246    ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
247
248    ASSERT_EQ(zx_handle_close(event), ZX_OK, "error during handle close");
249
250    END_TEST;
251}
252
253static bool wait_many_failures_test(void) {
254    BEGIN_TEST;
255
256    ASSERT_EQ(zx_object_wait_many(NULL, 0, zx_deadline_after(1)),
257              ZX_ERR_TIMED_OUT, "wait_many on zero handles should have timed out");
258
259    zx_handle_t handles[2] = { ZX_HANDLE_INVALID, ZX_HANDLE_INVALID};
260    ASSERT_EQ(zx_event_create(0u, &handles[0]), 0, "Error during event creation");
261
262    zx_wait_item_t items[2];
263    items[0].handle = handles[0];
264    items[0].waitfor = ZX_EVENT_SIGNALED;
265    items[1].handle = handles[1];
266    items[1].waitfor = ZX_EVENT_SIGNALED;
267    ASSERT_EQ(zx_object_wait_many(items, 2, ZX_TIME_INFINITE),
268              ZX_ERR_BAD_HANDLE, "Wait-many should have failed with ZX_ERR_BAD_HANDLE");
269
270    // Signal the event, to check that wait-many cleaned up correctly.
271    ASSERT_EQ(zx_object_signal(handles[0], 0u, ZX_EVENT_SIGNALED), ZX_OK,
272              "Error during event signal");
273
274    // TODO(vtl): Also test other failure code paths: 1. a handle not supporting waiting (i.e., not
275    // having a Waiter), 2. a handle having an I/O port bound.
276
277    ASSERT_EQ(zx_handle_close(handles[0]), ZX_OK, "Error during handle close");
278
279    END_TEST;
280}
281
282BEGIN_TEST_CASE(event_tests)
283RUN_TEST(basic_test)
284RUN_TEST(user_signals_test)
285RUN_TEST(wait_signals_test)
286RUN_TEST(reset_test)
287RUN_TEST(wait_many_failures_test)
288END_TEST_CASE(event_tests)
289
290int main(int argc, char** argv) {
291    return unittest_run_all_tests(argc, argv) ? 0 : -1;
292}
293