mirror of
https://github.com/minetest/minetest.git
synced 2025-09-18 03:15:20 +02:00
Add API to cancel async jobs (#14602)
* Implement API to cancel async jobs Co-authored-by: sfan5 <sfan5@live.de> * update AsyncJob:cancel documentation from review * Use IPC to unblock async * review * review async unblocking * review * Apply suggestions from code review Co-authored-by: sfan5 <sfan5@live.de> * minor licensing --------- Co-authored-by: y5nw <y5nw@protonmail.com> Co-authored-by: sfan5 <sfan5@live.de>
This commit is contained in:
@@ -96,38 +96,44 @@ void AsyncEngine::addWorkerThread()
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
u32 AsyncEngine::queueAsyncJob(std::string &&func, std::string &¶ms,
|
||||
const std::string &mod_origin)
|
||||
|
||||
u32 AsyncEngine::queueAsyncJob(LuaJobInfo &&job)
|
||||
{
|
||||
MutexAutoLock autolock(jobQueueMutex);
|
||||
u32 jobId = jobIdCounter++;
|
||||
|
||||
jobQueue.emplace_back();
|
||||
auto &to_add = jobQueue.back();
|
||||
to_add.id = jobId;
|
||||
to_add.function = std::move(func);
|
||||
to_add.params = std::move(params);
|
||||
to_add.mod_origin = mod_origin;
|
||||
assert(!job.function.empty());
|
||||
job.id = jobId;
|
||||
jobQueue.push_back(std::move(job));
|
||||
|
||||
jobQueueCounter.post();
|
||||
return jobId;
|
||||
}
|
||||
|
||||
u32 AsyncEngine::queueAsyncJob(std::string &&func, std::string &¶ms,
|
||||
const std::string &mod_origin)
|
||||
{
|
||||
LuaJobInfo to_add(std::move(func), std::move(params), mod_origin);
|
||||
return queueAsyncJob(std::move(to_add));
|
||||
}
|
||||
|
||||
u32 AsyncEngine::queueAsyncJob(std::string &&func, PackedValue *params,
|
||||
const std::string &mod_origin)
|
||||
{
|
||||
LuaJobInfo to_add(std::move(func), params, mod_origin);
|
||||
return queueAsyncJob(std::move(to_add));
|
||||
}
|
||||
|
||||
bool AsyncEngine::cancelAsyncJob(u32 id)
|
||||
{
|
||||
MutexAutoLock autolock(jobQueueMutex);
|
||||
u32 jobId = jobIdCounter++;
|
||||
|
||||
jobQueue.emplace_back();
|
||||
auto &to_add = jobQueue.back();
|
||||
to_add.id = jobId;
|
||||
to_add.function = std::move(func);
|
||||
to_add.params_ext.reset(params);
|
||||
to_add.mod_origin = mod_origin;
|
||||
|
||||
jobQueueCounter.post();
|
||||
return jobId;
|
||||
for (auto job = jobQueue.begin(); job != jobQueue.end(); job++) {
|
||||
if (job->id == id) {
|
||||
jobQueue.erase(job);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -419,3 +425,19 @@ void* AsyncWorkerThread::run()
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 ScriptApiAsync::queueAsync(std::string &&serialized_func,
|
||||
PackedValue *param, const std::string &mod_origin)
|
||||
{
|
||||
return asyncEngine.queueAsyncJob(std::move(serialized_func),
|
||||
param, mod_origin);
|
||||
}
|
||||
|
||||
bool ScriptApiAsync::cancelAsync(u32 id)
|
||||
{
|
||||
return asyncEngine.cancelAsyncJob(id);
|
||||
}
|
||||
|
||||
void ScriptApiAsync::stepAsync()
|
||||
{
|
||||
asyncEngine.step(getStack());
|
||||
}
|
||||
|
@@ -26,6 +26,12 @@ class AsyncEngine;
|
||||
struct LuaJobInfo
|
||||
{
|
||||
LuaJobInfo() = default;
|
||||
LuaJobInfo(std::string &&func, std::string &¶ms, const std::string &mod_origin = "") :
|
||||
function(func), params(params), mod_origin(mod_origin) {}
|
||||
LuaJobInfo(std::string &&func, PackedValue *params, const std::string &mod_origin = "") :
|
||||
function(func), mod_origin(mod_origin) {
|
||||
params_ext.reset(params);
|
||||
}
|
||||
|
||||
// Function to be called in async environment (from string.dump)
|
||||
std::string function;
|
||||
@@ -102,12 +108,26 @@ public:
|
||||
u32 queueAsyncJob(std::string &&func, PackedValue *params,
|
||||
const std::string &mod_origin = "");
|
||||
|
||||
/**
|
||||
* Try to cancel an async job
|
||||
* @param id The ID of the job
|
||||
* @return Whether the job was cancelled
|
||||
*/
|
||||
bool cancelAsyncJob(u32 id);
|
||||
|
||||
/**
|
||||
* Engine step to process finished jobs
|
||||
* @param L The Lua stack
|
||||
*/
|
||||
void step(lua_State *L);
|
||||
|
||||
/**
|
||||
* Get the maximum number of threads that can be used by the async environment
|
||||
*/
|
||||
unsigned int getThreadingCapacity() const {
|
||||
return MYMAX(workerThreads.size(), autoscaleMaxWorkers);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Get a Job from queue to be processed
|
||||
@@ -117,6 +137,13 @@ protected:
|
||||
*/
|
||||
bool getJob(LuaJobInfo *job);
|
||||
|
||||
/**
|
||||
* Queue an async job
|
||||
* @param job The job to queue (takes ownership!)
|
||||
* @return Id of the queued job
|
||||
*/
|
||||
u32 queueAsyncJob(LuaJobInfo &&job);
|
||||
|
||||
/**
|
||||
* Put a Job result back to result queue
|
||||
* @param result result of completed job
|
||||
@@ -206,3 +233,23 @@ private:
|
||||
// Counter semaphore for job dispatching
|
||||
Semaphore jobQueueCounter;
|
||||
};
|
||||
|
||||
class ScriptApiAsync:
|
||||
virtual public ScriptApiBase
|
||||
{
|
||||
public:
|
||||
ScriptApiAsync(Server *server): asyncEngine(server) {}
|
||||
|
||||
virtual void initAsync() = 0;
|
||||
void stepAsync();
|
||||
|
||||
u32 queueAsync(std::string &&serialized_func,
|
||||
PackedValue *param, const std::string &mod_origin);
|
||||
bool cancelAsync(u32 id);
|
||||
unsigned int getThreadingCapacity() const {
|
||||
return asyncEngine.getThreadingCapacity();
|
||||
}
|
||||
|
||||
protected:
|
||||
AsyncEngine asyncEngine;
|
||||
};
|
||||
|
@@ -3,6 +3,7 @@ file(GLOB common_SCRIPT_LUA_API_HDRS "${CMAKE_CURRENT_SOURCE_DIR}/*.h")
|
||||
set(common_SCRIPT_LUA_API_SRCS
|
||||
${common_SCRIPT_LUA_API_HDRS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_areastore.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_async.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_auth.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_base.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/l_craft.cpp
|
||||
|
64
src/script/lua_api/l_async.cpp
Normal file
64
src/script/lua_api/l_async.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
// Luanti
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#include "lua_api/l_internal.h"
|
||||
#include "lua_api/l_async.h"
|
||||
#include "cpp_api/s_async.h"
|
||||
|
||||
static std::string get_serialized_function(lua_State *L, int index)
|
||||
{
|
||||
luaL_checktype(L, index, LUA_TFUNCTION);
|
||||
call_string_dump(L, index);
|
||||
size_t func_length;
|
||||
const char *serialized_func_raw = lua_tolstring(L, -1, &func_length);
|
||||
std::string serialized_func(serialized_func_raw, func_length);
|
||||
lua_pop(L, 1);
|
||||
return serialized_func;
|
||||
}
|
||||
|
||||
// do_async_callback(func, params, mod_origin)
|
||||
int ModApiAsync::l_do_async_callback(lua_State *L)
|
||||
{
|
||||
NO_MAP_LOCK_REQUIRED;
|
||||
ScriptApiAsync *script = getScriptApi<ScriptApiAsync>(L);
|
||||
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
luaL_checktype(L, 3, LUA_TSTRING);
|
||||
|
||||
auto serialized_func = get_serialized_function(L, 1);
|
||||
PackedValue *param = script_pack(L, 2);
|
||||
std::string mod_origin = readParam<std::string>(L, 3);
|
||||
|
||||
u32 jobId = script->queueAsync(
|
||||
std::move(serialized_func),
|
||||
param, mod_origin);
|
||||
|
||||
lua_pushinteger(L, jobId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// cancel_async_callback(id)
|
||||
int ModApiAsync::l_cancel_async_callback(lua_State *L)
|
||||
{
|
||||
NO_MAP_LOCK_REQUIRED;
|
||||
ScriptApiAsync *script = getScriptApi<ScriptApiAsync>(L);
|
||||
u32 id = luaL_checkinteger(L, 1);
|
||||
lua_pushboolean(L, script->cancelAsync(id));
|
||||
return 1;
|
||||
}
|
||||
|
||||
// get_async_capacity()
|
||||
int ModApiAsync::l_get_async_threading_capacity(lua_State *L)
|
||||
{
|
||||
NO_MAP_LOCK_REQUIRED;
|
||||
ScriptApiAsync *script = getScriptApi<ScriptApiAsync>(L);
|
||||
lua_pushinteger(L, script->getThreadingCapacity());
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ModApiAsync::Initialize(lua_State *L, int top)
|
||||
{
|
||||
API_FCT(do_async_callback);
|
||||
API_FCT(cancel_async_callback);
|
||||
API_FCT(get_async_threading_capacity);
|
||||
}
|
19
src/script/lua_api/l_async.h
Normal file
19
src/script/lua_api/l_async.h
Normal file
@@ -0,0 +1,19 @@
|
||||
// Luanti
|
||||
// SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "lua_api/l_base.h"
|
||||
|
||||
class ModApiAsync : public ModApiBase
|
||||
{
|
||||
public:
|
||||
static void Initialize(lua_State *L, int top);
|
||||
private:
|
||||
// do_async_callback(func, params, mod_origin)
|
||||
static int l_do_async_callback(lua_State *L);
|
||||
// cancel_async_callback(id)
|
||||
static int l_cancel_async_callback(lua_State *L);
|
||||
// get_async_threading_capacity()
|
||||
static int l_get_async_threading_capacity(lua_State *L);
|
||||
};
|
@@ -625,33 +625,6 @@ int ModApiServer::l_notify_authentication_modified(lua_State *L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
// do_async_callback(func, params, mod_origin)
|
||||
int ModApiServer::l_do_async_callback(lua_State *L)
|
||||
{
|
||||
NO_MAP_LOCK_REQUIRED;
|
||||
ServerScripting *script = getScriptApi<ServerScripting>(L);
|
||||
|
||||
luaL_checktype(L, 1, LUA_TFUNCTION);
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
luaL_checktype(L, 3, LUA_TSTRING);
|
||||
|
||||
call_string_dump(L, 1);
|
||||
size_t func_length;
|
||||
const char *serialized_func_raw = lua_tolstring(L, -1, &func_length);
|
||||
|
||||
PackedValue *param = script_pack(L, 2);
|
||||
|
||||
std::string mod_origin = readParam<std::string>(L, 3);
|
||||
|
||||
u32 jobId = script->queueAsync(
|
||||
std::string(serialized_func_raw, func_length),
|
||||
param, mod_origin);
|
||||
|
||||
lua_settop(L, 0);
|
||||
lua_pushinteger(L, jobId);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// register_async_dofile(path)
|
||||
int ModApiServer::l_register_async_dofile(lua_State *L)
|
||||
{
|
||||
@@ -747,7 +720,6 @@ void ModApiServer::Initialize(lua_State *L, int top)
|
||||
API_FCT(unban_player_or_ip);
|
||||
API_FCT(notify_authentication_modified);
|
||||
|
||||
API_FCT(do_async_callback);
|
||||
API_FCT(register_async_dofile);
|
||||
API_FCT(serialize_roundtrip);
|
||||
|
||||
|
@@ -100,9 +100,6 @@ private:
|
||||
// notify_authentication_modified(name)
|
||||
static int l_notify_authentication_modified(lua_State *L);
|
||||
|
||||
// do_async_callback(func, params, mod_origin)
|
||||
static int l_do_async_callback(lua_State *L);
|
||||
|
||||
// register_async_dofile(path)
|
||||
static int l_register_async_dofile(lua_State *L);
|
||||
|
||||
|
@@ -9,6 +9,7 @@
|
||||
#include "filesys.h"
|
||||
#include "cpp_api/s_internal.h"
|
||||
#include "lua_api/l_areastore.h"
|
||||
#include "lua_api/l_async.h"
|
||||
#include "lua_api/l_auth.h"
|
||||
#include "lua_api/l_base.h"
|
||||
#include "lua_api/l_craft.h"
|
||||
@@ -39,7 +40,7 @@ extern "C" {
|
||||
|
||||
ServerScripting::ServerScripting(Server* server):
|
||||
ScriptApiBase(ScriptingType::Server),
|
||||
asyncEngine(server)
|
||||
ScriptApiAsync(server)
|
||||
{
|
||||
setGameDef(server);
|
||||
|
||||
@@ -118,18 +119,6 @@ void ServerScripting::initAsync()
|
||||
asyncEngine.initialize(0);
|
||||
}
|
||||
|
||||
void ServerScripting::stepAsync()
|
||||
{
|
||||
asyncEngine.step(getStack());
|
||||
}
|
||||
|
||||
u32 ServerScripting::queueAsync(std::string &&serialized_func,
|
||||
PackedValue *param, const std::string &mod_origin)
|
||||
{
|
||||
return asyncEngine.queueAsyncJob(std::move(serialized_func),
|
||||
param, mod_origin);
|
||||
}
|
||||
|
||||
void ServerScripting::InitializeModApi(lua_State *L, int top)
|
||||
{
|
||||
// Register reference classes (userdata)
|
||||
@@ -153,6 +142,7 @@ void ServerScripting::InitializeModApi(lua_State *L, int top)
|
||||
ModChannelRef::Register(L);
|
||||
|
||||
// Initialize mod api modules
|
||||
ModApiAsync::Initialize(L, top);
|
||||
ModApiAuth::Initialize(L, top);
|
||||
ModApiCraft::Initialize(L, top);
|
||||
ModApiEnv::Initialize(L, top);
|
||||
|
@@ -22,6 +22,7 @@ struct PackedValue;
|
||||
|
||||
class ServerScripting:
|
||||
virtual public ScriptApiBase,
|
||||
public ScriptApiAsync,
|
||||
public ScriptApiDetached,
|
||||
public ScriptApiEntity,
|
||||
public ScriptApiEnv,
|
||||
@@ -41,14 +42,7 @@ public:
|
||||
void saveGlobals();
|
||||
|
||||
// Initialize async engine, call this AFTER loading all mods
|
||||
void initAsync();
|
||||
|
||||
// Global step handler to collect async results
|
||||
void stepAsync();
|
||||
|
||||
// Pass job to async threads
|
||||
u32 queueAsync(std::string &&serialized_func,
|
||||
PackedValue *param, const std::string &mod_origin);
|
||||
void initAsync() override;
|
||||
|
||||
protected:
|
||||
// from ScriptApiSecurity:
|
||||
@@ -63,6 +57,4 @@ private:
|
||||
void InitializeModApi(lua_State *L, int top);
|
||||
|
||||
static void InitializeAsync(lua_State *L, int top);
|
||||
|
||||
AsyncEngine asyncEngine;
|
||||
};
|
||||
|
Reference in New Issue
Block a user