mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-08 13:50:20 +00:00
pthread_cond_broadcast had no test, and no test checked that one broadcast wakes every waiter. Check the FreeBSD 11.4 results of _pthread_cond_broadcast (lib/libthr/thread/thr_cond.c lines 495-544): a broadcast without waiters returns 0, a statically initialised condition variable is initialised and returns 0, a destroyed one returns EINVAL (22), and one broadcast wakes all three waiting threads. Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
87 lines
3.6 KiB
C++
87 lines
3.6 KiB
C++
#include "prx/libkernel/include/Api.hpp"
|
|
#include "SceTypes.hpp"
|
|
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <cstdint>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <thread>
|
|
#include <vector>
|
|
|
|
namespace {
|
|
|
|
constexpr int Waiters = 3;
|
|
constexpr int Einval = 22;
|
|
constexpr int Etimedout = 60;
|
|
|
|
void Require(bool value, const char* message) {
|
|
if (!value) {
|
|
std::fprintf(stderr, "%s\n", message);
|
|
std::abort();
|
|
}
|
|
}
|
|
|
|
struct State {
|
|
PthreadMutex mutex = nullptr;
|
|
PthreadCond cond = nullptr;
|
|
int waiting = 0;
|
|
bool go = false;
|
|
std::atomic<int> woken{0};
|
|
std::atomic<int> failed{0};
|
|
};
|
|
|
|
void Wait(State& state, std::chrono::steady_clock::time_point deadline) {
|
|
Require(pthread_mutex_lock_nid_postfix(&state.mutex) == 0, "waiter lock returns 0");
|
|
++state.waiting;
|
|
int result = 0;
|
|
while (!state.go && result == 0) {
|
|
const auto left = std::chrono::duration_cast<std::chrono::microseconds>(deadline - std::chrono::steady_clock::now()).count();
|
|
result = left > 0 ? pthread_cond_reltimedwait_np_nid_postfix(&state.cond, &state.mutex, static_cast<KernelUseconds>(left)) : Etimedout;
|
|
}
|
|
if (state.go && result == 0) ++state.woken;
|
|
else ++state.failed;
|
|
Require(pthread_mutex_unlock_nid_postfix(&state.mutex) == 0, "waiter unlock returns 0");
|
|
}
|
|
|
|
}
|
|
|
|
int main() {
|
|
PthreadCond idle = nullptr;
|
|
Require(pthread_cond_init_nid_postfix(&idle, nullptr) == 0, "cond init");
|
|
Require(pthread_cond_broadcast_nid_postfix(&idle) == 0, "broadcast without waiters returns 0");
|
|
Require(pthread_cond_destroy_nid_postfix(&idle) == 0, "destroy returns 0");
|
|
Require(pthread_cond_broadcast_nid_postfix(&idle) == Einval, "broadcast of a destroyed cond returns EINVAL");
|
|
|
|
PthreadCond fresh = nullptr;
|
|
Require(pthread_cond_broadcast_nid_postfix(&fresh) == 0, "broadcast of a statically initialised cond returns 0");
|
|
Require(fresh != nullptr, "broadcast of a statically initialised cond initialises it");
|
|
Require(pthread_cond_destroy_nid_postfix(&fresh) == 0, "destroy of the initialised cond returns 0");
|
|
|
|
State state;
|
|
Require(pthread_mutex_init_nid_postfix(&state.mutex, nullptr) == 0, "mutex init");
|
|
Require(pthread_cond_init_nid_postfix(&state.cond, nullptr) == 0, "cond init");
|
|
const auto deadline = std::chrono::steady_clock::now() + std::chrono::seconds(5);
|
|
std::vector<std::thread> threads;
|
|
for (int i = 0; i < Waiters; ++i) threads.emplace_back([&] { Wait(state, deadline); });
|
|
|
|
while (std::chrono::steady_clock::now() < deadline) {
|
|
Require(pthread_mutex_lock_nid_postfix(&state.mutex) == 0, "lock returns 0");
|
|
const bool all = state.waiting == Waiters;
|
|
Require(pthread_mutex_unlock_nid_postfix(&state.mutex) == 0, "unlock returns 0");
|
|
if (all) break;
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
|
}
|
|
Require(pthread_mutex_lock_nid_postfix(&state.mutex) == 0, "lock returns 0");
|
|
Require(state.waiting == Waiters, "all waiters started waiting");
|
|
state.go = true;
|
|
Require(pthread_cond_broadcast_nid_postfix(&state.cond) == 0, "broadcast with waiters returns 0");
|
|
Require(pthread_mutex_unlock_nid_postfix(&state.mutex) == 0, "unlock returns 0");
|
|
for (auto& thread : threads) thread.join();
|
|
std::fprintf(stderr, "woken %d of %d\n", state.woken.load(), Waiters);
|
|
Require(state.woken == Waiters && state.failed == 0, "one broadcast wakes every waiter");
|
|
Require(pthread_cond_destroy_nid_postfix(&state.cond) == 0, "destroy returns 0");
|
|
Require(pthread_mutex_destroy_nid_postfix(&state.mutex) == 0, "mutex destroy returns 0");
|
|
return 0;
|
|
}
|