[+] Linux Watcher
[*] Expand watcher API -> Breaking NT [*] Reexpand loop queue API -> Breaking NT [*] Linux CPUInfo clean up [*] Bug fix: mkdir should set execute flag... because directories are special [*] Refactor: Cleanup base64 [*] Bug fix: UNIX path normalization [*] Bug fix: missing O_CREAT flag (au auto-creates) [*] Normalize line endings
This commit is contained in:
parent
cf219eabaa
commit
3defb1bb14
@ -1,146 +1,146 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
namespace Aurora::Async
|
namespace Aurora::Async
|
||||||
{
|
{
|
||||||
#pragma region EASE_OF_READING
|
#pragma region EASE_OF_READING
|
||||||
struct BasicWorkStdFunc : IWorkItemHandler
|
struct BasicWorkStdFunc : IWorkItemHandler
|
||||||
{
|
{
|
||||||
AuFunction<void()> callback;
|
AuFunction<void()> callback;
|
||||||
AuFunction<void()> shutdown; // error
|
AuFunction<void()> shutdown; // error
|
||||||
|
|
||||||
BasicWorkStdFunc(AuFunction<void()> &&callback, AuFunction<void()> &&shutdown) : callback(std::move(callback)), shutdown(std::move(shutdown))
|
BasicWorkStdFunc(AuFunction<void()> &&callback, AuFunction<void()> &&shutdown) : callback(std::move(callback)), shutdown(std::move(shutdown))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
BasicWorkStdFunc(AuFunction<void()> &&callback) : callback(std::move(callback))
|
BasicWorkStdFunc(AuFunction<void()> &&callback) : callback(std::move(callback))
|
||||||
{}
|
{}
|
||||||
|
|
||||||
BasicWorkStdFunc(const AuFunction<void()> &callback) : callback(callback)
|
BasicWorkStdFunc(const AuFunction<void()> &callback) : callback(callback)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
BasicWorkStdFunc(const AuFunction<void()> &callback, const AuFunction<void()> &shutdown) : callback(callback), shutdown(shutdown)
|
BasicWorkStdFunc(const AuFunction<void()> &callback, const AuFunction<void()> &shutdown) : callback(callback), shutdown(shutdown)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if !defined(_CPPSHARP)
|
#if !defined(_CPPSHARP)
|
||||||
void DispatchFrame(ProcessInfo &info) override
|
void DispatchFrame(ProcessInfo &info) override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
Debug::PrintError();
|
Debug::PrintError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() override
|
void Shutdown() override
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (shutdown)
|
if (shutdown)
|
||||||
{
|
{
|
||||||
shutdown();
|
shutdown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (...)
|
catch (...)
|
||||||
{
|
{
|
||||||
Debug::PrintError();
|
Debug::PrintError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/// @hideinitializer
|
/// @hideinitializer
|
||||||
template<typename Frame_t = AuFunction<void()>, typename Cleanup_t = AuFunction<void()>>
|
template<typename Frame_t = AuFunction<void()>, typename Cleanup_t = AuFunction<void()>>
|
||||||
struct WorkItemCallable : IWorkItemHandler
|
struct WorkItemCallable : IWorkItemHandler
|
||||||
{
|
{
|
||||||
Frame_t frame;
|
Frame_t frame;
|
||||||
Cleanup_t cleanup;
|
Cleanup_t cleanup;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void DispatchFrame(ProcessInfo &info) override
|
void DispatchFrame(ProcessInfo &info) override
|
||||||
{
|
{
|
||||||
if constexpr (AuIsBaseOfTemplate<AuFunction, Frame_t>::value)
|
if constexpr (AuIsBaseOfTemplate<AuFunction, Frame_t>::value)
|
||||||
{
|
{
|
||||||
if (!frame)
|
if (!frame)
|
||||||
{
|
{
|
||||||
info.type = IWorkItemHandler::EProcessNext::eFinished;
|
info.type = IWorkItemHandler::EProcessNext::eFinished;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
frame();
|
frame();
|
||||||
info.type = IWorkItemHandler::EProcessNext::eFinished;
|
info.type = IWorkItemHandler::EProcessNext::eFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Shutdown() override
|
void Shutdown() override
|
||||||
{
|
{
|
||||||
if constexpr (AuIsBaseOfTemplate<AuFunction, Cleanup_t>::value)
|
if constexpr (AuIsBaseOfTemplate<AuFunction, Cleanup_t>::value)
|
||||||
{
|
{
|
||||||
if (!cleanup)
|
if (!cleanup)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
cleanup();
|
cleanup();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#define ASYNC_ERROR(exp) { if constexpr (AuIsSame_v<T, bool>) { SysPushErrorGen(exp); return {}; } else { throw AuString(exp); } }
|
#define ASYNC_ERROR(exp) { if constexpr (AuIsSame_v<T, bool>) { SysPushErrorGen(exp); return {}; } else { throw AuString(exp); } }
|
||||||
#define ASYNC_FINISH { if constexpr (AuIsSame_v<T, bool>) { return true; } }
|
#define ASYNC_FINISH { if constexpr (AuIsSame_v<T, bool>) { return true; } }
|
||||||
|
|
||||||
template<typename T = void, typename... Args, AU_TEMPLATE_ENABLE_WHEN(AuIsSame_v<T, bool> || AuIsVoid_v<T>)>
|
template<typename T = void, typename... Args, AU_TEMPLATE_ENABLE_WHEN(AuIsSame_v<T, bool> || AuIsVoid_v<T>)>
|
||||||
static AuFunction<T(Args&&...)> TranslateAsyncFunctionToDispatcherWithThread(WorkerId_t id, AuFunction<void(Args...)> func)
|
static AuFunction<T(Args&&...)> TranslateAsyncFunctionToDispatcherWithThread(WorkerId_t id, AuFunction<void(Args...)> func)
|
||||||
{
|
{
|
||||||
if (!func) return {};
|
if (!func) return {};
|
||||||
return [=](Args&&... in) -> T
|
return [=](Args&&... in) -> T
|
||||||
{
|
{
|
||||||
auto work = AuMakeShared<BasicWorkStdFunc>([=]() -> void {
|
auto work = AuMakeShared<BasicWorkStdFunc>([=]() -> void {
|
||||||
func(in...);
|
func(in...);
|
||||||
});
|
});
|
||||||
if (!work) ASYNC_ERROR("can't dispatch async call. out of memory");
|
if (!work) ASYNC_ERROR("can't dispatch async call. out of memory");
|
||||||
auto workItem = NewWorkItem(id, work);
|
auto workItem = NewWorkItem(id, work);
|
||||||
if (!workItem) ASYNC_ERROR("can't dispatch async call. out of memory");
|
if (!workItem) ASYNC_ERROR("can't dispatch async call. out of memory");
|
||||||
workItem->Dispatch();
|
workItem->Dispatch();
|
||||||
ASYNC_FINISH;
|
ASYNC_FINISH;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Async app only
|
/// Async app only
|
||||||
template<typename T = void, typename... Args, AU_TEMPLATE_ENABLE_WHEN(AuIsSame_v<T, bool> || AuIsVoid_v<T>)>
|
template<typename T = void, typename... Args, AU_TEMPLATE_ENABLE_WHEN(AuIsSame_v<T, bool> || AuIsVoid_v<T>)>
|
||||||
static AuFunction<T(Args&&...)> TranslateAsyncFunctionToDispatcher(AuFunction<void(Args...)> func)
|
static AuFunction<T(Args&&...)> TranslateAsyncFunctionToDispatcher(AuFunction<void(Args...)> func)
|
||||||
{
|
{
|
||||||
return TranslateAsyncFunctionToDispatcherWithThread(GetAsyncApp()->GetCurrentThread(), func);
|
return TranslateAsyncFunctionToDispatcherWithThread(GetAsyncApp()->GetCurrentThread(), func);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Async app only
|
/// Async app only
|
||||||
template<typename B = void, typename T, typename... Args, AU_TEMPLATE_ENABLE_WHEN(AuIsSame_v<T, bool> || AuIsVoid_v<T>)>
|
template<typename B = void, typename T, typename... Args, AU_TEMPLATE_ENABLE_WHEN(AuIsSame_v<T, bool> || AuIsVoid_v<T>)>
|
||||||
static AuFunction<T(AuFunction<void(const B&)>, Args...)> TranslateAsyncReturnableFunctionToDispatcherWithThread(WorkerId_t id, AuFunction<B(Args...)> func)
|
static AuFunction<T(AuFunction<void(const B&)>, Args...)> TranslateAsyncReturnableFunctionToDispatcherWithThread(WorkerId_t id, AuFunction<B(Args...)> func)
|
||||||
{
|
{
|
||||||
return [=](AuFunction<T(const B&)> callback, Args... in) -> T
|
return [=](AuFunction<T(const B&)> callback, Args... in) -> T
|
||||||
{
|
{
|
||||||
auto work = AuMakeShared<WorkPairImpl<AVoid, B>>();
|
auto work = AuMakeShared<WorkPairImpl<AVoid, B>>();
|
||||||
if (!work) ASYNC_ERROR("can't dispatch async call; out of memory");
|
if (!work) ASYNC_ERROR("can't dispatch async call; out of memory");
|
||||||
work.task.onProcess = [=](const AVoid &) -> B
|
work.task.onProcess = [=](const AVoid &) -> B
|
||||||
{
|
{
|
||||||
if (!func) return B{};
|
if (!func) return B{};
|
||||||
return func(in...);
|
return func(in...);
|
||||||
};
|
};
|
||||||
work.callback.onSuccess = [=](const AVoid &, const B &ret)
|
work.callback.onSuccess = [=](const AVoid &, const B &ret)
|
||||||
{
|
{
|
||||||
callback(ret);
|
callback(ret);
|
||||||
};
|
};
|
||||||
auto workItem = NewWorkItem(id, work);
|
auto workItem = NewWorkItem(id, work);
|
||||||
if (!workItem) ASYNC_ERROR("can't dispatch async call; out of memory");
|
if (!workItem) ASYNC_ERROR("can't dispatch async call; out of memory");
|
||||||
workItem->Dispatch();
|
workItem->Dispatch();
|
||||||
ASYNC_FINISH;
|
ASYNC_FINISH;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#undef ASYNC_ERROR
|
#undef ASYNC_ERROR
|
||||||
#undef ASYNC_FINISH
|
#undef ASYNC_FINISH
|
||||||
|
|
||||||
#pragma endregion EASE_OF_READING
|
#pragma endregion EASE_OF_READING
|
||||||
}
|
}
|
@ -19,17 +19,41 @@ namespace Aurora::IO::FS
|
|||||||
AuSPtr<UserWatchData> userData;
|
AuSPtr<UserWatchData> userData;
|
||||||
AuString path;
|
AuString path;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AUE_DEFINE(EWatchEvent,
|
||||||
|
(
|
||||||
|
eSelfModify,
|
||||||
|
eSelfDelete,
|
||||||
|
eFileModify,
|
||||||
|
eFileDelete,
|
||||||
|
eFileCreate
|
||||||
|
));
|
||||||
|
|
||||||
|
struct WatchRequest
|
||||||
|
{
|
||||||
|
WatchedFile watch;
|
||||||
|
|
||||||
|
// events are mere optimization hint. additional events may be provided, if available.
|
||||||
|
AuList<EWatchEvent> events;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WatchEvent
|
||||||
|
{
|
||||||
|
EWatchEvent event;
|
||||||
|
WatchedFile watch;
|
||||||
|
AuString file;
|
||||||
|
};
|
||||||
|
|
||||||
struct IWatcher
|
struct IWatcher
|
||||||
{
|
{
|
||||||
virtual bool AddWatch(const WatchedFile &file) = 0;
|
virtual bool AddWatch(const WatchRequest &file) = 0;
|
||||||
|
|
||||||
virtual bool RemoveByName(const AuString &path) = 0;
|
virtual bool RemoveByName(const AuString &path) = 0;
|
||||||
virtual bool RemoveByPrivateContext(const AuSPtr<UserWatchData> &file) = 0;
|
virtual bool RemoveByPrivateContext(const AuSPtr<UserWatchData> &file) = 0;
|
||||||
|
|
||||||
virtual AuSPtr<Loop::ILoopSource> AsLoopSource() = 0;
|
virtual AuSPtr<Loop::ILoopSource> AsLoopSource() = 0;
|
||||||
|
|
||||||
virtual AuList<WatchedFile> QueryUpdates() = 0;
|
virtual AuList<WatchEvent> QueryUpdates() = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
AUKN_SHARED_API(NewWatcher, IWatcher);
|
AUKN_SHARED_API(NewWatcher, IWatcher);
|
||||||
|
@ -50,6 +50,11 @@ namespace Aurora::Loop
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Updates the OS watchdog list cache concept after Source[Remove/Add[WithTimeout]]
|
* @brief Updates the OS watchdog list cache concept after Source[Remove/Add[WithTimeout]]
|
||||||
|
* Commits may occur while another thread is waiting on the loop queue.
|
||||||
|
* In those circumstances, they are internally preempted and rescheduled.
|
||||||
|
* WaitAnd has undefined behaviour across MT commit. Linux *might* work.
|
||||||
|
* NT wont until an APC-as-a-preempt-signal hack is implemented.
|
||||||
|
* Rare edge case for all i care - it'll remain a blocking edge case for.now.
|
||||||
* @return
|
* @return
|
||||||
* @note thread safe
|
* @note thread safe
|
||||||
*/
|
*/
|
||||||
@ -103,35 +108,48 @@ namespace Aurora::Loop
|
|||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Nonblocking wait-any for all objects in the loop queue
|
* @brief Nonblocking check for alert objects in the loop queue
|
||||||
* @warning (may yield to ILoopSourceSubscriber delegate on the current context)
|
|
||||||
* @return
|
* @return
|
||||||
* @note thread safe / nonblocking | can be called alongside any other function marked as such
|
* @note thread safe / nonblocking | can be called alongside any other function marked as such
|
||||||
*/
|
*/
|
||||||
virtual bool IsSignaled() = 0;
|
virtual bool IsSignaledPeek() = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Nonblocking wait-any for all objects in the loop queue
|
||||||
|
* @warning (may yield to ILoopSourceSubscriber delegate on the current context)
|
||||||
|
* @warning (technically unsafe, use alloc-unsafe extended version to acknowledge unlocked sources)
|
||||||
|
* @return
|
||||||
|
* @note thread safe / nonblocking | can be called alongside any other function marked as such
|
||||||
|
*/
|
||||||
|
virtual AuUInt32 PumpNonblocking() = 0;
|
||||||
|
|
||||||
|
virtual AuList<AuSPtr<ILoopSource>> PumpNonblockingEx() = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Waits on all the submitted loop sources until they are all complete or until the timeout has finished.
|
* @brief Waits on all the submitted loop sources until they are all complete or until the timeout has finished.
|
||||||
* Note: the completion of another Wait[All/Any[Ex]] call may result in a
|
* Note: the completion of another Wait[All/Any[Ex]] call may result in a
|
||||||
|
* @param timeout timeout in MS, zero = indefinite
|
||||||
* @return
|
* @return
|
||||||
* @warning (may yield to ILoopSourceSubscriber delegate on the current context)
|
* @warning (may yield to ILoopSourceSubscriber delegate on the current context)
|
||||||
* @warning (thread safety is limited blocking callers of the object)
|
* @warning (thread safety might be limited to blocking callers of the object)
|
||||||
*/
|
*/
|
||||||
virtual bool WaitAll (AuUInt32 timeout = 0) = 0;
|
virtual bool WaitAll (AuUInt32 timeout = 0) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Waits on all the loop sources until at least one is signaled.
|
* @brief Waits on all the loop sources until at least one is signaled.
|
||||||
* Additional work may be scheduled on other threads.
|
* @param timeout timeout in MS, zero = indefinite.
|
||||||
* @param timeout
|
* Use IsSignaledPump for nonblocking.
|
||||||
* @return
|
* @return
|
||||||
* @warning (may yield to ILoopSourceSubscriber delegate on the current context)
|
* @warning (may yield to ILoopSourceSubscriber delegate on the current context)
|
||||||
|
* @warning (technically unsafe, use alloc-unsafe extended version to acknowledge unlocked sources)
|
||||||
* @note thread safe | can be called alongside any other function marked as such
|
* @note thread safe | can be called alongside any other function marked as such
|
||||||
*/
|
*/
|
||||||
virtual AuUInt32 WaitAny (AuUInt32 timeout = 0) = 0;
|
virtual AuUInt32 WaitAny (AuUInt32 timeout = 0) = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief
|
* @brief Waits on all the loop sources until at least one is signaled.
|
||||||
* @param timeout
|
* @param timeout timeout in MS, zero = indefinite.
|
||||||
|
* Use IsSignaledPump for nonblocking.
|
||||||
* @return
|
* @return
|
||||||
* @warning (may yield to ILoopSourceSubscriber delegate on the current context)
|
* @warning (may yield to ILoopSourceSubscriber delegate on the current context)
|
||||||
* @note thread safe | can be called alongside any other function marked as such
|
* @note thread safe | can be called alongside any other function marked as such
|
||||||
|
@ -238,7 +238,11 @@ namespace Aurora::Async
|
|||||||
{
|
{
|
||||||
if (state->rateLimiter.CheckExchangePass())
|
if (state->rateLimiter.CheckExchangePass())
|
||||||
{
|
{
|
||||||
bShouldTrySleepForKernel = asyncLoop->IsSignaled();
|
#if defined(AURORA_PLATFORM_WIN32)
|
||||||
|
bShouldTrySleepForKernel = asyncLoop->PumpNonblocking();
|
||||||
|
#else
|
||||||
|
bShouldTrySleepForKernel = asyncLoop->IsSignaledPeek();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -256,18 +260,28 @@ namespace Aurora::Async
|
|||||||
{
|
{
|
||||||
AuThreading::ContextYield();
|
AuThreading::ContextYield();
|
||||||
block = false;
|
block = false;
|
||||||
bShouldTrySleepForKernel = asyncLoop->IsSignaled();
|
#if defined(AURORA_PLATFORM_WIN32)
|
||||||
|
bShouldTrySleepForKernel = asyncLoop->PumpNonblocking();
|
||||||
|
#else
|
||||||
|
bShouldTrySleepForKernel = asyncLoop->IsSignaledPeek();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
else if (runMode == ERunMode::eEfficient)
|
else if (runMode == ERunMode::eEfficient)
|
||||||
{
|
{
|
||||||
bShouldTrySleepForKernel = block;
|
bShouldTrySleepForKernel = block;
|
||||||
if (!block)
|
if (!block)
|
||||||
{
|
{
|
||||||
bShouldTrySleepForKernel = asyncLoop->IsSignaled();
|
bShouldTrySleepForKernel = asyncLoop->IsSignaledPeek();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bShouldTrySleepForKernel && asyncLoop->WaitAny(0))
|
if (bShouldTrySleepForKernel
|
||||||
|
// epoll and such like can be checked without read success. kevent works on availablity, not scheduling read like iosubmit, too.
|
||||||
|
// allow windows to atomically pump instead of wasting time buffering the primitives state
|
||||||
|
#if defined(AURORA_PLATFORM_WIN32)
|
||||||
|
&& asyncLoop->WaitAny(0)
|
||||||
|
#endif
|
||||||
|
)
|
||||||
{
|
{
|
||||||
PollInternal(block);
|
PollInternal(block);
|
||||||
success = true;
|
success = true;
|
||||||
|
@ -76,9 +76,9 @@ namespace Aurora::HWInfo
|
|||||||
bool bIsHyperThreaded {};
|
bool bIsHyperThreaded {};
|
||||||
for (auto &[threadId, coreStr] : cpuThreads)
|
for (auto &[threadId, coreStr] : cpuThreads)
|
||||||
{
|
{
|
||||||
auto cpuId = CpuBitId(threadId);
|
auto cpuId = CpuBitId(threadId);
|
||||||
|
|
||||||
auto coreID = ReadUInt(kBasePath + coreStr + "/topology/core_id");
|
auto coreID = ReadUInt(kBasePath + coreStr + "/topology/core_id");
|
||||||
auto cpuList = ReadString(kBasePath + coreStr +"/topology/core_cpus_list");
|
auto cpuList = ReadString(kBasePath + coreStr +"/topology/core_cpus_list");
|
||||||
|
|
||||||
auto isHVCore = AuStringContains(cpuList, ",");
|
auto isHVCore = AuStringContains(cpuList, ",");
|
||||||
@ -111,25 +111,25 @@ namespace Aurora::HWInfo
|
|||||||
children.Add(CpuBitId(word));
|
children.Add(CpuBitId(word));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gCpuInfo.uCores++;
|
||||||
gCpuInfo.coreTopology.push_back(children);
|
gCpuInfo.coreTopology.push_back(children);
|
||||||
gCpuInfo.threadTopology.push_back(children.lower);
|
gCpuInfo.threadTopology.push_back(children.lower);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gCpuInfo.uThreads++;
|
||||||
gCpuInfo.maskAllCores.Add(cpuId);
|
gCpuInfo.maskAllCores.Add(cpuId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetCpuTopologyLinux()
|
void SetCpuTopologyLinux()
|
||||||
{
|
{
|
||||||
gCpuInfo.uSocket = 1;
|
|
||||||
gCpuInfo.uCores = 1;
|
|
||||||
gCpuInfo.uThreads = get_nprocs();
|
|
||||||
|
|
||||||
gCpuInfo.bMaskMTHalf = true;
|
|
||||||
|
|
||||||
SetCaches();
|
SetCaches();
|
||||||
SetCpuA();
|
SetCpuA();
|
||||||
|
|
||||||
gCpuInfo.uCores = gCpuInfo.coreTopology.size();
|
gCpuInfo.uSocket = AuMax(gCpuInfo.uSocket, AuUInt8(1));
|
||||||
|
gCpuInfo.uCores = AuMax(gCpuInfo.uCores, AuUInt8(1));
|
||||||
|
gCpuInfo.uThreads = gCpuInfo.uThreads ?
|
||||||
|
gCpuInfo.uThreads :
|
||||||
|
get_nprocs();
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,7 +25,7 @@ namespace Aurora::IO::FS
|
|||||||
{
|
{
|
||||||
bool _MkDir(const AuString &path)
|
bool _MkDir(const AuString &path)
|
||||||
{
|
{
|
||||||
return mkdir(path.c_str(), 0760) == 0;
|
return mkdir(path.c_str(), 0775) == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
AUKN_SYM bool FilesInDirectory(const AuString &string, AuList<AuString> &files)
|
AUKN_SYM bool FilesInDirectory(const AuString &string, AuList<AuString> &files)
|
||||||
@ -56,7 +56,7 @@ namespace Aurora::IO::FS
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
AUKN_SYM bool ReadFile(const AuString &path, AuByteBuffer &buffer)
|
AUKN_SYM bool ReadFile(const AuString &path, AuByteBuffer &buffer)
|
||||||
|
@ -87,6 +87,10 @@ namespace Aurora::IO::FS
|
|||||||
{
|
{
|
||||||
result += kDoublePathSplitter;
|
result += kDoublePathSplitter;
|
||||||
}
|
}
|
||||||
|
else if (buffer.size() && buffer[0] == kPathSplitter)
|
||||||
|
{
|
||||||
|
result += kPathSplitter;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Technically, UTF-8 strings may contain the "NUL" byte.
|
Technically, UTF-8 strings may contain the "NUL" byte.
|
||||||
|
@ -90,7 +90,11 @@ namespace Aurora::IO::FS
|
|||||||
((!isFile) && (end)))
|
((!isFile) && (end)))
|
||||||
{
|
{
|
||||||
auto subpath = end ? cpath : AuString(cpath.begin(), cpath.begin() + i);
|
auto subpath = end ? cpath : AuString(cpath.begin(), cpath.begin() + i);
|
||||||
|
if (subpath.empty())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (DirExists(subpath))
|
if (DirExists(subpath))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
|
@ -303,11 +303,12 @@ namespace Aurora::IO::FS
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto fileHandle = open(pathex.c_str(),
|
auto fileHandle = open(pathex.c_str(),
|
||||||
openMode == EFileOpenMode::eRead ? O_RDONLY : O_RDWR);
|
openMode == EFileOpenMode::eRead ? O_RDONLY : (O_RDWR | O_CREAT),
|
||||||
|
0644);
|
||||||
|
|
||||||
if (fileHandle < 0)
|
if (fileHandle < 0)
|
||||||
{
|
{
|
||||||
SysPushErrorIO("Couldn't open file: {}", path);
|
SysPushErrorIO("Couldn't open file: {} ({}) {}", path, pathex, errno);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
384
Source/IO/FS/Watcher.Linux.cpp
Executable file
384
Source/IO/FS/Watcher.Linux.cpp
Executable file
@ -0,0 +1,384 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: Watcher.Linux.cpp
|
||||||
|
Date: 2022-4-10
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#include <Source/RuntimeInternal.hpp>
|
||||||
|
#include "FS.hpp"
|
||||||
|
#include "Watcher.Linux.hpp"
|
||||||
|
#include <sys/inotify.h>
|
||||||
|
|
||||||
|
#include <Source/Loop/LSHandle.hpp>
|
||||||
|
|
||||||
|
namespace Aurora::IO::FS
|
||||||
|
{
|
||||||
|
struct LinuxWatcherHandle : public Loop::LSHandle
|
||||||
|
{
|
||||||
|
LinuxWatcherHandle(int handle) : Loop::LSHandle(AuUInt(handle))
|
||||||
|
{}
|
||||||
|
|
||||||
|
virtual Loop::ELoopSource GetType() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
Loop::ELoopSource LinuxWatcherHandle::GetType()
|
||||||
|
{
|
||||||
|
return Loop::ELoopSource::eSourceFileWatcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
LinuxWatcher::~LinuxWatcher()
|
||||||
|
{
|
||||||
|
Deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxWatcher::AddWatch(const WatchRequest &request)
|
||||||
|
{
|
||||||
|
AuCtorCode_t code;
|
||||||
|
bool bIsDirectory {};
|
||||||
|
AuUInt32 mask {};
|
||||||
|
UnixCachedPath cached;
|
||||||
|
|
||||||
|
auto &file = request.watch;
|
||||||
|
|
||||||
|
AU_LOCK_GUARD(this->spinlock_);
|
||||||
|
|
||||||
|
if (this->inotifyHandle_ == -1)
|
||||||
|
{
|
||||||
|
SysPushErrorUninitialized();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuString translated;
|
||||||
|
if (!AuIOFS::NormalizePath(translated, file.path))
|
||||||
|
{
|
||||||
|
SysPushErrorIO();
|
||||||
|
translated = file.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the NT path in the midst of path normalization
|
||||||
|
cached.strNormalizedPath = AuTryConstruct<AuString>(code, translated);
|
||||||
|
if (!code)
|
||||||
|
{
|
||||||
|
SysPushErrorMem();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached.strTheCakeIsALie = AuTryConstruct<AuString>(code, file.path);
|
||||||
|
if (!code)
|
||||||
|
{
|
||||||
|
SysPushErrorMem();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached.userData = file.userData;
|
||||||
|
|
||||||
|
if (AuIOFS::DirExists(translated))
|
||||||
|
{
|
||||||
|
bIsDirectory = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask |= IN_ATTRIB;
|
||||||
|
|
||||||
|
if (bIsDirectory)
|
||||||
|
{
|
||||||
|
mask |= IN_MOVE_SELF;
|
||||||
|
|
||||||
|
if (AuExists(request.events, EWatchEvent::eFileCreate))
|
||||||
|
{
|
||||||
|
mask |= IN_CREATE | IN_MOVED_TO /*may as well. easter egg.*/;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AuExists(request.events, EWatchEvent::eFileDelete))
|
||||||
|
{
|
||||||
|
mask |= IN_DELETE | IN_MOVED_FROM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AuExists(request.events, EWatchEvent::eFileModify) || !bIsDirectory)
|
||||||
|
{
|
||||||
|
mask |= IN_MODIFY | IN_CLOSE_WRITE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//if (AuExists(request.events, EWatchEvent::eSelfDelete))
|
||||||
|
{
|
||||||
|
mask |= IN_DELETE_SELF | IN_MOVE_SELF;
|
||||||
|
}
|
||||||
|
|
||||||
|
mask = IN_ALL_EVENTS;
|
||||||
|
|
||||||
|
int ret = inotify_add_watch(this->inotifyHandle_, cached.strNormalizedPath.c_str(), mask);
|
||||||
|
if (ret == -1)
|
||||||
|
{
|
||||||
|
// TODO: push error
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached.watcherWd = ret;
|
||||||
|
|
||||||
|
if (!AuTryInsert(this->paths_, cached))
|
||||||
|
{
|
||||||
|
SysPushErrorMem();
|
||||||
|
auto ree = inotify_rm_watch(this->inotifyHandle_, ret);
|
||||||
|
if (ree == -1)
|
||||||
|
{
|
||||||
|
SysPushErrorGeneric();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxWatcher::RemoveByName(const AuString &path)
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->spinlock_);
|
||||||
|
AuString strNormalized;
|
||||||
|
|
||||||
|
if (!AuIOFS::NormalizePath(strNormalized, path))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AuRemoveIf(this->paths_, [&](const UnixCachedPath &object) -> bool
|
||||||
|
{
|
||||||
|
if ((strNormalized == object.strNormalizedPath) ||
|
||||||
|
(strNormalized.empty() && object.strNormalizedPath == path))
|
||||||
|
{
|
||||||
|
|
||||||
|
auto ree = inotify_rm_watch(this->inotifyHandle_, object.watcherWd);
|
||||||
|
if (ree == -1)
|
||||||
|
{
|
||||||
|
SysPushErrorGeneric();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxWatcher::RemoveByPrivateContext(const AuSPtr<UserWatchData> &file)
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->spinlock_);
|
||||||
|
|
||||||
|
return AuRemoveIf(this->paths_, [&](const UnixCachedPath &object) -> bool
|
||||||
|
{
|
||||||
|
if (file != object.userData)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto ree = inotify_rm_watch(this->inotifyHandle_, object.watcherWd);
|
||||||
|
if (ree == -1)
|
||||||
|
{
|
||||||
|
SysPushErrorGeneric();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AuSPtr<Loop::ILoopSource> LinuxWatcher::AsLoopSource()
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->spinlock_);
|
||||||
|
return this->loopSource_;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuList<WatchEvent> LinuxWatcher::QueryUpdates()
|
||||||
|
{
|
||||||
|
AU_LOCK_GUARD(this->spinlock_);
|
||||||
|
|
||||||
|
bool bSuccess {};
|
||||||
|
AuList<WatchEvent> ret;
|
||||||
|
|
||||||
|
if (this->inotifyHandle_ == -1)
|
||||||
|
{
|
||||||
|
SysPushErrorUninitialized();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
char buffer[4096];
|
||||||
|
|
||||||
|
int length = read(this->inotifyHandle_, &buffer, sizeof(buffer));
|
||||||
|
int index {};
|
||||||
|
|
||||||
|
if (length <= -1)
|
||||||
|
{
|
||||||
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
||||||
|
{
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SysPushErrorGeneric();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (index < length)
|
||||||
|
{
|
||||||
|
WatchEvent event;
|
||||||
|
auto & header = *(inotify_event *)(buffer + index);
|
||||||
|
index += sizeof(struct inotify_event) + header.len;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
printf("watcher event: %i %i %i\n", index, header.wd, header.mask);
|
||||||
|
#endif
|
||||||
|
if (!AuTryResize(event.file, header.len))
|
||||||
|
{
|
||||||
|
SysPushErrorMem("Out of memory -> can't consume body. Will misalign stream... Deinitializing...");
|
||||||
|
Deinit();
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuMemcpy(event.file.data(), header.name, header.len);
|
||||||
|
bool bFound {}, bCrinkled{};
|
||||||
|
|
||||||
|
for (auto & path : this->paths_)
|
||||||
|
{
|
||||||
|
if (path.watcherWd != header.wd)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
event.watch = WatchedFile {path.userData, path.strTheCakeIsALie};
|
||||||
|
bCrinkled = path.strNormalizedPath == event.file;
|
||||||
|
bFound = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bFound)
|
||||||
|
{
|
||||||
|
SysPushErrorGeneric("Couldn't find inotify wd for event");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.file.empty())
|
||||||
|
{
|
||||||
|
event.file = event.watch.path; // TODO: alloc
|
||||||
|
}
|
||||||
|
|
||||||
|
event.event = EWatchEvent::eEnumInvalid;
|
||||||
|
|
||||||
|
if (header.mask & (IN_CREATE | IN_MOVED_TO))
|
||||||
|
{
|
||||||
|
if (event.event != EWatchEvent::eEnumInvalid)
|
||||||
|
{
|
||||||
|
AuTryInsert(ret, event);
|
||||||
|
}
|
||||||
|
event.event = EWatchEvent::eFileCreate;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.mask & (IN_DELETE | IN_MOVED_FROM))
|
||||||
|
{
|
||||||
|
if (event.event != EWatchEvent::eEnumInvalid)
|
||||||
|
{
|
||||||
|
AuTryInsert(ret, event);
|
||||||
|
}
|
||||||
|
event.event = EWatchEvent::eFileDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.mask & (IN_DELETE_SELF | IN_MOVE_SELF))
|
||||||
|
{
|
||||||
|
if (event.event != EWatchEvent::eEnumInvalid)
|
||||||
|
{
|
||||||
|
AuTryInsert(ret, event);
|
||||||
|
}
|
||||||
|
event.event = EWatchEvent::eSelfDelete;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.mask & (IN_MODIFY))
|
||||||
|
{
|
||||||
|
if (event.event != EWatchEvent::eEnumInvalid)
|
||||||
|
{
|
||||||
|
AuTryInsert(ret, event);
|
||||||
|
}
|
||||||
|
event.event = EWatchEvent::eFileModify;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.file.empty() || (bCrinkled) || (header.mask & IN_MOVE_SELF))
|
||||||
|
{
|
||||||
|
if (event.event != EWatchEvent::eEnumInvalid)
|
||||||
|
{
|
||||||
|
AuTryInsert(ret, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (header.mask & IN_ISDIR)
|
||||||
|
{
|
||||||
|
event.event = EWatchEvent::eSelfModify;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
event.event = EWatchEvent::eFileModify;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.event != EWatchEvent::eEnumInvalid)
|
||||||
|
{
|
||||||
|
AuTryInsert(ret, event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LinuxWatcher::Init()
|
||||||
|
{
|
||||||
|
this->inotifyHandle_ = ::inotify_init1(IN_NONBLOCK);
|
||||||
|
if (this->inotifyHandle_ == -1)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this->loopSource_ = AuMakeShared<LinuxWatcherHandle>(this->inotifyHandle_);
|
||||||
|
return bool(this->loopSource_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LinuxWatcher::Deinit()
|
||||||
|
{
|
||||||
|
auto paths = AuExchange(this->paths_, {});
|
||||||
|
for (auto handle : paths)
|
||||||
|
{
|
||||||
|
if (handle.watcherWd != -1)
|
||||||
|
{
|
||||||
|
auto ree = inotify_rm_watch(this->inotifyHandle_, handle.watcherWd);
|
||||||
|
if (ree == -1)
|
||||||
|
{
|
||||||
|
SysPushErrorGeneric();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((this->inotifyHandle_ != 0) &&
|
||||||
|
(this->inotifyHandle_ != -1))
|
||||||
|
{
|
||||||
|
::close(AuExchange(this->inotifyHandle_, -1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM IWatcher *NewWatcherNew()
|
||||||
|
{
|
||||||
|
auto watcher = _new LinuxWatcher();
|
||||||
|
if (!watcher)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!watcher->Init())
|
||||||
|
{
|
||||||
|
delete watcher;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return watcher;
|
||||||
|
}
|
||||||
|
|
||||||
|
AUKN_SYM void NewWatcherRelease(IWatcher *watcher)
|
||||||
|
{
|
||||||
|
AuSafeDelete<LinuxWatcher *>(watcher);
|
||||||
|
}
|
||||||
|
}
|
45
Source/IO/FS/Watcher.Linux.hpp
Executable file
45
Source/IO/FS/Watcher.Linux.hpp
Executable file
@ -0,0 +1,45 @@
|
|||||||
|
/***
|
||||||
|
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
|
File: Watcher.Linux.hpp
|
||||||
|
Date: 2022-4-10
|
||||||
|
Author: Reece
|
||||||
|
***/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace Aurora::IO::FS
|
||||||
|
{
|
||||||
|
struct LinuxWatcherHandle;
|
||||||
|
|
||||||
|
struct UnixCachedPath
|
||||||
|
{
|
||||||
|
AuString strNormalizedPath;
|
||||||
|
AuString strTheCakeIsALie;
|
||||||
|
AuSPtr<UserWatchData> userData;
|
||||||
|
int watcherWd {-1};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LinuxWatcher : IWatcher
|
||||||
|
{
|
||||||
|
~LinuxWatcher();
|
||||||
|
|
||||||
|
virtual bool AddWatch(const WatchRequest &file) override;
|
||||||
|
|
||||||
|
virtual bool RemoveByName(const AuString &path) override;
|
||||||
|
virtual bool RemoveByPrivateContext(const AuSPtr<UserWatchData> &file) override;
|
||||||
|
|
||||||
|
virtual AuSPtr<Loop::ILoopSource> AsLoopSource() override;
|
||||||
|
|
||||||
|
virtual AuList<WatchEvent> QueryUpdates() override;
|
||||||
|
|
||||||
|
bool Init();
|
||||||
|
void Deinit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
AuSPtr<LinuxWatcherHandle> loopSource_;
|
||||||
|
AuThreadPrimitives::SpinLock spinlock_;
|
||||||
|
AuList<UnixCachedPath> paths_;
|
||||||
|
int inotifyHandle_ {-1};
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,8 @@
|
|||||||
File: Watcher.NT.cpp
|
File: Watcher.NT.cpp
|
||||||
Date: 2022-4-1
|
Date: 2022-4-1
|
||||||
Author: Reece
|
Author: Reece
|
||||||
|
Note: FindFirstChangeNotification locks the parent directory, every other library is garbage
|
||||||
|
Aurora shall implement this sewage instead
|
||||||
***/
|
***/
|
||||||
#include <Source/RuntimeInternal.hpp>
|
#include <Source/RuntimeInternal.hpp>
|
||||||
#include "FS.hpp"
|
#include "FS.hpp"
|
||||||
@ -66,21 +68,30 @@ namespace Aurora::IO::FS
|
|||||||
AuString strTheCakeIsALie;
|
AuString strTheCakeIsALie;
|
||||||
AuSPtr<UserWatchData> userData;
|
AuSPtr<UserWatchData> userData;
|
||||||
AuSPtr<NTWatchObject> watcher;
|
AuSPtr<NTWatchObject> watcher;
|
||||||
|
bool bIsDirectory {};
|
||||||
|
|
||||||
|
AuList<AuTuple<AuString, bool, AuUInt64>> lastTick;
|
||||||
|
|
||||||
FILETIME lastFileTime {};
|
FILETIME lastFileTime {};
|
||||||
|
|
||||||
bool CheckRun();
|
AuList<AuTuple<AuString, bool, AuUInt64>> GetCurrentState();
|
||||||
|
|
||||||
|
bool CheckDirDelta();
|
||||||
|
void Warm();
|
||||||
|
bool CheckRun(NTWatcher *parent);
|
||||||
|
bool AddEvent(NTWatcher *parent, EWatchEvent type, const AuString &path);
|
||||||
};
|
};
|
||||||
|
|
||||||
struct NTWatcher : IWatcher
|
struct NTWatcher : IWatcher
|
||||||
{
|
{
|
||||||
virtual bool AddWatch(const WatchedFile &file) override;
|
virtual bool WatchRequest(const WatchRequest &file) override;
|
||||||
|
|
||||||
virtual bool RemoveByName(const AuString &path) override;
|
virtual bool RemoveByName(const AuString &path) override;
|
||||||
virtual bool RemoveByPrivateContext(const AuSPtr<UserWatchData> &file) override;
|
virtual bool RemoveByPrivateContext(const AuSPtr<UserWatchData> &file) override;
|
||||||
|
|
||||||
virtual AuSPtr<Loop::ILoopSource> AsLoopSource() override;
|
virtual AuSPtr<Loop::ILoopSource> AsLoopSource() override;
|
||||||
|
|
||||||
virtual AuList<WatchedFile> QueryUpdates() override;
|
virtual AuList<WatchEvent> QueryUpdates() override;
|
||||||
|
|
||||||
bool Init();
|
bool Init();
|
||||||
bool GoBrr();
|
bool GoBrr();
|
||||||
@ -94,7 +105,7 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
AuList<WatchedFile> triggered_;
|
AuList<WatchEvent> triggered_;
|
||||||
AuSPtr<NTEvent> ntEvent_;
|
AuSPtr<NTEvent> ntEvent_;
|
||||||
AuList<NTCachedPath> paths_;
|
AuList<NTCachedPath> paths_;
|
||||||
|
|
||||||
@ -157,7 +168,69 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
// NT Cached Path / File Watch Item class
|
// NT Cached Path / File Watch Item class
|
||||||
|
|
||||||
bool NTCachedPath::CheckRun()
|
void NTCachedPath::Warm()
|
||||||
|
{
|
||||||
|
HANDLE hFile;
|
||||||
|
hFile = CreateFileW(AuLocale::ConvertFromUTF8(this->strNormalizedPath).c_str(),
|
||||||
|
GENERIC_READ,
|
||||||
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
|
NULL,
|
||||||
|
OPEN_EXISTING,
|
||||||
|
FILE_ATTRIBUTE_NORMAL | (this->bIsDirectory ? FILE_FLAG_BACKUP_SEMANTICS : 0),
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
if (hFile != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
if (!GetFileTime(hFile, NULL, NULL, &this->lastFileTime))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
CloseHandle(hFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
this->lastTick = GetCurrentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
AuList<AuTuple<AuString, bool, AuUInt64>> NTCachedPath::GetCurrentState()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NTCachedPath::CheckDirDelta()
|
||||||
|
{
|
||||||
|
auto next = GetCurrentState();
|
||||||
|
auto currentState = next;
|
||||||
|
auto oldState = AuExchange(this->lastTick, AuMove(next));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NTCachedPath::AddEvent(NTWatcher *parent, EWatchEvent type, const AuString &path)
|
||||||
|
{
|
||||||
|
AuCtorCode_t code;
|
||||||
|
auto watchedFile = AuTryConstruct<WatchedFile>(code, this->userData, this->strTheCakeIsALie);
|
||||||
|
if (!code)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WatchEvent event;
|
||||||
|
event.event = type;
|
||||||
|
event.watch = AuMove(watchedFile);
|
||||||
|
event.file = AuTryConstruct<AuString>(code, path);
|
||||||
|
if (!code)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AuTryInsert(parent->triggered_, AuMove(watchedFile)))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NTCachedPath::CheckRun(NTWatcher *parent)
|
||||||
{
|
{
|
||||||
FILETIME curFileTime {};
|
FILETIME curFileTime {};
|
||||||
HANDLE hFile;
|
HANDLE hFile;
|
||||||
@ -167,14 +240,24 @@ namespace Aurora::IO::FS
|
|||||||
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
NULL,
|
NULL,
|
||||||
OPEN_EXISTING,
|
OPEN_EXISTING,
|
||||||
FILE_ATTRIBUTE_NORMAL | (AuIOFS::DirExists(this->strNormalizedPath) ? FILE_FLAG_BACKUP_SEMANTICS : 0),
|
FILE_ATTRIBUTE_NORMAL | (this->bIsDirectory ? FILE_FLAG_BACKUP_SEMANTICS : 0),
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (hFile == INVALID_HANDLE_VALUE)
|
if (hFile == INVALID_HANDLE_VALUE)
|
||||||
{
|
{
|
||||||
return false;
|
if (GetLastError() == ERROR_FILE_NOT_FOUND)
|
||||||
|
{
|
||||||
|
// NOTE: we might want to add a check to prevent double dispatch
|
||||||
|
return AddEvent(parent, EWatchEvent::eSelfDelete, this->strTheCakeIsALie);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool bDirChanged = CheckDirDelta();
|
||||||
|
|
||||||
if (!GetFileTime(hFile, NULL, NULL, &curFileTime))
|
if (!GetFileTime(hFile, NULL, NULL, &curFileTime))
|
||||||
{
|
{
|
||||||
CloseHandle(hFile);
|
CloseHandle(hFile);
|
||||||
@ -188,7 +271,13 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
CloseHandle(hFile);
|
CloseHandle(hFile);
|
||||||
|
|
||||||
return ret;
|
if (!ret)
|
||||||
|
{
|
||||||
|
return bDirChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send a self modified update once the timestamp changes
|
||||||
|
return AddEvent(parent,this->bIsDirectory ? EWatchEvent::eSelfModify : EWatchEvent::eFileModify, this->strTheCakeIsALie) || bDirChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Directory Watcher Object
|
// Directory Watcher Object
|
||||||
@ -196,7 +285,7 @@ namespace Aurora::IO::FS
|
|||||||
bool NTWatchObject::Init(const AuString &usrStr)
|
bool NTWatchObject::Init(const AuString &usrStr)
|
||||||
{
|
{
|
||||||
AuCtorCode_t code;
|
AuCtorCode_t code;
|
||||||
|
|
||||||
this->strBaseDir = AuTryConstruct<AuString>(code, usrStr);
|
this->strBaseDir = AuTryConstruct<AuString>(code, usrStr);
|
||||||
if (!code)
|
if (!code)
|
||||||
{
|
{
|
||||||
@ -277,26 +366,12 @@ namespace Aurora::IO::FS
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!filesWatched.CheckRun())
|
if (!filesWatched.CheckRun(this->parent))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
bAnyTriggered = true;
|
bAnyTriggered = true;
|
||||||
|
|
||||||
AuCtorCode_t code;
|
|
||||||
auto watchedFile = AuTryConstruct<WatchedFile>(code, filesWatched.userData, filesWatched.strTheCakeIsALie);
|
|
||||||
if (!code)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!AuTryInsert(this->parent->triggered_, AuMove(watchedFile)))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bAnyTriggered = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this->whoAsked_.Flags & REQUEST_OPLOCK_OUTPUT_FLAG_ACK_REQUIRED)
|
if (this->whoAsked_.Flags & REQUEST_OPLOCK_OUTPUT_FLAG_ACK_REQUIRED)
|
||||||
@ -320,14 +395,15 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
// NT Watcher - primary interface implementation
|
// NT Watcher - primary interface implementation
|
||||||
|
|
||||||
bool NTWatcher::AddWatch(const WatchedFile &file)
|
bool NTWatcher::AddWatch(const WatchRequest &request)
|
||||||
{
|
{
|
||||||
AuCtorCode_t code;
|
AuCtorCode_t code;
|
||||||
AuSPtr<NTWatchObject> watcher;
|
AuSPtr<NTWatchObject> watcher;
|
||||||
NTCachedPath cached;
|
NTCachedPath cached;
|
||||||
|
|
||||||
AU_LOCK_GUARD(this->spinlock_);
|
auto &file = request.watch;
|
||||||
|
|
||||||
|
AU_LOCK_GUARD(this->spinlock_);
|
||||||
|
|
||||||
AuString translated;
|
AuString translated;
|
||||||
if (!AuIOFS::NormalizePath(translated, file.path))
|
if (!AuIOFS::NormalizePath(translated, file.path))
|
||||||
@ -350,17 +426,21 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
cached.userData = file.userData;
|
cached.userData = file.userData;
|
||||||
|
|
||||||
|
|
||||||
// Update the last edited timestamp as a frame of reference for fast compare of
|
|
||||||
// directory entries upon lock breakage
|
|
||||||
cached.CheckRun();
|
|
||||||
|
|
||||||
// Continue normalizing the parent path
|
// Continue normalizing the parent path
|
||||||
|
|
||||||
if (AuIOFS::FileExists(translated))
|
if (AuIOFS::FileExists(translated))
|
||||||
{
|
{
|
||||||
AuIOFS::GetDirectoryFromPath(translated, translated);
|
AuIOFS::GetDirectoryFromPath(translated, translated);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// dammit, had to move checkrun
|
||||||
|
cached.bIsDirectory = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the last edited timestamp as a frame of reference for fast compare of
|
||||||
|
// directory entries upon lock breakage
|
||||||
|
cached.Warm();
|
||||||
|
|
||||||
// Attempt to locate a watcher for the directoy
|
// Attempt to locate a watcher for the directoy
|
||||||
|
|
||||||
@ -387,7 +467,6 @@ namespace Aurora::IO::FS
|
|||||||
|
|
||||||
item->parent = this;
|
item->parent = this;
|
||||||
|
|
||||||
|
|
||||||
if (!item->Init(translated))
|
if (!item->Init(translated))
|
||||||
{
|
{
|
||||||
SysPushErrorGen();
|
SysPushErrorGen();
|
||||||
@ -497,9 +576,10 @@ namespace Aurora::IO::FS
|
|||||||
return AuStaticCast<Loop::ILSEvent>(this->ntEvent_);
|
return AuStaticCast<Loop::ILSEvent>(this->ntEvent_);
|
||||||
}
|
}
|
||||||
|
|
||||||
AuList<WatchedFile> NTWatcher::NTWatcher::QueryUpdates()
|
AuList<WatchEvent> NTWatcher::NTWatcher::QueryUpdates()
|
||||||
{
|
{
|
||||||
AU_LOCK_GUARD(this->spinlock_);
|
AU_LOCK_GUARD(this->spinlock_);
|
||||||
|
AsLoopSource().IsSignaled(); // nonblocking, wont latch
|
||||||
return AuExchange(this->triggered_, {});
|
return AuExchange(this->triggered_, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,119 +1,119 @@
|
|||||||
/***
|
/***
|
||||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
File: LSEvent.Linux.cpp
|
File: LSEvent.Linux.cpp
|
||||||
Date: 2022-4-4
|
Date: 2022-4-4
|
||||||
Author: Reece
|
Author: Reece
|
||||||
***/
|
***/
|
||||||
#include <Source/RuntimeInternal.hpp>
|
#include <Source/RuntimeInternal.hpp>
|
||||||
#include "LSEvent.hpp"
|
#include "LSEvent.hpp"
|
||||||
#include "LSFromFdNonblocking.hpp"
|
#include "LSFromFdNonblocking.hpp"
|
||||||
|
|
||||||
namespace Aurora::Loop
|
namespace Aurora::Loop
|
||||||
{
|
{
|
||||||
LSEvent::LSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers) : atomicRelease_(atomicRelease)
|
LSEvent::LSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers) : atomicRelease_(atomicRelease)
|
||||||
{
|
{
|
||||||
Init(triggered);
|
Init(triggered);
|
||||||
}
|
}
|
||||||
|
|
||||||
LSEvent::~LSEvent()
|
LSEvent::~LSEvent()
|
||||||
{
|
{
|
||||||
if ((this->handle != 0) &&
|
if ((this->handle != 0) &&
|
||||||
(this->handle != -1))
|
(this->handle != -1))
|
||||||
{
|
{
|
||||||
close(this->handle);
|
close(this->handle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LSEvent::OnTrigger(AuUInt handle)
|
bool LSEvent::OnTrigger(AuUInt handle)
|
||||||
{
|
{
|
||||||
AuUInt64 oldSemaphoreValue {};
|
AuUInt64 oldSemaphoreValue {};
|
||||||
|
|
||||||
if (!this->atomicRelease_)
|
if (!this->atomicRelease_)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue)) == 8;
|
return read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue)) == 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LSEvent::Init(bool init)
|
void LSEvent::Init(bool init)
|
||||||
{
|
{
|
||||||
handle = eventfd(init ? 1 : 0, EFD_NONBLOCK);
|
handle = eventfd(init ? 1 : 0, EFD_NONBLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LSEvent::Set()
|
bool LSEvent::Set()
|
||||||
{
|
{
|
||||||
AuUInt64 plsNoOverflow {1};
|
AuUInt64 plsNoOverflow {1};
|
||||||
|
|
||||||
if (write(this->handle, &plsNoOverflow, sizeof(plsNoOverflow)) != 8)
|
if (write(this->handle, &plsNoOverflow, sizeof(plsNoOverflow)) != 8)
|
||||||
{
|
{
|
||||||
// todo push error
|
// todo push error
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LSEvent::Reset()
|
bool LSEvent::Reset()
|
||||||
{
|
{
|
||||||
AuUInt64 oldSemaphoreValue {0};
|
AuUInt64 oldSemaphoreValue {0};
|
||||||
|
|
||||||
// RETURN VALUE IS USELESS - Failure is to be expected
|
// RETURN VALUE IS USELESS - Failure is to be expected
|
||||||
read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue));
|
read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LSEvent::IsSignaled()
|
bool LSEvent::IsSignaled()
|
||||||
{
|
{
|
||||||
return IsSignaledFromNonblockingImpl(this, this, &LSEvent::IsSignaledNonblocking);
|
return IsSignaledFromNonblockingImpl(this, this, &LSEvent::IsSignaledNonblocking);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LSEvent::IsSignaledNonblocking()
|
bool LSEvent::IsSignaledNonblocking()
|
||||||
{
|
{
|
||||||
if (!this->atomicRelease_)
|
if (!this->atomicRelease_)
|
||||||
{
|
{
|
||||||
fd_set set;
|
fd_set set;
|
||||||
struct timeval tv {};
|
struct timeval tv {};
|
||||||
|
|
||||||
FD_ZERO(&set);
|
FD_ZERO(&set);
|
||||||
FD_SET(this->handle, &set);
|
FD_SET(this->handle, &set);
|
||||||
|
|
||||||
auto active = select(this->handle + 1, &set, NULL, NULL, &tv);
|
auto active = select(this->handle + 1, &set, NULL, NULL, &tv);
|
||||||
if (active == -1)
|
if (active == -1)
|
||||||
{
|
{
|
||||||
// todo push error
|
// todo push error
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return active == 1;
|
return active == 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
AuUInt64 oldSemaphoreValue {};
|
AuUInt64 oldSemaphoreValue {};
|
||||||
return read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue)) == 8;
|
return read(this->handle, &oldSemaphoreValue, sizeof(oldSemaphoreValue)) == 8;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ELoopSource LSEvent::GetType()
|
ELoopSource LSEvent::GetType()
|
||||||
{
|
{
|
||||||
return ELoopSource::eSourceEvent;
|
return ELoopSource::eSourceEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
AUKN_SYM AuSPtr<ILSEvent> NewLSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers)
|
AUKN_SYM AuSPtr<ILSEvent> NewLSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers)
|
||||||
{
|
{
|
||||||
auto event = AuMakeShared<LSEvent>(triggered, atomicRelease, permitMultipleTriggers);
|
auto event = AuMakeShared<LSEvent>(triggered, atomicRelease, permitMultipleTriggers);
|
||||||
if (!event)
|
if (!event)
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!event->HasValidHandle())
|
if (!event->HasValidHandle())
|
||||||
{
|
{
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,31 +1,31 @@
|
|||||||
/***
|
/***
|
||||||
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
Copyright (C) 2022 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
||||||
|
|
||||||
File: LSEvent.Linux.hpp
|
File: LSEvent.Linux.hpp
|
||||||
Date: 2022-4-4
|
Date: 2022-4-4
|
||||||
Author: Reece
|
Author: Reece
|
||||||
***/
|
***/
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "LSHandle.hpp"
|
#include "LSHandle.hpp"
|
||||||
|
|
||||||
namespace Aurora::Loop
|
namespace Aurora::Loop
|
||||||
{
|
{
|
||||||
struct LSEvent : public ILSEvent, public LSHandle
|
struct LSEvent : public ILSEvent, public LSHandle
|
||||||
{
|
{
|
||||||
LSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers);
|
LSEvent(bool triggered, bool atomicRelease, bool permitMultipleTriggers);
|
||||||
~LSEvent();
|
~LSEvent();
|
||||||
|
|
||||||
bool Set() override;
|
bool Set() override;
|
||||||
bool Reset() override;
|
bool Reset() override;
|
||||||
|
|
||||||
virtual bool OnTrigger(AuUInt handle) override;
|
virtual bool OnTrigger(AuUInt handle) override;
|
||||||
|
|
||||||
bool IsSignaled() override;
|
bool IsSignaled() override;
|
||||||
virtual ELoopSource GetType() override;
|
virtual ELoopSource GetType() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void Init(bool init);
|
void Init(bool init);
|
||||||
bool IsSignaledNonblocking();
|
bool IsSignaledNonblocking();
|
||||||
bool atomicRelease_;
|
bool atomicRelease_;
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -353,7 +353,7 @@ namespace Aurora::Loop
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LoopQueue::IsSignaled()
|
bool LoopQueue::IsSignaledPeek()
|
||||||
{
|
{
|
||||||
fd_set readSet;
|
fd_set readSet;
|
||||||
struct timeval tv {};
|
struct timeval tv {};
|
||||||
@ -402,7 +402,6 @@ namespace Aurora::Loop
|
|||||||
// [==========] 1 test from 1 test suite ran. (11100 ms total)
|
// [==========] 1 test from 1 test suite ran. (11100 ms total)
|
||||||
// ...and a turbojet
|
// ...and a turbojet
|
||||||
|
|
||||||
|
|
||||||
//bool bTryAgain {};
|
//bool bTryAgain {};
|
||||||
//DoTick(timeout, {}, &bTryAgain);
|
//DoTick(timeout, {}, &bTryAgain);
|
||||||
// ...and + ~10ms latency
|
// ...and + ~10ms latency
|
||||||
@ -450,6 +449,35 @@ namespace Aurora::Loop
|
|||||||
return cTicked;
|
return cTicked;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
AuUInt32 LoopQueue::PumpNonblocking()
|
||||||
|
{
|
||||||
|
AuUInt32 cTicked {};
|
||||||
|
bool bTryAgain {};
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bTryAgain = false;
|
||||||
|
AuUInt32 ticked = DoTick(0, {}, &bTryAgain, true);
|
||||||
|
cTicked += ticked;
|
||||||
|
} while (bTryAgain);
|
||||||
|
|
||||||
|
return cTicked;
|
||||||
|
}
|
||||||
|
|
||||||
|
AuList<AuSPtr<ILoopSource>> LoopQueue::PumpNonblockingEx()
|
||||||
|
{
|
||||||
|
AuList<AuSPtr<ILoopSource>> ret;
|
||||||
|
bool bTryAgain {};
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bTryAgain = false;
|
||||||
|
AuUInt32 ticked = DoTick(0, &ret, &bTryAgain, true);
|
||||||
|
} while (bTryAgain);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
AuList<AuSPtr<ILoopSource>> LoopQueue::WaitAnyEx(AuUInt32 timeoutIn)
|
AuList<AuSPtr<ILoopSource>> LoopQueue::WaitAnyEx(AuUInt32 timeoutIn)
|
||||||
{
|
{
|
||||||
AuList<AuSPtr<ILoopSource>> ret;
|
AuList<AuSPtr<ILoopSource>> ret;
|
||||||
@ -461,12 +489,10 @@ namespace Aurora::Loop
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool bTryAgain {};
|
bool bTryAgain {};
|
||||||
AuUInt32 cTicked {};
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
bTryAgain = false;
|
bTryAgain = false;
|
||||||
AuUInt32 ticked = DoTick(timeout, &ret, &bTryAgain);
|
AuUInt32 ticked = DoTick(timeout, &ret, &bTryAgain);
|
||||||
cTicked += ticked;
|
|
||||||
} while (bTryAgain);
|
} while (bTryAgain);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
@ -582,7 +608,7 @@ namespace Aurora::Loop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
AuUInt32 LoopQueue::DoTick(AuUInt64 time, AuList<AuSPtr<ILoopSource>> *optOut, bool *tryAgain)
|
AuUInt32 LoopQueue::DoTick(AuUInt64 time, AuList<AuSPtr<ILoopSource>> *optOut, bool *tryAgain, bool nonblock)
|
||||||
{
|
{
|
||||||
AuUInt32 bTicked {};
|
AuUInt32 bTicked {};
|
||||||
AuUInt64 now {};
|
AuUInt64 now {};
|
||||||
@ -590,7 +616,6 @@ namespace Aurora::Loop
|
|||||||
|
|
||||||
AU_LOCK_GUARD(this->sourceMutex_->AsReadable());
|
AU_LOCK_GUARD(this->sourceMutex_->AsReadable());
|
||||||
|
|
||||||
|
|
||||||
for (const auto & source : this->sources_)
|
for (const auto & source : this->sources_)
|
||||||
{
|
{
|
||||||
if (source->sourceExtended)
|
if (source->sourceExtended)
|
||||||
@ -599,33 +624,39 @@ namespace Aurora::Loop
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// epoll_pwait2 is fucking broken and the dipshits who wrote the test used relative values
|
// syscall epoll_pwait2 is fucking broken and the dipshits who wrote the test used relative values
|
||||||
//
|
//
|
||||||
// Nothing I tried worked.
|
// Nothing I tried worked.
|
||||||
//
|
//
|
||||||
// Am I stupid? Probably, but...
|
// Am I stupid? Probably, but...
|
||||||
// (1) no one as far as i can tell has ever written anything using this api, per a github search
|
// (1) no one as far as i can tell has ever written anything using this syscall, per a github search
|
||||||
// (2) i found one reference that the that are the linux kernel developers used MONO time for this
|
// (2) i found one reference that the that are the linux kernel developers used MONO time for this
|
||||||
// one timespec API unlike everything else, using an abs value rel to that clock didn't change
|
// one timespec API unlike everything else; using an abs value rel to that clock didn't change
|
||||||
// anything.
|
// anything.
|
||||||
// (3) i found a test that would indicate its relative despite the fact UNIX/Linux sync APIs
|
// (3) i found a test that would indicate its relative despite the fact UNIX/Linux sync APIs
|
||||||
// tend to use abs time
|
// tend to use abs time
|
||||||
//
|
//
|
||||||
// What does my experience working on xenus tell me?
|
// What does my experience working on xenus tell me?
|
||||||
// Because the GOOOOGLERs in the form of linux kernel developers were faced with an issue that
|
// Because the gOOOOglers in the form of linux kernel developers were faced with an issue that
|
||||||
// couldn't be solved by involve copy/pasting memory map code, making a mess of public headers,
|
// couldn't be solved by involve copy/pasting memory map code, making a mess of public headers,
|
||||||
// or taking credit for third party driver code as their own kernel code, indeed are to blame
|
// or taking credit for third party driver code as their own kernel code; they indeed are to
|
||||||
// for making my life miserable once again.
|
// blame for making my life miserable once again. Something about this aint adding up.
|
||||||
|
|
||||||
|
AuInt64 deltaMS = 0;
|
||||||
|
if (time)
|
||||||
|
{
|
||||||
auto deltaMS = time ? AuMin(AuInt64(4), (AuInt64)time - (AuInt64)AuTime::CurrentClockMS()) : 0;
|
deltaMS = AuMin(AuInt64(4), (AuInt64)time - (AuInt64)AuTime::CurrentClockMS());
|
||||||
if (deltaMS < 0) deltaMS = 0;
|
if (deltaMS < 0)
|
||||||
|
{
|
||||||
|
deltaMS = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
deltaMS = nonblock ? 0 : -1;
|
||||||
|
}
|
||||||
|
|
||||||
int iEvents = epoll_wait(this->epollFd_, events, AuArraySize(events), deltaMS);
|
int iEvents = epoll_wait(this->epollFd_, events, AuArraySize(events), deltaMS);
|
||||||
|
|
||||||
|
|
||||||
if (iEvents == -1)
|
if (iEvents == -1)
|
||||||
{
|
{
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -85,9 +85,6 @@ namespace Aurora::Loop
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
bool IsValid();
|
bool IsValid();
|
||||||
bool RemoveSourceNB(const AuSPtr<ILoopSource> &source);
|
bool RemoveSourceNB(const AuSPtr<ILoopSource> &source);
|
||||||
bool WaitAnyNBSpurious(AuUInt32 timeout, AuUInt32 &chuggerIndex, AuList<AuSPtr<ILoopSource>> *trigger, bool poll);
|
bool WaitAnyNBSpurious(AuUInt32 timeout, AuUInt32 &chuggerIndex, AuList<AuSPtr<ILoopSource>> *trigger, bool poll);
|
||||||
|
@ -21,12 +21,14 @@ namespace Aurora::Loop
|
|||||||
|
|
||||||
for (const auto i : handles)
|
for (const auto i : handles)
|
||||||
{
|
{
|
||||||
|
if (i == -1) continue;
|
||||||
FD_SET(i, &readSet);
|
FD_SET(i, &readSet);
|
||||||
maxHandle = AuMax(maxHandle, i + 1);
|
maxHandle = AuMax(maxHandle, i + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const auto i : handlesWrite)
|
for (const auto i : handlesWrite)
|
||||||
{
|
{
|
||||||
|
if (i == -1) continue;
|
||||||
FD_SET(i, &writeSet);
|
FD_SET(i, &writeSet);
|
||||||
maxHandle = AuMax(maxHandle, i + 1);
|
maxHandle = AuMax(maxHandle, i + 1);
|
||||||
}
|
}
|
||||||
@ -67,13 +69,24 @@ namespace Aurora::Loop
|
|||||||
{
|
{
|
||||||
fd_set readSet, writeSet;
|
fd_set readSet, writeSet;
|
||||||
struct timeval tv {};
|
struct timeval tv {};
|
||||||
|
int maxFd {};
|
||||||
|
|
||||||
FD_ZERO(&readSet);
|
FD_ZERO(&readSet);
|
||||||
FD_ZERO(&writeSet);
|
FD_ZERO(&writeSet);
|
||||||
FD_SET(read, &readSet);
|
|
||||||
FD_SET(write, &writeSet);
|
if (read != -1)
|
||||||
|
{
|
||||||
|
maxFd = read + 1;
|
||||||
|
FD_SET(read, &readSet);
|
||||||
|
}
|
||||||
|
|
||||||
auto active = select(AuMax(read, write) + 1, read != -1 ? &readSet : NULL, write != -1 ? &writeSet : NULL, NULL, &tv);
|
if (write != -1)
|
||||||
|
{
|
||||||
|
FD_SET(write, &writeSet);
|
||||||
|
maxFd = AuMax(maxFd, int(write) + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto active = select(maxFd, read != -1 ? &readSet : NULL, write != -1 ? &writeSet : NULL, NULL, &tv);
|
||||||
if (active == -1)
|
if (active == -1)
|
||||||
{
|
{
|
||||||
// todo push error
|
// todo push error
|
||||||
|
@ -18,14 +18,14 @@ namespace Aurora::Parse
|
|||||||
if (!AuTryResize(decoded, length))
|
if (!AuTryResize(decoded, length))
|
||||||
{
|
{
|
||||||
SysPushErrorMem();
|
SysPushErrorMem();
|
||||||
return {};
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto status = base32_decode(in.data(), in.size(), reinterpret_cast<unsigned char *>(&decoded[0]), &length, BASE32_RFC4648);
|
auto status = base32_decode(in.data(), in.size(), reinterpret_cast<unsigned char *>(&decoded[0]), &length, BASE32_RFC4648);
|
||||||
if (!AuTryResize(decoded, length))
|
if (!AuTryResize(decoded, length))
|
||||||
{
|
{
|
||||||
SysPushErrorMem();
|
SysPushErrorMem();
|
||||||
return {};
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status == CRYPT_OK;
|
return status == CRYPT_OK;
|
||||||
@ -38,14 +38,14 @@ namespace Aurora::Parse
|
|||||||
if (!AuTryResize(encoded, length))
|
if (!AuTryResize(encoded, length))
|
||||||
{
|
{
|
||||||
SysPushErrorMem();
|
SysPushErrorMem();
|
||||||
return {};
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto status = base32_encode(reinterpret_cast<const unsigned char *>(buffer), length, &encoded[0], &outLength, BASE32_RFC4648);
|
auto status = base32_encode(reinterpret_cast<const unsigned char *>(buffer), length, &encoded[0], &outLength, BASE32_RFC4648);
|
||||||
if (!AuTryResize(encoded, length))
|
if (!AuTryResize(encoded, length))
|
||||||
{
|
{
|
||||||
SysPushErrorMem();
|
SysPushErrorMem();
|
||||||
return {};
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return status == CRYPT_OK;
|
return status == CRYPT_OK;
|
||||||
|
@ -14,56 +14,58 @@ namespace Aurora::Parse
|
|||||||
AUKN_SYM bool Base64Decode(const AuString &in, AuByteBuffer &decoded, bool url)
|
AUKN_SYM bool Base64Decode(const AuString &in, AuByteBuffer &decoded, bool url)
|
||||||
{
|
{
|
||||||
unsigned long length = in.size();
|
unsigned long length = in.size();
|
||||||
try
|
|
||||||
|
if (!AuTryResize(decoded, length))
|
||||||
{
|
{
|
||||||
decoded.resize(in.size());
|
SysPushErrorMem();
|
||||||
|
|
||||||
int status;
|
|
||||||
if (url)
|
|
||||||
{
|
|
||||||
status = base64url_decode(in.data(), in.size(), reinterpret_cast<unsigned char *>(&decoded[0]), &length);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = base64_decode(in.data(), in.size(), reinterpret_cast<unsigned char *>(&decoded[0]), &length);
|
|
||||||
}
|
|
||||||
|
|
||||||
decoded.resize(length);
|
|
||||||
return status == CRYPT_OK;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
AuLogWarn("Decoding error: {}", in);
|
|
||||||
Debug::PrintError();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int status;
|
||||||
|
if (url)
|
||||||
|
{
|
||||||
|
status = base64url_decode(in.data(), in.size(), reinterpret_cast<unsigned char *>(&decoded[0]), &length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = base64_decode(in.data(), in.size(), reinterpret_cast<unsigned char *>(&decoded[0]), &length);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AuTryResize(decoded, length))
|
||||||
|
{
|
||||||
|
SysPushErrorMem();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status == CRYPT_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
AUKN_SYM bool Base64Encode(const Memory::MemoryViewRead &input, AuString &encoded, bool url)
|
AUKN_SYM bool Base64Encode(const Memory::MemoryViewRead &input, AuString &encoded, bool url)
|
||||||
{
|
{
|
||||||
unsigned long outLength = input.length + (input.length / 3.0) + 16;
|
unsigned long outLength = input.length + (input.length / 3.0) + 16;
|
||||||
try
|
|
||||||
{
|
|
||||||
encoded.resize(outLength);
|
|
||||||
|
|
||||||
int status;
|
if (!AuTryResize(encoded, outLength))
|
||||||
if (url)
|
|
||||||
{
|
|
||||||
status = base64url_encode(reinterpret_cast<const unsigned char*>(input.ptr), input.length, &encoded[0], &outLength);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
status = base64_encode(reinterpret_cast<const unsigned char*>(input.ptr), input.length, &encoded[0], &outLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
encoded.resize(outLength);
|
|
||||||
return status == CRYPT_OK;
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
{
|
||||||
AuLogWarn("Encoding error");
|
SysPushErrorMem();
|
||||||
Debug::PrintError();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int status;
|
||||||
|
if (url)
|
||||||
|
{
|
||||||
|
status = base64url_encode(reinterpret_cast<const unsigned char*>(input.ptr), input.length, &encoded[0], &outLength);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
status = base64_encode(reinterpret_cast<const unsigned char*>(input.ptr), input.length, &encoded[0], &outLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AuTryResize(encoded, outLength))
|
||||||
|
{
|
||||||
|
SysPushErrorMem();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status == CRYPT_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user