sys_event_flag.cpp restored

This commit is contained in:
Nekotekina 2014-08-08 19:55:12 +04:00
parent b89c4fec3e
commit b00e0fcf69
12 changed files with 615 additions and 600 deletions

View file

@ -2,8 +2,6 @@
#include "Utilities/Log.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/lv2/sys_lwmutex.h"
#include "Emu/SysCalls/lv2/sys_event.h"
#include "Emu/Cell/RawSPUThread.h"

View file

@ -2,8 +2,6 @@
#include "Utilities/Log.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/SysCalls/lv2/sys_lwmutex.h"
#include "Emu/SysCalls/lv2/sys_event.h"
#include "SPUInstrTable.h"
#include "SPUDisAsm.h"

View file

@ -1,8 +1,6 @@
#include "stdafx.h"
#include "Utilities/Log.h"
#include "Emu/System.h"
#include "Emu/SysCalls/lv2/sys_lwmutex.h"
#include "Emu/SysCalls/lv2/sys_event.h"
#include "Emu/Cell/SPUThread.h"
#include "Emu/Cell/SPUDecoder.h"

View file

@ -3,6 +3,7 @@
#include "Emu/Event.h"
#include "Emu/SysCalls/lv2/sys_spu.h"
#include "Emu/SysCalls/lv2/sys_event.h"
#include "Emu/SysCalls/lv2/sys_event_flag.h"
#include "Emu/SysCalls/lv2/sys_time.h"
#include "MFC.h"
#include "Emu/SysCalls/ErrorCodes.h"

View file

@ -1,217 +1,5 @@
#pragma once
#include "Emu/SysCalls/lv2/sys_lwmutex.h"
#define FIX_SPUQ(x) ((u64)x | 0x5350555100000000ULL)
// arbitrary code to prevent "special" zero value in key argument
enum EventQueueType
{
SYS_PPU_QUEUE = 1,
SYS_SPU_QUEUE = 2,
};
enum EventQueueDestroyMode
{
// DEFAULT = 0,
SYS_EVENT_QUEUE_DESTROY_FORCE = 1,
};
enum EventPortType
{
SYS_EVENT_PORT_LOCAL = 1,
};
enum EventSourceType
{
SYS_SPU_THREAD_EVENT_USER = 1,
/* SYS_SPU_THREAD_EVENT_DMA = 2, */ // not supported
};
enum EventSourceKey : u64
{
SYS_SPU_THREAD_EVENT_USER_KEY = 0xFFFFFFFF53505501,
/* SYS_SPU_THREAD_EVENT_DMA_KEY = 0xFFFFFFFF53505502, */
};
struct sys_event_queue_attr
{
be_t<u32> protocol; // SYS_SYNC_PRIORITY or SYS_SYNC_FIFO
be_t<int> type; // SYS_PPU_QUEUE or SYS_SPU_QUEUE
union
{
char name[8];
u64 name_u64;
};
};
struct sys_event_data
{
be_t<u64> source;
be_t<u64> data1;
be_t<u64> data2;
be_t<u64> data3;
};
struct EventQueue;
struct EventPort
{
u64 name; // generated or user-specified code that is passed to sys_event_data struct
EventQueue* eq; // event queue this port has been connected to
std::mutex m_mutex; // may be locked until the event sending is finished
EventPort(u64 name = 0)
: eq(nullptr)
, name(name)
{
}
};
class EventRingBuffer
{
std::vector<sys_event_data> data;
std::mutex m_mutex;
u32 buf_pos;
u32 buf_count;
public:
const u32 size;
EventRingBuffer(u32 size)
: size(size)
, buf_pos(0)
, buf_count(0)
{
data.resize(size);
}
void clear()
{
std::lock_guard<std::mutex> lock(m_mutex);
buf_count = 0;
buf_pos = 0;
}
bool push(u64 name, u64 d1, u64 d2, u64 d3)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (buf_count >= size) return false;
sys_event_data& ref = data[(buf_pos + buf_count++) % size];
ref.source = name;
ref.data1 = d1;
ref.data2 = d2;
ref.data3 = d3;
return true;
}
bool pop(sys_event_data& ref)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!buf_count) return false;
sys_event_data& from = data[buf_pos];
buf_pos = (buf_pos + 1) % size;
buf_count--;
ref.source = from.source;
ref.data1 = from.data1;
ref.data2 = from.data2;
ref.data3 = from.data3;
return true;
}
u32 pop_all(sys_event_data* ptr, u32 max)
{
std::lock_guard<std::mutex> lock(m_mutex);
u32 res = 0;
while (buf_count && max)
{
sys_event_data& from = data[buf_pos];
ptr->source = from.source;
ptr->data1 = from.data1;
ptr->data2 = from.data2;
ptr->data3 = from.data3;
buf_pos = (buf_pos + 1) % size;
buf_count--;
max--;
ptr++;
res++;
}
return res;
}
u32 count() const
{
return buf_count;
}
};
class EventPortList
{
std::vector<EventPort*> data;
std::mutex m_mutex;
public:
void clear()
{
std::lock_guard<std::mutex> lock(m_mutex);
for (u32 i = 0; i < data.size(); i++)
{
std::lock_guard<std::mutex> lock2(data[i]->m_mutex);
data[i]->eq = nullptr; // force all ports to disconnect
}
data.clear();
}
void add(EventPort* port)
{
std::lock_guard<std::mutex> lock(m_mutex);
data.push_back(port);
}
void remove(EventPort* port)
{
std::lock_guard<std::mutex> lock(m_mutex);
for (u32 i = 0; i < data.size(); i++)
{
if (data[i] == port)
{
data.erase(data.begin() + i);
return;
}
}
}
};
struct EventQueue
{
SleepQueue sq;
EventPortList ports;
EventRingBuffer events;
SMutex owner;
const union
{
u64 name_u64;
char name[8];
};
const u32 protocol;
const int type;
const u64 key;
EventQueue(u32 protocol, int type, u64 name, u64 key, int size)
: type(type)
, protocol(protocol)
, name_u64(name)
, key(key)
, events(size) // size: max event count this queue can hold
{
}
};
#include "Emu/SysCalls/lv2/sys_event.h"
class EventManager
{

View file

@ -8,6 +8,7 @@
#include "lv2/lv2Fs.h"
#include "lv2/sys_cond.h"
#include "lv2/sys_event.h"
#include "lv2/sys_event_flag.h"
#include "lv2/sys_interrupt.h"
#include "lv2/sys_lwcond.h"
#include "lv2/sys_lwmutex.h"

View file

@ -8,7 +8,6 @@
#include "Emu/Cell/SPUThread.h"
#include "Emu/Event.h"
#include "sys_lwmutex.h"
#include "sys_event.h"
SysCallBase sys_event("sys_event");
@ -362,323 +361,4 @@ s32 sys_event_port_send(u32 eport_id, u64 data1, u64 data2, u64 data3)
}
return CELL_OK;
}
// sys_event_flag
s32 sys_event_flag_create(mem32_t eflag_id, mem_ptr_t<sys_event_flag_attr> attr, u64 init)
{
sys_event.Warning("sys_event_flag_create(eflag_id_addr=0x%x, attr_addr=0x%x, init=0x%llx)",
eflag_id.GetAddr(), attr.GetAddr(), init);
switch (attr->protocol.ToBE())
{
case se32(SYS_SYNC_PRIORITY): break;
case se32(SYS_SYNC_RETRY): sys_event.Todo("sys_event_flag_create(): SYS_SYNC_RETRY"); break;
case se32(SYS_SYNC_PRIORITY_INHERIT): sys_event.Todo("sys_event_flag_create(): SYS_SYNC_PRIORITY_INHERIT"); break;
case se32(SYS_SYNC_FIFO): break;
default: return CELL_EINVAL;
}
if (attr->pshared.ToBE() != se32(0x200))
{
return CELL_EINVAL;
}
switch (attr->type.ToBE())
{
case se32(SYS_SYNC_WAITER_SINGLE): break;
case se32(SYS_SYNC_WAITER_MULTIPLE): break;
default: return CELL_EINVAL;
}
eflag_id = sys_event.GetNewId(new EventFlag(init, (u32)attr->protocol, (int)attr->type));
sys_event.Warning("*** event_flag created [%s] (protocol=0x%x, type=0x%x): id = %d",
std::string(attr->name, 8).c_str(), (u32)attr->protocol, (int)attr->type, eflag_id.GetValue());
return CELL_OK;
}
s32 sys_event_flag_destroy(u32 eflag_id)
{
sys_event.Warning("sys_event_flag_destroy(eflag_id=%d)", eflag_id);
EventFlag* ef;
if(!sys_event.CheckId(eflag_id, ef)) return CELL_ESRCH;
if (ef->waiters.size()) // ???
{
return CELL_EBUSY;
}
Emu.GetIdManager().RemoveID(eflag_id);
return CELL_OK;
}
s32 sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result, u64 timeout)
{
sys_event.Log("sys_event_flag_wait(eflag_id=%d, bitptn=0x%llx, mode=0x%x, result_addr=0x%x, timeout=%lld)",
eflag_id, bitptn, mode, result.GetAddr(), timeout);
if (result.GetAddr()) 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;
}
EventFlag* ef;
if(!sys_event.CheckId(eflag_id, ef)) return CELL_ESRCH;
u32 tid = GetCurrentPPUThread().GetId();
{
SMutexLocker lock(ef->m_mutex);
if (ef->m_type == SYS_SYNC_WAITER_SINGLE && ef->waiters.size() > 0)
{
return CELL_EPERM;
}
EventFlagWaiter rec;
rec.bitptn = bitptn;
rec.mode = mode;
rec.tid = tid;
ef->waiters.push_back(rec);
if (ef->check() == tid)
{
u64 flags = ef->flags;
ef->waiters.erase(ef->waiters.end() - 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.GetAddr()) result = flags;
return CELL_OK;
}
}
u32 counter = 0;
const u32 max_counter = timeout ? (timeout / 1000) : ~0;
while (true)
{
if (ef->signal.unlock(tid, tid) == SMR_OK)
{
SMutexLocker lock(ef->m_mutex);
u64 flags = ef->flags;
for (u32 i = 0; i < ef->waiters.size(); i++)
{
if (ef->waiters[i].tid == tid)
{
ef->waiters.erase(ef->waiters.begin() +i);
if (mode & SYS_EVENT_FLAG_WAIT_CLEAR)
{
ef->flags &= ~bitptn;
}
else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL)
{
ef->flags = 0;
}
if (u32 target = ef->check())
{
// if signal, leave both mutexes locked...
ef->signal.unlock(tid, target);
ef->m_mutex.unlock(tid, target);
}
else
{
ef->signal.unlock(tid);
}
if (result.GetAddr()) result = flags;
return CELL_OK;
}
}
ef->signal.unlock(tid);
return CELL_ECANCELED;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (counter++ > max_counter)
{
SMutexLocker lock(ef->m_mutex);
for (u32 i = 0; i < ef->waiters.size(); i++)
{
if (ef->waiters[i].tid == tid)
{
ef->waiters.erase(ef->waiters.begin() + i);
break;
}
}
return CELL_ETIMEDOUT;
}
if (Emu.IsStopped())
{
LOG_WARNING(HLE, "sys_event_flag_wait(id=%d) aborted", eflag_id);
return CELL_OK;
}
}
}
s32 sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result)
{
sys_event.Log("sys_event_flag_trywait(eflag_id=%d, bitptn=0x%llx, mode=0x%x, result_addr=0x%x)",
eflag_id, bitptn, mode, result.GetAddr());
if (result.GetAddr()) 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;
}
EventFlag* ef;
if(!sys_event.CheckId(eflag_id, ef)) return CELL_ESRCH;
SMutexLocker lock(ef->m_mutex);
u64 flags = ef->flags;
if (((mode & SYS_EVENT_FLAG_WAIT_AND) && (flags & bitptn) == bitptn) ||
((mode & SYS_EVENT_FLAG_WAIT_OR) && (flags & bitptn)))
{
if (mode & SYS_EVENT_FLAG_WAIT_CLEAR)
{
ef->flags &= ~bitptn;
}
else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL)
{
ef->flags = 0;
}
if (result.GetAddr()) result = flags;
return CELL_OK;
}
return CELL_EBUSY;
}
s32 sys_event_flag_set(u32 eflag_id, u64 bitptn)
{
sys_event.Log("sys_event_flag_set(eflag_id=%d, bitptn=0x%llx)", eflag_id, bitptn);
EventFlag* ef;
if(!sys_event.CheckId(eflag_id, ef)) return CELL_ESRCH;
u32 tid = GetCurrentPPUThread().GetId();
ef->m_mutex.lock(tid);
ef->flags |= bitptn;
if (u32 target = ef->check())
{
// if signal, leave both mutexes locked...
ef->signal.lock(target);
ef->m_mutex.unlock(tid, target);
}
else
{
ef->m_mutex.unlock(tid);
}
return CELL_OK;
}
s32 sys_event_flag_clear(u32 eflag_id, u64 bitptn)
{
sys_event.Log("sys_event_flag_clear(eflag_id=%d, bitptn=0x%llx)", eflag_id, bitptn);
EventFlag* ef;
if(!sys_event.CheckId(eflag_id, ef)) return CELL_ESRCH;
SMutexLocker lock(ef->m_mutex);
ef->flags &= bitptn;
return CELL_OK;
}
s32 sys_event_flag_cancel(u32 eflag_id, mem32_t num)
{
sys_event.Log("sys_event_flag_cancel(eflag_id=%d, num_addr=0x%x)", eflag_id, num.GetAddr());
EventFlag* ef;
if(!sys_event.CheckId(eflag_id, ef)) return CELL_ESRCH;
std::vector<u32> tids;
{
SMutexLocker lock(ef->m_mutex);
tids.resize(ef->waiters.size());
for (u32 i = 0; i < ef->waiters.size(); i++)
{
tids[i] = ef->waiters[i].tid;
}
ef->waiters.clear();
}
for (u32 i = 0; i < tids.size(); i++)
{
ef->signal.lock(tids[i]);
}
if (Emu.IsStopped())
{
LOG_WARNING(HLE, "sys_event_flag_cancel(id=%d) aborted", eflag_id);
return CELL_OK;
}
if (num.GetAddr()) num = tids.size();
return CELL_OK;
}
s32 sys_event_flag_get(u32 eflag_id, mem64_t flags)
{
sys_event.Log("sys_event_flag_get(eflag_id=%d, flags_addr=0x%x)", eflag_id, flags.GetAddr());
EventFlag* ef;
if(!sys_event.CheckId(eflag_id, ef)) return CELL_ESRCH;
SMutexLocker lock(ef->m_mutex);
flags = ef->flags;
return CELL_OK;
}
}

