mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-04-20 22:05:12 +00:00
kernel: umtx: implement umtx_wake2_umutex
fixed shared unlock
This commit is contained in:
parent
c029923739
commit
091a9eec26
3 changed files with 102 additions and 33 deletions
|
|
@ -52,6 +52,8 @@ inline constexpr auto kUmtxOpSemWait = 19;
|
||||||
inline constexpr auto kUmtxOpSemWake = 20;
|
inline constexpr auto kUmtxOpSemWake = 20;
|
||||||
inline constexpr auto kUmtxOpNwakePrivate = 21;
|
inline constexpr auto kUmtxOpNwakePrivate = 21;
|
||||||
inline constexpr auto kUmtxOpMutexWake2 = 22;
|
inline constexpr auto kUmtxOpMutexWake2 = 22;
|
||||||
|
inline constexpr auto kUmtxOpMutexWake3 = 23;
|
||||||
|
|
||||||
|
|
||||||
inline constexpr auto kSemNamed = 2;
|
inline constexpr auto kSemNamed = 2;
|
||||||
|
|
||||||
|
|
@ -114,6 +116,6 @@ ErrorCode umtx_sem_wait(Thread *thread, ptr<usem> sem, std::uint64_t ut);
|
||||||
ErrorCode umtx_sem_wake(Thread *thread, ptr<usem> sem);
|
ErrorCode umtx_sem_wake(Thread *thread, ptr<usem> sem);
|
||||||
ErrorCode umtx_nwake_private(Thread *thread, ptr<void *> uaddrs,
|
ErrorCode umtx_nwake_private(Thread *thread, ptr<void *> uaddrs,
|
||||||
std::int64_t count);
|
std::int64_t count);
|
||||||
ErrorCode umtx_wake2_umutex(Thread *thread, ptr<void> obj, std::int64_t val,
|
ErrorCode umtx_wake2_umutex(Thread *thread, ptr<umutex> m, sint wakeFlags);
|
||||||
ptr<void> uaddr1, ptr<void> uaddr2);
|
ErrorCode umtx_wake3_umutex(Thread *thread, ptr<umutex> m, sint wakeFlags);
|
||||||
} // namespace orbis
|
} // namespace orbis
|
||||||
|
|
@ -168,11 +168,9 @@ orbis::SysResult orbis::sys__umtx_op(Thread *thread, ptr<void> obj, sint op,
|
||||||
case kUmtxOpNwakePrivate:
|
case kUmtxOpNwakePrivate:
|
||||||
return umtx_nwake_private(thread, (ptr<void *>)obj, val);
|
return umtx_nwake_private(thread, (ptr<void *>)obj, val);
|
||||||
case kUmtxOpMutexWake2:
|
case kUmtxOpMutexWake2:
|
||||||
return umtx_wake2_umutex(thread, obj, val, uaddr1, uaddr2);
|
return umtx_wake2_umutex(thread, (orbis::ptr<orbis::umutex>)obj, val);
|
||||||
case 23:
|
case kUmtxOpMutexWake3:
|
||||||
ORBIS_LOG_ERROR("sys__umtx_op: unknown wake operation", op, val, uaddr1, uaddr2);
|
return umtx_wake3_umutex(thread, (orbis::ptr<orbis::umutex>)obj, val);
|
||||||
// thread->where();
|
|
||||||
return umtx_wake_umutex(thread, (orbis::ptr<orbis::umutex>)obj, val);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ErrorCode::INVAL;
|
return ErrorCode::INVAL;
|
||||||
|
|
|
||||||
|
|
@ -41,15 +41,6 @@ uint UmtxChain::notify_n(const UmtxKey &key, sint count) {
|
||||||
|
|
||||||
uint n = 0;
|
uint n = 0;
|
||||||
while (count > 0) {
|
while (count > 0) {
|
||||||
while (true) {
|
|
||||||
auto flags = it->second.thr->suspendFlags.load();
|
|
||||||
if (~flags & kThreadSuspendFlag) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
orbis::scoped_unblock unblock;
|
|
||||||
it->second.thr->suspendFlags.wait(flags);
|
|
||||||
}
|
|
||||||
it->second.thr = nullptr;
|
it->second.thr = nullptr;
|
||||||
it->second.cv.notify_all(mtx);
|
it->second.cv.notify_all(mtx);
|
||||||
it = erase(it);
|
it = erase(it);
|
||||||
|
|
@ -57,7 +48,7 @@ uint UmtxChain::notify_n(const UmtxKey &key, sint count) {
|
||||||
n++;
|
n++;
|
||||||
count--;
|
count--;
|
||||||
|
|
||||||
if (it->first != key || it == sleep_queue.end()) {
|
if (it == sleep_queue.end() || it->first != key) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -211,7 +202,8 @@ static ErrorCode do_lock_normal(Thread *thread, ptr<umutex> m, uint flags,
|
||||||
orbis::scoped_unblock unblock;
|
orbis::scoped_unblock unblock;
|
||||||
error = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut));
|
error = orbis::toErrorCode(node->second.cv.wait(chain.mtx, ut));
|
||||||
}
|
}
|
||||||
if (error == ErrorCode{} && !isSpuriousWakeup(error) && node->second.thr == thread && m->owner.load() != 0) {
|
if (error == ErrorCode{} && !isSpuriousWakeup(error) &&
|
||||||
|
node->second.thr == thread && m->owner.load() != 0) {
|
||||||
error = ErrorCode::TIMEDOUT;
|
error = ErrorCode::TIMEDOUT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -244,17 +236,20 @@ static ErrorCode do_unlock_normal(Thread *thread, ptr<umutex> m, uint flags) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::size_t count = chain.sleep_queue.count(key);
|
std::size_t count = chain.sleep_queue.count(key);
|
||||||
bool ok = m->owner.compare_exchange_strong(
|
bool ok;
|
||||||
owner, count <= 1 ? kUmutexUnowned : kUmutexContested);
|
|
||||||
if (key.pid == 0) {
|
if (key.pid == 0) {
|
||||||
|
ok = m->owner.compare_exchange_strong(owner, kUmutexUnowned);
|
||||||
// IPC workaround (TODO)
|
// IPC workaround (TODO)
|
||||||
chain.notify_all(key);
|
chain.notify_all(key);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
return ErrorCode::INVAL;
|
return ErrorCode::INVAL;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (count)
|
|
||||||
chain.notify_all(key);
|
ok = m->owner.compare_exchange_strong(owner, count <= 1 ? kUmutexUnowned
|
||||||
|
: kUmutexContested);
|
||||||
|
chain.notify_one(key);
|
||||||
|
|
||||||
if (!ok)
|
if (!ok)
|
||||||
return ErrorCode::INVAL;
|
return ErrorCode::INVAL;
|
||||||
return {};
|
return {};
|
||||||
|
|
@ -718,23 +713,26 @@ orbis::ErrorCode orbis::umtx_wait_umutex(Thread *thread, ptr<umutex> m,
|
||||||
|
|
||||||
orbis::ErrorCode orbis::umtx_wake_umutex(Thread *thread, ptr<umutex> m,
|
orbis::ErrorCode orbis::umtx_wake_umutex(Thread *thread, ptr<umutex> m,
|
||||||
sint wakeFlags) {
|
sint wakeFlags) {
|
||||||
ORBIS_LOG_TRACE(__FUNCTION__, m);
|
ORBIS_LOG_TRACE(__FUNCTION__, thread->tid, m);
|
||||||
int owner = m->owner.load(std::memory_order::acquire);
|
|
||||||
if ((owner & ~kUmutexContested) != 0)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
uint flags;
|
uint flags;
|
||||||
if (ErrorCode err = uread(flags, &m->flags); err != ErrorCode{})
|
if (ErrorCode err = uread(flags, &m->flags); err != ErrorCode{})
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
auto [chain, key, lock] = g_context.getUmtxChain1(thread, flags, m);
|
auto [chain, key, lock] = g_context.getUmtxChain1(thread, flags, m);
|
||||||
|
|
||||||
|
int owner = m->owner.load(std::memory_order::acquire);
|
||||||
|
if ((owner & ~kUmutexContested) != 0)
|
||||||
|
return {};
|
||||||
|
|
||||||
std::size_t count = chain.sleep_queue.count(key);
|
std::size_t count = chain.sleep_queue.count(key);
|
||||||
if (count <= 1) {
|
if (count <= 1) {
|
||||||
owner = kUmutexContested;
|
owner = kUmutexContested;
|
||||||
m->owner.compare_exchange_strong(owner, kUmutexUnowned);
|
m->owner.compare_exchange_strong(owner, kUmutexUnowned);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (count != 0 && (owner & ~kUmutexContested) == 0) {
|
if (count != 0 && (owner & ~kUmutexContested) == 0) {
|
||||||
if ((wakeFlags & 0x400) || (flags & 1)) {
|
if (flags & 1) {
|
||||||
chain.notify_all(key);
|
chain.notify_all(key);
|
||||||
} else {
|
} else {
|
||||||
chain.notify_one(key);
|
chain.notify_one(key);
|
||||||
|
|
@ -822,10 +820,81 @@ orbis::ErrorCode orbis::umtx_nwake_private(Thread *thread, ptr<void *> uaddrs,
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
orbis::ErrorCode orbis::umtx_wake2_umutex(Thread *thread, ptr<void> obj,
|
orbis::ErrorCode orbis::umtx_wake2_umutex(Thread *thread, ptr<umutex> m,
|
||||||
std::int64_t val, ptr<void> uaddr1,
|
sint wakeFlags) {
|
||||||
ptr<void> uaddr2) {
|
ORBIS_LOG_NOTICE(__FUNCTION__, thread->tid, m);
|
||||||
ORBIS_LOG_TODO(__FUNCTION__, obj, val, uaddr1, uaddr2);
|
|
||||||
std::abort();
|
uint flags;
|
||||||
return ErrorCode::NOSYS;
|
if (ErrorCode err = uread(flags, &m->flags); err != ErrorCode{})
|
||||||
|
return err;
|
||||||
|
|
||||||
|
auto [chain, key, lock] = g_context.getUmtxChain1(thread, wakeFlags & 1, m);
|
||||||
|
|
||||||
|
int owner = 0;
|
||||||
|
|
||||||
|
std::size_t count = chain.sleep_queue.count(key);
|
||||||
|
|
||||||
|
if (count > 1) {
|
||||||
|
owner = m->owner.load(std::memory_order::acquire);
|
||||||
|
|
||||||
|
while ((owner & kUmutexContested) == 0) {
|
||||||
|
if (m->owner.compare_exchange_weak(owner, owner | kUmutexContested)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (count == 1) {
|
||||||
|
owner = m->owner.load(std::memory_order::acquire);
|
||||||
|
|
||||||
|
while ((owner & ~kUmutexContested) != 0 &&
|
||||||
|
(owner & kUmutexContested) == 0) {
|
||||||
|
if (m->owner.compare_exchange_weak(owner, owner | kUmutexContested)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count != 0 && (owner & ~kUmutexContested) == 0) {
|
||||||
|
chain.notify_one(key);
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
orbis::ErrorCode orbis::umtx_wake3_umutex(Thread *thread, ptr<umutex> m,
|
||||||
|
sint wakeFlags) {
|
||||||
|
ORBIS_LOG_NOTICE(__FUNCTION__, thread->tid, m);
|
||||||
|
|
||||||
|
uint flags;
|
||||||
|
if (ErrorCode err = uread(flags, &m->flags); err != ErrorCode{})
|
||||||
|
return err;
|
||||||
|
|
||||||
|
auto [chain, key, lock] = g_context.getUmtxChain1(thread, wakeFlags & 1, m);
|
||||||
|
|
||||||
|
int owner = 0;
|
||||||
|
std::size_t count = chain.sleep_queue.count(key);
|
||||||
|
|
||||||
|
if (count > 1) {
|
||||||
|
owner = m->owner.load(std::memory_order::acquire);
|
||||||
|
|
||||||
|
while ((owner & kUmutexContested) == 0) {
|
||||||
|
if (m->owner.compare_exchange_weak(owner, owner | kUmutexContested)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (count == 1) {
|
||||||
|
owner = m->owner.load(std::memory_order::acquire);
|
||||||
|
|
||||||
|
while ((owner & ~kUmutexContested) != 0 &&
|
||||||
|
(owner & kUmutexContested) == 0) {
|
||||||
|
if (m->owner.compare_exchange_weak(owner, owner | kUmutexContested)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count != 0 && (owner & ~kUmutexContested) == 0) {
|
||||||
|
chain.notify_one(key);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue