Lv2 Semaphore rewritten

This commit is contained in:
Nekotekina 2015-03-08 06:37:07 +03:00
parent dba249554d
commit 0f233beff9
3 changed files with 100 additions and 152 deletions

View file

@ -14,13 +14,13 @@
SemaphoreAttributes SyncPrimManager::GetSemaphoreData(u32 id) SemaphoreAttributes SyncPrimManager::GetSemaphoreData(u32 id)
{ {
std::shared_ptr<Semaphore> sem; std::shared_ptr<semaphore_t> sem;
if (!Emu.GetIdManager().GetIDData(id, sem)) if (!Emu.GetIdManager().GetIDData(id, sem))
{ {
return{}; return{};
} }
return{ std::string((const char*)&sem->name, 8), sem->value.read_sync(), sem->max }; return{ std::string((const char*)&sem->name, 8), sem->value, sem->max };
} }
LwMutexAttributes SyncPrimManager::GetLwMutexData(u32 id) LwMutexAttributes SyncPrimManager::GetLwMutexData(u32 id)

View file

@ -12,181 +12,141 @@
SysCallBase sys_semaphore("sys_semaphore"); SysCallBase sys_semaphore("sys_semaphore");
u32 semaphore_create(s32 initial_count, s32 max_count, u32 protocol, u64 name_u64) u32 semaphore_create(s32 initial_val, s32 max_val, u32 protocol, u64 name_u64)
{ {
std::shared_ptr<Semaphore> sem(new Semaphore(initial_count, max_count, protocol, name_u64)); std::shared_ptr<semaphore_t> sem(new semaphore_t(protocol, max_val, name_u64, initial_val));
const u32 id = Emu.GetIdManager().GetNewID(sem, TYPE_SEMAPHORE); return Emu.GetIdManager().GetNewID(sem, TYPE_SEMAPHORE);
sem->queue.set_full_name(fmt::Format("Semaphore(%d)", id));
sys_semaphore.Notice("*** semaphore created [%s] (protocol=0x%x): id = %d", std::string((const char*)&name_u64, 8).c_str(), protocol, id);
return id;
} }
s32 sys_semaphore_create(vm::ptr<u32> sem, vm::ptr<sys_semaphore_attribute> attr, s32 initial_count, s32 max_count) s32 sys_semaphore_create(vm::ptr<u32> sem, vm::ptr<sys_semaphore_attribute_t> attr, s32 initial_val, s32 max_val)
{ {
sys_semaphore.Warning("sys_semaphore_create(sem_addr=0x%x, attr_addr=0x%x, initial_count=%d, max_count=%d)", sys_semaphore.Warning("sys_semaphore_create(sem=*0x%x, attr=*0x%x, initial_val=%d, max_val=%d)", sem, attr, initial_val, max_val);
sem.addr(), attr.addr(), initial_count, max_count);
if (!sem) if (!sem || !attr)
{ {
sys_semaphore.Error("sys_semaphore_create(): invalid memory access (sem_addr=0x%x)", sem.addr());
return CELL_EFAULT; return CELL_EFAULT;
} }
if (!attr) if (max_val <= 0 || initial_val > max_val || initial_val < 0)
{ {
sys_semaphore.Error("sys_semaphore_create(): An invalid argument value is specified (attr_addr=0x%x)", attr.addr()); sys_semaphore.Error("sys_semaphore_create(): invalid parameters (initial_val=%d, max_val=%d)", initial_val, max_val);
return CELL_EFAULT;
}
if (max_count <= 0 || initial_count > max_count || initial_count < 0)
{
sys_semaphore.Error("sys_semaphore_create(): invalid parameters (initial_count=%d, max_count=%d)", initial_count, max_count);
return CELL_EINVAL; return CELL_EINVAL;
} }
switch (attr->protocol.data()) const u32 protocol = attr->protocol;
switch (protocol)
{ {
case se32(SYS_SYNC_FIFO): break; case SYS_SYNC_FIFO: break;
case se32(SYS_SYNC_PRIORITY): break; case SYS_SYNC_PRIORITY: break;
case se32(SYS_SYNC_PRIORITY_INHERIT): break; case SYS_SYNC_PRIORITY_INHERIT: break;
default: sys_semaphore.Error("Unknown protocol attribute (0x%x)", attr->protocol); return CELL_EINVAL; default: sys_semaphore.Error("sys_semaphore_create(): unknown protocol (0x%x)", protocol); return CELL_EINVAL;
} }
if (attr->pshared.data() != se32(0x200)) if (attr->pshared.data() != se32(0x200) || attr->ipc_key.data() || attr->flags.data())
{ {
sys_semaphore.Error("Unknown pshared attribute (0x%x)", attr->pshared); sys_semaphore.Error("sys_semaphore_create(): unknown attributes (pshared=0x%x, ipc_key=0x%x, flags=0x%x)", attr->pshared, attr->ipc_key, attr->flags);
return CELL_EINVAL; return CELL_EINVAL;
} }
*sem = semaphore_create(initial_count, max_count, attr->protocol, attr->name_u64); *sem = semaphore_create(initial_val, max_val, protocol, attr->name_u64);
return CELL_OK; return CELL_OK;
} }
s32 sys_semaphore_destroy(u32 sem_id) s32 sys_semaphore_destroy(u32 sem)
{ {
sys_semaphore.Warning("sys_semaphore_destroy(sem_id=%d)", sem_id); sys_semaphore.Warning("sys_semaphore_destroy(sem=%d)", sem);
std::shared_ptr<Semaphore> sem; LV2_LOCK;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
if (sem->queue.count()) // TODO: safely make object unusable if (semaphore->waiters)
{ {
return CELL_EBUSY; return CELL_EBUSY;
} }
Emu.GetIdManager().RemoveID(sem_id); Emu.GetIdManager().RemoveID(sem);
return CELL_OK; return CELL_OK;
} }
s32 sys_semaphore_wait(u32 sem_id, u64 timeout) s32 sys_semaphore_wait(u32 sem, u64 timeout)
{ {
sys_semaphore.Log("sys_semaphore_wait(sem_id=%d, timeout=%lld)", sem_id, timeout); sys_semaphore.Log("sys_semaphore_wait(sem=%d, timeout=0x%llx)", sem, timeout);
const u64 start_time = get_system_time(); const u64 start_time = get_system_time();
std::shared_ptr<Semaphore> sem; LV2_LOCK;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
const u32 tid = GetCurrentPPUThread().GetId(); // protocol is ignored in current implementation
s32 old_value; semaphore->waiters++; assert(semaphore->waiters > 0);
while (semaphore->value <= 0)
{ {
sem->value.atomic_op_sync([&old_value](s32& value)
{
old_value = value;
if (value > 0)
{
value--;
}
});
if (old_value > 0)
{
return CELL_OK;
}
sem->queue.push(tid, sem->protocol);
}
while (true)
{
if (sem->queue.pop(tid, sem->protocol))
{
break;
}
assert(!sem->value.read_sync());
std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack
if (timeout && get_system_time() - start_time > timeout) if (timeout && get_system_time() - start_time > timeout)
{ {
if (!sem->queue.invalidate(tid, sem->protocol)) semaphore->waiters--; assert(semaphore->waiters >= 0);
{
if (sem->queue.pop(tid, sem->protocol))
{
return CELL_OK;
}
assert(!"sys_semaphore_wait() failed (timeout)");
}
return CELL_ETIMEDOUT; return CELL_ETIMEDOUT;
} }
if (Emu.IsStopped()) if (Emu.IsStopped())
{ {
sys_semaphore.Warning("sys_semaphore_wait(%d) aborted", sem_id); sys_semaphore.Warning("sys_semaphore_wait(%d) aborted", sem);
return CELL_OK; return CELL_OK;
} }
semaphore->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
} }
semaphore->value--;
semaphore->waiters--; assert(semaphore->waiters >= 0);
return CELL_OK; return CELL_OK;
} }
s32 sys_semaphore_trywait(u32 sem_id) s32 sys_semaphore_trywait(u32 sem)
{ {
sys_semaphore.Log("sys_semaphore_trywait(sem_id=%d)", sem_id); sys_semaphore.Log("sys_semaphore_trywait(sem=%d)", sem);
std::shared_ptr<Semaphore> sem; LV2_LOCK;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
s32 old_value; if (semaphore->value <= 0 || semaphore->waiters)
sem->value.atomic_op_sync([&old_value](s32& value)
{
old_value = value;
if (value > 0)
{
value--;
}
});
if (old_value > 0)
{
return CELL_OK;
}
else
{ {
return CELL_EBUSY; return CELL_EBUSY;
} }
semaphore->value--;
return CELL_OK;
} }
s32 sys_semaphore_post(u32 sem_id, s32 count) s32 sys_semaphore_post(u32 sem, s32 count)
{ {
sys_semaphore.Log("sys_semaphore_post(sem_id=%d, count=%d)", sem_id, count); sys_semaphore.Log("sys_semaphore_post(sem=%d, count=%d)", sem, count);
std::shared_ptr<Semaphore> sem; LV2_LOCK;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
@ -196,52 +156,35 @@ s32 sys_semaphore_post(u32 sem_id, s32 count)
return CELL_EINVAL; return CELL_EINVAL;
} }
if (count + sem->value.read_sync() - (s32)sem->queue.count() > sem->max) if (semaphore->value + count > semaphore->max + semaphore->waiters)
{ {
return CELL_EBUSY; return CELL_EBUSY;
} }
while (count > 0) semaphore->value += count; assert(semaphore->value >= 0);
{ semaphore->cv.notify_all();
if (Emu.IsStopped())
{
sys_semaphore.Warning("sys_semaphore_post(%d) aborted", sem_id);
return CELL_OK;
}
if (u32 target = sem->queue.signal(sem->protocol))
{
count--;
}
else
{
sem->value.atomic_op([count](s32& value)
{
value += count;
});
count = 0;
}
}
return CELL_OK; return CELL_OK;
} }
s32 sys_semaphore_get_value(u32 sem_id, vm::ptr<s32> count) s32 sys_semaphore_get_value(u32 sem, vm::ptr<s32> count)
{ {
sys_semaphore.Log("sys_semaphore_get_value(sem_id=%d, count_addr=0x%x)", sem_id, count.addr()); sys_semaphore.Log("sys_semaphore_get_value(sem=%d, count=*0x%x)", sem, count);
if (!count) if (!count)
{ {
sys_semaphore.Error("sys_semaphore_get_value(): invalid memory access (addr=0x%x)", count.addr());
return CELL_EFAULT; return CELL_EFAULT;
} }
std::shared_ptr<Semaphore> sem; LV2_LOCK;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{ {
return CELL_ESRCH; return CELL_ESRCH;
} }
*count = sem->value.read_sync(); *count = std::max<s32>(0, semaphore->value - semaphore->waiters);
return CELL_OK; return CELL_OK;
} }

