[+] New Linux futex based IPCMutex to better ensure robust breakage
This commit is contained in:
parent
c730d4fc58
commit
5dd2be4763
@ -32,6 +32,10 @@
|
||||
#include "Grug/Grug.hpp"
|
||||
#include "Threading/Sleep.hpp"
|
||||
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
void LinuxSuperSecretIOTick();
|
||||
#endif
|
||||
|
||||
static void Init()
|
||||
{
|
||||
#if defined(AURORA_PLATFORM_WIN32)
|
||||
@ -40,6 +44,7 @@ static void Init()
|
||||
|
||||
Crypto::InitCrypto();
|
||||
|
||||
Aurora::RNG::Init();
|
||||
Aurora::Threading::InitSleep();
|
||||
Aurora::Process::InitProcessMap();
|
||||
Aurora::SWInfo::InitSwInfo();
|
||||
@ -56,7 +61,6 @@ static void Init()
|
||||
Aurora::Locale::Init();
|
||||
Aurora::CmdLine::Init();
|
||||
Aurora::Processes::Init();
|
||||
Aurora::RNG::Init();
|
||||
Aurora::Hashing::InitHashing();
|
||||
Aurora::Async::InitAsync();
|
||||
}
|
||||
@ -64,6 +68,10 @@ static void Init()
|
||||
static void Pump()
|
||||
{
|
||||
Aurora::Console::Pump();
|
||||
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
::LinuxSuperSecretIOTick();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void Deinit()
|
||||
|
@ -18,6 +18,11 @@
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include <Source/Console/Flusher.hpp>
|
||||
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
void LinuxSuperSecretIOTick();
|
||||
void LinuxSuperSecretFuckGlibc();
|
||||
#endif
|
||||
|
||||
namespace Aurora::Grug
|
||||
{
|
||||
static const auto kGrugSleepMs = 100;
|
||||
@ -36,6 +41,10 @@ namespace Aurora::Grug
|
||||
// grug require only 1 strand
|
||||
static void GrugWorld()
|
||||
{
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
LinuxSuperSecretFuckGlibc();
|
||||
#endif
|
||||
|
||||
// grug surive first night
|
||||
SlowStartupTasks();
|
||||
|
||||
@ -82,6 +91,11 @@ namespace Aurora::Grug
|
||||
// grug sleep 100ms
|
||||
AuThreading::Sleep(kGrugSleepMs);
|
||||
}
|
||||
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
::LinuxSuperSecretIOTick();
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10,19 +10,23 @@
|
||||
#include "IPC.hpp"
|
||||
#include "IPCHandle.hpp"
|
||||
#include "IPCMutexFutex.Linux.hpp"
|
||||
#include "IPCPrimitives.Linux.hpp"
|
||||
#include "IPCMemory.Unix.hpp"
|
||||
|
||||
// LINUX SYSCALL APIS
|
||||
#include <linux/futex.h>
|
||||
#include <syscall.h>
|
||||
#include <syscall.h>
|
||||
|
||||
// INTERNAL UTILS
|
||||
// ...IO / FD SHARING
|
||||
#include <Source/IO/UNIX/FDIpcServer.hpp>
|
||||
// ...TIME UTILS
|
||||
#include <Source/Time/Time.hpp>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// SysCalls
|
||||
// SYSCALLS
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#if !defined(FUTEX_OWNER_DIED)
|
||||
#define FUTEX_OWNER_DIED 0x40000000
|
||||
#endif
|
||||
|
||||
static int futex(uint32_t *uaddr, int futex_op, uint32_t val,
|
||||
const struct timespec *timeout, /* or: uint32_t val2 */
|
||||
uint32_t *uaddr2, uint32_t val3)
|
||||
@ -35,12 +39,17 @@ static int futex_wait(uint32_t *addr, uint32_t expected)
|
||||
return futex(addr, FUTEX_WAIT, expected, 0, 0, 0);
|
||||
}
|
||||
|
||||
static int futex_wait(uint32_t *addr, uint32_t expected, const struct timespec *timeout)
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT, expected, timeout, 0, 0);
|
||||
}
|
||||
|
||||
static int futex_wake(uint32_t *addr, uint32_t nthreads)
|
||||
{
|
||||
return futex(addr, FUTEX_WAKE, nthreads, 0, 0, 0);
|
||||
}
|
||||
|
||||
static long set_robust_list(struct procmon_head *head, size_t len)
|
||||
static long set_robust_list(struct robust_list_head *head, size_t len)
|
||||
{
|
||||
return syscall(SYS_set_robust_list, head, len);
|
||||
}
|
||||
@ -55,33 +64,56 @@ static long get_robust_list(int pid, struct robust_list_head **head_ptr, size_t
|
||||
// CONSTANTS
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const AuUInt8 kMaxFutexes = 128;
|
||||
static const AuUInt32 kFutexArraySize = AuUInt32(kMaxFutexes) * 4;
|
||||
static const AuUInt8 kMaxFutexes = 128;
|
||||
static const AuUInt32 kFutexArraySize = AuUInt32(kMaxFutexes) * (sizeof(AuUInt64) * 3);
|
||||
|
||||
static const AuUInt32 kFutexValueOwner = 0x80000000;
|
||||
static const AuUInt32 kFutexValueLocked = kFutexValueOwner | 1;
|
||||
static const AuUInt32 kFutexValueUnlocked = kFutexValueOwner;
|
||||
static const AuUInt32 kFutexIsValid = 0x80000000;
|
||||
static const AuUInt32 kFutexIsDead = 0x40000000;
|
||||
static const AuUInt32 kFutexIsHasOwner = 0x20000000;
|
||||
|
||||
//static const AuUInt32 kFutexValueLocked = kFutexIsHasOwner | kFutexIsValid | 1;
|
||||
static const AuUInt32 kFutexValueUnlocked = kFutexIsHasOwner;
|
||||
static const AuUInt32 kFutexValueNULL = 0;
|
||||
|
||||
static AuUInt32 gConstNull = 0;
|
||||
|
||||
static const AuUInt32 kFutexValueMask = kFutexValueLocked;
|
||||
static const AuUInt32 kFutexValueMaskOwner = kFutexValueOwner | FUTEX_OWNER_DIED;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// VARIABLES
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct FutexObject
|
||||
{
|
||||
union
|
||||
{
|
||||
AuUInt64 maxWord;
|
||||
void *nextPtr;
|
||||
};
|
||||
|
||||
union
|
||||
{
|
||||
AuUInt32 futex;
|
||||
AuUInt64 futexPadded;
|
||||
};
|
||||
};
|
||||
|
||||
static_assert(sizeof(FutexObject) == 16);
|
||||
|
||||
static AuThreadPrimitives::SpinLock gLock;
|
||||
static AuSPtr<AuIPC::IPCSharedMemory> gFutexSharedMemory;
|
||||
static AuUInt32 *gFutexArray;
|
||||
static FutexObject *gFutexArray;
|
||||
static bool gFutexInit {};
|
||||
|
||||
static AuIOIPC::IMutexClosedHook * gFutexCallbacks[kMaxFutexes];
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// IMPLEMENTATION
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static void InitFutexAPI()
|
||||
{
|
||||
static AuThreadPrimitives::SpinLock lock;
|
||||
|
||||
AU_LOCK_GUARD(lock);
|
||||
AU_LOCK_GUARD(gLock);
|
||||
|
||||
if (AuExchange(gFutexInit, true))
|
||||
{
|
||||
@ -91,24 +123,91 @@ static void InitFutexAPI()
|
||||
gFutexSharedMemory = AuIOIPC::NewSharedMemory(kFutexArraySize);
|
||||
SysAssert(gFutexSharedMemory);
|
||||
|
||||
gFutexArray = gFutexSharedMemory->GetMemory().Begin<AuUInt32>();
|
||||
gFutexArray = gFutexSharedMemory->GetMemory().Begin<FutexObject>();
|
||||
}
|
||||
|
||||
struct MagicFutexLinkHeader : robust_list_head
|
||||
{
|
||||
AuUInt32 linkCount {};
|
||||
};
|
||||
|
||||
struct FutexContext
|
||||
{
|
||||
bool bInit {};
|
||||
|
||||
// https://github.com/bminor/glibc/blob/78fb88827362fbd2cc8aa32892ae5b015106e25c/sysdeps/nptl/dl-tls_init_tp.c#L93
|
||||
// Every thread should have an arraylist head
|
||||
robust_list_head *head;
|
||||
|
||||
AuThreadPrimitives::SpinLock lock;
|
||||
AuUInt32 tid {};
|
||||
|
||||
bool Init();
|
||||
|
||||
bool SetGrugSelf();
|
||||
|
||||
bool Link(AuUInt32 *ptr);
|
||||
bool Unlink(AuUInt32 *ptr);
|
||||
|
||||
MagicFutexLinkHeader futexArrayHeader {};
|
||||
};
|
||||
|
||||
|
||||
static FutexContext gFutexContext;
|
||||
void LinuxSuperSecretFuckGlibc();
|
||||
static void LinuxLockFutex(AuUInt32 *futex);
|
||||
|
||||
void LinuxSuperSecretFuckGlibc()
|
||||
{
|
||||
// Fun: You have to define an entry-relative-offset within a linked list header, hard coding an ABI, for every user
|
||||
// program in the tasks/processes address space. Low-iq linux kernel comments state "tehe" check with the glibc folk.
|
||||
//
|
||||
// Counter, they aren't "folk," they aren't even real people.
|
||||
// Fuck you, and fuck your buzzword mutex that hasn't evolved into a stable API since its initial hack of an
|
||||
// implementation 20 years ago. I don't want to make a fucking array of pthreads (40+ bytes for a single atomic).
|
||||
// I don't want to fucking define a system-wide abi, matching ONE OF MANY fucking glibc ABIs, any given process
|
||||
// could be arbitrarily linked against.
|
||||
//
|
||||
// As a wise man once said, "Linus says fuck you Nvidia, I say f............! That's what I say."
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// Unrelated, here's what FreeBSD says about the UMUTEX_ROBUST list set operation:
|
||||
// Note that if any 32-bit ABI compatibility is being requested, then care
|
||||
// must be taken with robust lists. A single thread may not mix 32-bit com-
|
||||
// patible robust lists with native robust lists. The first
|
||||
// UMTX_OP_ROBUST_LISTS call in a given thread determines which ABI that
|
||||
// thread will use for robust lists going forward.
|
||||
//
|
||||
// Code: https://github.com/freebsd/freebsd-src/blob/27a9392d543933f1aaa4e4ddae2a1585a72db1b2/sys/kern/kern_umtx.c#L4150
|
||||
//
|
||||
// ...FreeBSD just copied the fucking glibc tards between 2012-present didn't they? I bet they were "inspired" by glibcs
|
||||
// nptl implementation on Linux, and thought hey, let's just copy this so we can have a functional CRT with shared pt-mutexes
|
||||
// ---------------------------------------------------------------------------------------------------------------------
|
||||
// I'll just have to use a dedicated watcher process that'll teardown a robust list of only standard
|
||||
// entries limited to the Aurora IPC origin. Will use the grug thread for this bc i dont foresee user
|
||||
// code locking shared pthread-mutexs under the few user-interfaces grug may call out to.
|
||||
//
|
||||
SysAssert(gFutexContext.SetGrugSelf());
|
||||
}
|
||||
|
||||
bool FutexContext::SetGrugSelf()
|
||||
{
|
||||
// Specify the magic word to use...
|
||||
this->tid = gettid();
|
||||
|
||||
// Update the header
|
||||
this->futexArrayHeader.list.next = &this->futexArrayHeader.list;
|
||||
this->futexArrayHeader.futex_offset = 8;
|
||||
this->futexArrayHeader.list_op_pending = NULL;
|
||||
|
||||
// Update TLS
|
||||
if (::set_robust_list(AuReinterpretCast<robust_list_head *>(&this->futexArrayHeader.list), sizeof(robust_list_head)) != 0)
|
||||
{
|
||||
SysPushErrorIO("Set robust list failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FutexContext::Init()
|
||||
{
|
||||
AU_LOCK_GUARD(this->lock);
|
||||
|
||||
if (AuExchange(this->bInit, true))
|
||||
{
|
||||
return true;
|
||||
@ -116,14 +215,70 @@ bool FutexContext::Init()
|
||||
|
||||
InitFutexAPI();
|
||||
|
||||
return this->bInit;
|
||||
return true;
|
||||
}
|
||||
|
||||
static thread_local FutexContext tlsFutexContext;
|
||||
bool FutexContext::Link(AuUInt32 *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Init())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AU_LOCK_GUARD(this->lock);
|
||||
auto temp = AuReinterpretCast<FutexObject *>(AuUInt(ptr) - offsetof(FutexObject, futex));
|
||||
|
||||
auto firstElement = futexArrayHeader.list.next;
|
||||
temp->nextPtr = firstElement;
|
||||
|
||||
futexArrayHeader.list_op_pending = nullptr;
|
||||
futexArrayHeader.list.next = (robust_list *)temp;
|
||||
futexArrayHeader.linkCount++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FutexContext::Unlink(AuUInt32 *ptr)
|
||||
{
|
||||
if (!ptr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Init())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
AU_LOCK_GUARD(this->lock);
|
||||
|
||||
FutexObject *cur { (FutexObject *)this->futexArrayHeader.list.next };
|
||||
FutexObject *prevLink { cur };
|
||||
|
||||
for (int i = 0; i < this->futexArrayHeader.linkCount; i++)
|
||||
{
|
||||
if (&cur->futex != ptr)
|
||||
{
|
||||
prevLink = cur;
|
||||
cur = (FutexObject *)cur->nextPtr;
|
||||
continue;
|
||||
}
|
||||
|
||||
prevLink->nextPtr = cur->nextPtr;
|
||||
this->futexArrayHeader.linkCount--;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static AuUInt8 AllocateFutex()
|
||||
{
|
||||
tlsFutexContext.Init();
|
||||
gFutexContext.Init();
|
||||
if (!gFutexArray)
|
||||
{
|
||||
return 255;
|
||||
@ -133,8 +288,13 @@ static AuUInt8 AllocateFutex()
|
||||
i < kMaxFutexes;
|
||||
i++)
|
||||
{
|
||||
if (AuAtomicCompareExchange<AuUInt32>(&gFutexArray[i], kFutexValueUnlocked, AuUInt32(0)) == 0)
|
||||
if (AuAtomicCompareExchange<AuUInt32>(&gFutexArray[i].futex, kFutexValueUnlocked, kFutexValueNULL) == kFutexValueNULL)
|
||||
{
|
||||
if (gFutexCallbacks[i])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
return i;
|
||||
}
|
||||
}
|
||||
@ -144,50 +304,119 @@ static AuUInt8 AllocateFutex()
|
||||
|
||||
static bool TryReleaseFutex(AuUInt8 index)
|
||||
{
|
||||
auto &cb = gFutexCallbacks[index];
|
||||
if (cb)
|
||||
auto old = gFutexCallbacks[index];
|
||||
auto oldState = gFutexArray[index].futexPadded;
|
||||
|
||||
if (AuAtomicCompareExchange<AuUInt32>(&gFutexArray[index].futex, kFutexValueUnlocked, oldState) != oldState)
|
||||
{
|
||||
if (!cb->OnClosed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((old) &&
|
||||
(!old->OnClosed()))
|
||||
{
|
||||
gFutexArray[index].futexPadded = oldState;
|
||||
return false;
|
||||
}
|
||||
|
||||
cb = nullptr;
|
||||
gFutexArray[index] = 0;
|
||||
gFutexCallbacks[index] = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void FreeFutex(AuUInt8 index)
|
||||
{
|
||||
gFutexArray[index] = 0;
|
||||
gFutexArray[index].futexPadded = 0;
|
||||
gFutexCallbacks[index] = nullptr;
|
||||
}
|
||||
|
||||
static void LinuxAddRobustFutexSlow(AuUInt32 *futex)
|
||||
{
|
||||
tlsFutexContext.Init();
|
||||
|
||||
SysAssert(gFutexContext.Init());
|
||||
SysAssert(gFutexContext.Link(futex));
|
||||
}
|
||||
|
||||
static void LinuxRemoveRobustFutexSlow(AuUInt32 *futex)
|
||||
{
|
||||
tlsFutexContext.Init();
|
||||
|
||||
SysAssert(gFutexContext.Init());
|
||||
if (!gFutexContext.Unlink(futex))
|
||||
{
|
||||
SysPushErrorIO("Unlink futex error: {}", fmt::ptr(futex));
|
||||
}
|
||||
}
|
||||
|
||||
static void LinuxLockFutex(AuUInt32 *futex)
|
||||
static bool LinuxLockFutex(AuUInt32 *futex, AuUInt32 timeout)
|
||||
{
|
||||
|
||||
bool bContended;
|
||||
struct timespec tspec;
|
||||
AuUInt32 value = gFutexContext.tid;
|
||||
|
||||
if (timeout)
|
||||
{
|
||||
AuTime::ms2tsabs(&tspec, timeout);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
bContended = AuAtomicCompareExchange<AuUInt32>(futex, value, kFutexValueUnlocked) != kFutexValueUnlocked;
|
||||
if (bContended)
|
||||
{
|
||||
int res = ::futex_wait(futex, kFutexValueUnlocked, timeout ? &tspec : nullptr);
|
||||
if (res < 0)
|
||||
{
|
||||
if (res != -EAGAIN)
|
||||
{
|
||||
SysPushErrorIO("FUTEX ERROR: {}", res);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//EAGAIN
|
||||
bContended = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// SUCCESS
|
||||
bContended = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (bContended);
|
||||
|
||||
::LinuxAddRobustFutexSlow(futex);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void LinuxUnlockFutex(AuUInt32 *futex)
|
||||
static bool LinuxTryLockFutex(AuUInt32 *futex)
|
||||
{
|
||||
|
||||
if (AuAtomicCompareExchange<AuUInt32>(futex,
|
||||
gFutexContext.tid,
|
||||
kFutexValueUnlocked) != kFutexValueUnlocked)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
LinuxAddRobustFutexSlow(futex);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void LinuxSuperSecretIOTick()
|
||||
static bool LinuxUnlockFutex(AuUInt32 *futex)
|
||||
{
|
||||
LinuxRemoveRobustFutexSlow(futex);
|
||||
|
||||
if (AuAtomicCompareExchange<AuUInt32>(futex,
|
||||
kFutexValueUnlocked,
|
||||
gFutexContext.tid) != gFutexContext.tid)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
::futex_wake(futex, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinuxSuperSecretIOTick()
|
||||
{
|
||||
if (!gFutexInit)
|
||||
{
|
||||
@ -199,20 +428,32 @@ static void LinuxSuperSecretIOTick()
|
||||
return;
|
||||
}
|
||||
|
||||
static Aurora::Utility::RateLimiter gLimiter;
|
||||
|
||||
if (!gLimiter.nextTriggerTime)
|
||||
{
|
||||
gLimiter.noCatchUp = true;
|
||||
gLimiter.SetNextStep(AuMSToNS<AuUInt64>(3'000));
|
||||
}
|
||||
|
||||
if (!gLimiter.CheckExchangePass())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (AuUInt32 i = 0;
|
||||
i < kMaxFutexes;
|
||||
i++)
|
||||
{
|
||||
if (gFutexArray[i] == FUTEX_OWNER_DIED)
|
||||
auto val = gFutexArray[i].futex;
|
||||
val &= ~(0x3fffffff);
|
||||
if ((val & kFutexIsDead) != 0)
|
||||
{
|
||||
TryReleaseFutex(i);
|
||||
::TryReleaseFutex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "IPCPrimitives.Linux.hpp"
|
||||
#include "IPCMemory.Unix.hpp"
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
static AuThreadPrimitives::SpinLock gLock;
|
||||
@ -222,7 +463,10 @@ namespace Aurora::IO::IPC
|
||||
// Mutexes
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IPCMutexProxy::IPCMutexProxy(AuUInt32 index) : mutex_(), bOwned(true), index_(index)
|
||||
IPCMutexProxy::IPCMutexProxy(AuUInt32 index) :
|
||||
mutex_(),
|
||||
bOwned(true),
|
||||
index_(index)
|
||||
{
|
||||
if (this->mutex_.HasValidHandle())
|
||||
{
|
||||
@ -237,6 +481,7 @@ namespace Aurora::IO::IPC
|
||||
}
|
||||
|
||||
this->mem_ = gFutexSharedMemory;
|
||||
this->mutex_.bNoAutoRel = true;
|
||||
}
|
||||
|
||||
IPCMutexProxy::IPCMutexProxy(int handle, AuSPtr<IPCSharedMemory> mem, AuUInt32 index) :
|
||||
@ -255,6 +500,8 @@ namespace Aurora::IO::IPC
|
||||
this->mutex_.~LSMutex();
|
||||
}
|
||||
}
|
||||
|
||||
this->mutex_.bNoAutoRel = true;
|
||||
}
|
||||
|
||||
IPCMutexProxy::~IPCMutexProxy()
|
||||
@ -263,23 +510,109 @@ namespace Aurora::IO::IPC
|
||||
|
||||
if (this->bOwned)
|
||||
{
|
||||
FreeFutex(this->index_);
|
||||
::FreeFutex(this->index_);
|
||||
}
|
||||
}
|
||||
|
||||
AuUInt32 *IPCMutexProxy::GetFutex()
|
||||
{
|
||||
return &this->mem_->GetMemory().Begin<FutexObject>()[this->index_].futex;
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::Unlock()
|
||||
{
|
||||
return this->mutex_.Unlock();
|
||||
auto futex = this->GetFutex();
|
||||
if (!futex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!::LinuxUnlockFutex(futex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SysAssert(this->mutex_.Unlock());
|
||||
this->leakSelf_.reset();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::IsSignaled()
|
||||
{
|
||||
return this->mutex_.IsSignaled();
|
||||
auto futex = this->GetFutex();
|
||||
if (!futex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!::LinuxTryLockFutex(futex))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->mutex_.IsSignaled())
|
||||
{
|
||||
::LinuxUnlockFutex(futex);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->leakSelf_ = AuSharedFromThis();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::WaitOn(AuUInt32 timeout)
|
||||
{
|
||||
return this->mutex_.WaitOn(timeout);
|
||||
auto futex = this->GetFutex();
|
||||
if (!futex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!::LinuxLockFutex(futex, timeout))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this->mutex_.IsSignaled())
|
||||
{
|
||||
::LinuxUnlockFutex(futex);
|
||||
return false;
|
||||
}
|
||||
|
||||
this->leakSelf_ = AuSharedFromThis();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::OnClosed()
|
||||
{
|
||||
auto futex = this->GetFutex();
|
||||
|
||||
if (this->pMutexClosedHook)
|
||||
{
|
||||
if (!this->pMutexClosedHook->OnClosed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (futex)
|
||||
{
|
||||
if (*futex == kFutexIsDead)
|
||||
{
|
||||
this->mutex_.Unlock();
|
||||
}
|
||||
|
||||
// This atomic is dumb and makes no sense
|
||||
if (AuAtomicCompareExchange<AuUInt32>(futex, kFutexValueUnlocked, kFutexIsDead) != kFutexIsDead)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Loop::ELoopSource IPCMutexProxy::GetType()
|
||||
@ -301,7 +634,7 @@ namespace Aurora::IO::IPC
|
||||
|
||||
AUKN_SYM AuSPtr<IPCMutex> NewMutex()
|
||||
{
|
||||
auto futex = AllocateFutex();
|
||||
auto futex = ::AllocateFutex();
|
||||
if (futex == 255)
|
||||
{
|
||||
return {};
|
||||
@ -404,7 +737,7 @@ namespace Aurora::IO::IPC
|
||||
return {};
|
||||
}
|
||||
|
||||
auto mem = decodedHandle.GetToken(IPC::EIPCHandleType::eIPCMemory, 0);
|
||||
auto mem = decodedHandle.GetToken(IPC::EIPCHandleType::eIPCMemory, 1);
|
||||
if (!mem)
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
@ -413,12 +746,4 @@ namespace Aurora::IO::IPC
|
||||
|
||||
return ImportMutexEx(val->token, mem->token, val->token.word);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Aurora::Grug
|
||||
{
|
||||
void LinuxSuperSecretIOTick()
|
||||
{
|
||||
::LinuxSuperSecretIOTick();
|
||||
}
|
||||
}
|
@ -10,11 +10,8 @@ namespace Aurora::IO::IPC
|
||||
virtual bool OnClosed() = 0;
|
||||
};
|
||||
|
||||
// IPC::IPCHandle handle;
|
||||
|
||||
struct IPCPipeImpl;
|
||||
|
||||
struct IPCMutexProxy : IPCMutex, Loop::ILoopSourceEx
|
||||
struct IPCMutexProxy : IPCMutex, Loop::ILoopSourceEx, AuEnableSharedFromThis<IPCMutexProxy>, IMutexClosedHook
|
||||
{
|
||||
IPCMutexProxy(AuUInt32 index);
|
||||
IPCMutexProxy(int handle, AuSPtr<IPCSharedMemory> mem, AuUInt32 index);
|
||||
@ -22,6 +19,8 @@ namespace Aurora::IO::IPC
|
||||
|
||||
PROXY_INTERNAL_INTERFACE(mutex_)
|
||||
|
||||
bool OnClosed() override;
|
||||
|
||||
bool Unlock() override;
|
||||
|
||||
bool IsSignaled() override;
|
||||
@ -29,12 +28,18 @@ namespace Aurora::IO::IPC
|
||||
Loop::ELoopSource GetType() override;
|
||||
|
||||
AuString ExportToString() override;
|
||||
|
||||
AuUInt32 *GetFutex();
|
||||
|
||||
IMutexClosedHook *pMutexClosedHook {};
|
||||
|
||||
private:
|
||||
bool bOwned {};
|
||||
IPCToken token_;
|
||||
AuSPtr<IPCSharedMemory> mem_;
|
||||
AuUInt32 index_;
|
||||
Loop::LSMutex mutex_;
|
||||
AuSPtr<void> leakSelf_;
|
||||
|
||||
friend IPCPipeImpl;
|
||||
};
|
||||
|
@ -24,6 +24,13 @@ namespace Aurora::IO::Loop
|
||||
|
||||
LSMutex::~LSMutex()
|
||||
{
|
||||
if ((!this->bOwns) &&
|
||||
(!this->bNoAutoRel /*must the remote grug clean up the IPC mutex? lets not hit a double unlock assert...*/))
|
||||
{
|
||||
SysPushErrorIO("Mutex owned by calling process during destruction... Forcefully unlocking...");
|
||||
Unlock();
|
||||
}
|
||||
|
||||
if ((this->handle != 0) &&
|
||||
(this->handle != -1))
|
||||
{
|
||||
@ -44,7 +51,12 @@ namespace Aurora::IO::Loop
|
||||
bool LSMutex::Unlock()
|
||||
{
|
||||
AuUInt64 plsNoOverflow {1};
|
||||
return ::write(this->handle, &plsNoOverflow, sizeof(plsNoOverflow)) == 8;
|
||||
bool ok = ::write(this->handle, &plsNoOverflow, sizeof(plsNoOverflow)) == 8;
|
||||
if (ok)
|
||||
{
|
||||
this->bOwns = false;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool LSMutex::IsSignaled()
|
||||
@ -60,8 +72,17 @@ namespace Aurora::IO::Loop
|
||||
bool LSMutex::IsSignaledNonblocking()
|
||||
{
|
||||
AuUInt64 oldSemaphoreValue {};
|
||||
auto ok = ::read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue)) == 8;
|
||||
auto read = ::read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue));
|
||||
auto ok = read == 8;
|
||||
SysAssertDbg(!ok || oldSemaphoreValue == 1, "Double unlock caught");
|
||||
if (!ok)
|
||||
{
|
||||
// TODO: Might hook here
|
||||
}
|
||||
else
|
||||
{
|
||||
this->bOwns = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
@ -24,8 +24,10 @@ namespace Aurora::IO::Loop
|
||||
bool WaitOn(AuUInt32 timeout) override;
|
||||
virtual ELoopSource GetType() override;
|
||||
|
||||
bool bNoAutoRel {};
|
||||
private:
|
||||
void Init();
|
||||
bool IsSignaledNonblocking();
|
||||
bool bOwns {};
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user