rpcs3/rpcs3/Emu/SysCalls/lv2/sys_event_flag.cpp
2015-07-10 04:31:41 +03:00

353 lines
6.2 KiB
C++

#include "stdafx.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/IdManager.h"
#include "Emu/SysCalls/SysCalls.h"
#include "Emu/Cell/PPUThread.h"
#include "sleep_queue.h"
#include "sys_event_flag.h"
SysCallBase sys_event_flag("sys_event_flag");
extern u64 get_system_time();
s32 sys_event_flag_create(vm::ptr<u32> id, vm::ptr<sys_event_flag_attribute_t> attr, u64 init)
{
sys_event_flag.Warning("sys_event_flag_create(id=*0x%x, attr=*0x%x, init=0x%llx)", id, attr, init);
if (!id || !attr)
{
return CELL_EFAULT;
}
const u32 protocol = attr->protocol;
switch (protocol)
{
case SYS_SYNC_FIFO: break;
case SYS_SYNC_RETRY: break;
case SYS_SYNC_PRIORITY: break;
case SYS_SYNC_PRIORITY_INHERIT: break;
default: sys_event_flag.Error("sys_event_flag_create(): unknown protocol (0x%x)", attr->protocol); return CELL_EINVAL;
}
if (attr->pshared != SYS_SYNC_NOT_PROCESS_SHARED || attr->ipc_key.data() || attr->flags.data())
{
sys_event_flag.Error("sys_event_flag_create(): unknown attributes (pshared=0x%x, ipc_key=0x%llx, flags=0x%x)", attr->pshared, attr->ipc_key, attr->flags);
return CELL_EINVAL;
}
const u32 type = attr->type;
switch (type)
{
case SYS_SYNC_WAITER_SINGLE: break;
case SYS_SYNC_WAITER_MULTIPLE: break;
default: sys_event_flag.Error("sys_event_flag_create(): unknown type (0x%x)", attr->type); return CELL_EINVAL;
}
*id = Emu.GetIdManager().make<lv2_event_flag_t>(init, protocol, type, attr->name_u64);
return CELL_OK;
}
s32 sys_event_flag_destroy(u32 id)
{
sys_event_flag.Warning("sys_event_flag_destroy(id=0x%x)", id);
LV2_LOCK;
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(id);
if (!ef)
{
return CELL_ESRCH;
}
if (ef->waiters)
{
return CELL_EBUSY;
}
Emu.GetIdManager().remove<lv2_event_flag_t>(id);
return CELL_OK;
}
s32 sys_event_flag_wait(u32 id, u64 bitptn, u32 mode, vm::ptr<u64> result, u64 timeout)
{
sys_event_flag.Log("sys_event_flag_wait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x, timeout=0x%llx)", id, bitptn, mode, result, timeout);
const u64 start_time = get_system_time();
LV2_LOCK;
if (result)
{
*result = 0;
}
switch (mode & 0xf)
{
case SYS_EVENT_FLAG_WAIT_AND: break;
case SYS_EVENT_FLAG_WAIT_OR: break;
default: return CELL_EINVAL;
}
switch (mode & ~0xf)
{
case 0: break;
case SYS_EVENT_FLAG_WAIT_CLEAR: break;
case SYS_EVENT_FLAG_WAIT_CLEAR_ALL: break;
default: return CELL_EINVAL;
}
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(id);
if (!ef)
{
return CELL_ESRCH;
}
if (ef->type == SYS_SYNC_WAITER_SINGLE && ef->waiters)
{
return CELL_EPERM;
}
while (ef->cancelled)
{
// wait until other threads return CELL_ECANCELED (to prevent modifying bit pattern)
ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
// protocol is ignored in current implementation
ef->waiters++;
while (true)
{
if (result)
{
*result = ef->flags;
}
if (mode & SYS_EVENT_FLAG_WAIT_AND && (ef->flags & bitptn) == bitptn)
{
break;
}
if (mode & SYS_EVENT_FLAG_WAIT_OR && ef->flags & bitptn)
{
break;
}
CHECK_EMU_STATUS;
if (ef->cancelled)
{
if (!--ef->cancelled)
{
ef->cv.notify_all();
}
return CELL_ECANCELED;
}
if (timeout && get_system_time() - start_time > timeout)
{
ef->waiters--;
return CELL_ETIMEDOUT;
}
ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
if (mode & SYS_EVENT_FLAG_WAIT_CLEAR)
{
ef->flags &= ~bitptn;
}
if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL)
{
ef->flags = 0;
}
if (--ef->waiters && ef->flags)
{
ef->cv.notify_one();
}
return CELL_OK;
}
s32 sys_event_flag_trywait(u32 id, u64 bitptn, u32 mode, vm::ptr<u64> result)
{
sys_event_flag.Log("sys_event_flag_trywait(id=0x%x, bitptn=0x%llx, mode=0x%x, result=*0x%x)", id, bitptn, mode, result);
LV2_LOCK;
if (result)
{
*result = 0;
}
switch (mode & 0xf)
{
case SYS_EVENT_FLAG_WAIT_AND: break;
case SYS_EVENT_FLAG_WAIT_OR: break;
default: return CELL_EINVAL;
}
switch (mode & ~0xf)
{
case 0: break;
case SYS_EVENT_FLAG_WAIT_CLEAR: break;
case SYS_EVENT_FLAG_WAIT_CLEAR_ALL: break;
default: return CELL_EINVAL;
}
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(id);
if (!ef)
{
return CELL_ESRCH;
}
if (!((mode & SYS_EVENT_FLAG_WAIT_AND) && (ef->flags & bitptn) == bitptn) && !((mode & SYS_EVENT_FLAG_WAIT_OR) && (ef->flags & bitptn)))
{
return CELL_EBUSY;
}
while (ef->cancelled)
{
ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
if (mode & SYS_EVENT_FLAG_WAIT_CLEAR)
{
ef->flags &= ~bitptn;
}
else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL)
{
ef->flags &= 0;
}
if (result)
{
*result = ef->flags;
}
return CELL_OK;
}
s32 sys_event_flag_set(u32 id, u64 bitptn)
{
sys_event_flag.Log("sys_event_flag_set(id=0x%x, bitptn=0x%llx)", id, bitptn);
LV2_LOCK;
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(id);
if (!ef)
{
return CELL_ESRCH;
}
while (ef->cancelled)
{
ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
ef->flags |= bitptn;
if (ef->waiters)
{
ef->cv.notify_all();
}
return CELL_OK;
}
s32 sys_event_flag_clear(u32 id, u64 bitptn)
{
sys_event_flag.Log("sys_event_flag_clear(id=0x%x, bitptn=0x%llx)", id, bitptn);
LV2_LOCK;
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(id);
if (!ef)
{
return CELL_ESRCH;
}
while (ef->cancelled)
{
ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
ef->flags &= bitptn;
return CELL_OK;
}
s32 sys_event_flag_cancel(u32 id, vm::ptr<u32> num)
{
sys_event_flag.Log("sys_event_flag_cancel(id=0x%x, num=*0x%x)", id, num);
LV2_LOCK;
if (num)
{
*num = 0;
}
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(id);
if (!ef)
{
return CELL_ESRCH;
}
while (ef->cancelled)
{
ef->cv.wait_for(lv2_lock, std::chrono::milliseconds(1));
}
if (num)
{
*num = ef->waiters;
}
if ((ef->cancelled = ef->waiters.exchange(0)))
{
ef->cv.notify_all();
}
return CELL_OK;
}
s32 sys_event_flag_get(u32 id, vm::ptr<u64> flags)
{
sys_event_flag.Log("sys_event_flag_get(id=0x%x, flags=*0x%x)", id, flags);
LV2_LOCK;
if (!flags)
{
return CELL_EFAULT;
}
const auto ef = Emu.GetIdManager().get<lv2_event_flag_t>(id);
if (!ef)
{
*flags = 0;
return CELL_ESRCH;
}
*flags = ef->flags;
return CELL_OK;
}