#pragma once #include "sys_sync.h" #include "Emu/Memory/vm_ptr.h" struct sys_mutex_attribute_t { be_t protocol; // SYS_SYNC_FIFO, SYS_SYNC_PRIORITY or SYS_SYNC_PRIORITY_INHERIT be_t recursive; // SYS_SYNC_RECURSIVE or SYS_SYNC_NOT_RECURSIVE be_t pshared; be_t adaptive; be_t ipc_key; be_t flags; be_t pad; union { nse_t name_u64; char name[sizeof(u64)]; }; }; struct lv2_mutex final : lv2_obj { static const u32 id_base = 0x85000000; const lv2_protocol protocol; const u32 recursive; const u32 shared; const u32 adaptive; const u64 key; const u64 name; const s32 flags; struct alignas(8) count_info { u32 mutex_count; // Mutex copies count (0 means doesn't exist anymore) u32 cond_count; // Condition Variables }; shared_mutex mutex; atomic_t owner{0}; atomic_t lock_count{0}; // Recursive Locks atomic_t obj_count{}; std::deque sq; lv2_mutex(u32 protocol, u32 recursive, u32 shared, u32 adaptive, u64 key, s32 flags, u64 name) : protocol{protocol} , recursive(recursive) , shared(shared) , adaptive(adaptive) , key(key) , name(name) , flags(flags) { } CellError on_id_create() { obj_count.atomic_op([](count_info& info){ info.mutex_count++; }); return {}; } CellError try_lock(u32 id) { const u32 value = owner; if (value >> 1 == id) { // Recursive locking if (recursive == SYS_SYNC_RECURSIVE) { if (lock_count == 0xffffffffu) { return CELL_EKRESOURCE; } lock_count++; return {}; } return CELL_EDEADLK; } if (value == 0) { if (owner.compare_and_swap_test(0, id << 1)) { return {}; } } return CELL_EBUSY; } bool try_own(cpu_thread& cpu, u32 id) { if (owner.fetch_op([&](u32& val) { if (val == 0) { val = id << 1; } else { val |= 1; } })) { sq.emplace_back(&cpu); return false; } return true; } CellError try_unlock(u32 id) { const u32 value = owner; if (value >> 1 != id) { return CELL_EPERM; } if (lock_count) { lock_count--; return {}; } if (value == id << 1) { if (owner.compare_and_swap_test(value, 0)) { return {}; } } return CELL_EBUSY; } template T* reown() { if (auto cpu = schedule(sq, protocol)) { owner = cpu->id << 1 | !sq.empty(); return static_cast(cpu); } else { owner = 0; return nullptr; } } }; class ppu_thread; // Syscalls error_code sys_mutex_create(ppu_thread& ppu, vm::ptr mutex_id, vm::ptr attr); error_code sys_mutex_destroy(ppu_thread& ppu, u32 mutex_id); error_code sys_mutex_lock(ppu_thread& ppu, u32 mutex_id, u64 timeout); error_code sys_mutex_trylock(ppu_thread& ppu, u32 mutex_id); error_code sys_mutex_unlock(ppu_thread& ppu, u32 mutex_id);