aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--actionqueue.lua74
-rw-r--r--chatcommands.lua22
-rw-r--r--circuit_breaker.lua138
-rw-r--r--globalstep.lua62
-rw-r--r--init.lua7
5 files changed, 188 insertions, 115 deletions
diff --git a/actionqueue.lua b/actionqueue.lua
deleted file mode 100644
index 3f1862f..0000000
--- a/actionqueue.lua
+++ /dev/null
@@ -1,74 +0,0 @@
--- smarter mesecons actionqueue
--- TODO: create PR if ot works properly
-
-
--- execute the stored functions on a globalstep
--- if however, the pos of a function is not loaded (get_node_or_nil == nil), do NOT execute the function
--- this makes sure that resuming mesecons circuits when restarting minetest works fine
--- However, even that does not work in some cases, that's why we delay the time the globalsteps
--- start to be execute by 5 seconds
-local get_highest_priority = function (actions)
- local highestp = -1
- local highesti
- for i, ac in ipairs(actions) do
- if ac.priority > highestp then
- highestp = ac.priority
- highesti = i
- end
- end
-
- return highesti
-end
-
-local m_time = 0
-local resumetime = mesecon.setting("resumetime", 4)
-minetest.register_globalstep(function (dtime)
- m_time = m_time + dtime
- -- don't even try if server has not been running for XY seconds; resumetime = time to wait
- -- after starting the server before processing the ActionQueue, don't set this too low
- if (m_time < resumetime) then return end
-
- if not mesecons_debug.enabled then
- return
- end
-
- local actions = mesecon.tablecopy(mesecon.queue.actions)
- local actions_now={}
-
- mesecon.queue.actions = {}
-
- -- sort actions into two categories:
- -- those toexecute now (actions_now) and those to execute later (mesecon.queue.actions)
- for _, ac in ipairs(actions) do
- if ac.time > 0 then
- ac.time = ac.time - dtime -- executed later
- table.insert(mesecon.queue.actions, ac)
- else
- table.insert(actions_now, ac)
- end
- end
-
- if #actions_now > 30000 then
- -- too much actions, purge them
- return
- end
-
- local t0 = minetest.get_us_time()
-
- while(#actions_now > 0) do -- execute highest priorities first, until all are executed
- local hp = get_highest_priority(actions_now)
- local action = actions_now[hp]
-
- local t1 = minetest.get_us_time()
- local diff = t1 - t0
- if diff > 75000 then
- -- execute remaining actions in next globalstep
- table.insert(mesecon.queue.actions, 1, action)
- else
- mesecon.queue:execute(action)
- table.remove(actions_now, hp)
- end
-
- end
-end)
-
diff --git a/chatcommands.lua b/chatcommands.lua
deleted file mode 100644
index f382b98..0000000
--- a/chatcommands.lua
+++ /dev/null
@@ -1,22 +0,0 @@
-
--- mesecons commands
-
-minetest.register_chatcommand("mesecons_enable", {
- description = "enables the mesecons globlastep",
- privs = {mesecons_debug=true},
- func = function()
- mesecons_debug.enabled = true
- return true, "mesecons enabled"
- end
-})
-
-minetest.register_chatcommand("mesecons_disable", {
- description = "disables the mesecons globlastep",
- privs = {mesecons_debug=true},
- func = function()
- mesecons_debug.enabled = false
- -- flush actions, while we are on it
- mesecon.queue.actions = {}
- return true, "mesecons disabled"
- end
-})
diff --git a/circuit_breaker.lua b/circuit_breaker.lua
new file mode 100644
index 0000000..ce1452a
--- /dev/null
+++ b/circuit_breaker.lua
@@ -0,0 +1,138 @@
+
+-- "circuit break" mapblocks in which mesecons took too long to execute
+-- TODO: toggleable hud with current cpu usage
+
+-- sample/reset interval
+local sample_interval = 10
+
+-- util
+-- minetest.hash_node_position(get_blockpos(pos))
+local function get_blockpos(pos)
+ return {x = math.floor(pos.x / 16),
+ y = math.floor(pos.y / 16),
+ z = math.floor(pos.z / 16)}
+end
+
+
+-- per block cpu time usage in micros
+local per_block_time_usage = {}
+
+-- max per block cpu time usage in micros
+local max_per_block_time_usage = {}
+
+-- disabled/dark mapblocks
+local dark_mapblocks = {}
+
+-- switch off setting
+local max_time_setting
+local dark_time
+
+function update_settings()
+ max_time_setting = tonumber( minetest.settings:get("mesecons_debug.circuit_breaker") or "75000" )
+ dark_time = tonumber( minetest.settings:get("mesecons_debug.dark_time") or "30000000" )
+end
+
+update_settings()
+
+-- periodic timer
+local timer = 0
+minetest.register_globalstep(function(dtime)
+ timer = timer + dtime
+ if timer < sample_interval then return end
+ timer=0
+
+ -- reset time usage
+ per_block_time_usage = {}
+
+ -- update settings, if changed
+ update_settings()
+
+end)
+
+-- mesecon mod overrides
+local old_execute = mesecon.queue.execute
+mesecon.queue.execute = function(self, action)
+ local blockpos = get_blockpos(action.pos)
+ local hash = minetest.hash_node_position(blockpos)
+ local t0 = minetest.get_us_time()
+
+ local dark_timer = dark_mapblocks[hash]
+ if dark_timer and dark_timer < t0 then
+ -- timeout expired, disable mapblock throttling
+ dark_mapblocks[hash] = nil
+ dark_timer = nil
+ end
+
+ local time_usage = per_block_time_usage[hash] or 0
+
+ old_execute(self, action)
+ local t1 = minetest.get_us_time()
+ local diff = t1 -t0
+ time_usage = time_usage + diff
+
+ -- update max stats
+ if (max_per_block_time_usage[hash] or 0) < time_usage then
+ max_per_block_time_usage[hash] = time_usage
+ end
+
+ if time_usage > max_time_setting and not dark_timer then
+ -- time usage exceeded, throttle mapblock
+ dark_mapblocks[hash] = t1 + dark_time
+ minetest.log("warning", "[mesecons_debug] throttled mapblock at " ..
+ minetest.pos_to_string(action.pos))
+ end
+
+ -- update time usage
+ per_block_time_usage[hash] = time_usage
+end
+
+local old_add_action = mesecon.queue.add_action
+mesecon.queue.add_action = function(self, pos, func, params, time, overwritecheck, priority)
+ time = time or 0
+ local blockpos = get_blockpos(pos)
+ local hash = minetest.hash_node_position(blockpos)
+
+ local dark_timer = dark_mapblocks[hash]
+ if dark_timer then
+ -- throttle add actions
+ time = time + 1
+ end
+
+ old_add_action(self, pos, func, params, time, overwritecheck, priority)
+end
+
+
+-- chat commands
+
+minetest.register_chatcommand("mesecons_debug_circuit_breaker_stats", {
+ description = "shows the stats for the current mapblock",
+ func = function(name)
+ local player = minetest.get_player_by_name(name)
+ local pos = player:get_pos()
+ local blockpos = get_blockpos(pos)
+ local hash = minetest.hash_node_position(blockpos)
+ local time_usage = max_per_block_time_usage[hash] or 0
+
+ local t0 = minetest.get_us_time()
+ local dark_timer = dark_mapblocks[hash]
+
+ local msg = "Max-time usage: " .. time_usage .. " micro-seconds " ..
+ "(sampled over " .. sample_interval .. " seconds)"
+
+ if dark_timer and dark_timer > t0 then
+ msg = msg .. " [Mapblock throttled!]"
+ end
+
+ return true, msg
+ end
+})
+
+minetest.register_chatcommand("mesecons_debug_circuit_breaker_stats_reset", {
+ description = "resets the max stats",
+ privs = {mesecons_debug=true},
+ func = function()
+ max_per_block_time_usage = {}
+ dark_mapblocks = {}
+ return true, "circuit breaker stats cleared!"
+ end
+})
diff --git a/globalstep.lua b/globalstep.lua
index cd0d95d..c00e132 100644
--- a/globalstep.lua
+++ b/globalstep.lua
@@ -1,7 +1,10 @@
+-- enable/disable mesecons entirely
+
+local enabled = true
+
-- globalstep on/off
-local i = 0
-for _, globalstep in ipairs(minetest.registered_globalsteps) do
+for i, globalstep in ipairs(minetest.registered_globalsteps) do
local info = minetest.callback_origins[globalstep]
if not info then
break
@@ -10,24 +13,34 @@ for _, globalstep in ipairs(minetest.registered_globalsteps) do
local modname = info.mod
if modname == "mesecons" then
- i = i + 1
- -- 1 = execute globalstep
- -- 2 = cooldown globalstep
- if i == 1 then
- local fn = function(dtime)
- globalstep(dtime)
- end
-
- minetest.callback_origins[fn] = info
- minetest.registered_globalsteps[i] = fn
+ local cooldown = 0
+ local fn = function(dtime)
+ if cooldown > 0 then
+ cooldown = cooldown - 1
+ return
+ end
+
+ if enabled then
+ local t0 = minetest.get_us_time()
+ globalstep(dtime)
+ local t1 = minetest.get_us_time()
+ local diff = t1 - t0
+ if diff > 75000 then
+ cooldown = 7
+ minetest.log("warning", "[mesecons_debug] cooldown triggered")
+ end
+ end
end
+
+ minetest.callback_origins[fn] = info
+ minetest.registered_globalsteps[i] = fn
end
end
-- execute()
local old_execute = mesecon.queue.execute
mesecon.queue.execute = function(...)
- if mesecons_debug.enabled then
+ if enabled then
old_execute(...)
end
end
@@ -35,9 +48,30 @@ end
-- add_action()
local old_add_action = mesecon.queue.add_action
mesecon.queue.add_action = function(...)
- if mesecons_debug.enabled then
+ if enabled then
old_add_action(...)
end
end
+-- mesecons commands
+
+minetest.register_chatcommand("mesecons_enable", {
+ description = "enables the mesecons globlastep",
+ privs = {mesecons_debug=true},
+ func = function()
+ enabled = true
+ return true, "mesecons enabled"
+ end
+})
+
+minetest.register_chatcommand("mesecons_disable", {
+ description = "disables the mesecons globlastep",
+ privs = {mesecons_debug=true},
+ func = function()
+ enabled = false
+ -- flush actions, while we are on it
+ mesecon.queue.actions = {}
+ return true, "mesecons disabled"
+ end
+})
diff --git a/init.lua b/init.lua
index 8a3eb3a..14dfe13 100644
--- a/init.lua
+++ b/init.lua
@@ -1,16 +1,13 @@
local MP = minetest.get_modpath("mesecons_debug")
-mesecons_debug = {
- enabled = true
-}
+mesecons_debug = {}
dofile(MP.."/privs.lua")
-dofile(MP.."/chatcommands.lua")
dofile(MP.."/api_action_on.lua")
dofile(MP.."/api_nodetimer.lua")
dofile(MP.."/register.lua")
dofile(MP.."/flush.lua")
dofile(MP.."/globalstep.lua")
-dofile(MP.."/actionqueue.lua")
+-- dofile(MP.."/circuit_breaker.lua")
print("[OK] mesecons_debug loaded")