AuroraRuntime/Source/Console/Commands/Commands.cpp
Reece 99c5e1fa65 A pretty large patch not worth breaking up into separate commits
[*] Split up Aurora Async
[*] Split Async app into seperate ThreadPool concept
[*] Fix various OSThread bugs and tls transfer issues
[*] Set default affinity to 0xFFFFFFFF
[*] Update Build script
[+] Add AuTuplePopFront
[+] New Network Interface (unimplemented)
[*] Stub out the interfaces required for a better logger
[*] Fix Win32 ShellExecute bug; windows 11 struggles without explicit com init per the docs - now deferring to thread pool
[*] Update gitignore
[*] Follow XDG home standard
[*] Refactor some namespaces to use the shorthand aliases
[*] Various stability fixes
2021-11-05 17:34:23 +00:00

201 lines
6.2 KiB
C++

/***
Copyright (C) 2021 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: Commands.cpp
Date: 2021-6-12
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "Commands.hpp"
namespace Aurora::Console::Commands
{
struct Command;
struct CommandDispatch;
static AuHashMap<AuString, Command> gCommands;
static AuList<Hooks::LineHook_cb> gLineCallbacks;
static AuList<CommandDispatch> gPendingCommands;
static auto gMutex = AuThreadPrimitives::MutexUnique();
static auto gPendingCommandsMutex = AuThreadPrimitives::MutexUnique();
static Async::WorkerPId_t gCommandDispatcher;
struct Command
{
AuString tag;
Parse::ParseObject commandStructure;
AuSPtr<ICommandSubscriber> callback;
Command(AuString tag, Parse::ParseObject commandStructure, const AuSPtr<ICommandSubscriber> &callback) : tag(tag), commandStructure(commandStructure), callback(callback) {}
Command(AuString tag, Parse::ParseObject commandStructure, AuSPtr<ICommandSubscriber> &&callback) : tag(tag), commandStructure(commandStructure), callback(std::move(callback)) {}
};
struct CommandDispatch
{
Parse::ParsedObject arguments;
AuSPtr<ICommandSubscriber> callback;
CommandDispatch(const Parse::ParsedObject &arguments, const AuSPtr<ICommandSubscriber> &callback) : arguments(arguments), callback(callback) {}
};
enum class EDispatchType
{
eNow,
eSys,
eAsync
};
static bool Dispatch(const AuString &string, EDispatchType type, Async::WorkerPId_t workerId)
{
Parse::ParseResult res;
AuSPtr<ICommandSubscriber> callback;
{
AU_LOCK_GUARD(gPendingCommandsMutex);
{
AuString tag;
AuString cmdParse;
AuMach offset;
auto commandSplit = string.find(" ");
if (commandSplit != AuString::npos)
{
tag = string.substr(0, commandSplit);
cmdParse = string.substr(commandSplit + 1);
}
else
{
tag = string;
}
auto cmdItr = gCommands.find(tag);
if (cmdItr == gCommands.end())
{
LogWarn("Command {} does not exist", tag);
return false;
}
auto const &cmdEntry = cmdItr->second;
offset = 0;
Parse::ParseState consumable(Parse::StringToConsumable(cmdParse, offset));
auto status = Parse::Parse(consumable, cmdEntry.commandStructure, res);
if (!status)
{
LogWarn("Couldn't parse command {}", string);
return false;
}
if (type == EDispatchType::eSys)
{
gPendingCommands.push_back(CommandDispatch(res.result, cmdEntry.callback));
}
else
{
callback = cmdEntry.callback;
}
}
}
if (type == EDispatchType::eNow)
{
callback->OnCommand(res.result);
}
else
{
Async::DispatchWork<CommandDispatch>(workerId,
Async::TaskFromConsumerRefT<CommandDispatch>([](const CommandDispatch &dispatch) -> void
{
dispatch.callback->OnCommand(dispatch.arguments);
}),
{},
CommandDispatch(res.result, callback),
false
);
}
return true;
}
AUKN_SYM void AddCommand(const AuString &tag, const Parse::ParseObject &commandStructure, const AuSPtr<ICommandSubscriber> &callback)
{
AU_LOCK_GUARD(gPendingCommandsMutex);
gCommands.insert(AuMakePair(tag, Command(tag, commandStructure, callback)));
}
AUKN_SYM bool DispatchCommand(const AuString &string)
{
return Dispatch(string, EDispatchType::eSys, {});
}
AUKN_SYM bool DispatchCommandThisThread(const AuString &string)
{
return Dispatch(string, EDispatchType::eNow, {});
}
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerId_t id)
{
return Dispatch(string, EDispatchType::eAsync, AuAsync::WorkerPId_t(AuUnsafeRaiiToShared(AuAsync::GetAsyncApp()), id));
}
AUKN_SYM bool DispatchCommandToAsyncRunner(const AuString &string, Async::WorkerPId_t id)
{
return Dispatch(string, EDispatchType::eAsync, id);
}
void UpdateDispatcher(AuOptional<Async::WorkerId_t> target)
{
AU_LOCK_GUARD(gMutex);
AU_LOCK_GUARD(gPendingCommandsMutex);
// process commands before async app termination
if ((!target.has_value()))
{
auto commands = std::exchange(gPendingCommands, {});
for (const auto &command : commands)
{
command.callback->OnCommand(command.arguments);
}
}
gCommandDispatcher = Async::WorkerPId_t(AuUnsafeRaiiToShared(AuAsync::GetAsyncApp()), target.value());
}
static void DispatchCommandsFromThis(const AuList<CommandDispatch> &commands)
{
for (const auto &command : commands)
{
command.callback->OnCommand(command.arguments);
}
}
void PumpCommands()
{
gMutex->Lock();
gPendingCommandsMutex->Lock();
auto commands = std::exchange(gPendingCommands, {});
gPendingCommandsMutex->Unlock();
if ((gCommandDispatcher.pool == nullptr) ||
((gCommandDispatcher.pool.get() == Async::GetAsyncApp()) &&
(gCommandDispatcher == Async::WorkerId_t{})))
{
DispatchCommandsFromThis(commands);
}
else
{
NewWorkItem(gCommandDispatcher,
AuMakeShared<Async::BasicWorkStdFunc>([&commands]()
{
DispatchCommandsFromThis(commands);
}),
true)->Dispatch()->BlockUntilComplete();
}
gMutex->Unlock();
}
}