blob: a3a75547757cae9d407e4aac5d391e8171435e65 [file] [log] [blame]
// Copyright 2020 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 <lib/zx/event.h>
#include <lib/zx/object.h>
#include <lib/zx/thread.h>
#include <zircon/types.h>
#include <thread>
#include <fbl/algorithm.h>
#include <zxtest/zxtest.h>
namespace {
constexpr zx::duration kPollingInterval = zx::msec(1);
// Wait, possibly forever, until |thread| has entered |state|.
zx_status_t WaitForState(const zx::unowned_thread& thread, zx_thread_state_t state) {
while (true) {
zx_info_thread_t info;
zx_status_t status = thread->get_info(ZX_INFO_THREAD, &info, sizeof(info), nullptr, nullptr);
if (status != ZX_OK) {
return status;
}
if (info.state == state) {
return ZX_OK;
}
zx::nanosleep(zx::deadline_after(kPollingInterval));
}
}
zx::event MakeEvent() {
zx::event ev;
zx::event::create(0u, &ev);
return ev;
}
TEST(ObjectWaitOneTest, WaitForEventSignaled) {
auto ev = MakeEvent();
ev.signal(0u, ZX_EVENT_SIGNALED);
zx_signals_t observed;
ASSERT_OK(ev.wait_one(ZX_EVENT_SIGNALED, zx::time::infinite(), &observed));
ASSERT_EQ(observed, ZX_EVENT_SIGNALED);
}
TEST(ObjectWaitOneTest, WaitForEventTimeout) {
auto ev = MakeEvent();
zx_signals_t observed;
ASSERT_EQ(ev.wait_one(ZX_EVENT_SIGNALED, zx::deadline_after(zx::msec(1)), &observed),
ZX_ERR_TIMED_OUT);
ASSERT_EQ(observed, 0u);
}
TEST(ObjectWaitOneTest, WaitForEventTimeoutPreSignalClear) {
auto ev = MakeEvent();
ev.signal(0u, ZX_EVENT_SIGNALED);
ev.signal(ZX_EVENT_SIGNALED, 0u);
zx_signals_t observed;
ASSERT_EQ(ev.wait_one(ZX_EVENT_SIGNALED, zx::deadline_after(zx::msec(1)), &observed),
ZX_ERR_TIMED_OUT);
ASSERT_EQ(observed, 0u);
}
TEST(ObjectWaitOneTest, WaitForEventThenSignal) {
auto ev = MakeEvent();
auto main_thread = zx::thread::self();
std::thread thread([&] {
ASSERT_OK(WaitForState(main_thread, ZX_THREAD_STATE_BLOCKED_WAIT_ONE));
ev.signal(0u, ZX_EVENT_SIGNALED);
});
zx_signals_t observed;
ASSERT_OK(ev.wait_one(ZX_EVENT_SIGNALED, zx::time::infinite(), &observed));
ASSERT_EQ(observed, ZX_EVENT_SIGNALED);
thread.join();
}
TEST(ObjectWaitManyTest, TooManyObjects) {
zx_wait_item_t items[ZX_WAIT_MANY_MAX_ITEMS + 1];
for (auto& item : items) {
item = zx_wait_item_t{
.handle = MakeEvent().release(), .waitfor = ZX_EVENT_SIGNALED, .pending = 0u};
}
ASSERT_EQ(zx::thread::wait_many(items, fbl::count_of(items), zx::time::infinite()),
ZX_ERR_OUT_OF_RANGE);
for (auto& item : items) {
ASSERT_OK(zx_handle_close(item.handle));
}
}
TEST(ObjectWaitManyTest, InvalidHandle) {
zx_wait_item_t items[3];
for (auto& item : items) {
item = zx_wait_item_t{
.handle = MakeEvent().release(), .waitfor = ZX_EVENT_SIGNALED, .pending = 0u};
}
ASSERT_OK(zx_handle_close(items[1].handle));
ASSERT_EQ(zx::thread::wait_many(items, fbl::count_of(items), zx::time::infinite()),
ZX_ERR_BAD_HANDLE);
items[1].handle = MakeEvent().release();
for (auto& item : items) {
ASSERT_OK(zx_handle_close(item.handle));
}
}
TEST(ObjectWaitManyTest, WaitForEventsSignaled) {
zx_wait_item_t items[8];
for (auto& item : items) {
item = zx_wait_item_t{
.handle = MakeEvent().release(), .waitfor = ZX_EVENT_SIGNALED, .pending = 0u};
}
// Signal some.
zx_signals_t to_signal[8] = {0u, 0u, ZX_EVENT_SIGNALED, 0u, 0u, ZX_EVENT_SIGNALED, 0u, 0u};
for (size_t ix = 0; ix != fbl::count_of(to_signal); ++ix) {
if (to_signal[ix]) {
ASSERT_OK(zx_object_signal(items[ix].handle, 0u, to_signal[ix]));
}
}
ASSERT_OK(zx::thread::wait_many(items, fbl::count_of(items), zx::time::infinite()));
for (size_t ix = 0; ix != fbl::count_of(to_signal); ++ix) {
ASSERT_EQ(items[ix].pending, to_signal[ix]);
ASSERT_OK(zx_handle_close(items[ix].handle));
}
}
TEST(ObjectWaitManyTest, WaitForEventsThenSignal) {
zx_wait_item_t items[8];
for (auto& item : items) {
item = zx_wait_item_t{
.handle = MakeEvent().release(), .waitfor = ZX_EVENT_SIGNALED, .pending = 0u};
}
auto main_thread = zx::thread::self();
zx_signals_t to_signal[8] = {0u, ZX_EVENT_SIGNALED, 0u, 0u, 0u, 0u, ZX_EVENT_SIGNALED, 0u};
std::thread thread([&] {
ASSERT_OK(WaitForState(main_thread, ZX_THREAD_STATE_BLOCKED_WAIT_MANY));
for (size_t ix = 0; ix != fbl::count_of(to_signal); ++ix) {
if (to_signal[ix]) {
ASSERT_OK(zx_object_signal(items[ix].handle, 0u, to_signal[ix]));
}
}
});
ASSERT_OK(zx::thread::wait_many(items, fbl::count_of(items), zx::time::infinite()));
thread.join();
int signal_count = 0;
for (size_t ix = 0; ix != fbl::count_of(to_signal); ++ix) {
signal_count += (items[ix].pending == ZX_EVENT_SIGNALED) ? 1 : 0;
ASSERT_OK(zx_handle_close(items[ix].handle));
}
// depending on timing, the client might not see all the signaled events, but since
// the wait completed, at least one of them is signaled.
EXPECT_GT(signal_count, 0);
}
} // namespace