blob: b1641f3e82ff7eea1960ea139ed96c33d0ad0809 [file] [log] [blame]
// Copyright 2025 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_H_
#define LIB_C_THREADS_THREAD_H_
#include <lib/zx/result.h>
#include <pthread.h>
#include <threads.h>
#include <zircon/sanitizer.h>
#include <cerrno>
#include <concepts>
#include <cstdint>
#include <memory>
#include "../asm-linkage.h"
#include "../startup/start-main.h"
#include "../zircon/vmar.h"
#include "../zircon/zx-name.h"
struct __pthread; // NOLINT(bugprone-reserved-identifier): "threads_impl.h"
namespace LIBC_NAMESPACE_DECL {
// The legacy code defines `struct __pthread` in "threads_impl.h".
using Thread = ::__pthread;
struct ThreadAttributes {
// ThreadCreate demands a nonempty name. Callers can use these to set one.
constexpr ThreadAttributes WithDefaultName(const ZxName& default_name) const {
ThreadAttributes result = *this;
if (name.empty()) {
result.name = default_name;
}
return result;
}
constexpr ThreadAttributes WithDefaultName(const char* fmt, auto... args) const {
ThreadAttributes result = *this;
if (name.empty()) {
result.name = ZxName::Printf(fmt, args...);
}
return result;
}
PageRoundedSize stack = InitialStackSize(); // Default to main thread's size.
PageRoundedSize guard{1}; // Default to a one-page guard.
bool detached = false; // If set, start detached.
ZxName name; // Optional.
};
// This has the same actual ABI as both int(void*), as used in C11
// thrd_create(); and void*(void*), as used in POSIX pthread_create().
using ThreadFunction = intptr_t(void*) [[clang::cfi_unchecked_callee]];
inline ThreadFunction* ToThreadFunction(int (*func)(void*)) {
return reinterpret_cast<ThreadFunction*>(reinterpret_cast<uintptr_t>(func));
}
inline ThreadFunction* ToThreadFunction(void* (*func)(void*)) {
return reinterpret_cast<ThreadFunction*>(reinterpret_cast<uintptr_t>(func));
}
// The C11 <threads.h> thrd_t is actually just the Thread*.
inline thrd_t ToC11Thread(Thread& thread) { return reinterpret_cast<thrd_t>(&thread); }
inline Thread* FromC11Thread(thrd_t thread) { return reinterpret_cast<Thread*>(thread); }
// The POSIX <pthread.h> pthread_t is the same thing too.
inline pthread_t ToPthread(Thread& thread) { return reinterpret_cast<pthread_t>(&thread); }
inline Thread* FromPthread(pthread_t thread) { return reinterpret_cast<Thread*>(thread); }
// The CreatedThread object owns a Thread object and the kernel thread created
// with it by ThreadCreate, along with its place on the gAllThreads list. All
// those get cleaned up together if the CreatedThread dies before it's consumed
// by a successful ThreadStart, transferring ownership to the running thread.
struct CreatedThreadDeleter {
void operator()(Thread*) const;
};
using CreatedThread = std::unique_ptr<Thread, CreatedThreadDeleter>;
// Create a new Thread. This does all the allocation and creates the kernel
// thread. The new Thread object is initialized, owns that zx::thread handle,
// and is attached to the global thread list. The thread is not running yet and
// will be destroyed when the CreatedThread object dies before ThreadStart.
// Kernel operations can now be done via the thread handle to affect the thread
// (set scheduling parameters, etc.) before it starts running.
zx::result<CreatedThread> ThreadCreate(ThreadAttributes attrs);
// After a new Thread has been fully created, this actually starts it running.
// The new thread will call ThreadExit(func(arg)). Once the thread is running,
// it owns its own storage and kernel handle, so the CreatedThread is released.
// But unless the thread is detached, the caller now owns it via the Thread*
// until that is passed to ThreadJoin (or detached).
zx::result<Thread*> ThreadStart(CreatedThread thread, ThreadFunction* func, void* arg);
// Combines ThreadCreate and ThreadStart. This is templatized with converter
// functions for the public thread and error types (ToC11Thread / ToPthread,
// C11ThreadError / PthreadError) rather than just being a non-template
// function returning zx::result<Thread*> because thrd_create is required to
// write its result parameter before the new thread might read it back out of
// that same memory; the caller unpacking the result would be too late.
template <std::invocable<Thread&> auto NewThread, std::invocable<zx_status_t> auto Status>
decltype(Status(std::declval<zx_status_t>())) ThreadCreateAndStart(
decltype(NewThread(std::declval<Thread&>()))* new_thread, ThreadAttributes attrs,
ThreadFunction* func, void* arg) {
using NewThreadType = decltype(NewThread(std::declval<Thread&>()));
zx::result created = ThreadCreate(attrs);
if (created.is_error()) [[unlikely]] {
return Status(created.error_value());
}
// The result parameter must be set before the new thread starts running.
// It's valid to use memory that the new thread will itself read from!
*new_thread = NewThread(**created);
zx::result started = ThreadStart(*std::move(created), func, arg);
if (started.is_error()) [[unlikely]] {
// The stale value was already written, but should never be used. It's
// undefined what value this gets in the error case, but it's a bad idea to
// leak what's effectively a known-stale pointer under any circumstances.
// In most cases, the result parameter just wouldn't be touched at all
// until all the error cases have been ruled out. But that's not possible
// since ThreadStart might fail though it must be after storing the result.
// Saving and restoring the old value instead of clearing it would just
// give the false impression of stability, when in fact it's just another
// new race. So always clobber the result parameter, leaving only the race
// when no new thread actually existed but the stale value was visible
// there briefly in memory _on this thread_ and _maybe_ on others.
*new_thread = NewThreadType{};
return Status(started.error_value());
}
return Status(ZX_OK);
}
// This underlies thrd_exit() and pthread_exit(); they differ only in value
// type. It calls thread-local destructors and so forth, and finally calls
// ThreadExitFinish to do the real tear-down work.
[[noreturn]] void ThreadExit(intptr_t value);
// The last phase of exit is compiled using only the basic machine ABI so it
// can do some stack-switching and then free all the main thread stacks.
[[noreturn]] void ThreadExitFinish(Thread& self) //
LIBC_ASM_LINKAGE_DECLARE(ThreadExitFinish);
// This underlies thrd_join() and pthread_join(). It yields the value passed
// to ThreadExit().
zx::result<intptr_t> ThreadJoin(Thread& thread);
// This underlies thrd_detach() and pthread_detach(). As soon as it returns
// success, the Thread reference is no longer safe to use in any way because
// the thread can exit and free the storage itself at any time.
zx::result<> ThreadDetach(Thread& thread);
// Make the callback on the thread's TLS segments.
void OnTlsSegments(Thread& thread, sanitizer_memory_snapshot_callback_t* callback,
void* callback_arg);
// Convert Zircon error to C11 <threads.h> return value.
constexpr int C11ThreadError(zx_status_t status) {
switch (status) {
case ZX_OK:
return thrd_success;
case ZX_ERR_NO_MEMORY:
return thrd_nomem;
case ZX_ERR_TIMED_OUT:
return thrd_timedout;
default:
return thrd_error;
}
}
// Convert Zircon error to POSIX errno.
constexpr int PthreadError(zx_status_t status) {
switch (status) {
case ZX_OK:
return 0;
case ZX_ERR_INVALID_ARGS:
return EINVAL;
case ZX_ERR_ACCESS_DENIED:
return EPERM;
case ZX_ERR_TIMED_OUT:
return ETIMEDOUT;
case ZX_ERR_NO_MEMORY:
return EAGAIN;
case ZX_ERR_NOT_FOUND:
// These two are possible in pthread_create due to thrd_set_zx_process.
case ZX_ERR_BAD_HANDLE:
case ZX_ERR_WRONG_TYPE:
return ESRCH;
default:
__builtin_abort();
}
}
} // namespace LIBC_NAMESPACE_DECL
#endif // LIB_C_THREADS_THREAD_H_