ksys/act: Implement BaseProcLink

This commit is contained in:
Léo Lam 2020-08-20 13:15:12 +02:00
parent f92ab2f559
commit afde5b2775
No known key found for this signature in database
GPG Key ID: 0DF30F9081000741
12 changed files with 303 additions and 163 deletions

View File

@ -35,8 +35,8 @@ add_executable(uking
src/KingSystem/ActorSystem/actBaseProcJob.h
src/KingSystem/ActorSystem/actBaseProcJobHandler.cpp
src/KingSystem/ActorSystem/actBaseProcJobHandler.h
src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.cpp
src/KingSystem/ActorSystem/actBaseProcLinkDataMgr.h
src/KingSystem/ActorSystem/actBaseProcLink.cpp
src/KingSystem/ActorSystem/actBaseProcLink.h
src/KingSystem/ActorSystem/actBaseProcMap.cpp
src/KingSystem/ActorSystem/actBaseProcMap.h
src/KingSystem/ActorSystem/actBaseProcMgr.cpp

View File

@ -74114,7 +74114,7 @@
0x0000007100dca6d4,_ZN15ActorInstParams3BufaSERS0_,56,
0x0000007100dca70c,_ZN13ActorAccessorD2Ev,56,_ZN4ksys3act24ActorLinkConstDataAccessD1Ev
0x0000007100dca744,ActorAccessor::acquire,88,_ZN4ksys3act24ActorLinkConstDataAccess7acquireEPNS0_8BaseProcE
0x0000007100dca79c,act::acquireActorFromGameOrHavokThread,256,_ZN4ksys3act11acquireProcEPNS0_24ActorLinkConstDataAccessEPNS0_8BaseProcERKN4sead14SafeStringBaseIcEE
0x0000007100dca79c,act::acquireActorFromGameOrHavokThread,256,_ZN4ksys3act11acquireProcEPNS0_24ActorLinkConstDataAccessEPNS0_8BaseProcERKN4sead14SafeStringBaseIcEEi
0x0000007100dca89c,GameFramework::ctor,96,
0x0000007100dca8fc,GameFramework::dtor,64,
0x0000007100dca93c,GameFramework::dtorDelete,72,
@ -89523,18 +89523,18 @@
0x00000071011bc42c,sub_71011BC42C,56,
0x00000071011bc464,sinitBaseProcHandle,172,
0x00000071011bc510,_ZN12BaseProcLinkC2Ev,20,_ZN4ksys3act12BaseProcLinkC1Ev
0x00000071011bc524,_ZN12BaseProcLink12acquireActorER13ActorAccessorP9ActorBase,308,
0x00000071011bc524,_ZN12BaseProcLink12acquireActorER13ActorAccessorP9ActorBase,308,_ZN4ksys3act12BaseProcLink7getProcEPNS0_24ActorLinkConstDataAccessEPNS0_8BaseProcE
0x00000071011bc658,BaseProcLinkData::lockCritSectionOnGameThreadOrHavokThread,68,_ZN4ksys3act16BaseProcLinkData12lockIfNeededEv
0x00000071011bc69c,BaseProcLinkData::checkIdAndEngaged,52,_ZNK4ksys3act16BaseProcLinkData7getProcEjb
0x00000071011bc6d0,_ZN12BaseProcLink12fromAccessorER13ActorAccessor,276,
0x00000071011bc7e4,BaseProcLink::checkHasActorById,40,
0x00000071011bc80c,_ZN12BaseProcLinkeqERS_,152,
0x00000071011bc8a4,BaseProcLink::hasAcquiredActor,100,
0x00000071011bc908,BaseProcLink::fromActor,328,
0x00000071011bca50,BaseProcLink::reset,52,
0x00000071011bca84,BaseProcLink::isStateCalc,204,
0x00000071011bcb50,BaseProcLink::isActor,228,
0x00000071011bcc34,_ZN12BaseProcLinkaSERS_,80,
0x00000071011bc6d0,_ZN12BaseProcLink12fromAccessorER13ActorAccessor,276,_ZN4ksys3act12BaseProcLink7getProcEPNS0_24ActorLinkConstDataAccessE
0x00000071011bc7e4,BaseProcLink::checkHasActorById,40,_ZNK4ksys3act12BaseProcLink11hasProcByIdEPNS0_8BaseProcE
0x00000071011bc80c,_ZN12BaseProcLinkeqERS_,152,_ZNK4ksys3act12BaseProcLinkeqERKS1_
0x00000071011bc8a4,BaseProcLink::hasAcquiredActor,100,_ZNK4ksys3act12BaseProcLink7hasProcEv
0x00000071011bc908,BaseProcLink::fromActor,328,_ZN4ksys3act12BaseProcLink7acquireEPNS0_8BaseProcEb
0x00000071011bca50,BaseProcLink::reset,52,_ZN4ksys3act12BaseProcLink5resetEv
0x00000071011bca84,BaseProcLink::isStateCalc,204,_ZNK4ksys3act12BaseProcLink18hasProcInCalcStateEv
0x00000071011bcb50,BaseProcLink::isActor,228,_ZNK4ksys3act12BaseProcLink30isAccessingSpecifiedProcUnsafeEPNS0_8BaseProcE
0x00000071011bcc34,_ZN12BaseProcLinkaSERS_,80,_ZN4ksys3act12BaseProcLinkaSERKS1_
0x00000071011bcc84,BaseProcLinkDataMgr::deleteInstance,132,_ZN4ksys3act19BaseProcLinkDataMgr18SingletonDisposer_D2Ev
0x00000071011bcd08,BaseProcLinkDataMgr::deleteInstance2,140,_ZN4ksys3act19BaseProcLinkDataMgr18SingletonDisposer_D0Ev
0x00000071011bcd94,BaseProcLinkDataMgr::createInstance,212,_ZN4ksys3act19BaseProcLinkDataMgr14createInstanceEPN4sead4HeapE

Can't render this file because it is too large.

@ -1 +1 @@
Subproject commit c53aff46fc42344958c42d0b69b27f992fc8d979
Subproject commit 9f70679d4cfbce260d967b27adf4c17f17ac8e34

View File

@ -33,7 +33,8 @@ void ActorLinkConstDataAccess::debugLog(s32, const sead::SafeString&) {
// Intentionally left empty.
}
bool acquireProc(ActorLinkConstDataAccess* accessor, BaseProc* proc, const sead::SafeString& from) {
bool acquireProc(ActorLinkConstDataAccess* accessor, BaseProc* proc, const sead::SafeString& from,
s32) {
bool acquired = false;
if (accessor) {

View File

@ -35,7 +35,8 @@ private:
/// Acquire the specified BaseProc using `accessor`. Using ActorLinkConstDataAccess is mandatory
/// when acquiring from a low priority thread (see BaseProcMgr for a definition).
bool acquireProc(ActorLinkConstDataAccess* accessor, BaseProc* proc, const sead::SafeString& from);
bool acquireProc(ActorLinkConstDataAccess* accessor, BaseProc* proc, const sead::SafeString& from,
s32 = 2);
} // namespace act

View File

@ -6,6 +6,7 @@
#include <prim/seadSafeString.h>
#include "KingSystem/ActorSystem/actBaseProc.h"
#include "KingSystem/ActorSystem/actBaseProcLink.h"
#include "KingSystem/MessageSystem/mesTransceiver.h"
#include "KingSystem/Utils/Types.h"

View File

@ -1,14 +1,12 @@
#include "KingSystem/ActorSystem/actBaseProc.h"
#include "KingSystem/ActorSystem/actBaseProcJobHandler.h"
#include "KingSystem/ActorSystem/actBaseProcLinkDataMgr.h"
#include "KingSystem/ActorSystem/actBaseProcLink.h"
#include "KingSystem/ActorSystem/actBaseProcMgr.h"
#include "KingSystem/ActorSystem/actBaseProcUnit.h"
#include "KingSystem/Terrain/teraSystem.h"
namespace ksys::act {
BaseProcLink::BaseProcLink() = default;
BaseProc::BaseProc(const CreateArg& arg)
: mName(arg.actor_name), mPriority(arg.class_info->priority) {
BaseProcMgr* mgr = BaseProcMgr::instance();

View File

@ -22,25 +22,6 @@ class ActorLinkConstDataAccess;
class BaseProc;
class BaseProcLinkData;
class BaseProcJobHandler;
class BaseProcLink {
public:
BaseProcLink();
~BaseProcLink() { reset(); }
BaseProcLink& operator=(const BaseProcLink&);
bool operator==(const BaseProcLink&) const;
bool operator!=(const BaseProcLink& rhs) const { return !operator==(rhs); }
BaseProc* fromAccessorAndActor(ActorLinkConstDataAccess&, BaseProc*);
BaseProc* fromAccessor(ActorLinkConstDataAccess&);
void reset();
private:
BaseProcLinkData* mData = nullptr;
u32 mId = -1;
bool mAcquired = false;
};
KSYS_CHECK_SIZE_NX150(BaseProcLink, 0x10);
class BaseProcUnit;
class BaseProcHandle {
@ -108,6 +89,9 @@ public:
bool deleteLater(DeleteReason reason);
void freeLinkData();
u32 getId() const { return mId; }
const sead::SafeString& getName() const { return mName; }
u8 getPriority() const { return mPriority; }
void setPriority(u8 priority) { mPriority = priority; }
@ -118,6 +102,7 @@ public:
/// For BaseProcLink or ActorLinkConstDataAccess.
bool acquire(ActorLinkConstDataAccess& accessor);
BaseProcLinkData* getBaseProcLinkData() const { return mBaseProcLinkData; }
/// For BaseProcLink or ActorLinkConstDataAccess.
void release();

View File

@ -0,0 +1,160 @@
#include "KingSystem/ActorSystem/actBaseProcLink.h"
#include <prim/seadScopedLock.h>
#include <thread/seadThread.h>
#include "KingSystem/ActorSystem/actActorLinkConstDataAccess.h"
#include "KingSystem/ActorSystem/actBaseProc.h"
#include "KingSystem/ActorSystem/actBaseProcMgr.h"
namespace ksys::act {
BaseProcLink::BaseProcLink() = default;
BaseProcLink& BaseProcLink::operator=(const BaseProcLink& rhs) {
if (this == &rhs)
return *this;
reset();
if (rhs.mData)
mData = rhs.mData;
mId = rhs.mId;
return *this;
}
bool BaseProcLink::operator==(const BaseProcLink& rhs) const {
return (!hasProc() && !rhs.hasProc()) || mId == rhs.mId;
}
bool BaseProcLink::hasProc() const {
return getProc();
}
bool BaseProcLink::hasProcInCalcState() const {
return getProcInContext(
[](BaseProc* proc) { return proc && proc->getState() == BaseProc::State::Calc; });
}
bool BaseProcLink::hasProcById(BaseProc* proc) const {
return proc != nullptr & mId != cInvalidId && mId == proc->getId();
}
BaseProc* BaseProcLink::getProc(ActorLinkConstDataAccess* accessor, BaseProc* other_proc) {
return getProcInContext([&](BaseProc* proc) -> BaseProc* {
if (proc && acquireProc(accessor, proc, "frm::BaseProcLink") &&
BaseProcMgr::instance()->isAccessingProcSafe(proc, other_proc)) {
return proc;
}
return nullptr;
});
}
BaseProc* BaseProcLink::getProc(ActorLinkConstDataAccess* accessor) {
return getProcInContext([&](BaseProc* proc) -> BaseProc* {
if (proc && acquireProc(accessor, proc, "frm::BaseProcLink"))
return proc;
return nullptr;
});
}
bool BaseProcLink::acquire(BaseProc* proc, bool acquire_immediately) {
reset();
if (!proc || proc->isDeletedOrDeleting())
return false;
mData = proc->getBaseProcLinkData();
if (!mData) {
static constexpr const char* sStateNames[] = {"Init", "Calc", "Sleep", "Delete"};
sead::FixedSafeString<256> message;
message.format("%s:%s", proc->getName().cstr(), sStateNames[u8(proc->getState())]);
return false;
}
mId = proc->getId();
if (acquire_immediately) {
mData->mRefCount.increment();
mAcquired = true;
}
return true;
}
void BaseProcLink::reset() {
if (mAcquired) {
mAcquired = false;
if (mData)
mData->mRefCount.decrement();
}
mId = cInvalidId;
}
bool BaseProcLink::isAccessingSpecifiedProcUnsafe(BaseProc* other) const {
return getProcInContext([&](BaseProc* proc) {
return proc && !BaseProcMgr::instance()->isAccessingProcSafe(proc, other);
});
}
SEAD_SINGLETON_DISPOSER_IMPL(BaseProcLinkDataMgr)
BaseProc* BaseProcLinkData::getProc(u32 id, bool allow_deleted) const {
if (id == u32(-1) || mId != id)
return nullptr;
if (!allow_deleted && (!mProc || mProc->getState() == BaseProc::State::Delete))
return nullptr;
return mProc;
}
sead::CriticalSection* BaseProcLinkData::lockIfNeeded() {
if (BaseProcMgr::instance()->isHighPriorityThread())
return nullptr;
mCS.lock();
return &mCS;
}
bool BaseProcLinkDataMgr::acquireLink(BaseProc* proc) {
auto lock = sead::makeScopedLock(mCS);
s32 index = mIndex;
for (s32 i = 0; i < mData.size(); ++i) {
const s32 j = index == 0x800 ? 0 : index;
auto& data = mData[j];
if (data.mId == u32(-1)) {
{
auto data_lock = sead::makeScopedLock(data.mCS);
data.mProc = proc;
data.mId = proc->mId;
proc->mBaseProcLinkData = &data;
}
mIndex = j + 1;
return true;
}
index = j + 1;
}
for (s32 i = 0; i < mData.size(); ++i) {
auto data_lock = sead::makeScopedLock(mData[i].mCS);
// Debug code was probably here?
}
return false;
}
bool BaseProcLinkDataMgr::releaseLink(BaseProc* proc) {
auto* data = proc->mBaseProcLinkData;
if (!data)
return false;
const auto thread = sead::ThreadMgr::instance()->getCurrentThread();
thread->getPriority();
auto lock = sead::makeScopedLock(data->mCS);
data->mProc = nullptr;
data->mId = -1;
proc->mBaseProcLinkData = nullptr;
return true;
}
} // namespace ksys::act

View File

@ -0,0 +1,119 @@
#pragma once
#include <basis/seadTypes.h>
#include <container/seadSafeArray.h>
#include <heap/seadDisposer.h>
#include <prim/seadScopeGuard.h>
#include <prim/seadScopedLock.h>
#include <thread/seadAtomic.h>
#include <thread/seadCriticalSection.h>
#include "KingSystem/Utils/Types.h"
namespace ksys::act {
class ActorLinkConstDataAccess;
class BaseProc;
class BaseProcLinkData;
class BaseProcLink {
public:
BaseProcLink();
~BaseProcLink() { reset(); }
BaseProcLink& operator=(const BaseProcLink& rhs);
bool operator==(const BaseProcLink& rhs) const;
bool operator!=(const BaseProcLink& rhs) const { return !operator==(rhs); }
explicit operator bool() const { return hasProc(); }
bool hasProc() const;
bool hasProcInCalcState() const;
bool hasProcById(BaseProc* proc) const;
BaseProc* getProc(ActorLinkConstDataAccess* accessor, BaseProc* other_proc);
BaseProc* getProc(ActorLinkConstDataAccess* accessor);
/// Acquire the specified BaseProc.
/// If a BaseProc has already been specified, it is released.
///
/// @param acquire_immediately If true, the BaseProc's reference count is immediately
/// incremented. Otherwise, full acquisition is delayed until
/// the next call to getProc(accessor[, proc]).
bool acquire(BaseProc* proc, bool acquire_immediately);
void reset();
bool isAccessingSpecifiedProcUnsafe(BaseProc* other) const;
private:
static constexpr u32 cInvalidId = -1;
BaseProc* getProc() const;
template <typename Function>
auto getProcInContext(const Function& function) const;
BaseProcLinkData* mData = nullptr;
u32 mId = cInvalidId;
bool mAcquired = false;
};
KSYS_CHECK_SIZE_NX150(BaseProcLink, 0x10);
class BaseProcLinkData {
public:
u32 id() const { return mId; }
s32 refCount() const { return mRefCount; }
/// Get the stored BaseProc.
BaseProc* getProc() const { return mProc; }
/// Get the stored BaseProc or nullptr if its ID or state are unexpected.
/// @param id The expected ID.
/// @param allow_deleted Whether to allow the BaseProc to be in the Delete state.
BaseProc* getProc(u32 id, bool allow_deleted) const;
/// Locks the critical section if on a low priority thread and returns it;
/// unlocking it is the responsibility of the caller.
/// Returns nullptr if on a high priority thread. In that case, nothing needs to be done.
sead::CriticalSection* lockIfNeeded();
private:
friend class BaseProcLink;
friend class BaseProcLinkDataMgr;
sead::CriticalSection mCS;
u32 mId = -1;
BaseProc* mProc = nullptr;
sead::Atomic<s32> mRefCount = 0;
};
KSYS_CHECK_SIZE_NX150(BaseProcLinkData, 0x58);
class BaseProcLinkDataMgr {
SEAD_SINGLETON_DISPOSER(BaseProcLinkDataMgr)
public:
bool acquireLink(BaseProc* proc);
bool releaseLink(BaseProc* proc);
private:
sead::CriticalSection mCS;
s32 mIndex = 0;
sead::SafeArray<BaseProcLinkData, 2048> mData{};
};
KSYS_CHECK_SIZE_NX150(BaseProcLinkDataMgr, 0x2C068);
inline BaseProc* BaseProcLink::getProc() const {
return mData->getProc(mId, mAcquired);
}
template <typename Function>
inline auto BaseProcLink::getProcInContext(const Function& function) const {
if (mId == cInvalidId)
return function(nullptr);
const auto guard = sead::makeScopeGuard([crit_section = mData->lockIfNeeded()] {
if (crit_section)
crit_section->unlock();
});
return function(getProc());
}
} // namespace ksys::act

View File

@ -1,71 +0,0 @@
#include "KingSystem/ActorSystem/actBaseProcLinkDataMgr.h"
#include <prim/seadScopedLock.h>
#include <thread/seadThread.h>
#include "KingSystem/ActorSystem/actBaseProc.h"
#include "KingSystem/ActorSystem/actBaseProcMgr.h"
namespace ksys::act {
SEAD_SINGLETON_DISPOSER_IMPL(BaseProcLinkDataMgr)
BaseProc* BaseProcLinkData::getProc(u32 id, bool allow_deleted) const {
if (id == u32(-1) || mId != id)
return nullptr;
if (!allow_deleted && mProc && mProc->getState() == BaseProc::State::Delete)
return nullptr;
return mProc;
}
sead::CriticalSection* BaseProcLinkData::lockIfNeeded() {
if (BaseProcMgr::instance()->isHighPriorityThread())
return nullptr;
mCS.lock();
return &mCS;
}
bool BaseProcLinkDataMgr::acquireLink(BaseProc* proc) {
auto lock = sead::makeScopedLock(mCS);
s32 index = mIndex;
for (s32 i = 0; i < mData.size(); ++i) {
const s32 j = index == 0x800 ? 0 : index;
auto& data = mData[j];
if (data.mId == u32(-1)) {
{
auto data_lock = sead::makeScopedLock(data.mCS);
data.mProc = proc;
data.mId = proc->mId;
proc->mBaseProcLinkData = &data;
}
mIndex = j + 1;
return true;
}
index = j + 1;
}
for (s32 i = 0; i < mData.size(); ++i) {
auto data_lock = sead::makeScopedLock(mData[i].mCS);
// Debug code was probably here?
}
return false;
}
bool BaseProcLinkDataMgr::releaseLink(BaseProc* proc) {
auto* data = proc->mBaseProcLinkData;
if (!data)
return false;
const auto thread = sead::ThreadMgr::instance()->getCurrentThread();
thread->getPriority();
auto lock = sead::makeScopedLock(data->mCS);
data->mProc = nullptr;
data->mId = -1;
proc->mBaseProcLinkData = nullptr;
return true;
}
} // namespace ksys::act

View File

@ -1,54 +0,0 @@
#pragma once
#include <container/seadSafeArray.h>
#include <heap/seadDisposer.h>
#include <prim/seadScopedLock.h>
#include <thread/seadCriticalSection.h>
#include "KingSystem/Utils/Types.h"
namespace ksys::act {
class BaseProc;
class BaseProcLinkData {
public:
u32 id() const { return mId; }
s32 refCount() const { return mRefCount; }
/// Get the stored BaseProc.
BaseProc* getProc() const { return mProc; }
/// Get the stored BaseProc or nullptr if its ID or state are unexpected.
/// @param id The expected ID.
/// @param allow_deleted Whether to allow the BaseProc to be in the Delete state.
BaseProc* getProc(u32 id, bool allow_deleted) const;
/// Locks the critical section if on a low priority thread and returns it;
/// unlocking it is the responsibility of the caller.
/// Returns nullptr if on a high priority thread. In that case, nothing needs to be done.
sead::CriticalSection* lockIfNeeded();
private:
friend class BaseProcLinkDataMgr;
sead::CriticalSection mCS;
u32 mId = -1;
BaseProc* mProc = nullptr;
s32 mRefCount = 0;
};
KSYS_CHECK_SIZE_NX150(BaseProcLinkData, 0x58);
class BaseProcLinkDataMgr {
SEAD_SINGLETON_DISPOSER(BaseProcLinkDataMgr)
public:
bool acquireLink(BaseProc* proc);
bool releaseLink(BaseProc* proc);
private:
sead::CriticalSection mCS;
s32 mIndex = 0;
sead::SafeArray<BaseProcLinkData, 2048> mData{};
};
KSYS_CHECK_SIZE_NX150(BaseProcLinkDataMgr, 0x2C068);
} // namespace ksys::act