Minor fixes

This commit is contained in:
Nekotekina 2016-08-20 00:14:10 +03:00
parent e32c49c12c
commit df6607e236
52 changed files with 309 additions and 282 deletions

View file

@ -812,15 +812,6 @@ struct psp2_event_flag final
static const u32 id_step = 1;
static const u32 id_count = 32767;
struct alignas(8) ctrl_t
{
u32 waiters;
u32 pattern;
};
atomic_t<ctrl_t> ctrl; // Sync variable
atomic_t<u64> wait_ctr{}; // FIFO ordering helper
using ipc = ipc_manager<psp2_event_flag, std::string>;
const std::string name; // IPC/Debug Name
@ -829,11 +820,15 @@ struct psp2_event_flag final
const u32 attr;
const u32 init;
atomic_t<u32> pattern{0}; // Sync variable
atomic_t<u32> waiters{0}; // Waiter number or waiter id
atomic_t<u64> wait_ctr; // FIFO ordering helper
psp2_event_flag(std::string&& name, u32 attr, u32 pattern)
: ctrl({0, pattern})
, name(std::move(name))
: name(std::move(name))
, attr(attr)
, init(pattern)
, pattern(pattern)
{
}
@ -856,14 +851,15 @@ struct psp2_event_flag final
// Commands
enum class task : u32
{
null = 0,
wait,
poll,
set,
clear,
cancel,
destroy,
signal,
null,
wait, // Check condition, enqueue waiting thread
poll, // Check condition only
set, // Set pattern bits and wake up threads
clear, // Clear pattern bits (bitwise AND)
cancel, // Wake up all threads with SCE_KERNEL_ERROR_WAIT_CANCEL
destroy, // Wake up all threads with SCE_KERNEL_ERROR_WAIT_DELETE
timeout, // Dequeue waiting thread (cleanup)
signal, // Signal selected thread (aux)
};
struct alignas(8) cmd_t
@ -907,13 +903,29 @@ struct psp2_event_flag final
case task::wait: op_wait(cmd.arg); break;
case task::poll: op_poll(cmd.arg); break;
case task::set: op_set(cmd.arg); break;
case task::clear: op_clear(cmd.arg); break;
case task::clear: pattern &= cmd.arg; break;
case task::cancel: op_stop(vm::cast(cmd.arg), SCE_KERNEL_ERROR_WAIT_CANCEL); break;
case task::destroy: op_stop(vm::cast(cmd.arg), SCE_KERNEL_ERROR_WAIT_DELETE); break;
case task::timeout:
{
// Timeout cleanup
idm::check<ARMv7Thread>(cmd.arg, [&](ARMv7Thread& cpu)
{
if (cpu.owner.compare_and_swap_test(this, nullptr))
{
cpu.GPR[0] = SCE_KERNEL_ERROR_WAIT_TIMEOUT;
cpu.GPR[1] = pattern;
waiters -= attr & SCE_KERNEL_ATTR_MULTI ? 1 : cpu.id;
}
});
break;
}
case task::signal:
{
idm::get<ARMv7Thread>(cmd.arg, [&](ARMv7Thread& cpu)
idm::check<ARMv7Thread>(cmd.arg, [](auto& cpu)
{
cpu.state += cpu_flag::signal;
cpu.lock_notify();
@ -955,28 +967,40 @@ struct psp2_event_flag final
private:
lf_fifo<atomic_t<cmd_t>, 16> m_workload;
// Check condition
void op_wait(u32 thread_id)
{
idm::get<ARMv7Thread>(thread_id, [&](ARMv7Thread& cpu)
idm::check<ARMv7Thread>(thread_id, [=](ARMv7Thread& cpu)
{
const u32 pattern = ctrl.atomic_op([&](psp2_event_flag::ctrl_t& state) -> u32
if (attr & SCE_KERNEL_ATTR_MULTI)
{
const u32 pat = state.pattern;
waiters++;
}
else if (!waiters.compare_and_swap_test(0, thread_id))
{
cpu.GPR[0] = SCE_KERNEL_ERROR_EVF_MULTI;
cpu.GPR[1] = pattern;
cpu.state += cpu_flag::signal;
cpu->lock_notify();
return;
}
const u32 old_pattern = pattern.atomic_op([&](u32& value) -> u32
{
const u32 pat = value;
if (pat_test(pat, cpu.GPR[1], cpu.GPR[0]))
{
state.pattern &= pat_clear(cpu.GPR[1], cpu.GPR[0]);
state.waiters -= attr & SCE_KERNEL_ATTR_MULTI ? 1 : cpu.id;
value &= pat_clear(cpu.GPR[1], cpu.GPR[0]);
return pat;
}
return 0;
});
if (pattern)
if (old_pattern)
{
waiters -= attr & SCE_KERNEL_ATTR_MULTI ? 1 : cpu.id;
cpu.GPR[0] = SCE_OK;
cpu.GPR[1] = pattern;
cpu.GPR[1] = old_pattern;
cpu.state += cpu_flag::signal;
cpu->lock_notify();
}
@ -988,17 +1012,16 @@ private:
});
}
// Check condition
void op_poll(u32 thread_id)
{
idm::get<ARMv7Thread>(thread_id, [&](ARMv7Thread& cpu)
idm::check<ARMv7Thread>(thread_id, [&](ARMv7Thread& cpu)
{
cpu.GPR[1] = ctrl.atomic_op([&](psp2_event_flag::ctrl_t& state) -> u32
cpu.GPR[1] = pattern.atomic_op([&](u32& value) -> u32
{
const u32 pat = state.pattern;
const u32 pat = value;
if (pat_test(pat, cpu.GPR[1], cpu.GPR[0]))
{
state.pattern &= pat_clear(cpu.GPR[1], cpu.GPR[0]);
value &= pat_clear(cpu.GPR[1], cpu.GPR[0]);
return pat;
}
@ -1007,26 +1030,24 @@ private:
});
}
// Set pattern bits and wake up threads
void op_set(u32 pattern)
void op_set(u32 _pattern)
{
const auto new_state = ctrl.op_fetch([&](psp2_event_flag::ctrl_t& state)
{
state.pattern |= pattern;
});
pattern |= _pattern;
if (new_state.waiters)
if (const u32 _waiters = waiters)
{
const u32 new_pattern = pattern;
std::vector<std::reference_wrapper<ARMv7Thread>> threads;
// Check and lock appropriate threads
// Enumerate appropriate threads
if (attr & SCE_KERNEL_ATTR_MULTI)
{
threads.reserve(new_state.waiters);
threads.reserve(_waiters);
idm::select<ARMv7Thread>([&](u32 id, ARMv7Thread& cpu)
idm::select<ARMv7Thread>([&](u32, ARMv7Thread& cpu)
{
if (cpu->lock_if([&] { return cpu.owner == this && pat_test(new_state.pattern, cpu.GPR[1], cpu.GPR[0]); }))
if (cpu.owner == this && pat_test(new_pattern, cpu.GPR[1], cpu.GPR[0]))
{
threads.emplace_back(cpu);
}
@ -1047,9 +1068,9 @@ private:
}
else
{
idm::get<ARMv7Thread>(new_state.waiters, [&](ARMv7Thread& cpu)
idm::check<ARMv7Thread>(_waiters, [&](ARMv7Thread& cpu)
{
if (cpu->lock_if([&] { return cpu.owner == this && pat_test(new_state.pattern, cpu.GPR[1], cpu.GPR[0]); }))
if (cpu.owner == this && pat_test(new_pattern, cpu.GPR[1], cpu.GPR[0]))
{
threads.emplace_back(cpu);
}
@ -1059,14 +1080,13 @@ private:
// Wake up threads
for (ARMv7Thread& cpu : threads)
{
const u32 old_pattern = ctrl.atomic_op([&](psp2_event_flag::ctrl_t& state) -> u32
const u32 old_pattern = pattern.atomic_op([&](u32& value) -> u32
{
const u32 pat = state.pattern;
const u32 pat = value;
if (pat_test(pat, cpu.GPR[1], cpu.GPR[0]))
{
state.pattern &= pat_clear(cpu.GPR[1], cpu.GPR[0]);
state.waiters -= attr & SCE_KERNEL_ATTR_MULTI ? 1 : cpu.id;
value &= pat_clear(cpu.GPR[1], cpu.GPR[0]);
return pat;
}
@ -1079,56 +1099,43 @@ private:
cpu.GPR[1] = old_pattern;
cpu.state += cpu_flag::signal;
cpu.owner = nullptr;
cpu->unlock();
cpu->notify();
}
else
{
cpu->unlock();
waiters -= attr & SCE_KERNEL_ATTR_MULTI ? 1 : cpu.id;
cpu->lock_notify();
}
}
}
}
// Clear pattern bits (bitwise AND)
void op_clear(u32 pattern)
{
ctrl.atomic_op([&](psp2_event_flag::ctrl_t& state)
{
state.pattern &= pattern;
});
}
// Wake up all threads
void op_stop(vm::ptr<u32> ptr, s32 error)
{
s32 result = 0;
const u32 pattern = ptr ? ptr->value() : ctrl.load().pattern;
const u32 _pattern = ptr ? ptr->value() : pattern.load();
std::vector<std::reference_wrapper<ARMv7Thread>> threads;
idm::select<ARMv7Thread>([&](u32, ARMv7Thread& cpu)
{
if (cpu->lock_if([&] { return cpu.owner == this; }))
if (cpu.owner == this)
{
cpu.GPR[0] = error;
cpu.GPR[1] = pattern;
cpu.state += cpu_flag::signal;
cpu.owner = nullptr;
cpu->unlock();
cpu->notify();
result++;
threads.emplace_back(cpu);
}
});
if (ptr)
{
*ptr = result;
*ptr = static_cast<u32>(threads.size());
}
ctrl.atomic_op([&](psp2_event_flag::ctrl_t& state)
for (ARMv7Thread& cpu : threads)
{
state.pattern = pattern;
state.waiters = attr & SCE_KERNEL_ATTR_MULTI ? state.waiters - result : 0;
});
cpu.GPR[0] = error;
cpu.GPR[1] = _pattern;
cpu.state += cpu_flag::signal;
cpu.owner = nullptr;
cpu.lock_notify();
}
pattern = _pattern;
waiters = 0;
}
};
@ -1226,62 +1233,66 @@ error_code sceKernelWaitEventFlag(ARMv7Thread& cpu, s32 evfId, u32 bitPattern, u
return SCE_KERNEL_ERROR_INVALID_UID;
}
// First chance
const auto state = evf->ctrl.fetch_op([&](psp2_event_flag::ctrl_t& state)
{
if (!state.waiters && psp2_event_flag::pat_test(state.pattern, bitPattern, waitMode))
{
state.pattern &= psp2_event_flag::pat_clear(bitPattern, waitMode);
}
else if (evf->attr & SCE_KERNEL_ATTR_MULTI)
{
state.waiters++;
}
else if (!state.waiters)
{
state.waiters = cpu.id;
}
});
// First chance (TODO)
//const auto state = evf->ctrl.fetch_op([&](psp2_event_flag::ctrl_t& state)
//{
// if (!state.waiters && psp2_event_flag::pat_test(state.pattern, bitPattern, waitMode))
// {
// state.pattern &= psp2_event_flag::pat_clear(bitPattern, waitMode);
// }
// else if (evf->attr & SCE_KERNEL_ATTR_MULTI)
// {
// state.waiters++;
// }
// else if (!state.waiters)
// {
// state.waiters = cpu.id;
// }
//});
if (state.waiters && !(evf->attr & SCE_KERNEL_ATTR_MULTI))
{
return SCE_KERNEL_ERROR_EVF_MULTI;
}
//if (evf->waiters && !(evf->attr & SCE_KERNEL_ATTR_MULTI))
//{
// return SCE_KERNEL_ERROR_EVF_MULTI;
//}
if (!state.waiters && psp2_event_flag::pat_test(state.pattern, bitPattern, waitMode))
{
if (pResultPat) *pResultPat = state.pattern;
return SCE_OK;
}
//if (!state.waiters && psp2_event_flag::pat_test(state.pattern, bitPattern, waitMode))
//{
// if (pResultPat) *pResultPat = state.pattern;
// return SCE_OK;
//}
// Set register values for external use
cpu.GPR[0] = waitMode;
cpu.GPR[1] = bitPattern;
// Second chance
if (evf->exec(psp2_event_flag::task::wait, cpu.id) && cpu.state.test_and_reset(cpu_flag::signal))
if (!evf->exec(psp2_event_flag::task::wait, cpu.id) || !cpu.state.test_and_reset(cpu_flag::signal))
{
if (pResultPat) *pResultPat = cpu.GPR[1];
return SCE_OK;
thread_lock lock(cpu);
if (!thread_ctrl::wait_for(timeout, [&] { return cpu.state.test_and_reset(cpu_flag::signal); }))
{
if (!evf->exec(psp2_event_flag::task::timeout, cpu.id))
{
if (!evf->exec(psp2_event_flag::task::signal, cpu.id))
{
thread_ctrl::wait([&] { return cpu.state.test_and_reset(cpu_flag::signal); });
}
else
{
cpu.state -= cpu_flag::signal;
}
}
}
}
thread_lock entry(cpu);
if (!thread_ctrl::wait_for(timeout, [&] { return cpu.state.test_and_reset(cpu_flag::signal); }))
if (cpu.GPR[0] == SCE_KERNEL_ERROR_EVF_MULTI)
{
// Timeout cleanup
cpu.owner = nullptr;
cpu.GPR[0] = SCE_KERNEL_ERROR_WAIT_TIMEOUT;
cpu.GPR[1] = evf->ctrl.atomic_op([&](psp2_event_flag::ctrl_t& state)
{
state.waiters -= evf->attr & SCE_KERNEL_ATTR_MULTI ? 1 : cpu.id;
return state.pattern;
});
return SCE_KERNEL_ERROR_EVF_MULTI;
}
if (pResultPat) *pResultPat = cpu.GPR[1];
if (pTimeout) *pTimeout = static_cast<u32>(std::max<s64>(0, timeout - (get_system_time() - start_time)));
return not_an_error(cpu.GPR[0]);
}
@ -1303,23 +1314,17 @@ error_code sceKernelPollEventFlag(ARMv7Thread& cpu, s32 evfId, u32 bitPattern, u
return SCE_KERNEL_ERROR_INVALID_UID;
}
// First chance
const auto state = evf->ctrl.fetch_op([&](psp2_event_flag::ctrl_t& state)
{
if (!state.waiters && psp2_event_flag::pat_test(state.pattern, bitPattern, waitMode))
{
state.pattern &= psp2_event_flag::pat_clear(bitPattern, waitMode);
}
});
// First chance (TODO)
//const auto state = evf->ctrl.fetch_op([&](psp2_event_flag::ctrl_t& state)
//{
// if (!state.waiters && psp2_event_flag::pat_test(state.pattern, bitPattern, waitMode))
// {
// state.pattern &= psp2_event_flag::pat_clear(bitPattern, waitMode);
// }
//});
if (psp2_event_flag::pat_test(state.pattern, bitPattern, waitMode))
if (psp2_event_flag::pat_test(evf->pattern, bitPattern, waitMode))
{
if (!state.waiters)
{
*pResultPat = state.pattern;
return SCE_OK;
}
cpu.GPR[0] = waitMode;
cpu.GPR[1] = bitPattern;
@ -1407,11 +1412,8 @@ error_code sceKernelGetEventFlagInfo(s32 evfId, vm::ptr<SceKernelEventFlagInfo>
pInfo->attr = evf->attr;
pInfo->initPattern = evf->init;
const auto state = evf->ctrl.load();
pInfo->currentPattern = state.pattern;
pInfo->numWaitThreads = evf->attr & SCE_KERNEL_ATTR_MULTI ? state.waiters : state.waiters != 0;
pInfo->currentPattern = evf->pattern;
pInfo->numWaitThreads = evf->attr & SCE_KERNEL_ATTR_MULTI ? evf->waiters.load() : evf->waiters != 0;
return SCE_OK;
}