blob: 677d248ca99b9f98167efd160ce50735fd25e1d2 [file] [log] [blame]
// Copyright 2018 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.
#ifndef _ALL_SOURCE
#define _ALL_SOURCE // Enables thrd_create_with_name in <threads.h>.
#endif
#include <stddef.h>
#include <string>
#include <threads.h>
#include <zircon/syscalls/object.h>
#include <zircon/threads.h>
#include <zx/event.h>
#include <zx/process.h>
#include <zx/time.h>
#include "garnet/lib/debugger_utils/jobs.h"
#include "garnet/lib/debugger_utils/threads.h"
#include "garnet/lib/debugger_utils/util.h"
#include "garnet/public/lib/fxl/logging.h"
#include "gtest/gtest.h"
namespace debugger_utils {
namespace {
// Threads should eventually suspend and resume. Leave it to the watchdog
// to handle problems.
constexpr zx::duration kThreadSuspendTimeout = zx::duration::infinite();
constexpr zx::duration kThreadRunningTimeout = zx::duration::infinite();
constexpr size_t kNumTestThreads = 10u;
int ThreadFunction(void* arg) {
auto keep_running = reinterpret_cast<std::atomic_bool*>(arg);
while (*keep_running) {
zx::nanosleep(zx::deadline_after(zx::msec(100)));
}
return 0;
}
void ShutdownThreads(const std::vector<thrd_t>& threads,
std::atomic_bool* keep_running) {
*keep_running = false;
for (auto& thread : threads) {
int result = thrd_join(thread, NULL);
FXL_DCHECK(result == thrd_success);
}
}
bool CreateThreads(size_t num_threads, std::vector<thrd_t>* threads,
std::atomic_bool* keep_running) {
for (size_t i = 0; i < num_threads; ++i) {
char name[sizeof("thread") + 10];
snprintf(name, sizeof(name), "thread%zu", i);
thrd_t thread;
if (thrd_create_with_name(&thread, ThreadFunction, keep_running, name) !=
thrd_success) {
goto Fail;
}
threads->push_back(thread);
}
return true;
Fail:
ShutdownThreads(*threads, keep_running);
return false;
}
zx_status_t GetThreadState(const zx::thread& thread, uint32_t* state) {
zx_info_thread_t info;
auto status = thread.get_info(ZX_INFO_THREAD, &info, sizeof(info),
nullptr, nullptr);
if (status != ZX_OK) {
return status;
}
*state = info.state;
return ZX_OK;
}
void CheckThreadSuspended(const zx::thread& thread, const char* msg) {
uint32_t state;
EXPECT_EQ(ZX_OK, GetThreadState(thread, &state)) << msg;
EXPECT_EQ(ZX_THREAD_STATE_SUSPENDED, state) << msg;
}
void CheckThreadRunning(const zx::thread& thread, const char* msg) {
uint32_t state;
EXPECT_EQ(ZX_OK, GetThreadState(thread, &state)) << msg;
// Our threads are either running or sleeping.
EXPECT_TRUE(state == ZX_THREAD_STATE_RUNNING ||
state == ZX_THREAD_STATE_BLOCKED_SLEEPING) << msg;
}
void WaitThreadsRunning(const std::vector<zx::thread>& threads,
const char* msg) {
for (const auto& thread : threads) {
EXPECT_EQ(ZX_OK, thread.wait_one(ZX_THREAD_RUNNING | ZX_THREAD_TERMINATED,
zx::deadline_after(kThreadRunningTimeout),
nullptr)) << msg;
CheckThreadRunning(thread, msg);
}
}
TEST(Threads, WithThreadSuspended) {
std::atomic_bool keep_running{true};
std::vector<thrd_t> threads;
std::vector<zx::thread> zx_threads;
ASSERT_TRUE(CreateThreads(1u, &threads, &keep_running));
zx_threads.push_back(zx::thread(thrd_get_zx_handle(threads[0])));
WaitThreadsRunning(zx_threads, "pre-suspend");
EXPECT_EQ(ZX_OK, WithThreadSuspended(zx_threads[0], kThreadSuspendTimeout,
[] (const zx::thread& thread) -> zx_status_t {
CheckThreadSuspended(thread, "inside WithThreadSuspended");
return ZX_OK;
}));
// When a thread's suspend token is closed it does not necessarily
// return to the RUNNING state immediately,
WaitThreadsRunning(zx_threads, "post-suspend");
// Release the handle as it will be closed elsewhere.
EXPECT_NE(ZX_HANDLE_INVALID, zx_threads[0].release());
ShutdownThreads(threads, &keep_running);
}
TEST(Threads, WithAllThreadsSuspended) {
std::atomic_bool keep_running{true};
std::vector<thrd_t> threads;
std::vector<zx::thread> zx_threads;
ASSERT_TRUE(CreateThreads(kNumTestThreads, &threads, &keep_running));
for (const auto& thread : threads) {
zx_threads.push_back(zx::thread(thrd_get_zx_handle(thread)));
}
WaitThreadsRunning(zx_threads, "pre-suspend");
EXPECT_EQ(ZX_OK, WithAllThreadsSuspended(
zx_threads, kThreadSuspendTimeout,
[&zx_threads] (const zx::thread& thread) -> zx_status_t {
for (const auto& t : zx_threads) {
CheckThreadSuspended(t, "inside WithAllThreadsSuspended");
}
return ZX_OK;
}));
// When a thread's suspend token is closed it does not necessarily
// return to the RUNNING state immediately.
WaitThreadsRunning(zx_threads, "post-suspend");
// Release the handles as they will be closed elsewhere.
for (auto& thread : zx_threads) {
EXPECT_NE(ZX_HANDLE_INVALID, thread.release());
}
ShutdownThreads(threads, &keep_running);
}
} // namespace
} // namespace debugger_utils