blob: f8e7f78c6a4dbe3282a871f73ca2de16d3a0d2cd [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 <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <threads.h>
#include <unistd.h>
#include <zircon/compiler.h>
#include <zircon/syscalls.h>
#include <unittest/unittest.h>
static bool wait(zx_handle_t event, zx_handle_t quit_event) {
zx_status_t ms;
zx_wait_item_t items[2];
items[0].waitfor = ZX_EVENT_SIGNALED;
items[0].handle = event;
items[1].waitfor = ZX_EVENT_SIGNALED;
items[1].handle = quit_event;
ms = zx_object_wait_many(items, 2, ZX_TIME_INFINITE);
if (ms < 0)
return false;
return (items[1].pending & ZX_EVENT_SIGNALED);
}
static bool wait_user(zx_handle_t event, zx_handle_t quit_event, zx_signals_t user_signal) {
zx_status_t ms;
zx_wait_item_t items[2];
items[0].waitfor = user_signal;
items[0].handle = event;
items[1].waitfor = ZX_EVENT_SIGNALED;
items[1].handle = quit_event;
ms = zx_object_wait_many(items, 2, ZX_TIME_INFINITE);
if (ms < 0)
return false;
return (items[1].pending & ZX_EVENT_SIGNALED);
}
static int thread_fn_1(void* arg) {
zx_handle_t* events = (zx_handle_t*)(arg);
do {
zx_nanosleep(zx_deadline_after(ZX_MSEC(200)));
__UNUSED zx_status_t status = zx_object_signal(events[1], 0u, ZX_EVENT_SIGNALED);
assert(status == ZX_OK);
} while (!wait(events[2], events[0]));
return 0;
}
static int thread_fn_2(void* arg) {
zx_handle_t* events = (zx_handle_t*)(arg);
while (!wait(events[1], events[0])) {
zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
__UNUSED zx_status_t status = zx_object_signal(events[2], 0u, ZX_EVENT_SIGNALED);
assert(status == ZX_OK);
}
return 0;
}
static bool basic_test(void) {
BEGIN_TEST;
zx_handle_t events[3];
ASSERT_EQ(zx_event_create(0u, &events[0]), 0, "Error during event create");
ASSERT_EQ(zx_event_create(0u, &events[1]), 0, "Error during event create");
ASSERT_EQ(zx_event_create(0u, &events[2]), 0, "Error during event create");
thrd_t threads[4];
int ret = thrd_create_with_name(&threads[3], thread_fn_1, events, "master");
ASSERT_EQ(ret, thrd_success, "Error during thread creation");
for (int ix = 0; ix != 3; ++ix) {
ret = thrd_create_with_name(&threads[ix], thread_fn_2, events, "worker");
ASSERT_EQ(ret, thrd_success, "Error during thread creation");
}
zx_nanosleep(zx_deadline_after(ZX_MSEC(400)));
zx_object_signal(events[0], 0u, ZX_EVENT_SIGNALED);
for (int ix = 0; ix != 4; ++ix) {
ASSERT_EQ(thrd_join(threads[ix], NULL), thrd_success, "Error during wait");
}
ASSERT_GE(zx_handle_close(events[0]), 0, "Error during event-0 close");
ASSERT_GE(zx_handle_close(events[1]), 0, "Error during event-1 close");
ASSERT_GE(zx_handle_close(events[2]), 0, "Error during event-2 close");
END_TEST;
}
static int thread_fn_3(void* arg) {
zx_handle_t* events = (zx_handle_t*)(arg);
do {
zx_nanosleep(zx_deadline_after(ZX_MSEC(200)));
zx_object_signal(events[1], ZX_USER_SIGNAL_ALL, ZX_USER_SIGNAL_1);
} while (!wait_user(events[2], events[0], ZX_USER_SIGNAL_2));
return 0;
}
static int thread_fn_4(void* arg) {
zx_handle_t* events = (zx_handle_t*)(arg);
while (!wait_user(events[1], events[0], ZX_USER_SIGNAL_1)) {
zx_nanosleep(zx_deadline_after(ZX_MSEC(100)));
zx_object_signal(events[2], ZX_USER_SIGNAL_ALL, ZX_USER_SIGNAL_2);
}
return 0;
}
static bool user_signals_test(void) {
BEGIN_TEST;
zx_handle_t events[3];
ASSERT_GE(zx_event_create(0U, &events[0]), 0, "Error during event create");
ASSERT_GE(zx_event_create(0U, &events[1]), 0, "Error during event create");
ASSERT_GE(zx_event_create(0U, &events[2]), 0, "Error during event create");
thrd_t threads[4];
int ret = thrd_create_with_name(&threads[3], thread_fn_3, events, "master");
ASSERT_EQ(ret, thrd_success, "Error during thread creation");
for (int ix = 0; ix != 3; ++ix) {
ret = thrd_create_with_name(&threads[ix], thread_fn_4, events, "workers");
ASSERT_EQ(ret, thrd_success, "Error during thread creation");
}
zx_nanosleep(zx_deadline_after(ZX_MSEC(400)));
zx_object_signal(events[0], 0u, ZX_EVENT_SIGNALED);
for (int ix = 0; ix != 4; ++ix) {
ASSERT_EQ(thrd_join(threads[ix], NULL), thrd_success, "Error during wait");
}
ASSERT_GE(zx_handle_close(events[0]), 0, "Error during event-0 close");
ASSERT_GE(zx_handle_close(events[1]), 0, "Error during event-1 close");
ASSERT_GE(zx_handle_close(events[2]), 0, "Error during event-2 close");
END_TEST;
}
static int thread_fn_closer(void* arg) {
zx_nanosleep(zx_deadline_after(ZX_MSEC(200)));
zx_handle_t handle = *((zx_handle_t*)arg);
int rc = (int)zx_handle_close(handle);
return rc;
}
static bool wait_signals_test(void) {
BEGIN_TEST;
zx_handle_t events[3];
ASSERT_EQ(zx_event_create(0U, &events[0]), 0, "Error during event create");
ASSERT_EQ(zx_event_create(0U, &events[1]), 0, "Error during event create");
ASSERT_EQ(zx_event_create(0U, &events[2]), 0, "Error during event create");
zx_status_t status;
zx_signals_t pending;
zx_wait_item_t items[3];
items[0].waitfor = ZX_EVENT_SIGNALED;
items[0].handle = events[0];
items[1].waitfor = ZX_EVENT_SIGNALED;
items[1].handle = events[1];
items[2].waitfor = ZX_EVENT_SIGNALED;
items[2].handle = events[2];
status = zx_object_wait_one(events[0], ZX_EVENT_SIGNALED, zx_deadline_after(1u), &pending);
ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
ASSERT_EQ(pending, 0u, "");
status = zx_object_wait_many(items, 3, zx_deadline_after(1));
ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
ASSERT_EQ(items[0].pending, 0u, "");
ASSERT_EQ(items[1].pending, 0u, "");
ASSERT_EQ(items[2].pending, 0u, "");
status = zx_object_wait_one(events[0], ZX_EVENT_SIGNALED, 0u, &pending);
ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
ASSERT_EQ(pending, 0u, "");
status = zx_object_wait_many(items, 3, 0);
ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
ASSERT_EQ(items[0].pending, 0u, "");
ASSERT_EQ(items[1].pending, 0u, "");
ASSERT_EQ(items[2].pending, 0u, "");
ASSERT_GE(zx_object_signal(events[0], 0u, ZX_EVENT_SIGNALED), 0, "Error during event signal");
status = zx_object_wait_one(events[0], ZX_EVENT_SIGNALED, zx_deadline_after(1u), &pending);
ASSERT_EQ(status, 0, "wait failed");
ASSERT_EQ(pending, ZX_EVENT_SIGNALED, "Error during wait call");
status = zx_object_wait_many(items, 3, zx_deadline_after(1));
ASSERT_EQ(status, 0, "wait failed");
ASSERT_EQ(items[0].pending,
ZX_EVENT_SIGNALED, "Error during wait call");
status = zx_object_wait_one(events[0], ZX_EVENT_SIGNALED, 0u, &pending);
ASSERT_EQ(status, ZX_OK, "wait failed");
ASSERT_EQ(pending, ZX_EVENT_SIGNALED, "Error during wait call");
ASSERT_GE(zx_object_signal(events[0], ZX_EVENT_SIGNALED, 0u), 0, "Error during event reset");
ASSERT_GE(zx_object_signal(events[2], 0u, ZX_EVENT_SIGNALED), 0, "Error during event signal");
status = zx_object_wait_many(items, 3, zx_deadline_after(1));
ASSERT_EQ(status, 0, "wait failed");
ASSERT_EQ(items[2].pending,
ZX_EVENT_SIGNALED, "Error during wait call");
thrd_t thread;
int ret = thrd_create_with_name(&thread, thread_fn_closer, &events[1], "closer");
ASSERT_EQ(ret, thrd_success, "Error during thread creation");
status = zx_object_wait_one(events[1], ZX_EVENT_SIGNALED, ZX_TIME_INFINITE, NULL);
ASSERT_EQ(status, ZX_ERR_CANCELED, "Error during wait");
ASSERT_EQ(thrd_join(thread, NULL), thrd_success, "Error during thread close");
ASSERT_GE(zx_handle_close(events[0]), 0, "Error during event-0 close");
ASSERT_GE(zx_handle_close(events[2]), 0, "Error during event-2 close");
END_TEST;
}
static bool reset_test(void) {
BEGIN_TEST;
zx_handle_t event;
ASSERT_EQ(zx_event_create(0U, &event), 0, "Error during event creation");
ASSERT_GE(zx_object_signal(event, 0u, ZX_EVENT_SIGNALED), 0, "Error during event signal");
ASSERT_GE(zx_object_signal(event, ZX_EVENT_SIGNALED, 0u), 0, "Error during event reset");
zx_status_t status;
status = zx_object_wait_one(event, ZX_EVENT_SIGNALED, zx_deadline_after(1u), NULL);
ASSERT_EQ(status, ZX_ERR_TIMED_OUT, "wait should have timeout");
ASSERT_EQ(zx_handle_close(event), ZX_OK, "error during handle close");
END_TEST;
}
static bool wait_many_failures_test(void) {
BEGIN_TEST;
ASSERT_EQ(zx_object_wait_many(NULL, 0, zx_deadline_after(1)),
ZX_ERR_TIMED_OUT, "wait_many on zero handles should have timed out");
zx_handle_t handles[2] = { ZX_HANDLE_INVALID, ZX_HANDLE_INVALID};
ASSERT_EQ(zx_event_create(0u, &handles[0]), 0, "Error during event creation");
zx_wait_item_t items[2];
items[0].handle = handles[0];
items[0].waitfor = ZX_EVENT_SIGNALED;
items[1].handle = handles[1];
items[1].waitfor = ZX_EVENT_SIGNALED;
ASSERT_EQ(zx_object_wait_many(items, 2, ZX_TIME_INFINITE),
ZX_ERR_BAD_HANDLE, "Wait-many should have failed with ZX_ERR_BAD_HANDLE");
// Signal the event, to check that wait-many cleaned up correctly.
ASSERT_EQ(zx_object_signal(handles[0], 0u, ZX_EVENT_SIGNALED), ZX_OK,
"Error during event signal");
// TODO(vtl): Also test other failure code paths: 1. a handle not supporting waiting (i.e., not
// having a Waiter), 2. a handle having an I/O port bound.
ASSERT_EQ(zx_handle_close(handles[0]), ZX_OK, "Error during handle close");
END_TEST;
}
BEGIN_TEST_CASE(event_tests)
RUN_TEST(basic_test)
RUN_TEST(user_signals_test)
RUN_TEST(wait_signals_test)
RUN_TEST(reset_test)
RUN_TEST(wait_many_failures_test)
END_TEST_CASE(event_tests)