View file

@ -1,77 +1,216 @@
#pragma once
#include "Emu/Event.h"
enum
#include "sys_lwmutex.h"
#define FIX_SPUQ(x) ((u64)x | 0x5350555100000000ULL)
// arbitrary code to prevent "special" zero value in key argument
enum EventQueueType
{
SYS_SYNC_WAITER_SINGLE = 0x10000,
SYS_SYNC_WAITER_MULTIPLE = 0x20000,
SYS_EVENT_FLAG_WAIT_AND = 0x01,
SYS_EVENT_FLAG_WAIT_OR = 0x02,
SYS_EVENT_FLAG_WAIT_CLEAR = 0x10,
SYS_EVENT_FLAG_WAIT_CLEAR_ALL = 0x20,
SYS_PPU_QUEUE = 1,
SYS_SPU_QUEUE = 2,
};
struct sys_event_flag_attr
enum EventQueueDestroyMode
{
be_t<u32> protocol;
be_t<u32> pshared;
be_t<u64> ipc_key;
be_t<int> flags;
be_t<int> type;
char name[8];
// DEFAULT = 0,
SYS_EVENT_QUEUE_DESTROY_FORCE = 1,
};
struct EventFlagWaiter
enum EventPortType
{
u32 tid;
u32 mode;
u64 bitptn;
SYS_EVENT_PORT_LOCAL = 1,
};
struct EventFlag
enum EventSourceType
{
SMutex m_mutex;
u64 flags;
std::vector<EventFlagWaiter> waiters;
SMutex signal;
const u32 m_protocol;
const int m_type;
SYS_SPU_THREAD_EVENT_USER = 1,
/* SYS_SPU_THREAD_EVENT_DMA = 2, */ // not supported
};
EventFlag(u64 pattern, u32 protocol, int type)
: flags(pattern)
, m_protocol(protocol)
, m_type(type)
enum EventSourceKey : u64
{
SYS_SPU_THREAD_EVENT_USER_KEY = 0xFFFFFFFF53505501,
/* SYS_SPU_THREAD_EVENT_DMA_KEY = 0xFFFFFFFF53505502, */
};
struct sys_event_queue_attr
{
be_t<u32> protocol; // SYS_SYNC_PRIORITY or SYS_SYNC_FIFO
be_t<int> type; // SYS_PPU_QUEUE or SYS_SPU_QUEUE
union
{
char name[8];
u64 name_u64;
};
};
struct sys_event_data
{
be_t<u64> source;
be_t<u64> data1;
be_t<u64> data2;
be_t<u64> data3;
};
struct EventQueue;
struct EventPort
{
u64 name; // generated or user-specified code that is passed to sys_event_data struct
EventQueue* eq; // event queue this port has been connected to
std::mutex m_mutex; // may be locked until the event sending is finished
EventPort(u64 name = 0)
: eq(nullptr)
, name(name)
{
}
};
u32 check()
class EventRingBuffer
{
std::vector<sys_event_data> data;
std::mutex m_mutex;
u32 buf_pos;
u32 buf_count;
public:
const u32 size;
EventRingBuffer(u32 size)
: size(size)
, buf_pos(0)
, buf_count(0)
{
SleepQueue sq; // TODO: implement without SleepQueue
data.resize(size);
}
u32 target = 0;
void clear()
{
std::lock_guard<std::mutex> lock(m_mutex);
buf_count = 0;
buf_pos = 0;
}
for (u32 i = 0; i < waiters.size(); i++)
bool push(u64 name, u64 d1, u64 d2, u64 d3)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (buf_count >= size) return false;
sys_event_data& ref = data[(buf_pos + buf_count++) % size];
ref.source = name;
ref.data1 = d1;
ref.data2 = d2;
ref.data3 = d3;
return true;
}
bool pop(sys_event_data& ref)
{
std::lock_guard<std::mutex> lock(m_mutex);
if (!buf_count) return false;
sys_event_data& from = data[buf_pos];
buf_pos = (buf_pos + 1) % size;
buf_count--;
ref.source = from.source;
ref.data1 = from.data1;
ref.data2 = from.data2;
ref.data3 = from.data3;
return true;
}
u32 pop_all(sys_event_data* ptr, u32 max)
{
std::lock_guard<std::mutex> lock(m_mutex);
u32 res = 0;
while (buf_count && max)
{
if (((waiters[i].mode & SYS_EVENT_FLAG_WAIT_AND) && (flags & waiters[i].bitptn) == waiters[i].bitptn) ||
((waiters[i].mode & SYS_EVENT_FLAG_WAIT_OR) && (flags & waiters[i].bitptn)))
sys_event_data& from = data[buf_pos];
ptr->source = from.source;
ptr->data1 = from.data1;
ptr->data2 = from.data2;
ptr->data3 = from.data3;
buf_pos = (buf_pos + 1) % size;
buf_count--;
max--;
ptr++;
res++;
}
return res;
}
u32 count() const
{
return buf_count;
}
};
class EventPortList
{
std::vector<EventPort*> data;
std::mutex m_mutex;
public:
void clear()
{
std::lock_guard<std::mutex> lock(m_mutex);
for (u32 i = 0; i < data.size(); i++)
{
std::lock_guard<std::mutex> lock2(data[i]->m_mutex);
data[i]->eq = nullptr; // force all ports to disconnect
}
data.clear();
}
void add(EventPort* port)
{
std::lock_guard<std::mutex> lock(m_mutex);
data.push_back(port);
}
void remove(EventPort* port)
{
std::lock_guard<std::mutex> lock(m_mutex);
for (u32 i = 0; i < data.size(); i++)
{
if (data[i] == port)
{
if (m_protocol == SYS_SYNC_FIFO)
{
target = waiters[i].tid;
break;
}
sq.list.push_back(waiters[i].tid);
data.erase(data.begin() + i);
return;
}
}
}
};
if (m_protocol == SYS_SYNC_PRIORITY)
{
target = sq.pop_prio();
}
struct EventQueue
{
SleepQueue sq;
EventPortList ports;
EventRingBuffer events;
SMutex owner;
return target;
const union
{
u64 name_u64;
char name[8];
};
const u32 protocol;
const int type;
const u64 key;
EventQueue(u32 protocol, int type, u64 name, u64 key, int size)
: type(type)
, protocol(protocol)
, name_u64(name)
, key(key)
, events(size) // size: max event count this queue can hold
{
}
};
@ -87,12 +226,3 @@ s32 sys_event_port_destroy(u32 eport_id);
s32 sys_event_port_connect_local(u32 event_port_id, u32 event_queue_id);
s32 sys_event_port_disconnect(u32 eport_id);
s32 sys_event_port_send(u32 event_port_id, u64 data1, u64 data2, u64 data3);
s32 sys_event_flag_create(mem32_t eflag_id, mem_ptr_t<sys_event_flag_attr> attr, u64 init);
s32 sys_event_flag_destroy(u32 eflag_id);
s32 sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result, u64 timeout);
s32 sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result);
s32 sys_event_flag_set(u32 eflag_id, u64 bitptn);
s32 sys_event_flag_clear(u32 eflag_id, u64 bitptn);
s32 sys_event_flag_cancel(u32 eflag_id, mem32_t num);
s32 sys_event_flag_get(u32 eflag_id, mem64_t flags);

