blob: 85a1d28508155be424b6217758f080de2874a195 [file]
// Copyright 2026 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 LIB_C_THREADS_THREAD_STORAGE_TEST_UTILS_H_
#define LIB_C_THREADS_THREAD_STORAGE_TEST_UTILS_H_
#include <lib/elfldltl/tls-layout.h>
#include <lib/fit/function.h>
#include <zircon/types.h>
#include <span>
#include <string_view>
#include "thread-storage.h"
namespace LIBC_NAMESPACE_DECL {
// This provides a simple way of setting gTlsLayout and gInitializeTls within a test to
// be used by ThreadStorage::GetTlsLayout and ThreadStorage::InitializeTls in test code.
class LibcThreadTestScopedTlsGlobals {
public:
using TlsLayout = elfldltl::TlsLayout<>;
using InitTlsFn = fit::function<void(std::span<std::byte>, size_t)>;
// This is a large enough size (far larger than a test would need), but harmlessly small in even
// the 38-bit address space for riscv64-fuchsia. Tests can allocate a VMAR of this size to place
// test VMOs here.
static constexpr size_t kTestVmarSizeBytes = 1 << 30;
LibcThreadTestScopedTlsGlobals(TlsLayout tls_layout, InitTlsFn initialize_tls) {
gTlsLayout = tls_layout;
gInitializeTls = std::move(initialize_tls);
}
~LibcThreadTestScopedTlsGlobals() {
gTlsLayout = {};
gInitializeTls = {};
}
static inline TlsLayout gTlsLayout;
static inline InitTlsFn gInitializeTls;
};
// This is a helper subclass for just recording the thread_block and tp_offset
// from a ThreadStorage::Allocate and checking the resulting storage. Users can
// also supply their own `expect_true` function for printing assertion messages.
//
// Example usage:
//
// LibcThreadTestStorage thread_storage(layout);
// auto result = thread_storage.Allocate(handles, stack_size, guard_size);
// thread_storage.Check([](bool check, std::string_view message){
// ZX_ASSERT_MSG(check, "%s", message.data());
// });
//
class LibcThreadTestStorage : public LibcThreadTestScopedTlsGlobals {
public:
LibcThreadTestStorage(TlsLayout tls_layout)
: LibcThreadTestScopedTlsGlobals(tls_layout,
[this](std::span<std::byte> thread_block, size_t tp_offset) {
// Just record the resulting thread block calculations.
found_thread_block_ = thread_block;
found_tp_offset_ = tp_offset;
}) {}
zx::result<Thread*> Allocate(thrd_zx_create_handles_t allocate_from,
PageRoundedSize stack_size = PageRoundedSize::Page(),
PageRoundedSize guard_size = PageRoundedSize::Page(),
std::string_view vmo_name = "thread-storage-test") {
return storage_.Allocate(allocate_from, vmo_name, stack_size, guard_size);
}
void Check(fit::function<void(bool, std::string_view)> expect_true, Thread* result);
private:
ThreadStorage storage_;
zx::result<Thread*> result_;
size_t found_tp_offset_;
std::span<std::byte> found_thread_block_;
};
} // namespace LIBC_NAMESPACE_DECL
#endif // LIB_C_THREADS_THREAD_STORAGE_TEST_UTILS_H_