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)
{
std::shared_ptr<Semaphore> sem;
std::shared_ptr<semaphore_t> sem;
if (!Emu.GetIdManager().GetIDData(id, sem))
{
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)

View file

@ -12,181 +12,141 @@
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);
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;
return Emu.GetIdManager().GetNewID(sem, TYPE_SEMAPHORE);
}
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)",
sem.addr(), attr.addr(), initial_count, max_count);
sys_semaphore.Warning("sys_semaphore_create(sem=*0x%x, attr=*0x%x, initial_val=%d, max_val=%d)", sem, attr, initial_val, max_val);
if (!sem)
if (!sem || !attr)
{
sys_semaphore.Error("sys_semaphore_create(): invalid memory access (sem_addr=0x%x)", sem.addr());
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());
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);
sys_semaphore.Error("sys_semaphore_create(): invalid parameters (initial_val=%d, max_val=%d)", initial_val, max_val);
return CELL_EINVAL;
}
switch (attr->protocol.data())
const u32 protocol = attr->protocol;
switch (protocol)
{
case se32(SYS_SYNC_FIFO): break;
case se32(SYS_SYNC_PRIORITY): break;
case se32(SYS_SYNC_PRIORITY_INHERIT): break;
default: sys_semaphore.Error("Unknown protocol attribute (0x%x)", attr->protocol); return CELL_EINVAL;
case SYS_SYNC_FIFO: break;
case SYS_SYNC_PRIORITY: break;
case SYS_SYNC_PRIORITY_INHERIT: break;
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;
}
*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;
}
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;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
LV2_LOCK;
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{
return CELL_ESRCH;
}
if (sem->queue.count()) // TODO: safely make object unusable
if (semaphore->waiters)
{
return CELL_EBUSY;
}
Emu.GetIdManager().RemoveID(sem_id);
Emu.GetIdManager().RemoveID(sem);
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();
std::shared_ptr<Semaphore> sem;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
LV2_LOCK;
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{
return CELL_ESRCH;
}
const u32 tid = GetCurrentPPUThread().GetId();
s32 old_value;
// protocol is ignored in current implementation
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 (!sem->queue.invalidate(tid, sem->protocol))
{
if (sem->queue.pop(tid, sem->protocol))
{
return CELL_OK;
}
assert(!"sys_semaphore_wait() failed (timeout)");
}
semaphore->waiters--; assert(semaphore->waiters >= 0);
return CELL_ETIMEDOUT;
}
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;
}
semaphore->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
semaphore->value--;
semaphore->waiters--; assert(semaphore->waiters >= 0);
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;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
LV2_LOCK;
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{
return CELL_ESRCH;
}
s32 old_value;
sem->value.atomic_op_sync([&old_value](s32& value)
{
old_value = value;
if (value > 0)
{
value--;
}
});
if (old_value > 0)
{
return CELL_OK;
}
else
if (semaphore->value <= 0 || semaphore->waiters)
{
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;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
LV2_LOCK;
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{
return CELL_ESRCH;
}
@ -196,52 +156,35 @@ s32 sys_semaphore_post(u32 sem_id, s32 count)
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;
}
while (count > 0)
{
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;
}
}
semaphore->value += count; assert(semaphore->value >= 0);
semaphore->cv.notify_all();
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)
{
sys_semaphore.Error("sys_semaphore_get_value(): invalid memory access (addr=0x%x)", count.addr());
return CELL_EFAULT;
}
std::shared_ptr<Semaphore> sem;
if (!Emu.GetIdManager().GetIDData(sem_id, sem))
LV2_LOCK;
std::shared_ptr<semaphore_t> semaphore;
if (!Emu.GetIdManager().GetIDData(sem, semaphore))
{
return CELL_ESRCH;
}
*count = sem->value.read_sync();
*count = std::max<s32>(0, semaphore->value - semaphore->waiters);
return CELL_OK;
}

View file

@ -1,12 +1,13 @@
#pragma once
struct sys_semaphore_attribute
struct sys_semaphore_attribute_t
{
be_t<u32> protocol;
be_t<u32> pshared; // undefined
be_t<u64> ipc_key; // undefined
be_t<s32> flags; // undefined
be_t<u32> pad; // not used
be_t<u32> pshared;
be_t<u64> ipc_key;
be_t<s32> flags;
be_t<u32> pad;
union
{
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 s32 max;
const u64 name;
Semaphore(s32 initial_count, s32 max_count, u32 protocol, u64 name)
: max(max_count)
, protocol(protocol)
std::atomic<s32> value;
// 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)
, value(value)
, waiters(0)
{
value.write_relaxed(initial_count);
}
};
// 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
s32 sys_semaphore_create(vm::ptr<u32> sem, vm::ptr<sys_semaphore_attribute> attr, s32 initial_count, s32 max_count);
s32 sys_semaphore_destroy(u32 sem_id);
s32 sys_semaphore_wait(u32 sem_id, u64 timeout);
s32 sys_semaphore_trywait(u32 sem_id);
s32 sys_semaphore_post(u32 sem_id, s32 count);
s32 sys_semaphore_get_value(u32 sem_id, vm::ptr<s32> 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);
s32 sys_semaphore_wait(u32 sem, u64 timeout);
s32 sys_semaphore_trywait(u32 sem);
s32 sys_semaphore_post(u32 sem, s32 count);
s32 sys_semaphore_get_value(u32 sem, vm::ptr<s32> count);