View file

@ -0,0 +1,329 @@
#include "stdafx.h"
#include "Utilities/Log.h"
#include "Emu/Memory/Memory.h"
#include "Emu/System.h"
#include "Emu/Cell/PPUThread.h"
#include "Emu/SysCalls/SysCalls.h"
#include "sys_event_flag.h"
SysCallBase sys_event_flag("sys_event_flag");
s32 sys_event_flag_create(mem32_t eflag_id, mem_ptr_t<sys_event_flag_attr> attr, u64 init)
{
sys_event_flag.Warning("sys_event_flag_create(eflag_id_addr=0x%x, attr_addr=0x%x, init=0x%llx)",
eflag_id.GetAddr(), attr.GetAddr(), init);
switch (attr->protocol.ToBE())
{
case se32(SYS_SYNC_PRIORITY): break;
case se32(SYS_SYNC_RETRY): sys_event_flag.Todo("sys_event_flag_create(): SYS_SYNC_RETRY"); break;
case se32(SYS_SYNC_PRIORITY_INHERIT): sys_event_flag.Todo("sys_event_flag_create(): SYS_SYNC_PRIORITY_INHERIT"); break;
case se32(SYS_SYNC_FIFO): break;
default: return CELL_EINVAL;
}
if (attr->pshared.ToBE() != se32(0x200))
{
return CELL_EINVAL;
}
switch (attr->type.ToBE())
{
case se32(SYS_SYNC_WAITER_SINGLE): break;
case se32(SYS_SYNC_WAITER_MULTIPLE): break;
default: return CELL_EINVAL;
}
eflag_id = sys_event_flag.GetNewId(new EventFlag(init, (u32)attr->protocol, (int)attr->type));
sys_event_flag.Warning("*** event_flag created [%s] (protocol=0x%x, type=0x%x): id = %d",
std::string(attr->name, 8).c_str(), (u32)attr->protocol, (int)attr->type, eflag_id.GetValue());
return CELL_OK;
}
s32 sys_event_flag_destroy(u32 eflag_id)
{
sys_event_flag.Warning("sys_event_flag_destroy(eflag_id=%d)", eflag_id);
EventFlag* ef;
if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH;
if (ef->waiters.size()) // ???
{
return CELL_EBUSY;
}
Emu.GetIdManager().RemoveID(eflag_id);
return CELL_OK;
}
s32 sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result, u64 timeout)
{
sys_event_flag.Log("sys_event_flag_wait(eflag_id=%d, bitptn=0x%llx, mode=0x%x, result_addr=0x%x, timeout=%lld)",
eflag_id, bitptn, mode, result.GetAddr(), timeout);
if (result.GetAddr()) 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;
}
EventFlag* ef;
if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH;
u32 tid = GetCurrentPPUThread().GetId();
{
SMutexLocker lock(ef->m_mutex);
if (ef->m_type == SYS_SYNC_WAITER_SINGLE && ef->waiters.size() > 0)
{
return CELL_EPERM;
}
EventFlagWaiter rec;
rec.bitptn = bitptn;
rec.mode = mode;
rec.tid = tid;
ef->waiters.push_back(rec);
if (ef->check() == tid)
{
u64 flags = ef->flags;
ef->waiters.erase(ef->waiters.end() - 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.GetAddr()) result = flags;
return CELL_OK;
}
}
u32 counter = 0;
const u32 max_counter = timeout ? (timeout / 1000) : ~0;
while (true)
{
if (ef->signal.unlock(tid, tid) == SMR_OK)
{
SMutexLocker lock(ef->m_mutex);
u64 flags = ef->flags;
for (u32 i = 0; i < ef->waiters.size(); i++)
{
if (ef->waiters[i].tid == tid)
{
ef->waiters.erase(ef->waiters.begin() + i);
if (mode & SYS_EVENT_FLAG_WAIT_CLEAR)
{
ef->flags &= ~bitptn;
}
else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL)
{
ef->flags = 0;
}
if (u32 target = ef->check())
{
// if signal, leave both mutexes locked...
ef->signal.unlock(tid, target);
ef->m_mutex.unlock(tid, target);
}
else
{
ef->signal.unlock(tid);
}
if (result.GetAddr()) result = flags;
return CELL_OK;
}
}
ef->signal.unlock(tid);
return CELL_ECANCELED;
}
std::this_thread::sleep_for(std::chrono::milliseconds(1));
if (counter++ > max_counter)
{
SMutexLocker lock(ef->m_mutex);
for (u32 i = 0; i < ef->waiters.size(); i++)
{
if (ef->waiters[i].tid == tid)
{
ef->waiters.erase(ef->waiters.begin() + i);
break;
}
}
return CELL_ETIMEDOUT;
}
if (Emu.IsStopped())
{
LOG_WARNING(HLE, "sys_event_flag_wait(id=%d) aborted", eflag_id);
return CELL_OK;
}
}
}
s32 sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result)
{
sys_event_flag.Log("sys_event_flag_trywait(eflag_id=%d, bitptn=0x%llx, mode=0x%x, result_addr=0x%x)",
eflag_id, bitptn, mode, result.GetAddr());
if (result.GetAddr()) 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;
}
EventFlag* ef;
if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH;
SMutexLocker lock(ef->m_mutex);
u64 flags = ef->flags;
if (((mode & SYS_EVENT_FLAG_WAIT_AND) && (flags & bitptn) == bitptn) ||
((mode & SYS_EVENT_FLAG_WAIT_OR) && (flags & bitptn)))
{
if (mode & SYS_EVENT_FLAG_WAIT_CLEAR)
{
ef->flags &= ~bitptn;
}
else if (mode & SYS_EVENT_FLAG_WAIT_CLEAR_ALL)
{
ef->flags = 0;
}
if (result.GetAddr()) result = flags;
return CELL_OK;
}
return CELL_EBUSY;
}
s32 sys_event_flag_set(u32 eflag_id, u64 bitptn)
{
sys_event_flag.Log("sys_event_flag_set(eflag_id=%d, bitptn=0x%llx)", eflag_id, bitptn);
EventFlag* ef;
if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH;
u32 tid = GetCurrentPPUThread().GetId();
ef->m_mutex.lock(tid);
ef->flags |= bitptn;
if (u32 target = ef->check())
{
// if signal, leave both mutexes locked...
ef->signal.lock(target);
ef->m_mutex.unlock(tid, target);
}
else
{
ef->m_mutex.unlock(tid);
}
return CELL_OK;
}
s32 sys_event_flag_clear(u32 eflag_id, u64 bitptn)
{
sys_event_flag.Log("sys_event_flag_clear(eflag_id=%d, bitptn=0x%llx)", eflag_id, bitptn);
EventFlag* ef;
if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH;
SMutexLocker lock(ef->m_mutex);
ef->flags &= bitptn;
return CELL_OK;
}
s32 sys_event_flag_cancel(u32 eflag_id, mem32_t num)
{
sys_event_flag.Log("sys_event_flag_cancel(eflag_id=%d, num_addr=0x%x)", eflag_id, num.GetAddr());
EventFlag* ef;
if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH;
std::vector<u32> tids;
{
SMutexLocker lock(ef->m_mutex);
tids.resize(ef->waiters.size());
for (u32 i = 0; i < ef->waiters.size(); i++)
{
tids[i] = ef->waiters[i].tid;
}
ef->waiters.clear();
}
for (u32 i = 0; i < tids.size(); i++)
{
ef->signal.lock(tids[i]);
}
if (Emu.IsStopped())
{
LOG_WARNING(HLE, "sys_event_flag_cancel(id=%d) aborted", eflag_id);
return CELL_OK;
}
if (num.GetAddr()) num = tids.size();
return CELL_OK;
}
s32 sys_event_flag_get(u32 eflag_id, mem64_t flags)
{
sys_event_flag.Log("sys_event_flag_get(eflag_id=%d, flags_addr=0x%x)", eflag_id, flags.GetAddr());
EventFlag* ef;
if (!sys_event_flag.CheckId(eflag_id, ef)) return CELL_ESRCH;
SMutexLocker lock(ef->m_mutex);
flags = ef->flags;
return CELL_OK;
}

