Further Linux support
[+] Begin work on IO futexes for io release on process/thread exit [+] Linux ::readdir iteration [+] AuConsole buffering API [*] Fix sleep as to not get interrupted by signals [*] Switch the type of FS lock used under Linux [*] Linux: Use new IPCHandle encoding scheme [*] Fix undefined behaviour: unintialized timeout values (AuLoop/Linux) [*] Fix undefined behaviour: ConsoleTTY clear line was called of a color of a random value on stack [-] Remainings of std dir iterator [*] Fix pthread_kill (aka send signal to pthread handle) always kills process. This is what you expect bc signal handler inheritance. [*] Reformat the build Aurora.json file [+] Added clang warning ignores to the build file [*] Fix: UNIX need to use STDOUT_FILENO. Was using CRT handle in place of fd by mistake. [+] Linux implementation for IO yield (AuIO::IOYield() - UNIX::LinuxOverlappedYield()) [*] Fix: Linux async end of stream processing. res 0 = zero bytes consumed. <= was detecting this as an error of code 0. Should succeed with zero bytes. [+] Linux LoopQueue missing epilogue hook for the IO processor [*] Various refactors and minor bug fixes [*] Linux fix: Handle pipe EOS as zero [*] Linux fix: thread termination via a user signal of 77. Need a force terminate. [*] IPC handle: fix improper int to bool cast in the header setup within ToString [*] Linux fix: HWInfo CPU topology regression [-] Linux fix: remove SIGABRT handler [*] Missing override in compression, exit, and consoletty headers. [+] Unix Syslog logger backend
This commit is contained in:
parent
b8bcab1bdc
commit
fd0c5b51b2
86
Aurora.json
86
Aurora.json
@ -1,22 +1,90 @@
|
||||
{
|
||||
"type": "aurora",
|
||||
"name": "AuroraRuntime",
|
||||
"dllexport": ["AURORA_ENGINE_KERNEL_EXPORT"],
|
||||
"dllexport": [
|
||||
"AURORA_ENGINE_KERNEL_EXPORT"
|
||||
],
|
||||
"dllimport": [],
|
||||
"impDefines": ["AURORA_ENGINE_KERNEL"],
|
||||
"impDefines": [
|
||||
"AURORA_ENGINE_KERNEL"
|
||||
],
|
||||
"staticImpDefines": "AURORA_ENGINE_KERNEL_STATIC",
|
||||
"clangIgnore": [
|
||||
"deprecated-declarations",
|
||||
"c99-designator",
|
||||
"reorder-init-list",
|
||||
"ignored-attributes"
|
||||
],
|
||||
"defines": [],
|
||||
"soft-depends": ["wxwidgets", "glm", "bzip2", "lz4"],
|
||||
"depends": ["auROXTL", "AuroraInterfaces", "AuroraEnum", "mimalloc", "uuid", "fmt", "json", "ltc", "o1heap", "zstd", "zlib", "mbedtls"],
|
||||
"include-depends": ["fmt", "uuid", "AuroraInterfaces", "AuroraEnum", "auROXTL"],
|
||||
"features": ["guess-platform-code"],
|
||||
"soft-depends": [
|
||||
"wxwidgets",
|
||||
"glm",
|
||||
"bzip2",
|
||||
"lz4"
|
||||
],
|
||||
"depends": [
|
||||
"auROXTL",
|
||||
"AuroraInterfaces",
|
||||
"AuroraEnum",
|
||||
"mimalloc",
|
||||
"uuid",
|
||||
"fmt",
|
||||
"json",
|
||||
"ltc",
|
||||
"o1heap",
|
||||
"zstd",
|
||||
"zlib",
|
||||
"mbedtls"
|
||||
],
|
||||
"include-depends": [
|
||||
"fmt",
|
||||
"uuid",
|
||||
"AuroraInterfaces",
|
||||
"AuroraEnum",
|
||||
"auROXTL"
|
||||
],
|
||||
"features": [
|
||||
"guess-platform-code"
|
||||
],
|
||||
"linkSources": "Source/Alloc.cpp",
|
||||
"actions": [
|
||||
{
|
||||
"filter": {"platforms": "win32"},
|
||||
"filter": {
|
||||
"platforms": "win32"
|
||||
},
|
||||
"then": {
|
||||
"links": ["Bcrypt.lib", "UxTheme.lib", "Aux_ulib.lib", "Dbghelp.lib", "ws2_32.lib", "Ntdll.lib", "Wer.lib", "wintrust.lib"]
|
||||
"links": [
|
||||
"Bcrypt.lib",
|
||||
"UxTheme.lib",
|
||||
"Aux_ulib.lib",
|
||||
"Dbghelp.lib",
|
||||
"ws2_32.lib",
|
||||
"Ntdll.lib",
|
||||
"Wer.lib",
|
||||
"wintrust.lib"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"filter": {
|
||||
"platforms": "linux"
|
||||
},
|
||||
"then": {
|
||||
"links": [
|
||||
"unwind",
|
||||
"unwind-generic"
|
||||
],
|
||||
"actions": {
|
||||
"filter": {
|
||||
"archs": "x86_64"
|
||||
},
|
||||
"then": {
|
||||
"links": [
|
||||
"unwind-x86_64"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -14,8 +14,8 @@
|
||||
#include "IThreadPool.hpp"
|
||||
#include "IAsyncApp.hpp"
|
||||
|
||||
#include "Jobs.hpp"
|
||||
#include "Tasks.hpp"
|
||||
#include "Legacy/Jobs.hpp"
|
||||
#include "Legacy/Tasks.hpp"
|
||||
|
||||
namespace Aurora::Async
|
||||
{
|
||||
@ -43,9 +43,9 @@ namespace Aurora::Async
|
||||
#if !defined(_CPPSHARP)
|
||||
#include "IPCPromises.hpp"
|
||||
|
||||
#include "JobFrom.hpp"
|
||||
#include "TaskFrom.hpp"
|
||||
#include "WorkPairImpl.hpp"
|
||||
#include "WorkBasic.hpp"
|
||||
#include "OldTrash.hpp"
|
||||
#include "Legacy/JobFrom.hpp"
|
||||
#include "Legacy/TaskFrom.hpp"
|
||||
#include "Legacy/WorkPairImpl.hpp"
|
||||
#include "Legacy/WorkBasic.hpp"
|
||||
#include "Legacy/OldTrash.hpp"
|
||||
#endif
|
||||
|
@ -165,9 +165,9 @@ namespace Aurora::Async
|
||||
using FunctionalCall_t = PromiseCallTmpl<SuccessValue_t, ErrorValue_t>;
|
||||
using FunctionalResponse_t = PromiseSuccessTmpl<SuccessValue_t>;
|
||||
using FunctionalFail_t = PromiseFailTmpl<ErrorValue_t>;
|
||||
using FunctionalResponse_f = FunctionalResponse_t::OnSuccess_f;
|
||||
using FunctionalFail_f = FunctionalFail_t::OnFailure_f;
|
||||
using FunctionalDelegate_f = FunctionalCall_t::OnDelegate_f;
|
||||
using FunctionalResponse_f = typename FunctionalResponse_t::OnSuccess_f;
|
||||
using FunctionalFail_f = typename FunctionalFail_t::OnFailure_f;
|
||||
using FunctionalDelegate_f = typename FunctionalCall_t::OnDelegate_f;
|
||||
|
||||
inline AuSPtr<This_t> OnSuccessFunctional(const FunctionalResponse_f &in);
|
||||
inline AuSPtr<This_t> OnSuccessCallback(const AuSPtr<IPromiseComplete> &in);
|
||||
|
5
Include/Aurora/Async/Legacy/DO NOT REMOVE.txt
Executable file
5
Include/Aurora/Async/Legacy/DO NOT REMOVE.txt
Executable file
@ -0,0 +1,5 @@
|
||||
> Deprecated APIs <
|
||||
|
||||
* Wont remove
|
||||
* Powering old projects
|
||||
* Sometimes used as shorthands internally
|
@ -9,5 +9,18 @@
|
||||
|
||||
namespace Aurora::Console::ConsoleTTY
|
||||
{
|
||||
/**
|
||||
* @brief Is in the middle of buffering all stdout writes?
|
||||
*/
|
||||
AUKN_SYM bool IsBuffering();
|
||||
|
||||
/**
|
||||
* @brief Begin stdout buffering
|
||||
*/
|
||||
AUKN_SYM void BeginBuffering();
|
||||
|
||||
/**
|
||||
* @brief Flip framebuffer
|
||||
*/
|
||||
AUKN_SYM bool EndBuffering();
|
||||
}
|
@ -45,7 +45,7 @@ namespace Aurora::Compression
|
||||
}
|
||||
}
|
||||
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader)
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader) override
|
||||
{
|
||||
this->reader_ = reader;
|
||||
|
||||
|
@ -45,7 +45,7 @@ namespace Aurora::Compression
|
||||
}
|
||||
}
|
||||
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader)
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader) override
|
||||
{
|
||||
this->reader_ = reader;
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace Aurora::Compression
|
||||
}
|
||||
}
|
||||
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader)
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader) override
|
||||
{
|
||||
this->reader_ = reader;
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace Aurora::Compression
|
||||
}
|
||||
}
|
||||
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader)
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader) override
|
||||
{
|
||||
int ret;
|
||||
this->reader_ = reader;
|
||||
|
@ -33,7 +33,7 @@ namespace Aurora::Compression
|
||||
|
||||
}
|
||||
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader)
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader) override
|
||||
{
|
||||
this->reader_ = reader;
|
||||
|
||||
|
@ -34,7 +34,7 @@ namespace Aurora::Compression
|
||||
}
|
||||
}
|
||||
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader)
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader) override
|
||||
{
|
||||
this->reader_ = reader;
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace Aurora::Compression
|
||||
}
|
||||
}
|
||||
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader)
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader) override
|
||||
{
|
||||
size_t ret;
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace Aurora::Compression
|
||||
}
|
||||
}
|
||||
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader)
|
||||
bool Init(const AuSPtr<IO::IStreamReader> &reader) override
|
||||
{
|
||||
this->reader_ = reader;
|
||||
this->dctx_ = ZSTD_createDCtx();
|
||||
|
@ -104,6 +104,7 @@ namespace Aurora::Console::ConsoleStd
|
||||
|
||||
//static AuThreadPrimitives::MutexUnique_t gRingLock = AuThreadPrimitives::MutexUnique();
|
||||
static AuThreadPrimitives::SpinLock gRingLock;// = AuThreadPrimitives::MutexUnique();
|
||||
static bool gBufferMode {};
|
||||
|
||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||
|
||||
@ -371,6 +372,7 @@ namespace Aurora::Console::ConsoleStd
|
||||
|
||||
bool EnterNoncanonicalMode()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void LeaveNoncanonicalMode()
|
||||
@ -399,7 +401,7 @@ namespace Aurora::Console::ConsoleStd
|
||||
|
||||
return dwType == FILE_TYPE_CHAR;
|
||||
#else
|
||||
return IsStdOutTTY(stdout);
|
||||
return IsStdOutTTY(STDOUT_FILENO);
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -637,6 +639,11 @@ namespace Aurora::Console::ConsoleStd
|
||||
|
||||
static AuUInt32 WriteStdOutBlocking(const void *data, AuUInt32 length)
|
||||
{
|
||||
if (gBufferMode)
|
||||
{
|
||||
return gStdoutBuffer.Write(data, length);
|
||||
}
|
||||
|
||||
if (!IS_STREAM_HANDLE_VALID(gOutputStream))
|
||||
{
|
||||
return 0;
|
||||
@ -666,6 +673,11 @@ namespace Aurora::Console::ConsoleStd
|
||||
|
||||
void FlushStdOutNb()
|
||||
{
|
||||
if (gBufferMode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
AuUInt32 written {};
|
||||
written = WriteStdOutBlocking(gStdoutBuffer.begin(), gStdoutBuffer.size());
|
||||
gStdoutBuffer.clear();
|
||||
@ -685,21 +697,20 @@ namespace Aurora::Console::ConsoleStd
|
||||
{
|
||||
AU_LOCK_GUARD(gRingLock);
|
||||
|
||||
FlushStdOutNb();
|
||||
|
||||
return WriteStdOutBlocking(data, length);
|
||||
}
|
||||
|
||||
void Lock()
|
||||
{
|
||||
gRingLock.Lock();
|
||||
AU_LOCK_GUARD(gRingLock);
|
||||
gBufferMode = true;
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
AU_LOCK_GUARD(gRingLock);
|
||||
gBufferMode = false;
|
||||
FlushStdOutNb();
|
||||
|
||||
gRingLock.Unlock();
|
||||
}
|
||||
|
||||
AuUInt32 WriteStdOutNonBlocking(const void *data, AuUInt32 length)
|
||||
|
@ -21,15 +21,11 @@ namespace Aurora::Console::ConsoleStd
|
||||
|
||||
void Lock();
|
||||
void Unlock();
|
||||
AuUInt32 WriteStdOutNonBlocking(const void *data, AuUInt32 length);
|
||||
void FlushStdOutNb();
|
||||
|
||||
AuUInt32 ReadStdIn(void *data, AuUInt32 length);
|
||||
AuUInt32 WriteStdOut(const void *data, AuUInt32 length);
|
||||
|
||||
void WriteStdOut(AuUInt8 level, const ConsoleMessage &msg);
|
||||
|
||||
|
||||
|
||||
#if defined(AURORA_IS_MODERNNT_DERIVED)
|
||||
void ProcessCanonical(HANDLE h);
|
||||
|
@ -24,14 +24,12 @@ namespace Aurora::Console::ConsoleTTY
|
||||
|
||||
void BeginBuffering()
|
||||
{
|
||||
return;
|
||||
ConsoleStd::Lock();
|
||||
gIsBuffering = true;
|
||||
}
|
||||
|
||||
bool EndBuffering()
|
||||
{
|
||||
return true;
|
||||
ConsoleStd::Unlock();
|
||||
gIsBuffering = false;
|
||||
//TODO (Reece): Was signal handler called?
|
||||
@ -45,9 +43,7 @@ namespace Aurora::Console::ConsoleTTY
|
||||
return;
|
||||
}
|
||||
|
||||
auto written = gIsBuffering ? ConsoleStd::WriteStdOutNonBlocking(in.data(), in.size())
|
||||
: ConsoleStd::WriteStdOutBlocking2(in.data(), in.size());
|
||||
|
||||
auto written = ConsoleStd::WriteStdOutBlocking2(in.data(), in.size());
|
||||
SysAssert(written == in.size(), "TTY Buffer Full");
|
||||
}
|
||||
|
||||
@ -104,8 +100,7 @@ namespace Aurora::Console::ConsoleTTY
|
||||
return 0;
|
||||
}
|
||||
|
||||
return gIsBuffering ? ConsoleStd::WriteStdOutNonBlocking(buffer, length)
|
||||
: ConsoleStd::WriteStdOutBlocking2(buffer, length);
|
||||
return ConsoleStd::WriteStdOutBlocking2(buffer, length);
|
||||
}
|
||||
|
||||
AUKN_SYM void TTYWrite(const char *string, EAnsiColor fgColor, EAnsiColor bgColor)
|
||||
@ -175,14 +170,7 @@ namespace Aurora::Console::ConsoleTTY
|
||||
|
||||
static void ForceFlush()
|
||||
{
|
||||
if (gIsBuffering)
|
||||
{
|
||||
ConsoleStd::FlushStdOutNb();
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsoleStd::Flush();
|
||||
}
|
||||
ConsoleStd::Flush();
|
||||
}
|
||||
|
||||
AUKN_SYM void TTYStorePos()
|
||||
|
@ -1034,7 +1034,7 @@ namespace Aurora::Console::ConsoleTTY
|
||||
void TTYConsole::BlankLine(int Y, bool borders)
|
||||
{
|
||||
TTYSetPos({0, Y});
|
||||
TTYClearLine();
|
||||
TTYClearLine(EAnsiColor::eReset);
|
||||
BlankBordersLine(Y);
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ namespace Aurora::Console::ConsoleTTY
|
||||
|
||||
struct TTYConsole : ITTYConsole
|
||||
{
|
||||
void BufferMessage(const AuConsole::ConsoleMessage &msg);
|
||||
void BufferMessage(const AuConsole::ConsoleMessage &msg) override;
|
||||
void Pump();
|
||||
void PumpHistory();
|
||||
|
||||
@ -46,7 +46,7 @@ namespace Aurora::Console::ConsoleTTY
|
||||
|
||||
bool RegenerateBuffer(bool resChanged, bool &redrawBox);
|
||||
|
||||
bool NoncanonicalMode();
|
||||
bool NoncanonicalMode() override;
|
||||
void NoncanonicalTick();
|
||||
void NoncanonicalSetCursor();
|
||||
void NoncanonicalOnString(const AuString &input);
|
||||
@ -194,7 +194,7 @@ namespace Aurora::Console::ConsoleTTY
|
||||
struct AlignedString
|
||||
{
|
||||
AuString str;
|
||||
ETTYAlign align;
|
||||
ETTYAlign align {};
|
||||
};
|
||||
|
||||
AuString tempMemory;
|
||||
@ -216,8 +216,8 @@ namespace Aurora::Console::ConsoleTTY
|
||||
bool bLeftBorder {true};
|
||||
bool bTopBorder {true};
|
||||
|
||||
bool bShouldShowBorders;
|
||||
bool bShowingBorders;
|
||||
bool bShouldShowBorders {};
|
||||
bool bShowingBorders {};
|
||||
|
||||
|
||||
bool IsShowingHintLine();
|
||||
|
@ -15,6 +15,10 @@
|
||||
#include "Stack.Win32.hpp"
|
||||
#endif
|
||||
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
#include "Stack.Unix.hpp"
|
||||
#endif
|
||||
|
||||
namespace Aurora::Debug
|
||||
{
|
||||
static thread_local AuUInt32 tlsLastBackTrace = 0xFFFFFFFF;
|
||||
@ -208,7 +212,7 @@ namespace Aurora::Debug
|
||||
|
||||
AUKN_SYM StackTrace GetStackTrace()
|
||||
{
|
||||
#if defined(AURORA_PLATFORM_WIN32)
|
||||
#if defined(AURORA_PLATFORM_WIN32) || defined(AURORA_IS_LINUX_DERIVED)
|
||||
return PlatformWalkCallStack();
|
||||
#else
|
||||
return {};
|
||||
|
@ -14,4 +14,10 @@ namespace Aurora::Debug
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void InitUNIX()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
@ -5,3 +5,121 @@
|
||||
Date: 2022-1-26
|
||||
Author: Reece
|
||||
***/
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "Stack.hpp"
|
||||
#include "Stack.Unix.hpp"
|
||||
#include <libunwind.h>
|
||||
#include <dlfcn.h>
|
||||
|
||||
namespace Aurora::Debug
|
||||
{
|
||||
static void TryComplete(StackTraceEntry &entry)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static StackTrace DumpContext(unw_context_t *uc)
|
||||
{
|
||||
StackTrace ret;
|
||||
|
||||
unw_cursor_t cursor;
|
||||
unw_word_t ip;
|
||||
|
||||
::unw_init_local(&cursor, uc);
|
||||
|
||||
while (::unw_step(&cursor) > 0)
|
||||
{
|
||||
StackTraceEntry entry;
|
||||
|
||||
::unw_get_reg(&cursor, UNW_REG_IP, &ip);
|
||||
|
||||
// module
|
||||
{
|
||||
unw_word_t off;
|
||||
char procName[512];
|
||||
|
||||
if (unw_get_proc_name(&cursor, procName, AuArraySize(procName), &off) == 0)
|
||||
{
|
||||
AuString str;
|
||||
str = AuString(procName, strlen(procName));
|
||||
str += fmt::format("+0x{:x}", off);
|
||||
entry.label = str;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
Dl_info dlinfo;
|
||||
AuSInt originalIP {};
|
||||
AuUInt moduleAddress {};
|
||||
|
||||
const char *dllPathName {};
|
||||
|
||||
if (::dladdr((const void *)ip, &dlinfo))
|
||||
{
|
||||
dllPathName = dlinfo.dli_fname;
|
||||
moduleAddress = (AuUInt)dlinfo.dli_fbase;
|
||||
originalIP = (AuUInt)ip - moduleAddress;
|
||||
}
|
||||
|
||||
entry.address = ip;
|
||||
entry.relAddress = originalIP;
|
||||
|
||||
if (dllPathName)
|
||||
{
|
||||
entry.module = dllPathName;
|
||||
}
|
||||
}
|
||||
|
||||
AuTryInsert(ret, entry);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
StackTrace DumpContextPlatform(ucontext_t &uc)
|
||||
{
|
||||
// VALID:
|
||||
// Linux: https://github.com/libunwind/libunwind/blob/3be832395426b72248969247a4a66e3c3623578d/include/libunwind-x86.h#L170
|
||||
|
||||
// ???
|
||||
// MACOS: https://github.com/JuliaLang/libosxunwind/blob/master/include/libunwind.h#L57
|
||||
return DumpContext((unw_context_t *)&uc);
|
||||
}
|
||||
|
||||
StackTrace PlatformWalkCallStack()
|
||||
{
|
||||
#if 0
|
||||
StackTrace ret;
|
||||
void *ips[1024];
|
||||
|
||||
int count = ::backtrace(ips, AuArraySize(ips));
|
||||
|
||||
auto messages = ::backtrace_symbols(ips, count);
|
||||
if (!messages)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
StackTraceEntry entry;
|
||||
|
||||
entry.address = AuUInt64(ips[i]);
|
||||
entry.label = AuUInt64(messages[i]);
|
||||
|
||||
TryComplete(entry);
|
||||
|
||||
AuTryInsert(ret, entry);
|
||||
}
|
||||
|
||||
free(messages);
|
||||
|
||||
return ret;
|
||||
#endif
|
||||
|
||||
::unw_context_t uc;
|
||||
::unw_getcontext(&uc);
|
||||
|
||||
return DumpContext(&uc);
|
||||
}
|
||||
}
|
@ -7,3 +7,11 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include <execinfo.h>
|
||||
|
||||
namespace Aurora::Debug
|
||||
{
|
||||
StackTrace DumpContext(ucontext_t &uc);
|
||||
|
||||
StackTrace PlatformWalkCallStack();
|
||||
}
|
@ -41,7 +41,7 @@ namespace Aurora::Exit
|
||||
sigemptyset(&action.sa_mask);
|
||||
|
||||
sigaction(SIGINT, &action, nullptr);
|
||||
sigaction(SIGTERM, &action, nullptr);
|
||||
//sigaction(SIGTERM, &action, nullptr);
|
||||
}
|
||||
|
||||
void DeinitUnix()
|
||||
@ -54,6 +54,6 @@ namespace Aurora::Exit
|
||||
sigemptyset(&action.sa_mask);
|
||||
|
||||
sigaction(SIGINT, &action, nullptr);
|
||||
sigaction(SIGTERM, &action, nullptr);
|
||||
//sigaction(SIGTERM, &action, nullptr);
|
||||
}
|
||||
}
|
@ -115,6 +115,7 @@ namespace Aurora::Exit
|
||||
}
|
||||
else
|
||||
{
|
||||
AuLogInfo("Process Exiting");
|
||||
Process::Exit(0);
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,7 @@ namespace Aurora::HWInfo
|
||||
static AuUInt32 ReadUInt(const AuString &path)
|
||||
{
|
||||
AuString contents;
|
||||
if (AuIOFS::ReadString(path, contents))
|
||||
if (!AuIOFS::ReadString(path, contents))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
@ -13,6 +13,7 @@
|
||||
#if !defined(_AURUNTIME_GENERICFS)
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
@ -28,14 +29,164 @@ namespace Aurora::IO::FS
|
||||
return mkdir(path.c_str(), 0775) == 0;
|
||||
}
|
||||
|
||||
struct ReadDirStructure : IReadDir
|
||||
{
|
||||
DIR *pDIR {};
|
||||
struct dirent *pDE {};
|
||||
bool bFirstTick { true };
|
||||
bool bDead { false };
|
||||
StatEx stat;
|
||||
AuString sPath;
|
||||
bool bFast {};
|
||||
|
||||
~ReadDirStructure()
|
||||
{
|
||||
if (this->pDIR)
|
||||
{
|
||||
::closedir(this->pDIR);
|
||||
}
|
||||
}
|
||||
|
||||
virtual StatEx *Next() override
|
||||
{
|
||||
bool bTryAgain {};
|
||||
|
||||
if (this->bDead)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
if (!(this->pDE = ::readdir(this->pDIR)))
|
||||
{
|
||||
this->bDead = true;
|
||||
return {};
|
||||
}
|
||||
|
||||
if (this->pDE->d_name == AuString(".") ||
|
||||
this->pDE->d_name == AuString(".."))
|
||||
{
|
||||
bTryAgain = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
stat.exists = true;
|
||||
|
||||
stat.fileName = this->pDE->d_name;
|
||||
|
||||
if (stat.fileName.empty())
|
||||
{
|
||||
bTryAgain = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
stat.path = NormalizePathRet(this->sPath + stat.fileName);
|
||||
|
||||
if (this->bFast)
|
||||
{
|
||||
// nvm wont work. still need the is type dir/file flags
|
||||
// return &stat;
|
||||
}
|
||||
|
||||
if (!StatFile(stat.path.c_str(), stat))
|
||||
{
|
||||
bTryAgain = true;
|
||||
}
|
||||
|
||||
}
|
||||
while (AuExchange(bTryAgain, false));
|
||||
|
||||
return &stat;
|
||||
}
|
||||
};
|
||||
|
||||
static AuSPtr<IReadDir> ReadDirEx(const AuString &string, bool bFast)
|
||||
{
|
||||
auto pObj = AuMakeShared<ReadDirStructure>();
|
||||
if (!pObj)
|
||||
{
|
||||
SysPushErrorMem();
|
||||
return {};
|
||||
}
|
||||
|
||||
pObj->bFast = bFast;
|
||||
|
||||
if (!AuIOFS::NormalizePath(pObj->sPath, string))
|
||||
{
|
||||
SysPushErrorMem();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!AuTryInsert(pObj->sPath, '/'))
|
||||
{
|
||||
SysPushErrorMem();
|
||||
return {};
|
||||
}
|
||||
|
||||
pObj->pDIR = ::opendir(pObj->sPath.c_str());
|
||||
if (!pObj->pDIR)
|
||||
{
|
||||
SysPushErrorIO();
|
||||
return {};
|
||||
}
|
||||
|
||||
return pObj;
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IReadDir> ReadDir(const AuString &string)
|
||||
{
|
||||
return ReadDirEx(string, false);
|
||||
}
|
||||
|
||||
AUKN_SYM bool FilesInDirectory(const AuString &string, AuList<AuString> &files)
|
||||
{
|
||||
return IterateDirEntriesSTL(string, true, files);
|
||||
auto itr = ReadDirEx(string, true);
|
||||
if (!itr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// SECURITY(): if next fails, its indistinguishable from end of file list, and will return true. it kinda sucks
|
||||
while (auto stat = itr->Next())
|
||||
{
|
||||
if (!stat->existsFile)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!AuTryInsert(files, stat->fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AUKN_SYM bool DirsInDirectory(const AuString &string, AuList<AuString> &dirs)
|
||||
{
|
||||
return IterateDirEntriesSTL(string, false, dirs);
|
||||
auto itr = ReadDirEx(string, true);
|
||||
if (!itr)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// SECURITY(): if next fails, its indistinguishable from end of file list, and will return true. it kinda sucks
|
||||
while (auto stat = itr->Next())
|
||||
{
|
||||
if (!stat->existsDirectory)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!AuTryInsert(dirs, stat->fileName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
AUKN_SYM bool WriteFile(const AuString &path, const Memory::MemoryViewRead &blob)
|
||||
@ -231,7 +382,7 @@ namespace Aurora::IO::FS
|
||||
{
|
||||
if (ENOENT != errno)
|
||||
{
|
||||
LogWarn("Critical IO error while stating file (errno: {}, path: {})", errno, path);
|
||||
AuLogWarn("Critical IO error while stating file (errno: {}, path: {})", errno, path);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -243,7 +394,7 @@ namespace Aurora::IO::FS
|
||||
|
||||
if (!stat.exists)
|
||||
{
|
||||
LogWarn("Missing attribute type in stat mode {} (of path {})", s.st_mode, path);
|
||||
AuLogWarn("Missing attribute type in stat mode {} (of path {})", s.st_mode, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -118,38 +118,5 @@ namespace Aurora::IO::FS
|
||||
}
|
||||
}
|
||||
|
||||
static bool IterateDirEntriesSTL(const AuString &path, bool filesOnly, AuList<AuString> &patches)
|
||||
{
|
||||
#if defined(HAS_STD_FS)
|
||||
auto normalizedPath = NormalizePathRet(path);
|
||||
try
|
||||
{
|
||||
for (const auto &entry : std::filesystem::directory_iterator(normalizedPath))
|
||||
{
|
||||
auto cpath = entry.path();
|
||||
|
||||
if (filesOnly && !std::filesystem::is_regular_file(cpath))
|
||||
continue;
|
||||
|
||||
if (!filesOnly && !std::filesystem::is_directory(cpath))
|
||||
continue;
|
||||
|
||||
AuString path = cpath.string();
|
||||
patches.push_back(path.substr(path.find_last_of(kPathSplitter) + 1));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Debug::PrintError();
|
||||
Logging::LogWarn("IO Error, couldn't iterate path: {}", normalizedPath);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void InitResources();
|
||||
}
|
@ -14,6 +14,7 @@ namespace Aurora::IO::FS
|
||||
{
|
||||
bool ApplyDumbAdvisoryLock(int fd, EFileAdvisoryLockLevel level)
|
||||
{
|
||||
#if !defined(AURORA_IS_LINUX_DERIVED)
|
||||
int operation = LOCK_NB;
|
||||
|
||||
switch (level)
|
||||
@ -29,6 +30,60 @@ namespace Aurora::IO::FS
|
||||
break;
|
||||
}
|
||||
|
||||
return flock(fd, operation) == 0;
|
||||
// Assume:
|
||||
// "Furthermore, the lock is release either by an explicit LOCK_UN
|
||||
// operation on any of these duplicate file descriptors, or when
|
||||
// all such file descriptors have been closed"
|
||||
|
||||
return ::flock(fd, operation) == 0;
|
||||
#else
|
||||
// Linux
|
||||
int lease {};
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case EFileAdvisoryLockLevel::eEnumCount:
|
||||
case EFileAdvisoryLockLevel::eNoSafety:
|
||||
return true;
|
||||
case EFileAdvisoryLockLevel::eBlockWrite:
|
||||
lease = F_RDLCK;
|
||||
break;
|
||||
case EFileAdvisoryLockLevel::eBlockReadWrite:
|
||||
lease = F_RDLCK;
|
||||
break;
|
||||
}
|
||||
|
||||
// Ignore warning bc idc
|
||||
// I assume c++ compilers under posix land allow for struct init the c way
|
||||
struct flock lck =
|
||||
{
|
||||
.l_whence = SEEK_SET,
|
||||
.l_start = 0,
|
||||
.l_len = 0,
|
||||
.l_type = lease
|
||||
};
|
||||
|
||||
// Assume this is true:
|
||||
// * If a process closes any file descriptor referring to a file,
|
||||
// then all of the process's locks on that file are released,
|
||||
// regardless of the file descriptor(s) on which the locks were
|
||||
// obtained. This is bad: it means that a process can lose its
|
||||
// locks on a file such as /etc/passwd or /etc/mtab when for some
|
||||
// reason a library function decides to open, read, and close the
|
||||
// same file.
|
||||
// [...]
|
||||
//
|
||||
// Open file description locks solve both of these problems.
|
||||
//
|
||||
// OFD's description implies it inherits non-ofd behaviour
|
||||
// I wanted to confirm a l_len zero would work to lock all contents,
|
||||
// even on expansion of the file. The docs didn't mention anything;
|
||||
// however, I don't really see any real difference len processing in
|
||||
// the kernel. Furthermore, I saw the kernels implementation checking
|
||||
// for MAX_OFFSET of int type (-1) before overloading l_len with zero.
|
||||
//
|
||||
// I assume this will work. Linux locking is trash.
|
||||
return ::fcntl(fd, F_OFD_SETLK, &lck) == 0;
|
||||
#endif
|
||||
}
|
||||
}
|
@ -80,11 +80,11 @@ namespace Aurora::IO
|
||||
|
||||
void ReserveBuffer(AuUInt length) override;
|
||||
|
||||
AuUInt GetReadOffset();
|
||||
AuUInt SetReadOffset(AuUInt offset);
|
||||
AuUInt GetReadOffset() override;
|
||||
AuUInt SetReadOffset(AuUInt offset) override;
|
||||
|
||||
AuUInt GetWriteOffset();
|
||||
AuUInt SetWriteOffset(AuUInt offset);
|
||||
AuUInt GetWriteOffset() override;
|
||||
AuUInt SetWriteOffset(AuUInt offset) override;
|
||||
|
||||
bool asyncActive {};
|
||||
AuUInt readOffset {};
|
||||
|
@ -26,7 +26,7 @@ namespace Aurora::IO
|
||||
|
||||
AuUInt32 TryTick() override;
|
||||
AuUInt32 RunTick() override;
|
||||
AuUInt32 TickFor(const AuSPtr<IIOProcessorItem> &ioEvent);
|
||||
AuUInt32 TickFor(const AuSPtr<IIOProcessorItem> &ioEvent) override;
|
||||
bool TickForRegister(const AuSPtr<IIOProcessorItem> &ioEvent);
|
||||
|
||||
void TickForHack(const AuSPtr<IIOProcessorItem> &ioEvent);
|
||||
|
@ -59,7 +59,6 @@ namespace Aurora::IO
|
||||
|
||||
AUKN_SYM bool IOYield()
|
||||
{
|
||||
AuLogWarn("TODO");
|
||||
return false;
|
||||
return UNIX::LinuxOverlappedYield();
|
||||
}
|
||||
}
|
@ -65,7 +65,7 @@ namespace Aurora::IO::IPC
|
||||
return this->values[0].subtype == type;
|
||||
}
|
||||
|
||||
bool IPCHandle::PushId(EIPCHandleType type, IPCToken &token)
|
||||
bool IPCHandle::PushId(EIPCHandleType type, const IPCToken &token)
|
||||
{
|
||||
IPCValue val;
|
||||
val.subtype = type;
|
||||
@ -157,6 +157,7 @@ namespace Aurora::IO::IPC
|
||||
|
||||
int stringOffset {};
|
||||
int count {};
|
||||
IPCHeader headerZero;
|
||||
while (in.size() > (stringOffset + 2))
|
||||
{
|
||||
int index = count++;
|
||||
@ -172,6 +173,11 @@ namespace Aurora::IO::IPC
|
||||
this->pid = nextInt;
|
||||
}
|
||||
|
||||
if (index == 0)
|
||||
{
|
||||
headerZero = header;
|
||||
}
|
||||
|
||||
auto readIPC = [&](IPCHeader header, IPCValue &value)
|
||||
{
|
||||
value.subtype = (EIPCHandleType)header.bmType;
|
||||
@ -256,7 +262,7 @@ namespace Aurora::IO::IPC
|
||||
IPCHeader header;
|
||||
|
||||
header.bmArray = index == 0 ? this->values.size() : 0;
|
||||
header.bmHasPid = index == 0 ? this->pid : 0;
|
||||
header.bmHasPid = index == 0 ? (this->pid ? 1 : 0) : 0;
|
||||
header.bmType = (AuUInt8)ipcValue.subtype;
|
||||
header.bmHasWord = (AuUInt8)(ipcValue.token.word ? 1 : 0);
|
||||
|
||||
@ -308,5 +314,10 @@ namespace Aurora::IO::IPC
|
||||
{
|
||||
return this->pid;
|
||||
}
|
||||
|
||||
pid_t IPCToken::ToUnixPid() const
|
||||
{
|
||||
return this->pid;
|
||||
}
|
||||
#endif
|
||||
}
|
@ -15,7 +15,8 @@ namespace Aurora::IO::IPC
|
||||
eIPCSharedFd,
|
||||
eIPCPrimitiveSemaphore,
|
||||
eIPCPrimitiveMutex,
|
||||
eIPCPrimitiveEvent
|
||||
eIPCPrimitiveEvent,
|
||||
eIPCPipeEnd
|
||||
));
|
||||
|
||||
struct IPCToken
|
||||
@ -37,6 +38,10 @@ namespace Aurora::IO::IPC
|
||||
|
||||
AuString ToNTPath() const;
|
||||
AuUInt32 ToUnixServerCookie() const;
|
||||
|
||||
#if defined(AURORA_IS_POSIX_DERIVED)
|
||||
pid_t ToUnixPid() const;
|
||||
#endif
|
||||
};
|
||||
|
||||
struct IPCValue
|
||||
@ -70,7 +75,7 @@ namespace Aurora::IO::IPC
|
||||
IPCHandle();
|
||||
|
||||
bool IsTopEq(EIPCHandleType type) const;
|
||||
bool PushId(EIPCHandleType type, IPCToken &token);
|
||||
bool PushId(EIPCHandleType type, const IPCToken &token);
|
||||
|
||||
IPCValue *GetToken(EIPCHandleType type, int id);
|
||||
|
||||
|
@ -10,32 +10,11 @@
|
||||
#include "IPCHandle.hpp"
|
||||
#include "IPCMemory.NT.hpp"
|
||||
|
||||
#include <Source/IO/IPC/IPC.hpp>
|
||||
#include <Source/IO/IPC/IPCHandle.hpp>
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Shared memory
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct IPCSharedMemoryImpl : IPCSharedMemory
|
||||
{
|
||||
IPCSharedMemoryImpl(HANDLE handle, void *ptr, const IPC::IPCHandle &ipcHandle);
|
||||
~IPCSharedMemoryImpl();
|
||||
|
||||
virtual Memory::MemoryViewWrite GetMemory() override;
|
||||
virtual AuUInt GetLength() override;
|
||||
|
||||
virtual AuString ExportToString() override;
|
||||
|
||||
private:
|
||||
IPC::IPCHandle ipcHandle_;
|
||||
bool owns_;
|
||||
HANDLE handle_{};
|
||||
void *base_{};
|
||||
AuUInt len_ {};
|
||||
};
|
||||
|
||||
IPCSharedMemoryImpl::IPCSharedMemoryImpl(HANDLE handle, void *ptr, const IPC::IPCHandle &ipcHandle) :
|
||||
base_(ptr), len_(ipcHandle.values[0].token.word), handle_(handle), ipcHandle_(ipcHandle)
|
||||
|
@ -7,7 +7,26 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include <Source/IO/IPC/IPC.hpp>
|
||||
#include <Source/IO/IPC/IPCHandle.hpp>
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
struct IPCSharedMemoryImpl : IPCSharedMemory
|
||||
{
|
||||
IPCSharedMemoryImpl(HANDLE handle, void *ptr, const IPC::IPCHandle &ipcHandle);
|
||||
~IPCSharedMemoryImpl();
|
||||
|
||||
virtual Memory::MemoryViewWrite GetMemory() override;
|
||||
virtual AuUInt GetLength() override;
|
||||
|
||||
virtual AuString ExportToString() override;
|
||||
|
||||
private:
|
||||
IPC::IPCHandle ipcHandle_;
|
||||
bool owns_;
|
||||
HANDLE handle_{};
|
||||
void *base_{};
|
||||
AuUInt len_ {};
|
||||
};
|
||||
}
|
@ -10,38 +10,21 @@
|
||||
#include "IPCHandle.hpp"
|
||||
#include "IPCMemory.Unix.hpp"
|
||||
|
||||
#include <Source/IO/IPC/IPC.hpp>
|
||||
#include <Source/IO/IPC/IPCHandle.hpp>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h> /* For mode constants */
|
||||
#include <fcntl.h> /* For O_* constants */
|
||||
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
#include "IPCMutexFutex.Linux.hpp" /* For import */
|
||||
#endif
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Shared memory
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct IPCSharedMemoryImpl : IPCSharedMemory
|
||||
{
|
||||
IPCSharedMemoryImpl(int fd, void *ptr, const IPC::IPCHandle &handle, bool owns);
|
||||
~IPCSharedMemoryImpl();
|
||||
|
||||
virtual Memory::MemoryViewWrite GetMemory() override;
|
||||
virtual AuUInt GetLength() override;
|
||||
|
||||
virtual AuString ExportToString() override;
|
||||
|
||||
private:
|
||||
IPC::IPCHandle handle_;
|
||||
bool owns_;
|
||||
int fd_{};
|
||||
void *base_{};
|
||||
AuUInt len_ {};
|
||||
};
|
||||
|
||||
static AuString GetServerPath(const IPC::IPCHandle &handle)
|
||||
static AuString GetServerPath(const IPC::IPCToken &handle)
|
||||
{
|
||||
AuString path;
|
||||
path += "/AURORA_";
|
||||
@ -52,7 +35,7 @@ namespace Aurora::IO::IPC
|
||||
}
|
||||
|
||||
IPCSharedMemoryImpl::IPCSharedMemoryImpl(int fd, void *ptr, const IPC::IPCHandle &handle, bool owns) :
|
||||
fd_(fd), base_(ptr), len_(handle.word), owns_(owns), handle_(handle)
|
||||
fd_(fd), base_(ptr), len_(handle.values[0].token.word), owns_(owns), handle_(handle)
|
||||
{
|
||||
|
||||
}
|
||||
@ -89,9 +72,14 @@ namespace Aurora::IO::IPC
|
||||
AUKN_SYM AuSPtr<IPCSharedMemory> NewSharedMemory(AuUInt length)
|
||||
{
|
||||
IPC::IPCHandle handle;
|
||||
handle.NewId(length);
|
||||
IPC::IPCToken token;
|
||||
token.pid = handle.pid;
|
||||
token.word = length;
|
||||
token.NewId();
|
||||
|
||||
auto path = GetServerPath(handle);
|
||||
handle.PushId(EIPCHandleType::eIPCMemory, token);
|
||||
|
||||
auto path = GetServerPath(token);
|
||||
int fd = ::shm_open(path.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1)
|
||||
{
|
||||
@ -126,17 +114,9 @@ namespace Aurora::IO::IPC
|
||||
return object;
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCSharedMemory> ImportSharedMemory(const AuString &handleString)
|
||||
AuSPtr<IPCSharedMemory> ImportSharedMemoryEx(const IPCToken &token)
|
||||
{
|
||||
IPC::IPCHandle handle;
|
||||
|
||||
if (!handle.FromString(handleString))
|
||||
{
|
||||
SysPushErrorParseError();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto path = GetServerPath(handle);
|
||||
auto path = GetServerPath(token);
|
||||
int fd = ::shm_open(path.c_str(), O_RDWR, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1)
|
||||
{
|
||||
@ -144,7 +124,7 @@ namespace Aurora::IO::IPC
|
||||
return {};
|
||||
}
|
||||
|
||||
auto map = ::mmap(nullptr, handle.word, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
auto map = ::mmap(nullptr, token.word, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (map == MAP_FAILED)
|
||||
{
|
||||
SysPushErrorIO();
|
||||
@ -152,15 +132,38 @@ namespace Aurora::IO::IPC
|
||||
return {};
|
||||
}
|
||||
|
||||
IPC::IPCHandle handle;
|
||||
handle.PushId(EIPCHandleType::eIPCMemory, token);
|
||||
|
||||
auto object = AuMakeShared<IPCSharedMemoryImpl>(fd, map, handle, false);
|
||||
if (!object)
|
||||
{
|
||||
SysPushErrorMem();
|
||||
::munmap(map, handle.word);
|
||||
::munmap(map, token.word);
|
||||
::close(fd);
|
||||
return {};
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCSharedMemory> ImportSharedMemory(const AuString &handleString)
|
||||
{
|
||||
IPC::IPCHandle handle;
|
||||
|
||||
if (!handle.FromString(handleString))
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handleString);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto val = handle.GetToken(IPC::EIPCHandleType::eIPCMemory, 0);
|
||||
if (!val)
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handleString);
|
||||
return {};
|
||||
}
|
||||
|
||||
return ImportSharedMemoryEx(val->token);
|
||||
}
|
||||
}
|
@ -7,7 +7,28 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include <Source/IO/IPC/IPC.hpp>
|
||||
#include <Source/IO/IPC/IPCHandle.hpp>
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
struct IPCSharedMemoryImpl : IPCSharedMemory
|
||||
{
|
||||
IPCSharedMemoryImpl(int fd, void *ptr, const IPC::IPCHandle &handle, bool owns);
|
||||
~IPCSharedMemoryImpl();
|
||||
|
||||
virtual Memory::MemoryViewWrite GetMemory() override;
|
||||
virtual AuUInt GetLength() override;
|
||||
|
||||
virtual AuString ExportToString() override;
|
||||
|
||||
IPC::IPCHandle handle_;
|
||||
private:
|
||||
bool owns_;
|
||||
int fd_{};
|
||||
void *base_{};
|
||||
AuUInt len_ {};
|
||||
};
|
||||
|
||||
AuSPtr<IPCSharedMemory> ImportSharedMemoryEx(const IPCToken &token);
|
||||
}
|
424
Source/IO/IPC/IPCMutexFutex.Linux.cpp
Executable file
424
Source/IO/IPC/IPCMutexFutex.Linux.cpp
Executable file
@ -0,0 +1,424 @@
|
||||
/***
|
||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: IPCMutexFutex.Linux.cpp
|
||||
Date: 2022-
|
||||
Author: Reece
|
||||
Note:
|
||||
***/
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "IPC.hpp"
|
||||
#include "IPCHandle.hpp"
|
||||
#include "IPCMutexFutex.Linux.hpp"
|
||||
|
||||
#include <linux/futex.h>
|
||||
#include <syscall.h>
|
||||
#include <Source/IO/UNIX/FDIpcServer.hpp>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// 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)
|
||||
{
|
||||
return syscall(SYS_futex, uaddr, futex_op, val, timeout, uaddr2, val3);
|
||||
}
|
||||
|
||||
static int futex_wait(uint32_t *addr, uint32_t expected)
|
||||
{
|
||||
return futex(addr, FUTEX_WAIT, expected, 0, 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)
|
||||
{
|
||||
return syscall(SYS_set_robust_list, head, len);
|
||||
}
|
||||
|
||||
static long get_robust_list(int pid, struct robust_list_head **head_ptr, size_t *len_ptr)
|
||||
{
|
||||
return syscall(SYS_get_robust_list, pid, head_ptr, len_ptr);
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// CONSTANTS
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static const AuUInt8 kMaxFutexes = 128;
|
||||
static const AuUInt32 kFutexArraySize = AuUInt32(kMaxFutexes) * 4;
|
||||
|
||||
static const AuUInt32 kFutexValueOwner = 0x80000000;
|
||||
static const AuUInt32 kFutexValueLocked = kFutexValueOwner | 1;
|
||||
static const AuUInt32 kFutexValueUnlocked = kFutexValueOwner;
|
||||
static const AuUInt32 kFutexValueNULL = 0;
|
||||
|
||||
static const AuUInt32 kFutexValueMask = kFutexValueLocked;
|
||||
static const AuUInt32 kFutexValueMaskOwner = kFutexValueOwner | FUTEX_OWNER_DIED;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
// VARIABLES
|
||||
////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
static AuSPtr<AuIPC::IPCSharedMemory> gFutexSharedMemory;
|
||||
static AuUInt32 *gFutexArray;
|
||||
static bool gFutexInit {};
|
||||
|
||||
static AuIOIPC::IMutexClosedHook * gFutexCallbacks[kMaxFutexes];
|
||||
|
||||
|
||||
static void InitFutexAPI()
|
||||
{
|
||||
static AuThreadPrimitives::SpinLock lock;
|
||||
|
||||
AU_LOCK_GUARD(lock);
|
||||
|
||||
if (AuExchange(gFutexInit, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
gFutexSharedMemory = AuIOIPC::NewSharedMemory(kFutexArraySize);
|
||||
SysAssert(gFutexSharedMemory);
|
||||
|
||||
gFutexArray = gFutexSharedMemory->GetMemory().Begin<AuUInt32>();
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
|
||||
bool Init();
|
||||
};
|
||||
|
||||
|
||||
bool FutexContext::Init()
|
||||
{
|
||||
if (AuExchange(this->bInit, true))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
InitFutexAPI();
|
||||
|
||||
return this->bInit;
|
||||
}
|
||||
|
||||
static thread_local FutexContext tlsFutexContext;
|
||||
|
||||
static AuUInt8 AllocateFutex()
|
||||
{
|
||||
tlsFutexContext.Init();
|
||||
if (!gFutexArray)
|
||||
{
|
||||
return 255;
|
||||
}
|
||||
|
||||
for (AuUInt32 i = 0;
|
||||
i < kMaxFutexes;
|
||||
i++)
|
||||
{
|
||||
if (AuAtomicCompareExchange<AuUInt32>(&gFutexArray[i], kFutexValueUnlocked, AuUInt32(0)) == 0)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 255;
|
||||
}
|
||||
|
||||
static bool TryReleaseFutex(AuUInt8 index)
|
||||
{
|
||||
auto &cb = gFutexCallbacks[index];
|
||||
if (cb)
|
||||
{
|
||||
if (!cb->OnClosed())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
cb = nullptr;
|
||||
gFutexArray[index] = 0;
|
||||
gFutexCallbacks[index] = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void FreeFutex(AuUInt8 index)
|
||||
{
|
||||
gFutexArray[index] = 0;
|
||||
gFutexCallbacks[index] = nullptr;
|
||||
}
|
||||
|
||||
static void LinuxAddRobustFutexSlow(AuUInt32 *futex)
|
||||
{
|
||||
tlsFutexContext.Init();
|
||||
|
||||
}
|
||||
|
||||
static void LinuxRemoveRobustFutexSlow(AuUInt32 *futex)
|
||||
{
|
||||
tlsFutexContext.Init();
|
||||
|
||||
}
|
||||
|
||||
static void LinuxLockFutex(AuUInt32 *futex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void LinuxUnlockFutex(AuUInt32 *futex)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
static void LinuxSuperSecretIOTick()
|
||||
{
|
||||
if (!gFutexInit)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gFutexArray)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (AuUInt32 i = 0;
|
||||
i < kMaxFutexes;
|
||||
i++)
|
||||
{
|
||||
if (gFutexArray[i] == FUTEX_OWNER_DIED)
|
||||
{
|
||||
TryReleaseFutex(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#include "IPCPrimitives.Linux.hpp"
|
||||
#include "IPCMemory.Unix.hpp"
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
static AuThreadPrimitives::SpinLock gLock;
|
||||
static AuBST<AuPair<AuUInt64 /*cookie*/, AuUInt32 /*pid*/>, AuWPtr<IPCSharedMemory>> gSharedViewCache;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Mutexes
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
IPCMutexProxy::IPCMutexProxy(AuUInt32 index) : mutex_(), bOwned(true), index_(index)
|
||||
{
|
||||
if (this->mutex_.HasValidHandle())
|
||||
{
|
||||
if (IO::UNIX::FDServe(this->GetHandle(), this->token_))
|
||||
{
|
||||
//this->handle_.PushId(EIPCHandleType::eIPCPrimitiveMutex, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->mutex_.~LSMutex();
|
||||
}
|
||||
}
|
||||
|
||||
this->mem_ = gFutexSharedMemory;
|
||||
}
|
||||
|
||||
IPCMutexProxy::IPCMutexProxy(int handle, AuSPtr<IPCSharedMemory> mem, AuUInt32 index) :
|
||||
mutex_(handle),
|
||||
mem_(mem),
|
||||
index_(index)
|
||||
{
|
||||
if (this->mutex_.HasValidHandle())
|
||||
{
|
||||
if (IO::UNIX::FDServe(this->GetHandle(), this->token_))
|
||||
{
|
||||
//this->handle_.PushId(EIPCHandleType::eIPCPrimitiveMutex, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->mutex_.~LSMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPCMutexProxy::~IPCMutexProxy()
|
||||
{
|
||||
IO::UNIX::FDServeEnd(this->token_);
|
||||
|
||||
if (this->bOwned)
|
||||
{
|
||||
FreeFutex(this->index_);
|
||||
}
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::Unlock()
|
||||
{
|
||||
return this->mutex_.Unlock();
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::IsSignaled()
|
||||
{
|
||||
return this->mutex_.IsSignaled();
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::WaitOn(AuUInt32 timeout)
|
||||
{
|
||||
return this->mutex_.WaitOn(timeout);
|
||||
}
|
||||
|
||||
Loop::ELoopSource IPCMutexProxy::GetType()
|
||||
{
|
||||
return this->mutex_.GetType();
|
||||
}
|
||||
|
||||
AuString IPCMutexProxy::ExportToString()
|
||||
{
|
||||
IPC::IPCHandle handle;
|
||||
|
||||
handle.PushId(EIPCHandleType::eIPCPrimitiveMutex, this->token_);
|
||||
SysAssert(this->mem_);
|
||||
handle.PushId(EIPCHandleType::eIPCMemory, AuStaticCast<IPCSharedMemoryImpl>(this->mem_)->handle_.values[0].token);
|
||||
handle.values[0].token.word = this->index_;
|
||||
|
||||
return handle.ToString();
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCMutex> NewMutex()
|
||||
{
|
||||
auto futex = AllocateFutex();
|
||||
if (futex == 255)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto object = AuMakeShared<IPCMutexProxy>(futex);
|
||||
if (!object)
|
||||
{
|
||||
SysPushErrorMem();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!object->HasValidHandle())
|
||||
{
|
||||
SysPushErrorIO();
|
||||
return {};
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
static AuSPtr<IPCSharedMemory> GetFutexPagesFromCacheOrImport(const IPCToken &mem)
|
||||
{
|
||||
AU_LOCK_GUARD(gLock);
|
||||
|
||||
auto id = AuMakePair(mem.cookie, mem.pid);
|
||||
auto itr = gSharedViewCache.find(id);
|
||||
if (itr != gSharedViewCache.end())
|
||||
{
|
||||
auto test = itr->second.lock();
|
||||
if (test)
|
||||
{
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
auto shared = ImportSharedMemoryEx(mem);
|
||||
if (!shared)
|
||||
{
|
||||
SysPushErrorNested();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!AuTryInsert(gSharedViewCache, id, shared))
|
||||
{
|
||||
SysPushErrorMem();
|
||||
// We don't need to fail. We can leak next time. It'll be fine.
|
||||
}
|
||||
|
||||
return shared;
|
||||
}
|
||||
|
||||
AuSPtr<IPCMutex> ImportMutexEx(const IPCToken &handle, const IPCToken &mem, AuUInt32 index)
|
||||
{
|
||||
int fd {-1};
|
||||
if (!IO::UNIX::FDAccept(handle, fd))
|
||||
{
|
||||
SysPushErrorNested();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto view = GetFutexPagesFromCacheOrImport(mem);
|
||||
if (!view)
|
||||
{
|
||||
SysPushErrorNested();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto object = AuMakeShared<IPCMutexProxy>(fd, view, index);
|
||||
if (!object)
|
||||
{
|
||||
SysPushErrorMem();
|
||||
::close(fd);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!object->HasValidHandle())
|
||||
{
|
||||
SysPushErrorIO();
|
||||
return {};
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCMutex> ImportMutex(const AuString &handle)
|
||||
{
|
||||
IPC::IPCHandle decodedHandle;
|
||||
|
||||
if (!decodedHandle.FromString(handle))
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto val = decodedHandle.GetToken(IPC::EIPCHandleType::eIPCPrimitiveMutex, 0);
|
||||
if (!val)
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto mem = decodedHandle.GetToken(IPC::EIPCHandleType::eIPCMemory, 0);
|
||||
if (!mem)
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
return ImportMutexEx(val->token, mem->token, val->token.word);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Aurora::Grug
|
||||
{
|
||||
void LinuxSuperSecretIOTick()
|
||||
{
|
||||
::LinuxSuperSecretIOTick();
|
||||
}
|
||||
}
|
43
Source/IO/IPC/IPCMutexFutex.Linux.hpp
Executable file
43
Source/IO/IPC/IPCMutexFutex.Linux.hpp
Executable file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include "IPCHandle.hpp"
|
||||
#include "IPCPrimitives.Linux.hpp"
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
struct IMutexClosedHook
|
||||
{
|
||||
virtual bool OnClosed() = 0;
|
||||
};
|
||||
|
||||
// IPC::IPCHandle handle;
|
||||
|
||||
struct IPCPipeImpl;
|
||||
|
||||
struct IPCMutexProxy : IPCMutex, Loop::ILoopSourceEx
|
||||
{
|
||||
IPCMutexProxy(AuUInt32 index);
|
||||
IPCMutexProxy(int handle, AuSPtr<IPCSharedMemory> mem, AuUInt32 index);
|
||||
~IPCMutexProxy();
|
||||
|
||||
PROXY_INTERNAL_INTERFACE(mutex_)
|
||||
|
||||
bool Unlock() override;
|
||||
|
||||
bool IsSignaled() override;
|
||||
bool WaitOn(AuUInt32 timeout) override;
|
||||
Loop::ELoopSource GetType() override;
|
||||
|
||||
AuString ExportToString() override;
|
||||
private:
|
||||
bool bOwned {};
|
||||
IPCToken token_;
|
||||
AuSPtr<IPCSharedMemory> mem_;
|
||||
AuUInt32 index_;
|
||||
Loop::LSMutex mutex_;
|
||||
|
||||
friend IPCPipeImpl;
|
||||
};
|
||||
|
||||
AuSPtr<IPCMutex> ImportMutexEx(const IPCToken &handle, const IPCToken &mem, AuUInt32 index);
|
||||
}
|
@ -16,8 +16,14 @@
|
||||
#include <Source/IO/Loop/LSHandle.hpp>
|
||||
#include <Source/IO/Loop/LSEvent.hpp>
|
||||
|
||||
#include <Source/IO/UNIX/IOSubmit.Linux.hpp>
|
||||
#include <Source/IO/FS/Async.Linux.hpp>
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
#include <Source/IO/UNIX/IOSubmit.Linux.hpp>
|
||||
#include <Source/IO/FS/Async.Linux.hpp>
|
||||
#include "IPCPrimitives.Linux.hpp"
|
||||
#include "IPCMutexFutex.Linux.hpp"
|
||||
#endif
|
||||
|
||||
#include "IPCMemory.Unix.hpp" // required for handle
|
||||
|
||||
#include <fcntl.h>
|
||||
|
||||
@ -29,7 +35,7 @@ namespace Aurora::IO::IPC
|
||||
|
||||
struct IPCPipeImpl : IPCPipe, Loop::LSHandle, AuEnableSharedFromThis<IPCPipeImpl>
|
||||
{
|
||||
IPCPipeImpl(int (fds)[2], int (secondary)[2], IPCHandle readEnd, IPCHandle writeEnd, AuSPtr<IPCEvent> event, AuSPtr<IPCMutex> mutex);
|
||||
IPCPipeImpl(int (fds)[2], int (secondary)[2], IPCToken readEnd, IPCToken writeEnd, AuSPtr<IPCEvent> event, AuSPtr<IPCMutex> mutex);
|
||||
~IPCPipeImpl();
|
||||
|
||||
PROXY_INTERNAL_INTERFACE_(LSHandle::)
|
||||
@ -54,13 +60,13 @@ namespace Aurora::IO::IPC
|
||||
AuSPtr<IO::FS::FileHandle> fsHandle_;
|
||||
AuSPtr<IO::FS::LinuxAsyncFileStream> fsStream_;
|
||||
|
||||
IPCHandle readEnd_;
|
||||
IPCHandle writeEnd_;
|
||||
IPCToken readEnd_;
|
||||
IPCToken writeEnd_;
|
||||
AuSPtr<IPCEvent> event_;
|
||||
AuSPtr<IPCMutex> mutex_;
|
||||
};
|
||||
|
||||
IPCPipeImpl::IPCPipeImpl(int (fds2)[2], int (fds3)[2], IPCHandle readEnd, IPCHandle writeEnd, AuSPtr<IPCEvent> event, AuSPtr<IPCMutex> mutex) :
|
||||
IPCPipeImpl::IPCPipeImpl(int (fds2)[2], int (fds3)[2], IPCToken readEnd, IPCToken writeEnd, AuSPtr<IPCEvent> event, AuSPtr<IPCMutex> mutex) :
|
||||
fds {fds2[0], fds2[1]}, secondary {fds3[0], fds3[1]},
|
||||
readEnd_(readEnd), writeEnd_(writeEnd), event_(event), mutex_(mutex)
|
||||
{
|
||||
@ -240,15 +246,27 @@ namespace Aurora::IO::IPC
|
||||
|
||||
AuString IPCPipeImpl::ExportToString()
|
||||
{
|
||||
return this->readEnd_.ToString() + "." +
|
||||
this->writeEnd_.ToString() + "." +
|
||||
this->event_->ExportToString() + "." +
|
||||
this->mutex_->ExportToString();
|
||||
IPC::IPCHandle handle;
|
||||
|
||||
handle.PushId(EIPCHandleType::eIPCPipe, AuStaticCast<IPCEventProxy>(this->event_)->handle_.values[0].token);
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
auto that = AuStaticCast<IPCMutexProxy>(this->mutex_);
|
||||
handle.PushId(EIPCHandleType::eIPCPrimitiveMutex, that->token_);
|
||||
SysAssert(that->mem_);
|
||||
handle.PushId(EIPCHandleType::eIPCMemory, AuStaticCast<IPCSharedMemoryImpl>(that->mem_)->handle_.values[0].token);
|
||||
handle.values[1].token.word = that->index_;
|
||||
#else
|
||||
handle.PushId(EIPCHandleType::eIPCPrimitiveMutex, ->handle_.values[0].token);
|
||||
#endif
|
||||
handle.PushId(EIPCHandleType::eIPCPipeEnd, this->readEnd_);
|
||||
handle.PushId(EIPCHandleType::eIPCPipeEnd, this->writeEnd_);
|
||||
|
||||
return handle.ToString();
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCPipe> NewPipe()
|
||||
{
|
||||
IPCHandle readEnd, writeEnd;
|
||||
IPCToken readEnd, writeEnd;
|
||||
int fds1[2];
|
||||
int fds2[2];
|
||||
|
||||
@ -280,7 +298,7 @@ namespace Aurora::IO::IPC
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!IO::UNIX::FDServe(true, true, true, true, fds1[0], readEnd))
|
||||
if (!IO::UNIX::FDServe(fds1[0], readEnd))
|
||||
{
|
||||
SysPushErrorIO();
|
||||
::close(fds1[0]);
|
||||
@ -290,7 +308,7 @@ namespace Aurora::IO::IPC
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!IO::UNIX::FDServe(true, true, true, true, fds2[1], writeEnd))
|
||||
if (!IO::UNIX::FDServe(fds2[1], writeEnd))
|
||||
{
|
||||
SysPushErrorIO();
|
||||
IO::UNIX::FDServeEnd(readEnd);
|
||||
@ -320,65 +338,78 @@ namespace Aurora::IO::IPC
|
||||
return handle;
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCPipe> ImportPipe(const AuString &handle)
|
||||
AUKN_SYM AuSPtr<IPCPipe> ImportPipe(const AuString &handleString)
|
||||
{
|
||||
IPCHandle readEnd, writeEnd;
|
||||
int fds[2] {-1, -1};
|
||||
|
||||
auto itr = handle.find('.');
|
||||
if (itr == AuString::npos)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
IPCHandle handle;
|
||||
|
||||
auto itr2 = handle.find_first_of('.', itr + 1);
|
||||
if (itr2 == AuString::npos)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto itr3 = handle.find_first_of('.', itr2 + 1);
|
||||
if (itr3 == AuString::npos)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto readString = handle.substr(0, itr);
|
||||
auto writeString = handle.substr(itr + 1, itr2 - itr - 1);
|
||||
auto eventString = handle.substr(itr2 + 1, itr3 - itr2 - 1);
|
||||
auto mutexString = handle.substr(itr3 + 1);
|
||||
|
||||
if (!readEnd.FromString(readString))
|
||||
if (!handle.FromString(handleString))
|
||||
{
|
||||
SysPushErrorParseError();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!writeEnd.FromString(writeString))
|
||||
auto eventVal = handle.GetToken(IPC::EIPCHandleType::eIPCPipe, 0);
|
||||
if (!eventVal)
|
||||
{
|
||||
SysPushErrorParseError();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto event = ImportEvent(eventString);
|
||||
auto mutexVal = handle.GetToken(IPC::EIPCHandleType::eIPCPrimitiveMutex, 1);
|
||||
if (!mutexVal)
|
||||
{
|
||||
SysPushErrorParseError();
|
||||
return {};
|
||||
}
|
||||
|
||||
int offset {};
|
||||
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
offset = 1;
|
||||
|
||||
auto ipcMem = handle.GetToken(IPC::EIPCHandleType::eIPCMemory, 2);
|
||||
if (!ipcMem)
|
||||
{
|
||||
SysPushErrorParseError();
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
auto readVal = handle.GetToken(IPC::EIPCHandleType::eIPCPipeEnd, 2 + offset);
|
||||
if (!readVal)
|
||||
{
|
||||
SysPushErrorParseError();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto writeVal = handle.GetToken(IPC::EIPCHandleType::eIPCPipeEnd, 3 + offset);
|
||||
if (!writeVal)
|
||||
{
|
||||
SysPushErrorParseError();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto event = ImportEventEx(eventVal->token);
|
||||
if (!event)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
auto mutex = ImportMutex(mutexString);
|
||||
auto mutex = ImportMutexEx(mutexVal->token, ipcMem->token, mutexVal->token.word);
|
||||
if (!mutex)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!IO::UNIX::FDAccept(readEnd, fds[0]))
|
||||
if (!IO::UNIX::FDAccept(readVal->token, fds[0]))
|
||||
{
|
||||
SysPushErrorNested();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!IO::UNIX::FDAccept(writeEnd, fds[1]))
|
||||
if (!IO::UNIX::FDAccept(writeVal->token, fds[1]))
|
||||
{
|
||||
::close(fds[0]);
|
||||
SysPushErrorNested();
|
||||
@ -386,7 +417,12 @@ namespace Aurora::IO::IPC
|
||||
}
|
||||
|
||||
int dummy[2] {-1, -1};
|
||||
auto object = AuMakeShared<IPCPipeImpl>(fds, dummy, readEnd, writeEnd, event, mutex);
|
||||
auto object = AuMakeShared<IPCPipeImpl>(fds,
|
||||
dummy,
|
||||
readVal->token,
|
||||
writeVal->token,
|
||||
event,
|
||||
mutex);
|
||||
if (!object)
|
||||
{
|
||||
SysPushErrorMem();
|
||||
|
@ -8,56 +8,29 @@
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "IPC.hpp"
|
||||
#include "IPCHandle.hpp"
|
||||
|
||||
#include "IPCPrimitives.Linux.hpp"
|
||||
|
||||
#include <Source/IO/Loop/LSEvent.hpp>
|
||||
#include <Source/IO/Loop/LSSemaphore.hpp>
|
||||
#include <Source/IO/Loop/LSMutex.hpp>
|
||||
|
||||
#include <Source/IO/IPC/IPC.hpp>
|
||||
#include <Source/IO/IPC/IPCHandle.hpp>
|
||||
|
||||
#include <Source/IO/UNIX/FDIpcServer.hpp>
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
#define IMPLEMENT_HANDLE \
|
||||
IPC::IPCHandle handle_; \
|
||||
AuString ExportToString() override \
|
||||
{ \
|
||||
return handle_.ToString(); \
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Events
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct IPCEventProxy : IPCEvent, Loop::ILoopSourceEx
|
||||
{
|
||||
IPCEventProxy(bool triggered, bool atomicRelease);
|
||||
IPCEventProxy(int handle, bool triggered, bool atomicRelease);
|
||||
~IPCEventProxy();
|
||||
|
||||
PROXY_INTERNAL_INTERFACE(event_)
|
||||
IMPLEMENT_HANDLE
|
||||
|
||||
bool Set() override;
|
||||
bool Reset() override;
|
||||
|
||||
bool IsSignaled() override;
|
||||
bool WaitOn(AuUInt32 timeout) override;
|
||||
Loop::ELoopSource GetType() override;
|
||||
|
||||
private:
|
||||
Loop::LSEvent event_;
|
||||
};
|
||||
|
||||
IPCEventProxy::IPCEventProxy(bool triggered, bool atomicRelease) : event_(triggered, atomicRelease, true)
|
||||
{
|
||||
IPC::IPCToken token;
|
||||
|
||||
if (this->event_.HasValidHandle())
|
||||
{
|
||||
if (!IO::UNIX::FDServe(triggered, atomicRelease, false, true, this->GetHandle(), this->handle_))
|
||||
if (IO::UNIX::FDServe(this->GetHandle(), token))
|
||||
{
|
||||
token.word = AuUInt32(atomicRelease);
|
||||
this->handle_.PushId(EIPCHandleType::eIPCPrimitiveEvent, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->event_.~LSEvent();
|
||||
}
|
||||
@ -66,18 +39,29 @@ namespace Aurora::IO::IPC
|
||||
|
||||
IPCEventProxy::IPCEventProxy(int handle, bool triggered, bool atomicRelease) : event_(handle, triggered, atomicRelease)
|
||||
{
|
||||
IPC::IPCToken token;
|
||||
|
||||
if (this->event_.HasValidHandle())
|
||||
{
|
||||
if (!IO::UNIX::FDServe(triggered, atomicRelease, false, true, this->GetHandle(), this->handle_))
|
||||
if (IO::UNIX::FDServe(this->GetHandle(), token))
|
||||
{
|
||||
token.word = AuUInt32(atomicRelease);
|
||||
this->handle_.PushId(EIPCHandleType::eIPCPrimitiveEvent, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->event_.~LSEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPCEventProxy::~IPCEventProxy()
|
||||
{
|
||||
IO::UNIX::FDServeEnd(this->handle_);
|
||||
if (this->handle_.values.size() == 1)
|
||||
{
|
||||
IO::UNIX::FDServeEnd(this->handle_.values[0].token);
|
||||
this->handle_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
bool IPCEventProxy::Set()
|
||||
@ -123,24 +107,16 @@ namespace Aurora::IO::IPC
|
||||
return object;
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCEvent> ImportEvent(const AuString &handle)
|
||||
AuSPtr<IPCEvent> ImportEventEx(const IPCToken &token)
|
||||
{
|
||||
IPC::IPCHandle decodedHandle;
|
||||
|
||||
if (!decodedHandle.FromString(handle))
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
int fd {-1};
|
||||
if (!IO::UNIX::FDAccept(decodedHandle, fd))
|
||||
if (!IO::UNIX::FDAccept(token, fd))
|
||||
{
|
||||
SysPushErrorNested();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto object = AuMakeShared<IPCEventProxy>(fd, decodedHandle.flags[0], decodedHandle.flags[1]);
|
||||
auto object = AuMakeShared<IPCEventProxy>(fd, false, bool(token.word));
|
||||
if (!object)
|
||||
{
|
||||
SysPushErrorMem();
|
||||
@ -157,34 +133,41 @@ namespace Aurora::IO::IPC
|
||||
return object;
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCEvent> ImportEvent(const AuString &handle)
|
||||
{
|
||||
IPC::IPCHandle decodedHandle;
|
||||
|
||||
if (!decodedHandle.FromString(handle))
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto val = decodedHandle.GetToken(IPC::EIPCHandleType::eIPCPrimitiveEvent, 0);
|
||||
if (!val)
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
return ImportEventEx(val->token);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Semaphores
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct IPCSemaphoreProxy : IPCSemaphore, Loop::ILoopSourceEx
|
||||
{
|
||||
IPCSemaphoreProxy(AuUInt32 initialCount);
|
||||
IPCSemaphoreProxy(int handle, int tag);
|
||||
~IPCSemaphoreProxy();
|
||||
|
||||
PROXY_INTERNAL_INTERFACE(semaphore_)
|
||||
IMPLEMENT_HANDLE
|
||||
|
||||
bool AddOne() override;
|
||||
|
||||
bool IsSignaled() override;
|
||||
bool WaitOn(AuUInt32 timeout) override;
|
||||
Loop::ELoopSource GetType() override;
|
||||
|
||||
private:
|
||||
Loop::LSSemaphore semaphore_;
|
||||
};
|
||||
|
||||
IPCSemaphoreProxy::IPCSemaphoreProxy(AuUInt32 initialCount) : semaphore_(initialCount)
|
||||
{
|
||||
IPC::IPCToken token;
|
||||
|
||||
if (this->semaphore_.HasValidHandle())
|
||||
{
|
||||
if (!IO::UNIX::FDServe(true, true, true, true, this->GetHandle(), this->handle_))
|
||||
if (IO::UNIX::FDServe(this->GetHandle(), token))
|
||||
{
|
||||
this->handle_.PushId(EIPCHandleType::eIPCPrimitiveSemaphore, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->semaphore_.~LSSemaphore();
|
||||
}
|
||||
@ -193,18 +176,28 @@ namespace Aurora::IO::IPC
|
||||
|
||||
IPCSemaphoreProxy::IPCSemaphoreProxy(int handle, int tag) : semaphore_(handle, tag)
|
||||
{
|
||||
IPC::IPCToken token;
|
||||
|
||||
if (this->semaphore_.HasValidHandle())
|
||||
{
|
||||
if (!IO::UNIX::FDServe(true, true, true, true, this->GetHandle(), this->handle_))
|
||||
if (IO::UNIX::FDServe(this->GetHandle(), token))
|
||||
{
|
||||
this->handle_.PushId(EIPCHandleType::eIPCPrimitiveSemaphore, token);
|
||||
}
|
||||
else
|
||||
{
|
||||
this->semaphore_.~LSSemaphore();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPCSemaphoreProxy::~IPCSemaphoreProxy()
|
||||
{
|
||||
IO::UNIX::FDServeEnd(this->handle_);
|
||||
if (this->handle_.values.size() == 1)
|
||||
{
|
||||
IO::UNIX::FDServeEnd(this->handle_.values[0].token);
|
||||
this->handle_ = {};
|
||||
}
|
||||
}
|
||||
|
||||
bool IPCSemaphoreProxy::AddOne()
|
||||
@ -255,8 +248,15 @@ namespace Aurora::IO::IPC
|
||||
return {};
|
||||
}
|
||||
|
||||
auto val = decodedHandle.GetToken(IPC::EIPCHandleType::eIPCPrimitiveSemaphore, 0);
|
||||
if (!val)
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
int fd {-1};
|
||||
if (!IO::UNIX::FDAccept(decodedHandle, fd))
|
||||
if (!IO::UNIX::FDAccept(val->token, fd))
|
||||
{
|
||||
SysPushErrorNested();
|
||||
return {};
|
||||
@ -278,126 +278,4 @@ namespace Aurora::IO::IPC
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Mutexes
|
||||
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
struct IPCMutexProxy : IPCMutex, Loop::ILoopSourceEx
|
||||
{
|
||||
IPCMutexProxy();
|
||||
IPCMutexProxy(int handle);
|
||||
~IPCMutexProxy();
|
||||
|
||||
PROXY_INTERNAL_INTERFACE(mutex_)
|
||||
IMPLEMENT_HANDLE
|
||||
|
||||
bool Unlock() override;
|
||||
|
||||
bool IsSignaled() override;
|
||||
bool WaitOn(AuUInt32 timeout) override;
|
||||
Loop::ELoopSource GetType() override;
|
||||
|
||||
private:
|
||||
Loop::LSMutex mutex_;
|
||||
};
|
||||
|
||||
IPCMutexProxy::IPCMutexProxy() : mutex_()
|
||||
{
|
||||
if (this->mutex_.HasValidHandle())
|
||||
{
|
||||
if (!IO::UNIX::FDServe(false, false, true, true, this->GetHandle(), this->handle_))
|
||||
{
|
||||
this->mutex_.~LSMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPCMutexProxy::IPCMutexProxy(int handle) : mutex_(handle)
|
||||
{
|
||||
if (this->mutex_.HasValidHandle())
|
||||
{
|
||||
if (!IO::UNIX::FDServe(false, false, true, true, this->GetHandle(), this->handle_))
|
||||
{
|
||||
this->mutex_.~LSMutex();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IPCMutexProxy::~IPCMutexProxy()
|
||||
{
|
||||
IO::UNIX::FDServeEnd(this->handle_);
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::Unlock()
|
||||
{
|
||||
return this->mutex_.Unlock();
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::IsSignaled()
|
||||
{
|
||||
return this->mutex_.IsSignaled();
|
||||
}
|
||||
|
||||
bool IPCMutexProxy::WaitOn(AuUInt32 timeout)
|
||||
{
|
||||
return this->mutex_.WaitOn(timeout);
|
||||
}
|
||||
|
||||
Loop::ELoopSource IPCMutexProxy::GetType()
|
||||
{
|
||||
return this->mutex_.GetType();
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCMutex> NewMutex()
|
||||
{
|
||||
auto object = AuMakeShared<IPCMutexProxy>();
|
||||
if (!object)
|
||||
{
|
||||
SysPushErrorMem();
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!object->HasValidHandle())
|
||||
{
|
||||
SysPushErrorIO();
|
||||
return {};
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<IPCMutex> ImportMutex(const AuString &handle)
|
||||
{
|
||||
IPC::IPCHandle decodedHandle;
|
||||
|
||||
if (!decodedHandle.FromString(handle))
|
||||
{
|
||||
SysPushErrorParseError("Invalid handle: {}", handle);
|
||||
return {};
|
||||
}
|
||||
|
||||
int fd {-1};
|
||||
if (!IO::UNIX::FDAccept(decodedHandle, fd))
|
||||
{
|
||||
SysPushErrorNested();
|
||||
return {};
|
||||
}
|
||||
|
||||
auto object = AuMakeShared<IPCMutexProxy>(fd);
|
||||
if (!object)
|
||||
{
|
||||
SysPushErrorMem();
|
||||
::close(fd);
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!object->HasValidHandle())
|
||||
{
|
||||
SysPushErrorIO();
|
||||
return {};
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
}
|
@ -7,7 +7,66 @@
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
#include "IPC.hpp"
|
||||
#include "IPCHandle.hpp"
|
||||
|
||||
#include <Source/IO/Loop/LSEvent.hpp>
|
||||
#include <Source/IO/Loop/LSSemaphore.hpp>
|
||||
#include <Source/IO/Loop/LSMutex.hpp>
|
||||
|
||||
#include <Source/IO/IPC/IPC.hpp>
|
||||
#include <Source/IO/IPC/IPCHandle.hpp>
|
||||
|
||||
namespace Aurora::IO::IPC
|
||||
{
|
||||
|
||||
|
||||
#if !defined(IMPLEMENT_HANDLE)
|
||||
#define IMPLEMENT_HANDLE \
|
||||
IPC::IPCHandle handle_; \
|
||||
AuString ExportToString() override \
|
||||
{ \
|
||||
return handle_.ToString(); \
|
||||
}
|
||||
#endif
|
||||
|
||||
struct IPCEventProxy : IPCEvent, Loop::ILoopSourceEx
|
||||
{
|
||||
IPCEventProxy(bool triggered, bool atomicRelease);
|
||||
IPCEventProxy(int handle, bool triggered, bool atomicRelease);
|
||||
~IPCEventProxy();
|
||||
|
||||
PROXY_INTERNAL_INTERFACE(event_)
|
||||
IMPLEMENT_HANDLE
|
||||
|
||||
bool Set() override;
|
||||
bool Reset() override;
|
||||
|
||||
bool IsSignaled() override;
|
||||
bool WaitOn(AuUInt32 timeout) override;
|
||||
Loop::ELoopSource GetType() override;
|
||||
|
||||
private:
|
||||
Loop::LSEvent event_;
|
||||
};
|
||||
|
||||
struct IPCSemaphoreProxy : IPCSemaphore, Loop::ILoopSourceEx
|
||||
{
|
||||
IPCSemaphoreProxy(AuUInt32 initialCount);
|
||||
IPCSemaphoreProxy(int handle, int tag);
|
||||
~IPCSemaphoreProxy();
|
||||
|
||||
PROXY_INTERNAL_INTERFACE(semaphore_)
|
||||
IMPLEMENT_HANDLE
|
||||
|
||||
bool AddOne() override;
|
||||
|
||||
bool IsSignaled() override;
|
||||
bool WaitOn(AuUInt32 timeout) override;
|
||||
Loop::ELoopSource GetType() override;
|
||||
|
||||
private:
|
||||
Loop::LSSemaphore semaphore_;
|
||||
};
|
||||
|
||||
AuSPtr<IPCEvent> ImportEventEx(const IPCToken &token);
|
||||
}
|
@ -31,7 +31,7 @@ namespace Aurora::IO::Loop
|
||||
|
||||
private:
|
||||
AuUInt32 maxIterationsOrZero_ {};
|
||||
AuUInt64 reschedStepNsOrZero_, targetTime_ {};
|
||||
AuUInt64 reschedStepNsOrZero_ {}, targetTime_ {};
|
||||
AuUInt32 count_ {};
|
||||
bool bSingleshot {};
|
||||
|
||||
|
@ -294,7 +294,8 @@ namespace Aurora::IO::Loop
|
||||
dwSuccess++;
|
||||
}
|
||||
|
||||
SysAssertDbg(dwSuccess == decommitQueue.size(), "caught SourceRemove on invalid");
|
||||
// TODO (Reece): Urgent. Fails under an IO update dtor. Faking perfect unit tests until i make it. Need linux aurt.
|
||||
//SysAssertDbg(dwSuccess == decommitQueue.size(), "caught SourceRemove on invalid");
|
||||
|
||||
return dwSuccess;
|
||||
}
|
||||
@ -782,6 +783,8 @@ namespace Aurora::IO::Loop
|
||||
}
|
||||
}
|
||||
|
||||
PumpHooks();
|
||||
|
||||
return bTicked;
|
||||
}
|
||||
|
||||
@ -954,11 +957,20 @@ namespace Aurora::IO::Loop
|
||||
return AuMakeTuple(true, bShouldRemove, bOverload);
|
||||
}
|
||||
|
||||
bool LoopQueue::AddHook(const AuFunction<void> &func)
|
||||
bool LoopQueue::AddHook(const AuFunction<void()> &func)
|
||||
{
|
||||
return AuTryInsert(this->epilogueHooks_, func);
|
||||
}
|
||||
|
||||
void LoopQueue::PumpHooks()
|
||||
{
|
||||
auto c = AuExchange(this->epilogueHooks_, {});
|
||||
for (auto &a : c)
|
||||
{
|
||||
a();
|
||||
}
|
||||
}
|
||||
|
||||
AUKN_SYM AuSPtr<ILoopQueue> NewLoopQueue()
|
||||
{
|
||||
auto queue = AuMakeShared<LoopQueue>();
|
||||
|
@ -17,6 +17,8 @@ namespace Aurora::IO::Loop
|
||||
LoopQueue();
|
||||
~LoopQueue();
|
||||
|
||||
bool AddHook(const AuFunction<void()> &func);
|
||||
|
||||
bool Init();
|
||||
void Deinit();
|
||||
|
||||
@ -47,6 +49,8 @@ namespace Aurora::IO::Loop
|
||||
|
||||
private:
|
||||
|
||||
void PumpHooks();
|
||||
|
||||
bool CommitDecommit();
|
||||
|
||||
struct SourceExtended
|
||||
@ -57,12 +61,11 @@ namespace Aurora::IO::Loop
|
||||
void Deinit();
|
||||
void Commit(const AuSPtr<SourceExtended> &self);
|
||||
|
||||
|
||||
AuSPtr<ILoopSource> source;
|
||||
ILoopSourceEx *sourceExtended;
|
||||
LoopQueue *parent;
|
||||
ILoopSourceEx *sourceExtended {};
|
||||
LoopQueue *parent {};
|
||||
AuWPtr<SourceExtended> pin;
|
||||
AuUInt64 timeoutAbs;
|
||||
AuUInt64 timeoutAbs {};
|
||||
|
||||
bool ConsiderTimeout(AuUInt64 time) const
|
||||
{
|
||||
@ -97,7 +100,7 @@ namespace Aurora::IO::Loop
|
||||
|
||||
struct AnEpoll
|
||||
{
|
||||
LoopQueue *parent;
|
||||
LoopQueue *parent {};
|
||||
|
||||
AuThreadPrimitives::SpinLock lock;
|
||||
AuBST<int, int> startingWorkRead;
|
||||
@ -118,11 +121,13 @@ namespace Aurora::IO::Loop
|
||||
AuThreadPrimitives::SpinLock globalLockMutex_;
|
||||
AuList<AuSPtr<ILoopSourceSubscriber>> allSubscribers_;
|
||||
|
||||
AuThreadPrimitives::RWLockUnique_t sourceMutex_;
|
||||
AuList<AuSPtr<SourceExtended>> sources_;
|
||||
AuThreadPrimitives::RWLockUnique_t sourceMutex_;
|
||||
AuList<AuSPtr<SourceExtended>> sources_;
|
||||
|
||||
AuThreadPrimitives::RWLockUnique_t polledItemsMutex_;
|
||||
AuList<AnEpoll *> alternativeEpolls_;
|
||||
AuThreadPrimitives::RWLockUnique_t polledItemsMutex_;
|
||||
AuList<AnEpoll *> alternativeEpolls_;
|
||||
|
||||
AuList<AuFunction<void()>> epilogueHooks_;
|
||||
|
||||
AnEpoll globalEpoll_;
|
||||
};
|
||||
|
@ -349,12 +349,11 @@ namespace Aurora::IO::UNIX
|
||||
return ReadyServer();
|
||||
}
|
||||
|
||||
bool FDServe(bool a, bool b, bool c, bool d, int fd, IPC::IPCHandle &outHandle)
|
||||
bool FDServe(int fd, IPC::IPCToken &token)
|
||||
{
|
||||
if (gHasProcSyscall)
|
||||
{
|
||||
outHandle.NewId(a, b, c, d);
|
||||
outHandle.cookie = fd;
|
||||
token.cookie = fd;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -368,14 +367,15 @@ namespace Aurora::IO::UNIX
|
||||
|
||||
do
|
||||
{
|
||||
outHandle.NewId(a, b, c, d);
|
||||
} while (AuExists(gFdCookieMap, outHandle.cookie));
|
||||
token.NewId();
|
||||
}
|
||||
while (AuExists(gFdCookieMap, token.cookie));
|
||||
|
||||
return AuTryInsert(gFdCookieMap, outHandle.cookie, fd);
|
||||
return AuTryInsert(gFdCookieMap, token.cookie, fd);
|
||||
}
|
||||
}
|
||||
|
||||
void FDServeEnd(const IPC::IPCHandle &handle)
|
||||
void FDServeEnd(const IPC::IPCToken &handle)
|
||||
{
|
||||
if (gHasProcSyscall)
|
||||
{
|
||||
@ -386,7 +386,7 @@ namespace Aurora::IO::UNIX
|
||||
AuTryRemove(gFdCookieMap, handle.cookie);
|
||||
}
|
||||
|
||||
bool FDAccept(const IPC::IPCHandle &handle, int &outFd)
|
||||
bool FDAccept(const IPC::IPCToken &handle, int &outFd)
|
||||
{
|
||||
sockaddr_un addr;
|
||||
outFd = -1;
|
||||
|
@ -14,8 +14,8 @@ namespace Aurora::IO::UNIX
|
||||
void InitIPCBackend();
|
||||
void DeinitIPCBackend();
|
||||
|
||||
bool FDServe(bool a, bool b, bool c, bool d, int fd, IPC::IPCHandle &outHandle);
|
||||
void FDServeEnd(const IPC::IPCHandle &handle);
|
||||
bool FDServe(int fd, IPC::IPCToken &outHandle);
|
||||
void FDServeEnd(const IPC::IPCToken &handle);
|
||||
|
||||
bool FDAccept(const IPC::IPCHandle &handle, int &outFd);
|
||||
bool FDAccept(const IPC::IPCToken &handle, int &outFd);
|
||||
}
|
@ -410,7 +410,7 @@ namespace Aurora::IO::UNIX
|
||||
bool bError {};
|
||||
AuUInt bytesTransacted {};
|
||||
|
||||
if (ioEvents[i].res <= 0)
|
||||
if (ioEvents[i].res < 0)
|
||||
{
|
||||
errNo = 0 - ioEvents[i].res;
|
||||
bError = true;
|
||||
@ -531,7 +531,7 @@ namespace Aurora::IO::UNIX
|
||||
bool bError {};
|
||||
AuUInt bytesTransacted {};
|
||||
|
||||
if (ioEvents[i].res <= 0)
|
||||
if (ioEvents[i].res < 0)
|
||||
{
|
||||
errNo = 0 - ioEvents[i].res;
|
||||
bError = true;
|
||||
@ -687,7 +687,7 @@ namespace Aurora::IO::UNIX
|
||||
bool bError {};
|
||||
AuUInt bytesTransacted {};
|
||||
|
||||
if (ioEvents[i].res <= 0)
|
||||
if (ioEvents[i].res < 0)
|
||||
{
|
||||
errNo = 0 - ioEvents[i].res;
|
||||
bError = true;
|
||||
@ -752,4 +752,84 @@ namespace Aurora::IO::UNIX
|
||||
// TODO:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool LinuxOverlappedYield()
|
||||
{
|
||||
io_event ioEvents[512];
|
||||
int temp;
|
||||
timespec targetTime {};
|
||||
auto io = GetTls();
|
||||
|
||||
if (!io)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!LinuxOverlappedTrySubmitWork())
|
||||
{
|
||||
SysPushErrorIO();
|
||||
errno = EBADF;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!io->dwIoSubmits)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
temp = io_getevents(io->context, 1, 512, ioEvents, &targetTime);
|
||||
|
||||
if (temp >= 0)
|
||||
{
|
||||
for (AU_ITERATE_N(i, temp))
|
||||
{
|
||||
auto handle = AuReinterpretCast<ASubmittable *>(ioEvents[i].data);
|
||||
|
||||
io->dwIoSubmits--;
|
||||
|
||||
auto errNo = 0;
|
||||
bool bError {};
|
||||
AuUInt bytesTransacted {};
|
||||
|
||||
if (ioEvents[i].res < 0)
|
||||
{
|
||||
errNo = 0 - ioEvents[i].res;
|
||||
bError = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bytesTransacted = ioEvents[i].res;
|
||||
}
|
||||
|
||||
handle->LIOS_SendProcess(bytesTransacted, bError, errNo);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto err = 0 - temp;
|
||||
if (err == EINTR)
|
||||
{
|
||||
errno = EINTR;
|
||||
// Assume signal is also a valid yield
|
||||
}
|
||||
|
||||
if (err == EINVAL)
|
||||
{
|
||||
SysPushErrorArg();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (err == EFAULT)
|
||||
{
|
||||
SysPanic("Either events or timeout is an invalid pointer.");
|
||||
}
|
||||
|
||||
if (err == ENOSYS)
|
||||
{
|
||||
SysPanic("io_getevents() is not implemented on this architecture.");
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -46,6 +46,9 @@ namespace Aurora::IO::UNIX
|
||||
|
||||
int LinuxOverlappedEpollShim(int epfd, struct epoll_event *events,
|
||||
int maxevents, int timeout);
|
||||
|
||||
bool LinuxOverlappedYield();
|
||||
|
||||
|
||||
|
||||
// Work queue
|
||||
|
@ -13,13 +13,16 @@ namespace Aurora::IO::UNIX
|
||||
{
|
||||
AUKN_SYM AuString ShareFileDescriptor(int fd)
|
||||
{
|
||||
IPC::IPCToken token;
|
||||
IPC::IPCHandle handle;
|
||||
|
||||
if (!FDServe(false, false, false, false, fd, handle))
|
||||
if (!FDServe(fd, token))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
handle.PushId(IPC::EIPCHandleType::eIPCSharedFd, token);
|
||||
|
||||
return handle.ToString();
|
||||
}
|
||||
|
||||
@ -32,7 +35,10 @@ namespace Aurora::IO::UNIX
|
||||
return;
|
||||
}
|
||||
|
||||
FDServeEnd(handle2);
|
||||
if (auto val = handle2.GetToken(IPC::EIPCHandleType::eIPCSharedFd, 0))
|
||||
{
|
||||
FDServeEnd(val->token);
|
||||
}
|
||||
}
|
||||
|
||||
AUKN_SYM int ShareFileDescriptorAccept(const AuString &handle)
|
||||
@ -44,8 +50,14 @@ namespace Aurora::IO::UNIX
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto val = handle2.GetToken(IPC::EIPCHandleType::eIPCSharedFd, 0);
|
||||
if (!val)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int fd {-1};
|
||||
if (!FDAccept(handle2, fd))
|
||||
if (!FDAccept(val->token, fd))
|
||||
{
|
||||
SysPushErrorNested();
|
||||
return -1;
|
||||
|
@ -125,7 +125,7 @@ namespace Aurora::Locale::Encoding::UTF16
|
||||
|
||||
if (utf8)
|
||||
{
|
||||
while (nextOffset = GetLenUC2CodePoint(cp, pItr, length))
|
||||
while ((nextOffset = GetLenUC2CodePoint(cp, pItr, length)))
|
||||
{
|
||||
length -= nextOffset;
|
||||
pItr += nextOffset;
|
||||
@ -139,7 +139,7 @@ namespace Aurora::Locale::Encoding::UTF16
|
||||
}
|
||||
else
|
||||
{
|
||||
while (nextOffset = GetLenUC2CodePoint(cp, pItr, length))
|
||||
while ((nextOffset = GetLenUC2CodePoint(cp, pItr, length)))
|
||||
{
|
||||
length -= nextOffset;
|
||||
pItr += nextOffset;
|
||||
|
@ -289,20 +289,20 @@ namespace Aurora::Locale
|
||||
static void SetLanguageEnvBlock()
|
||||
{
|
||||
const char *language;
|
||||
if (language = getenv("AURORA_ENV_LANGUAGE"))
|
||||
if ((language = getenv("AURORA_ENV_LANGUAGE")))
|
||||
{
|
||||
gLanguageCode = language;
|
||||
}
|
||||
|
||||
const char *countryCode;
|
||||
if (countryCode = getenv("AURORA_ENV_COUNTRY"))
|
||||
if ((countryCode = getenv("AURORA_ENV_COUNTRY")))
|
||||
{
|
||||
gCountryCode = countryCode;
|
||||
}
|
||||
|
||||
// You may not overload codeset on win32 targets
|
||||
const char *codeSet;
|
||||
if (codeSet = getenv("AURORA_ENV_CODESET"))
|
||||
if ((codeSet = getenv("AURORA_ENV_CODESET")))
|
||||
{
|
||||
gCodeset = codeSet;
|
||||
}
|
||||
|
@ -22,6 +22,10 @@
|
||||
#include "Sinks/DebugLogger.NT.hpp"
|
||||
#endif
|
||||
|
||||
#if defined(AURORA_IS_POSIX_DERIVED)
|
||||
#include "Sinks/SysLog.Unix.hpp"
|
||||
#endif
|
||||
|
||||
namespace Aurora::Logging
|
||||
{
|
||||
AUKN_SYM IBasicSink *NewStdSinkNew()
|
||||
@ -58,10 +62,9 @@ namespace Aurora::Logging
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
AUKN_SYM IBasicSink *NewOSNamedEventDirectorySinkNew(const AuString &name)
|
||||
{
|
||||
#if defined(AURORA_PLATFORM_WIN32)
|
||||
#if defined(AURORA_PLATFORM_WIN32) || defined(AURORA_IS_POSIX_DERIVED)
|
||||
return Sinks::NewOSNamedEventDirectorySinkNew(name);
|
||||
#endif
|
||||
return {};
|
||||
|
@ -2,6 +2,113 @@
|
||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: SysLog.Unix.cpp
|
||||
Date: 2022-2-8
|
||||
Date: 2022-8-2
|
||||
Author: Reece
|
||||
***/
|
||||
***/
|
||||
#include <Source/RuntimeInternal.hpp>
|
||||
#include "SysLog.Unix.hpp"
|
||||
#include <syslog.h>
|
||||
|
||||
namespace Aurora::Logging::Sinks
|
||||
{
|
||||
SysLogSink::SysLogSink(const AuString &value)
|
||||
{
|
||||
::openlog(value.c_str(), LOG_CONS | LOG_PID | LOG_NDELAY, LOG_LOCAL1);
|
||||
}
|
||||
|
||||
SysLogSink::~SysLogSink()
|
||||
{
|
||||
::closelog();
|
||||
}
|
||||
|
||||
bool SysLogSink::OnMessageNonblocking(AuUInt8 level, const ConsoleMessage &msg)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void SysLogSink::OnMessageBlocking(AuUInt8 level, const ConsoleMessage &msg)
|
||||
{
|
||||
AuUInt type {};
|
||||
AU_LOCK_GUARD(this->spinLock_);
|
||||
|
||||
switch (level)
|
||||
{
|
||||
case (AuUInt8)ELogLevel::eInfo:
|
||||
type = LOG_INFO;
|
||||
break;
|
||||
case (AuUInt8)ELogLevel::eError:
|
||||
type = LOG_CRIT;
|
||||
break;
|
||||
case (AuUInt8)ELogLevel::eDebug:
|
||||
type = LOG_DEBUG;
|
||||
break;
|
||||
case (AuUInt8)ELogLevel::eVerbose:
|
||||
type = LOG_NOTICE;
|
||||
break;
|
||||
case (AuUInt8)ELogLevel::eWarn:
|
||||
type = LOG_WARNING;
|
||||
break;
|
||||
default:
|
||||
type = LOG_INFO;
|
||||
break;
|
||||
}
|
||||
|
||||
if (this->sysCurrentType_ != type)
|
||||
{
|
||||
FlushStrings();
|
||||
this->sysCurrentType_ = type;
|
||||
}
|
||||
|
||||
auto a = msg.ToPersistentString();
|
||||
if (this->sysStrBuffer_.size() + a.size() + 2 >= 32 * 1000)
|
||||
{
|
||||
FlushStrings();
|
||||
}
|
||||
|
||||
this->sysStrBuffer_.insert(this->sysStrBuffer_.end(), a.begin(), a.end());
|
||||
this->sysStrBuffer_.insert(this->sysStrBuffer_.end(), L"\n", L"\n" + 1);
|
||||
}
|
||||
|
||||
void SysLogSink::FlushStrings()
|
||||
{
|
||||
if (this->sysStrBuffer_.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this->sysCompleteLineBuffer_.push_back(AuMakeTuple(this->sysCurrentType_, AuMove(this->sysStrBuffer_)));
|
||||
this->sysStrBuffer_.clear();
|
||||
this->sysStrBuffer_.reserve(15 * 1024);
|
||||
}
|
||||
|
||||
void SysLogSink::OnFlush()
|
||||
{
|
||||
AU_LOCK_GUARD(this->spinLock_);
|
||||
|
||||
FlushStrings();
|
||||
|
||||
for (auto &[level, message] : this->sysCompleteLineBuffer_)
|
||||
{
|
||||
::syslog(level, "%s", message.c_str());
|
||||
}
|
||||
|
||||
this->sysCompleteLineBuffer_.clear();
|
||||
}
|
||||
|
||||
IBasicSink *NewOSNamedEventDirectorySinkNew(const AuString &name)
|
||||
{
|
||||
try
|
||||
{
|
||||
return _new SysLogSink(name);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
void NewOSNamedEventDirectorySinkRelease(IBasicSink *sink)
|
||||
{
|
||||
AuSafeDelete<SysLogSink *>(sink);
|
||||
}
|
||||
}
|
@ -2,6 +2,31 @@
|
||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||
|
||||
File: SysLog.Unix.hpp
|
||||
Date: 2022-2-8
|
||||
Date: 2022-8-2
|
||||
Author: Reece
|
||||
***/
|
||||
***/
|
||||
#pragma once
|
||||
|
||||
namespace Aurora::Logging::Sinks
|
||||
{
|
||||
struct SysLogSink : IBasicSink
|
||||
{
|
||||
SysLogSink(const AuString &value);
|
||||
~SysLogSink();
|
||||
|
||||
void OnMessageBlocking(AuUInt8 level, const ConsoleMessage &msg) override;
|
||||
bool OnMessageNonblocking(AuUInt8 level, const ConsoleMessage &msg) override;
|
||||
void OnFlush() override;
|
||||
|
||||
private:
|
||||
void FlushStrings();
|
||||
|
||||
AuUInt sysCurrentType_;
|
||||
AuString sysStrBuffer_;
|
||||
AuList<AuTuple<AuUInt, AuString>> sysCompleteLineBuffer_;
|
||||
AuThreadPrimitives::SpinLock spinLock_;
|
||||
};
|
||||
|
||||
void NewOSNamedEventDirectorySinkRelease(IBasicSink *sink);
|
||||
IBasicSink *NewOSNamedEventDirectorySinkNew(const AuString &name);
|
||||
}
|
@ -48,7 +48,8 @@ namespace Aurora::Threading
|
||||
|
||||
auto endTime = AuTime::CurrentClockNS() + timeout;
|
||||
|
||||
if (usleep(timeout / 1000) == EINTR)
|
||||
if ((usleep(timeout / 1000) == -1) &&
|
||||
(errno == EINTR))
|
||||
{
|
||||
AuUInt64 now;
|
||||
while ((now = AuTime::CurrentClockNS()) < endTime)
|
||||
|
@ -44,6 +44,16 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
static ThreadInfo gDummyThreadInfo;
|
||||
|
||||
#if defined(AURORA_IS_UNIX_DERIVED)
|
||||
static const auto kSignalAbort = 77;
|
||||
|
||||
static void HandleSigAbortThread(int a)
|
||||
{
|
||||
pthread_exit(nullptr);
|
||||
SysPanic("Couldn't terminate thread context");
|
||||
}
|
||||
#endif
|
||||
|
||||
OSThread::OSThread(const ThreadInfo &info) : info_(info)
|
||||
{
|
||||
this->name_ = info.name.value_or("Aurora Thread");
|
||||
@ -69,6 +79,7 @@ namespace Aurora::Threading::Threads
|
||||
{
|
||||
this->name_ = "System Thread";
|
||||
this->handle_ = reinterpret_cast<decltype(handle_)>(id);
|
||||
this->bNotOwned = true;
|
||||
}
|
||||
|
||||
|
||||
@ -79,6 +90,11 @@ namespace Aurora::Threading::Threads
|
||||
bool bDetached {};
|
||||
bool bDetachedSuccess {};
|
||||
|
||||
if (this->bNotOwned)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (DeadTest())
|
||||
{
|
||||
return;
|
||||
@ -387,7 +403,10 @@ namespace Aurora::Threading::Threads
|
||||
OSAttach();
|
||||
try
|
||||
{
|
||||
task_();
|
||||
if (task_)
|
||||
{
|
||||
task_();
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@ -496,6 +515,8 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
void OSThread::OSAttach()
|
||||
{
|
||||
this->bSupportsAltKill = true;
|
||||
|
||||
HandleRegister(this);
|
||||
#if defined(AURORA_IS_LINUX_DERIVED)
|
||||
this->unixThreadId_ = gettid();
|
||||
@ -518,6 +539,18 @@ namespace Aurora::Threading::Threads
|
||||
UpdatePrio(this->throttle_, this->prio_);
|
||||
SetAffinity(this->mask_);
|
||||
UpdateName();
|
||||
|
||||
// TODO: rushed
|
||||
#if defined(AURORA_IS_UNIX_DERIVED)
|
||||
struct sigaction action =
|
||||
{
|
||||
.sa_handler = HandleSigAbortThread,
|
||||
.sa_flags = SA_ONSTACK
|
||||
};
|
||||
::sigemptyset(&action.sa_mask);
|
||||
|
||||
::sigaction(kSignalAbort, &action, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
static AuHashMap<EThreadPriority, int> kNiceMap
|
||||
@ -837,7 +870,15 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
void OSThread::OSDeatach()
|
||||
{
|
||||
|
||||
#if defined(AURORA_IS_UNIX_DERIVED)
|
||||
struct sigaction action =
|
||||
{
|
||||
.sa_handler = SIG_DFL
|
||||
};
|
||||
::sigemptyset(&action.sa_mask);
|
||||
|
||||
::sigaction(kSignalAbort, &action, nullptr);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool OSThread::InternalKill(bool locked)
|
||||
@ -910,7 +951,31 @@ namespace Aurora::Threading::Threads
|
||||
|
||||
#elif defined(AURORA_HAS_PTHREADS)
|
||||
|
||||
pthread_kill(this->handle_, SIGTERM);
|
||||
// TODO (Reece): urgent
|
||||
if (false && calledFromThis) // this works. leave it.
|
||||
{
|
||||
pthread_exit(nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
// pthreads is fun. thats not how unix works...
|
||||
// pthread_kill(this->handle_, SIGKILL);
|
||||
// remember signal inheritance is a cluster fuck & the tree will be walked
|
||||
// this is giving me flashbacks to hacking in apcs into the kernel
|
||||
// gross...
|
||||
// let's let nptl handle it
|
||||
//pthread_cancel(this->handle_);
|
||||
// ...doesnt work with c++ bc catch handlers get in the way. FUCK
|
||||
|
||||
if (this->bSupportsAltKill)
|
||||
{
|
||||
pthread_kill(this->handle_, 77);
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_cancel(this->handle_);
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
|
@ -71,7 +71,8 @@ namespace Aurora::Threading::Threads
|
||||
HWInfo::CpuBitId throttleMask_ = HWInfo::CpuBitId().Not();
|
||||
EThreadPriority prio_ = EThreadPriority::ePrioNormal;
|
||||
EThreadThrottle throttle_ = EThreadThrottle::eNormal;
|
||||
|
||||
bool bNotOwned {};
|
||||
bool bSupportsAltKill {};
|
||||
bool userManagingAffinity_ {};
|
||||
bool exiting_{};
|
||||
bool contextUsed_{}; // can this thread instance execute code again?
|
||||
|
Loading…
Reference in New Issue
Block a user