mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-10-27 00:28:14 +00:00
Merge pull request #10385 from alvaroelpob/feat/libkernel-pthread-setname
feat(libkernel): implement pthread_mutex_setname_np, pthread_rwlock_setname_np and pthread_barrier_setname_np
This commit is contained in:
10 files changed
+137
-18
No files matched your search
@@ -538,6 +538,16 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(guest_pthread_barrier PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_pthread_setname_tests ${LIBS_INCLUDE_DIR}/tests/GuestPthreadSetname.cpp)
|
||||
target_include_directories(guest_pthread_setname_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_pthread_setname_tests PRIVATE libkernel libc)
|
||||
configure_windows_unwind(guest_pthread_setname_tests)
|
||||
add_test(NAME guest_pthread_setname COMMAND guest_pthread_setname_tests)
|
||||
set_tests_properties(guest_pthread_setname PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_pthread_setname PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libkernel>")
|
||||
endif()
|
||||
add_test_executable(guest_kernel_is_stack_tests ${LIBS_INCLUDE_DIR}/tests/GuestKernelIsStack.cpp)
|
||||
target_include_directories(guest_kernel_is_stack_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_kernel_is_stack_tests PRIVATE libkernel libc)
|
||||
|
||||
@@ -91,4 +91,17 @@ int APS5_VABI pthread_barrier_destroy_nid_postfix(PthreadBarrierPrivate** barrie
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_barrier_setname_np_nid_postfix(PthreadBarrierPrivate** barrier, const char* name) {
|
||||
if (!barrier) throw std::invalid_argument("pthread_barrier_setname_np: null barrier");
|
||||
if (!*barrier) throw std::invalid_argument("pthread_barrier_setname_np: barrier is not initialized or was destroyed");
|
||||
auto& value = **barrier;
|
||||
std::lock_guard lock(value.mutex);
|
||||
if (value.named) return 35;
|
||||
if (name) {
|
||||
value.name = name;
|
||||
value.named = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -47,6 +47,8 @@ struct PthreadMutexPrivate {
|
||||
int _count;
|
||||
int _spinloops = 0;
|
||||
int _yieldloops = 0;
|
||||
std::string _name;
|
||||
bool _named = false;
|
||||
|
||||
PthreadMutexPrivate() : _type(MutexType::ErrorCheck), _owner(std::thread::id{}), _count(0) {}
|
||||
|
||||
@@ -66,6 +68,8 @@ struct PthreadRwlockPrivate {
|
||||
std::atomic<std::thread::id> _writer;
|
||||
std::atomic<int> _readers{0};
|
||||
int _waitingWriters = 0;
|
||||
std::string _name;
|
||||
bool _named = false;
|
||||
};
|
||||
|
||||
struct PthreadCondattrPrivate {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "prx/libkernel/include/Api.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Mutex.hpp"
|
||||
#include "prx/libkernel/include/Api.hpp"
|
||||
#include "../include/Pthread.hpp"
|
||||
@@ -21,6 +22,7 @@ constexpr int sceDeadlock = static_cast<int>(0x8002000bu);
|
||||
constexpr int sceBusy = static_cast<int>(0x80020010u);
|
||||
constexpr int sceTimedOut = static_cast<int>(0x8002003cu);
|
||||
constexpr std::int32_t posixFailure = -1;
|
||||
constexpr int posixAgain = 35;
|
||||
std::mutex& InitializationMutex() {
|
||||
static std::mutex mutex;
|
||||
return mutex;
|
||||
@@ -282,4 +284,20 @@ int APS5_VABI scePthreadMutexSetyieldloops(PthreadMutex* mutex, int count) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutex_setname_np_nid_postfix(PthreadMutex* mutex, const char* name) {
|
||||
if (!mutex)
|
||||
throw std::invalid_argument("pthread_mutex_setname_np: null mutex");
|
||||
std::lock_guard lock(InitializationMutex());
|
||||
const auto current = std::atomic_ref<PthreadMutex>(*mutex).load(std::memory_order_acquire);
|
||||
if (!isInitializedMutex(current))
|
||||
throw std::invalid_argument("pthread_mutex_setname_np: mutex is statically initialized or destroyed");
|
||||
if (current->_named)
|
||||
return posixAgain;
|
||||
if (name) {
|
||||
current->_name = name;
|
||||
current->_named = true;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
#include "prx/libkernel/include/Api.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Rwlock.hpp"
|
||||
#include "../include/Pthread.hpp"
|
||||
#include "../include/Rwlock.hpp"
|
||||
@@ -17,6 +18,8 @@ static constexpr int SCE_KERNEL_ERROR_ETIMEDOUT = 0x8002003C;
|
||||
static constexpr int SCE_KERNEL_ERROR_EINVAL = 0x80020016;
|
||||
static constexpr int FIRST_STORED_RWLOCK_TYPE = 1;
|
||||
static constexpr int LAST_STORED_RWLOCK_TYPE = 2;
|
||||
static constexpr int POSIX_EINVAL = 22;
|
||||
static constexpr int POSIX_EAGAIN = 35;
|
||||
|
||||
static PthreadRwlockPrivate* const DestroyedRwlock = reinterpret_cast<PthreadRwlockPrivate*>(std::uintptr_t{1});
|
||||
|
||||
@@ -188,4 +191,17 @@ int APS5_VABI scePthreadRwlockattrSettype(PthreadRwlockattr* attr, int type) {
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_rwlock_setname_np_nid_postfix(PthreadRwlock* rwlock, const char* name) {
|
||||
if (!rwlock) throw std::runtime_error("pthread_rwlock_setname_np: null rwlock");
|
||||
PthreadRwlock current = std::atomic_ref<PthreadRwlock>(*rwlock).load(std::memory_order_acquire);
|
||||
if (current == nullptr) throw std::runtime_error("pthread_rwlock_setname_np: statically initialized rwlock");
|
||||
if (current == DestroyedRwlock) throw std::runtime_error("pthread_rwlock_setname_np: destroyed rwlock");
|
||||
std::lock_guard state(current->_state);
|
||||
if (current->_named) return POSIX_EAGAIN;
|
||||
if (!name) return POSIX_EINVAL;
|
||||
current->_name = name;
|
||||
current->_named = true;
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@
|
||||
#include <condition_variable>
|
||||
#include <atomic>
|
||||
#include <thread>
|
||||
#include <string>
|
||||
|
||||
struct KernelIovec {
|
||||
void* base;
|
||||
@@ -116,6 +117,8 @@ struct PthreadBarrierPrivate {
|
||||
unsigned arrived = 0;
|
||||
unsigned active = 0;
|
||||
std::uint64_t generation = 0;
|
||||
std::string name;
|
||||
bool named = false;
|
||||
};
|
||||
|
||||
struct PthreadBarrierattrPrivate {
|
||||
@@ -414,6 +417,7 @@ int APS5_VABI pthread_barrierattr_setpshared_nid_postfix(PthreadBarrierattrPriva
|
||||
int APS5_VABI pthread_barrier_init_nid_postfix(PthreadBarrierPrivate** barrier, PthreadBarrierattrPrivate* const* attr, unsigned count);
|
||||
int APS5_VABI pthread_barrier_wait_nid_postfix(PthreadBarrierPrivate** barrier);
|
||||
int APS5_VABI pthread_barrier_destroy_nid_postfix(PthreadBarrierPrivate** barrier);
|
||||
int APS5_VABI pthread_barrier_setname_np_nid_postfix(PthreadBarrierPrivate** barrier, const char* name);
|
||||
int APS5_VABI pthread_cond_destroy_nid_postfix(PthreadCond* cond);
|
||||
int APS5_VABI pthread_cond_broadcast_nid_postfix(PthreadCond* cond);
|
||||
int APS5_VABI pthread_cond_init_nid_postfix(PthreadCond* cond, const PthreadCondattr* attr);
|
||||
@@ -440,6 +444,7 @@ int APS5_VABI pthread_mutex_getspinloops_np_nid_postfix(PthreadMutex* mutex, int
|
||||
int APS5_VABI pthread_mutex_setspinloops_np_nid_postfix(PthreadMutex* mutex, int count);
|
||||
int APS5_VABI pthread_mutex_getyieldloops_np_nid_postfix(PthreadMutex* mutex, int* count);
|
||||
int APS5_VABI pthread_mutex_setyieldloops_np_nid_postfix(PthreadMutex* mutex, int count);
|
||||
int APS5_VABI pthread_mutex_setname_np_nid_postfix(PthreadMutex* mutex, const char* name);
|
||||
int APS5_VABI pthread_mutexattr_destroy_nid_postfix(PthreadMutexattr* attr);
|
||||
int APS5_VABI pthread_mutexattr_init_nid_postfix(PthreadMutexattr* attr);
|
||||
int APS5_VABI pthread_mutexattr_getpshared_nid_postfix(const PthreadMutexattr* attr, int* pshared);
|
||||
@@ -466,6 +471,7 @@ int APS5_VABI pthread_rwlock_tryrdlock_nid_postfix(PthreadRwlock* rwlock);
|
||||
int APS5_VABI pthread_rwlock_trywrlock_nid_postfix(PthreadRwlock* rwlock);
|
||||
int APS5_VABI pthread_rwlock_unlock_nid_postfix(PthreadRwlock* rwlock);
|
||||
int APS5_VABI pthread_rwlock_wrlock_nid_postfix(PthreadRwlock* rwlock);
|
||||
int APS5_VABI pthread_rwlock_setname_np_nid_postfix(PthreadRwlock* rwlock, const char* name);
|
||||
int APS5_VABI sem_init_nid_postfix(void* sem, int pshared, unsigned int value);
|
||||
int APS5_VABI sem_destroy_nid_postfix(void* sem);
|
||||
int APS5_VABI sem_wait_nid_postfix(void* sem);
|
||||
|
||||
@@ -291,11 +291,6 @@ int APS5_VABI pthread_attr_setstackaddr_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_barrier_setname_np_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_get_specificarray_np_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
@@ -326,11 +321,6 @@ int APS5_VABI pthread_mutex_init_for_mono_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutex_setname_np_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_mutex_setprioceiling_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
@@ -362,11 +352,6 @@ int APS5_VABI pthread_resume_user_context_np_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_rwlock_setname_np_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI pthread_set_user_context_np_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
|
||||
@@ -144,7 +144,6 @@ int APS5_VABI pthread_attr_getsolosched_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_attr_setaffinity_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_attr_setcreatesuspend_np_nid_postfix(PthreadAttr* attr);
|
||||
int APS5_VABI pthread_attr_setstackaddr_nid_postfix(void);
|
||||
int APS5_VABI pthread_barrier_setname_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_get_specificarray_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_get_user_context_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_getstack_np_nid_postfix(void);
|
||||
@@ -152,14 +151,12 @@ int APS5_VABI pthread_main_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_multi_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_mutex_getprioceiling_nid_postfix(void);
|
||||
int APS5_VABI pthread_mutex_init_for_mono_nid_postfix(void);
|
||||
int APS5_VABI pthread_mutex_setname_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_mutex_setprioceiling_nid_postfix(void);
|
||||
int APS5_VABI pthread_mutexattr_getgen_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_mutexattr_setgen_np_nid_postfix(void);
|
||||
void APS5_VABI pthread_resume_all_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_resume_np_nid_postfix(Pthread thread);
|
||||
int APS5_VABI pthread_resume_user_context_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_rwlock_setname_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_set_user_context_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_single_np_nid_postfix(void);
|
||||
int APS5_VABI pthread_suspend_all_np_nid_postfix(void);
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
#include "prx/libkernel/include/Api.hpp"
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libkernel/Pthread/include/Pthread.hpp"
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <source_location>
|
||||
#include <stdexcept>
|
||||
|
||||
static constexpr int GuestEagain = 35;
|
||||
|
||||
static void Require(bool condition, std::source_location location = std::source_location::current()) {
|
||||
if (condition) return;
|
||||
std::fprintf(stderr, "pthread setname check failed at line %u\n", location.line());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
template<typename TAction> static void RequireThrows(TAction action, std::source_location location = std::source_location::current()) {
|
||||
try { action(); } catch (const std::exception&) { return; }
|
||||
Require(false, location);
|
||||
}
|
||||
|
||||
static void TestMutex() {
|
||||
PthreadMutex mutex = nullptr;
|
||||
Require(pthread_mutex_init_nid_postfix(&mutex, nullptr) == 0);
|
||||
Require(pthread_mutex_setname_np_nid_postfix(&mutex, "first") == 0 && mutex->_name == "first");
|
||||
Require(pthread_mutex_setname_np_nid_postfix(&mutex, "second") == GuestEagain && mutex->_name == "first");
|
||||
Require(pthread_mutex_lock_nid_postfix(&mutex) == 0);
|
||||
Require(pthread_mutex_setname_np_nid_postfix(&mutex, "held") == GuestEagain && mutex->_name == "first");
|
||||
Require(pthread_mutex_unlock_nid_postfix(&mutex) == 0);
|
||||
RequireThrows([] { pthread_mutex_setname_np_nid_postfix(nullptr, "mutex"); });
|
||||
Require(pthread_mutex_destroy_nid_postfix(&mutex) == 0);
|
||||
RequireThrows([&] { pthread_mutex_setname_np_nid_postfix(&mutex, "destroyed"); });
|
||||
}
|
||||
|
||||
static void TestRwlock() {
|
||||
PthreadRwlock rwlock = nullptr;
|
||||
Require(pthread_rwlock_init_nid_postfix(&rwlock, nullptr) == 0);
|
||||
Require(pthread_rwlock_setname_np_nid_postfix(&rwlock, "first") == 0 && rwlock->_name == "first");
|
||||
Require(pthread_rwlock_rdlock_nid_postfix(&rwlock) == 0);
|
||||
Require(pthread_rwlock_setname_np_nid_postfix(&rwlock, "read") == GuestEagain && rwlock->_name == "first");
|
||||
Require(pthread_rwlock_unlock_nid_postfix(&rwlock) == 0);
|
||||
RequireThrows([] { pthread_rwlock_setname_np_nid_postfix(nullptr, "rwlock"); });
|
||||
Require(pthread_rwlock_destroy_nid_postfix(&rwlock) == 0);
|
||||
RequireThrows([&] { pthread_rwlock_setname_np_nid_postfix(&rwlock, "destroyed"); });
|
||||
PthreadRwlock unset = nullptr;
|
||||
RequireThrows([&] { pthread_rwlock_setname_np_nid_postfix(&unset, "static"); });
|
||||
}
|
||||
|
||||
static void TestBarrier() {
|
||||
PthreadBarrierPrivate* barrier = nullptr;
|
||||
Require(pthread_barrier_init_nid_postfix(&barrier, nullptr, 1) == 0);
|
||||
Require(pthread_barrier_setname_np_nid_postfix(&barrier, "first") == 0 && barrier->name == "first");
|
||||
Require(pthread_barrier_wait_nid_postfix(&barrier) == -1);
|
||||
Require(pthread_barrier_setname_np_nid_postfix(&barrier, "second") == GuestEagain && barrier->name == "first");
|
||||
RequireThrows([] { pthread_barrier_setname_np_nid_postfix(nullptr, "barrier"); });
|
||||
Require(pthread_barrier_destroy_nid_postfix(&barrier) == 0);
|
||||
RequireThrows([&] { pthread_barrier_setname_np_nid_postfix(&barrier, "destroyed"); });
|
||||
PthreadBarrierPrivate* unset = nullptr;
|
||||
RequireThrows([&] { pthread_barrier_setname_np_nid_postfix(&unset, "unset"); });
|
||||
}
|
||||
|
||||
int main() {
|
||||
TestMutex();
|
||||
TestRwlock();
|
||||
TestBarrier();
|
||||
std::puts("pthread setname tests passed");
|
||||
}
|
||||
@@ -288,6 +288,9 @@
|
||||
- [pthread_cond_signalto_np, scePthreadCondSignalto](../../../core/libs/prx/libkernel/Pthread/src/Cond.cpp) (libkernel) wake every waiter of the condition variable, not only the given thread, and do not check the thread; the other waiters see a spurious wakeup. Which waiters the console wakes, and its result for a thread that is not waiting, are unverified
|
||||
- [pthread_rwlockattr_gettype_np, scePthreadRwlockattrGettype](../../../core/libs/prx/libkernel/Pthread/Posix/Rwlock.cpp) (libkernel) report the type stored by `scePthreadRwlockattrSettype`, which accepts any value and which read-write locks do not apply; the console's valid types and their effect are unverified. A null or uninitialized attribute throws, as for `pthread_rwlockattr_getpshared`
|
||||
- [pthread_cond_reltimedwait_np](../../../core/libs/prx/libkernel/Pthread/Posix/Cond.cpp) (libkernel) takes `(cond, mutex, usec)`, a relative timeout in microseconds, and is `scePthreadCondTimedwait` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadCondTimedwait` ([`condvar.cpp`](https://github.com/shadps4-emu/shadPS4/blob/3a9f57265d28975658f1ca941daa5c879c7a74bd/src/core/libraries/kernel/threads/condvar.cpp#L229-L234)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `sem_reltimedwait_np`. Unverified on the console
|
||||
- [pthread_mutex_setname_np](../../../core/libs/prx/libkernel/Pthread/src/Mutex.cpp) (libkernel) - the console passes `name` to the kernel's `namedobj_create` and stores the returned id instead of the name; here the name is copied and never read back. Once named, a mutex returns `EAGAIN` (35) for every later call and keeps its name, as [firmware 13.42's module run on a PC](https://github.com/boykopovar/AnyPS5/pull/10385#issuecomment-6415603047) does. The kernel's handling of a null `name` is unmeasured: here it returns 0 and leaves the mutex unnamed. A null mutex pointer faults in the module and throws here; a statically initialized or destroyed mutex also throws, and the module's result for those is unmeasured. The module's separate branch for a field holding `0x5eaf00d`, which copies the name into the object, is known only from disassembly and is not reproduced
|
||||
- [pthread_rwlock_setname_np](../../../core/libs/prx/libkernel/Pthread/src/Rwlock.cpp) (libkernel) - the console passes `name` to the kernel's `namedobj_create` and stores the returned id instead of the name; here the name is copied and never read back. Once named, a lock returns `EAGAIN` (35) for every later call and keeps its name, as [firmware 13.42's module run on a PC](https://github.com/boykopovar/AnyPS5/pull/10385#issuecomment-6415603047) does. The kernel's handling of a null `name` is unmeasured: here it returns `EINVAL`, following [shadPS4](https://github.com/shadps4-emu/shadPS4/blob/91e6afa69b0a64b0afaa0281b80f8fbb0cd88136/src/core/libraries/kernel/threads/rwlock.cpp#L79-L87). A null lock pointer, a statically initialized lock and a destroyed one fault in the module and throw here
|
||||
- [pthread_barrier_setname_np](../../../core/libs/prx/libkernel/Pthread/Posix/Barrier.cpp) (libkernel) - the console passes `name` to the kernel's `namedobj_create` and stores the returned id instead of the name; here the name is copied and never read back. Once named, a barrier returns `EAGAIN` (35) for every later call and keeps its name, as [firmware 13.42's module run on a PC](https://github.com/boykopovar/AnyPS5/pull/10385#issuecomment-6415603047) does. The kernel's handling of a null `name` is unmeasured: here it returns 0 and leaves the barrier unnamed. A null barrier pointer, an unset barrier and a destroyed one fault in the module and throw here
|
||||
- [pthread_mutex_reltimedlock_np](../../../core/libs/prx/libkernel/Pthread/Posix/Mutex.cpp) (libkernel) takes `(mutex, usec)`, a relative timeout in microseconds, and is `scePthreadMutexTimedlock` with POSIX error numbers. FreeBSD 11.4 has no such function; the signature comes only from shadPS4, which registers one function for this NID and for `scePthreadMutexTimedlock` ([`mutex.cpp`](https://github.com/shadps4-emu/shadPS4/blob/7c960eae136b956cf4c1d72aacd2426b650330df/src/core/libraries/kernel/threads/mutex.cpp#L284-L287)). shadPS4 reads a 64-bit count; here it is the 32-bit `SceKernelUseconds` of the SCE twin, as for `pthread_cond_reltimedwait_np`, so a count of 2^32 microseconds or more would be truncated if the console reads 64 bits. Unverified on the console
|
||||
- [sysctl kern.arandom](../../../core/libs/prx/libkernel/System/src/SystemConfiguration.cpp) (libkernel) serves FreeBSD 11's MIB {1, 37} (CTL_KERN, KERN_ARANDOM) and the "kern.arandom" sysctlbyname lookup through std::random_device, returning min(*oldLength, 256) random bytes. Following FreeBSD's sysctl_kern_arnd, a null buffer reports min(*oldLength, 256) without writing, a null length returns 0 without writing, and requests with a new value return EPERM (1) as the node is read-only. Cryptographic quality depends on the host's random device implementation.
|
||||
- [pthread_atfork](../../../core/libs/prx/libkernel/Pthread/Posix/Atfork.cpp) (libkernel) appends the three handlers to a process-wide list and returns 0, or `ENOMEM` when the entry cannot be allocated, as FreeBSD 11.4's [`_pthread_atfork`](https://github.com/freebsd/freebsd-src/blob/8c639945a44588f6cfdddfb057bb5aad88735da0/lib/libthr/thread/thr_fork.c#L78-L100) does; null handlers are accepted. The handlers never run: `fork` throws and `vfork` is absent, so no child process is created. Handlers of a module that is unloaded stay in the list, where FreeBSD's `__pthread_cxa_finalize` removes them. PPSA97358 has a source-level call from the constructor `rcu_init` ([rcu.c](https://github.com/ZiZc3/XPSemu/blob/8eec07b4e31f66dce8e0b05c1ca1281af303e933/util/rcu.c#L488-L495)), but current-main runtime reachability is unverified
|
||||
|
||||
Reference in new issue
Block a user