View file

@ -0,0 +1,84 @@
#pragma once
enum
{
SYS_SYNC_WAITER_SINGLE = 0x10000,
SYS_SYNC_WAITER_MULTIPLE = 0x20000,
SYS_EVENT_FLAG_WAIT_AND = 0x01,
SYS_EVENT_FLAG_WAIT_OR = 0x02,
SYS_EVENT_FLAG_WAIT_CLEAR = 0x10,
SYS_EVENT_FLAG_WAIT_CLEAR_ALL = 0x20,
};
struct sys_event_flag_attr
{
be_t<u32> protocol;
be_t<u32> pshared;
be_t<u64> ipc_key;
be_t<int> flags;
be_t<int> type;
char name[8];
};
struct EventFlagWaiter
{
u32 tid;
u32 mode;
u64 bitptn;
};
struct EventFlag
{
SMutex m_mutex;
u64 flags;
std::vector<EventFlagWaiter> waiters;
SMutex signal;
const u32 m_protocol;
const int m_type;
EventFlag(u64 pattern, u32 protocol, int type)
: flags(pattern)
, m_protocol(protocol)
, m_type(type)
{
}
u32 check()
{
SleepQueue sq; // TODO: implement without SleepQueue
u32 target = 0;
for (u32 i = 0; i < waiters.size(); i++)
{
if (((waiters[i].mode & SYS_EVENT_FLAG_WAIT_AND) && (flags & waiters[i].bitptn) == waiters[i].bitptn) ||
((waiters[i].mode & SYS_EVENT_FLAG_WAIT_OR) && (flags & waiters[i].bitptn)))
{
if (m_protocol == SYS_SYNC_FIFO)
{
target = waiters[i].tid;
break;
}
sq.list.push_back(waiters[i].tid);
}
}
if (m_protocol == SYS_SYNC_PRIORITY)
{
target = sq.pop_prio();
}
return target;
}
};
s32 sys_event_flag_create(mem32_t eflag_id, mem_ptr_t<sys_event_flag_attr> attr, u64 init);
s32 sys_event_flag_destroy(u32 eflag_id);
s32 sys_event_flag_wait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result, u64 timeout);
s32 sys_event_flag_trywait(u32 eflag_id, u64 bitptn, u32 mode, mem64_t result);
s32 sys_event_flag_set(u32 eflag_id, u64 bitptn);
s32 sys_event_flag_clear(u32 eflag_id, u64 bitptn);
s32 sys_event_flag_cancel(u32 eflag_id, mem32_t num);
s32 sys_event_flag_get(u32 eflag_id, mem64_t flags);

