[+] New Linux futex based IPCMutex to better ensure robust breakage

This commit is contained in:
Reece Wilson 2022-08-07 05:18:34 +01:00
parent c730d4fc58
commit 5dd2be4763
6 changed files with 448 additions and 73 deletions

View File

@ -32,6 +32,10 @@
#include "Grug/Grug.hpp" #include "Grug/Grug.hpp"
#include "Threading/Sleep.hpp" #include "Threading/Sleep.hpp"
#if defined(AURORA_IS_LINUX_DERIVED)
void LinuxSuperSecretIOTick();
#endif
static void Init() static void Init()
{ {
#if defined(AURORA_PLATFORM_WIN32) #if defined(AURORA_PLATFORM_WIN32)
@ -40,6 +44,7 @@ static void Init()
Crypto::InitCrypto(); Crypto::InitCrypto();
Aurora::RNG::Init();
Aurora::Threading::InitSleep(); Aurora::Threading::InitSleep();
Aurora::Process::InitProcessMap(); Aurora::Process::InitProcessMap();
Aurora::SWInfo::InitSwInfo(); Aurora::SWInfo::InitSwInfo();
@ -56,7 +61,6 @@ static void Init()
Aurora::Locale::Init(); Aurora::Locale::Init();
Aurora::CmdLine::Init(); Aurora::CmdLine::Init();
Aurora::Processes::Init(); Aurora::Processes::Init();
Aurora::RNG::Init();
Aurora::Hashing::InitHashing(); Aurora::Hashing::InitHashing();
Aurora::Async::InitAsync(); Aurora::Async::InitAsync();
} }
@ -64,6 +68,10 @@ static void Init()
static void Pump() static void Pump()
{ {
Aurora::Console::Pump(); Aurora::Console::Pump();
#if defined(AURORA_IS_LINUX_DERIVED)
::LinuxSuperSecretIOTick();
#endif
} }
static void Deinit() static void Deinit()

View File

@ -18,6 +18,11 @@
#include <Source/RuntimeInternal.hpp> #include <Source/RuntimeInternal.hpp>
#include <Source/Console/Flusher.hpp> #include <Source/Console/Flusher.hpp>
#if defined(AURORA_IS_LINUX_DERIVED)
void LinuxSuperSecretIOTick();
void LinuxSuperSecretFuckGlibc();
#endif
namespace Aurora::Grug namespace Aurora::Grug
{ {
static const auto kGrugSleepMs = 100; static const auto kGrugSleepMs = 100;
@ -36,6 +41,10 @@ namespace Aurora::Grug
// grug require only 1 strand // grug require only 1 strand
static void GrugWorld() static void GrugWorld()
{ {
#if defined(AURORA_IS_LINUX_DERIVED)
LinuxSuperSecretFuckGlibc();
#endif
// grug surive first night // grug surive first night
SlowStartupTasks(); SlowStartupTasks();
@ -82,6 +91,11 @@ namespace Aurora::Grug
// grug sleep 100ms // grug sleep 100ms
AuThreading::Sleep(kGrugSleepMs); AuThreading::Sleep(kGrugSleepMs);
} }
#if defined(AURORA_IS_LINUX_DERIVED)
::LinuxSuperSecretIOTick();
#endif
} }
} }

View File

