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