View file

@ -95,6 +95,7 @@
<ClCompile Include="Emu\SysCalls\lv2\lv2Fs.cpp" />
<ClCompile Include="Emu\SysCalls\lv2\sys_cond.cpp" />
<ClCompile Include="Emu\SysCalls\lv2\sys_event.cpp" />
<ClCompile Include="Emu\SysCalls\lv2\sys_event_flag.cpp" />
<ClCompile Include="Emu\SysCalls\lv2\sys_interrupt.cpp" />
<ClCompile Include="Emu\SysCalls\lv2\sys_lwcond.cpp" />
<ClCompile Include="Emu\SysCalls\lv2\sys_lwmutex.cpp" />
@ -321,6 +322,7 @@
<ClInclude Include="Emu\SysCalls\lv2\lv2Fs.h" />
<ClInclude Include="Emu\SysCalls\lv2\sys_cond.h" />
<ClInclude Include="Emu\SysCalls\lv2\sys_event.h" />
<ClInclude Include="Emu\SysCalls\lv2\sys_event_flag.h" />
<ClInclude Include="Emu\SysCalls\lv2\sys_interrupt.h" />
<ClInclude Include="Emu\SysCalls\lv2\sys_lwcond.h" />
<ClInclude Include="Emu\SysCalls\lv2\sys_lwmutex.h" />
@ -568,4 +570,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
</Project>

View file

@ -587,6 +587,9 @@
<ClCompile Include="Emu\RSX\RSXThread.cpp">
<Filter>Emu\RSX</Filter>
</ClCompile>
<ClCompile Include="Emu\SysCalls\lv2\sys_event_flag.cpp">
<Filter>Emu\SysCalls\lv2</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Crypto\aes.h">
@ -1117,5 +1120,8 @@
<ClInclude Include="Emu\RSX\GCM.h">
<Filter>Emu\RSX</Filter>
</ClInclude>
<ClInclude Include="Emu\SysCalls\lv2\sys_event_flag.h">
<Filter>Emu\SysCalls\lv2</Filter>
</ClInclude>
</ItemGroup>
</Project>
</Project>