@ -10,19 +10,23 @@
#include "IPC.hpp" #include "IPC.hpp"
#include "IPCHandle.hpp" #include "IPCHandle.hpp"
#include "IPCMutexFutex.Linux.hpp" #include "IPCMutexFutex.Linux.hpp"
#include "IPCPrimitives.Linux.hpp"
#include "IPCMemory.Unix.hpp"
// LINUX SYSCALL APIS
#include <linux/futex.h> #include <linux/futex.h>
#include <syscall.h> #include <syscall.h>
// INTERNAL UTILS
// ...IO / FD SHARING
#include <Source/IO/UNIX/FDIpcServer.hpp> #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, static int futex(uint32_t *uaddr, int futex_op, uint32_t val,
const struct timespec *timeout, /* or: uint32_t val2 */ const struct timespec *timeout, /* or: uint32_t val2 */
uint32_t *uaddr2, uint32_t val3) 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); 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) static int futex_wake(uint32_t *addr, uint32_t nthreads)
{ {
return futex(addr, FUTEX_WAKE, nthreads, 0, 0, 0); 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); return syscall(SYS_set_robust_list, head, len);
} }
@ -56,32 +65,55 @@ static long get_robust_list(int pid, struct robust_list_head **head_ptr, size_t
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
static const AuUInt8 kMaxFutexes = 128; static const AuUInt8 kMaxFutexes = 128;
static const AuUInt32 kFutexArraySize = AuUInt32(kMaxFutexes) * 4; static const AuUInt32 kFutexArraySize = AuUInt32(kMaxFutexes) * (sizeof(AuUInt64) * 3);
static const AuUInt32 kFutexValueOwner = 0x80000000; static const AuUInt32 kFutexIsValid = 0x80000000;
static const AuUInt32 kFutexValueLocked = kFutexValueOwner | 1; static const AuUInt32 kFutexIsDead = 0x40000000;
static const AuUInt32 kFutexValueUnlocked = kFutexValueOwner; static const AuUInt32 kFutexIsHasOwner = 0x20000000;
//static const AuUInt32 kFutexValueLocked = kFutexIsHasOwner | kFutexIsValid | 1;
static const AuUInt32 kFutexValueUnlocked = kFutexIsHasOwner;
static const AuUInt32 kFutexValueNULL = 0; static const AuUInt32 kFutexValueNULL = 0;
static const AuUInt32 kFutexValueMask = kFutexValueLocked; static AuUInt32 gConstNull = 0;
static const AuUInt32 kFutexValueMaskOwner = kFutexValueOwner | FUTEX_OWNER_DIED;
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
// VARIABLES // 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 AuSPtr<AuIPC::IPCSharedMemory> gFutexSharedMemory;
static AuUInt32 *gFutexArray; static FutexObject *gFutexArray;
static bool gFutexInit {}; static bool gFutexInit {};
static AuIOIPC::IMutexClosedHook * gFutexCallbacks[kMaxFutexes]; static AuIOIPC::IMutexClosedHook * gFutexCallbacks[kMaxFutexes];
////////////////////////////////////////////////////////////////////////////////////
// IMPLEMENTATION
////////////////////////////////////////////////////////////////////////////////////
static void InitFutexAPI() static void InitFutexAPI()
{ {
static AuThreadPrimitives::SpinLock lock; AU_LOCK_GUARD(gLock);
AU_LOCK_GUARD(lock);
if (AuExchange(gFutexInit, true)) if (AuExchange(gFutexInit, true))
{ {
@ -91,24 +123,91 @@ static void InitFutexAPI()
gFutexSharedMemory = AuIOIPC::NewSharedMemory(kFutexArraySize); gFutexSharedMemory = AuIOIPC::NewSharedMemory(kFutexArraySize);
SysAssert(gFutexSharedMemory); SysAssert(gFutexSharedMemory);
gFutexArray = gFutexSharedMemory->GetMemory().Begin<AuUInt32>(); gFutexArray = gFutexSharedMemory->GetMemory().Begin<FutexObject>();
} }
struct MagicFutexLinkHeader : robust_list_head
{
AuUInt32 linkCount {};
};
struct FutexContext struct FutexContext
{ {
bool bInit {}; bool bInit {};
AuThreadPrimitives::SpinLock lock;
// https://github.com/bminor/glibc/blob/78fb88827362fbd2cc8aa32892ae5b015106e25c/sysdeps/nptl/dl-tls_init_tp.c#L93 AuUInt32 tid {};
// Every thread should have an arraylist head
robust_list_head *head;
bool Init(); 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() bool FutexContext::Init()
{ {
AU_LOCK_GUARD(this->lock);
if (AuExchange(this->bInit, true)) if (AuExchange(this->bInit, true))
{ {
return true; return true;
@ -116,14 +215,70 @@ bool FutexContext::Init()
InitFutexAPI(); 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() static AuUInt8 AllocateFutex()
{ {
tlsFutexContext.Init(); gFutexContext.Init();
if (!gFutexArray) if (!gFutexArray)
{ {
return 255; return 255;
@ -133,8 +288,13 @@ static AuUInt8 AllocateFutex()
i < kMaxFutexes; i < kMaxFutexes;
i++) 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; return i;
} }
} }
@ -144,50 +304,119 @@ static AuUInt8 AllocateFutex()
static bool TryReleaseFutex(AuUInt8 index) static bool TryReleaseFutex(AuUInt8 index)
{ {
auto &cb = gFutexCallbacks[index]; auto old = gFutexCallbacks[index];
if (cb) auto oldState = gFutexArray[index].futexPadded;
{
if (!cb->OnClosed()) if (AuAtomicCompareExchange<AuUInt32>(&gFutexArray[index].futex, kFutexValueUnlocked, oldState) != oldState)
{ {
return false; return false;
} }
if ((old) &&
(!old->OnClosed()))
{
gFutexArray[index].futexPadded = oldState;
return false;
} }
cb = nullptr;
gFutexArray[index] = 0;
gFutexCallbacks[index] = nullptr; gFutexCallbacks[index] = nullptr;
return true; return true;
} }
static void FreeFutex(AuUInt8 index) static void FreeFutex(AuUInt8 index)
{ {
gFutexArray[index] = 0; gFutexArray[index].futexPadded = 0;
gFutexCallbacks[index] = nullptr; gFutexCallbacks[index] = nullptr;
} }
static void LinuxAddRobustFutexSlow(AuUInt32 *futex) static void LinuxAddRobustFutexSlow(AuUInt32 *futex)
{ {
tlsFutexContext.Init(); SysAssert(gFutexContext.Init());
SysAssert(gFutexContext.Link(futex));
} }
static void LinuxRemoveRobustFutexSlow(AuUInt32 *futex) static void LinuxRemoveRobustFutexSlow(AuUInt32 *futex)
{ {
tlsFutexContext.Init(); SysAssert(gFutexContext.Init());
if (!gFutexContext.Unlink(futex))
}
static void LinuxLockFutex(AuUInt32 *futex)
{ {
SysPushErrorIO("Unlink futex error: {}", fmt::ptr(futex));
}
} }
static void LinuxUnlockFutex(AuUInt32 *futex) static bool LinuxLockFutex(AuUInt32 *futex, AuUInt32 timeout)
{ {
bool bContended;
struct timespec tspec;
AuUInt32 value = gFutexContext.tid;
if (timeout)
{
AuTime::ms2tsabs(&tspec, timeout);
} }
static void LinuxSuperSecretIOTick() 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 bool LinuxTryLockFutex(AuUInt32 *futex)
{
if (AuAtomicCompareExchange<AuUInt32>(futex,
gFutexContext.tid,
kFutexValueUnlocked) != kFutexValueUnlocked)
{
return false;
}
LinuxAddRobustFutexSlow(futex);
return true;
}
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) if (!gFutexInit)
{ {
@ -199,20 +428,32 @@ static void LinuxSuperSecretIOTick()
return; 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; for (AuUInt32 i = 0;
i < kMaxFutexes; i < kMaxFutexes;
i++) 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 namespace Aurora::IO::IPC
{ {
static AuThreadPrimitives::SpinLock gLock; static AuThreadPrimitives::SpinLock gLock;
@ -222,7 +463,10 @@ namespace Aurora::IO::IPC
// Mutexes // Mutexes
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
IPCMutexProxy::IPCMutexProxy(AuUInt32 index) : mutex_(), bOwned(true), index_(index) IPCMutexProxy::IPCMutexProxy(AuUInt32 index) :
mutex_(),
bOwned(true),
index_(index)
{ {
if (this->mutex_.HasValidHandle()) if (this->mutex_.HasValidHandle())
{ {
@ -237,6 +481,7 @@ namespace Aurora::IO::IPC
} }
this->mem_ = gFutexSharedMemory; this->mem_ = gFutexSharedMemory;
this->mutex_.bNoAutoRel = true;
} }
IPCMutexProxy::IPCMutexProxy(int handle, AuSPtr<IPCSharedMemory> mem, AuUInt32 index) : IPCMutexProxy::IPCMutexProxy(int handle, AuSPtr<IPCSharedMemory> mem, AuUInt32 index) :
@ -255,6 +500,8 @@ namespace Aurora::IO::IPC
this->mutex_.~LSMutex(); this->mutex_.~LSMutex();
} }
} }
this->mutex_.bNoAutoRel = true;
} }
IPCMutexProxy::~IPCMutexProxy() IPCMutexProxy::~IPCMutexProxy()
@ -263,23 +510,109 @@ namespace Aurora::IO::IPC
if (this->bOwned) if (this->bOwned)
{ {
FreeFutex(this->index_); ::FreeFutex(this->index_);
} }
} }
AuUInt32 *IPCMutexProxy::GetFutex()
{
return &this->mem_->GetMemory().Begin<FutexObject>()[this->index_].futex;
}
bool IPCMutexProxy::Unlock() 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() 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) 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() Loop::ELoopSource IPCMutexProxy::GetType()
@ -301,7 +634,7 @@ namespace Aurora::IO::IPC
AUKN_SYM AuSPtr<IPCMutex> NewMutex() AUKN_SYM AuSPtr<IPCMutex> NewMutex()
{ {
auto futex = AllocateFutex(); auto futex = ::AllocateFutex();
if (futex == 255) if (futex == 255)
{ {
return {}; return {};
@ -404,7 +737,7 @@ namespace Aurora::IO::IPC
return {}; return {};
} }
auto mem = decodedHandle.GetToken(IPC::EIPCHandleType::eIPCMemory, 0); auto mem = decodedHandle.GetToken(IPC::EIPCHandleType::eIPCMemory, 1);
if (!mem) if (!mem)
{ {
SysPushErrorParseError("Invalid handle: {}", handle); SysPushErrorParseError("Invalid handle: {}", handle);
@ -414,11 +747,3 @@ namespace Aurora::IO::IPC
return ImportMutexEx(val->token, mem->token, val->token.word); return ImportMutexEx(val->token, mem->token, val->token.word);
} }
} }
namespace Aurora::Grug
{
void LinuxSuperSecretIOTick()
{
::LinuxSuperSecretIOTick();
}
}

View File

@ -10,11 +10,8 @@ namespace Aurora::IO::IPC
virtual bool OnClosed() = 0; virtual bool OnClosed() = 0;
}; };
// IPC::IPCHandle handle;
struct IPCPipeImpl; struct IPCPipeImpl;
struct IPCMutexProxy : IPCMutex, Loop::ILoopSourceEx, AuEnableSharedFromThis<IPCMutexProxy>, IMutexClosedHook
struct IPCMutexProxy : IPCMutex, Loop::ILoopSourceEx
{ {
IPCMutexProxy(AuUInt32 index); IPCMutexProxy(AuUInt32 index);
IPCMutexProxy(int handle, AuSPtr<IPCSharedMemory> mem, AuUInt32 index); IPCMutexProxy(int handle, AuSPtr<IPCSharedMemory> mem, AuUInt32 index);
@ -22,6 +19,8 @@ namespace Aurora::IO::IPC
PROXY_INTERNAL_INTERFACE(mutex_) PROXY_INTERNAL_INTERFACE(mutex_)
bool OnClosed() override;
bool Unlock() override; bool Unlock() override;
bool IsSignaled() override; bool IsSignaled() override;
@ -29,12 +28,18 @@ namespace Aurora::IO::IPC
Loop::ELoopSource GetType() override; Loop::ELoopSource GetType() override;
AuString ExportToString() override; AuString ExportToString() override;
AuUInt32 *GetFutex();
IMutexClosedHook *pMutexClosedHook {};
private: private:
bool bOwned {}; bool bOwned {};
IPCToken token_; IPCToken token_;
AuSPtr<IPCSharedMemory> mem_; AuSPtr<IPCSharedMemory> mem_;
AuUInt32 index_; AuUInt32 index_;
Loop::LSMutex mutex_; Loop::LSMutex mutex_;
AuSPtr<void> leakSelf_;
friend IPCPipeImpl; friend IPCPipeImpl;
}; };

View File

@ -24,6 +24,13 @@ namespace Aurora::IO::Loop
LSMutex::~LSMutex() 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) && if ((this->handle != 0) &&
(this->handle != -1)) (this->handle != -1))
{ {
@ -44,7 +51,12 @@ namespace Aurora::IO::Loop
bool LSMutex::Unlock() bool LSMutex::Unlock()
{ {
AuUInt64 plsNoOverflow {1}; 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() bool LSMutex::IsSignaled()
@ -60,8 +72,17 @@ namespace Aurora::IO::Loop
bool LSMutex::IsSignaledNonblocking() bool LSMutex::IsSignaledNonblocking()
{ {
AuUInt64 oldSemaphoreValue {}; 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"); SysAssertDbg(!ok || oldSemaphoreValue == 1, "Double unlock caught");
if (!ok)
{
// TODO: Might hook here
}
else
{
this->bOwns = true;
}
return ok; return ok;
} }

View File

@ -24,8 +24,10 @@ namespace Aurora::IO::Loop
bool WaitOn(AuUInt32 timeout) override; bool WaitOn(AuUInt32 timeout) override;
virtual ELoopSource GetType() override; virtual ELoopSource GetType() override;
bool bNoAutoRel {};
private: private:
void Init(); void Init();
bool IsSignaledNonblocking(); bool IsSignaledNonblocking();
bool bOwns {};
}; };
} }