blob: 138c482990cc95d2759124d2906481165eb4c494 [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 "garnet/lib/debugger_utils/threads.h"
#include <lib/zx/event.h>
#include <lib/zx/process.h>
#include <lib/zx/time.h>
#include <stddef.h>
#include <threads.h>
#include <zircon/syscalls/object.h>
#include <zircon/threads.h>
#include <string>
#include <gtest/gtest.h>
#include "garnet/lib/debugger_utils/jobs.h"
#include "garnet/lib/debugger_utils/util.h"
#include "src/lib/fxl/logging.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);
FX_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;
}
void CheckThreadSuspended(const zx::thread& thread, const char* msg) {
EXPECT_EQ(GetThreadOsState(thread), ZX_THREAD_STATE_SUSPENDED) << msg;
}
void CheckThreadRunning(const zx::thread& thread, const char* msg) {
uint32_t state = GetThreadOsState(thread);
// 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);
}
// TODO(34880): Disabled until fixed.
TEST(Threads, DISABLED_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