View file

@ -1,12 +1,13 @@
#pragma once #pragma once
struct sys_semaphore_attribute struct sys_semaphore_attribute_t
{ {
be_t<u32> protocol; be_t<u32> protocol;
be_t<u32> pshared; // undefined be_t<u32> pshared;
be_t<u64> ipc_key; // undefined be_t<u64> ipc_key;
be_t<s32> flags; // undefined be_t<s32> flags;
be_t<u32> pad; // not used be_t<u32> pad;
union union
{ {
char name[8]; char name[8];
@ -14,31 +15,35 @@ struct sys_semaphore_attribute
}; };
}; };
struct Semaphore struct semaphore_t
{ {
sleep_queue_t queue;
atomic_le_t<s32> value;
const s32 max;
const u32 protocol; const u32 protocol;
const s32 max;
const u64 name; const u64 name;
Semaphore(s32 initial_count, s32 max_count, u32 protocol, u64 name) std::atomic<s32> value;
: max(max_count)
, protocol(protocol) // TODO: use sleep queue, possibly remove condition variable
std::condition_variable cv;
std::atomic<s32> waiters;
semaphore_t(u32 protocol, s32 max, u64 name, s32 value)
: protocol(protocol)
, max(max)
, name(name) , name(name)
, value(value)
, waiters(0)
{ {
value.write_relaxed(initial_count);
} }
}; };
// Aux // Aux
u32 semaphore_create(s32 initial_count, s32 max_count, u32 protocol, u64 name_u64); u32 semaphore_create(s32 initial_val, s32 max_val, u32 protocol, u64 name_u64);
// SysCalls // SysCalls
s32 sys_semaphore_create(vm::ptr<u32> sem, vm::ptr<sys_semaphore_attribute> attr, s32 initial_count, s32 max_count); s32 sys_semaphore_create(vm::ptr<u32> sem, vm::ptr<sys_semaphore_attribute_t> attr, s32 initial_val, s32 max_val);
s32 sys_semaphore_destroy(u32 sem_id); s32 sys_semaphore_destroy(u32 sem);
s32 sys_semaphore_wait(u32 sem_id, u64 timeout); s32 sys_semaphore_wait(u32 sem, u64 timeout);
s32 sys_semaphore_trywait(u32 sem_id); s32 sys_semaphore_trywait(u32 sem);
s32 sys_semaphore_post(u32 sem_id, s32 count); s32 sys_semaphore_post(u32 sem, s32 count);
s32 sys_semaphore_get_value(u32 sem_id, vm::ptr<s32> count); s32 sys_semaphore_get_value(u32 sem, vm::ptr<s32> count);