summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormpeterv <mpeterval@gmail.com>2016-04-17 18:56:11 +0300
committermpeterv <mpeterval@gmail.com>2016-04-17 18:56:11 +0300
commit1234e0ffe19f3c6a9100d736d1456f63b7eb5dff (patch)
tree777fb1f20188d52bfa3da53ed8254f440609c7fb
parent4bac04e98c8da125d9d33a3324e753434e3571e5 (diff)
downloadluacov-1234e0ffe19f3c6a9100d736d1456f63b7eb5dff.tar.gz
Accumulate new coverage instead of storing all of it
Instead of loading all stats at start, updating them gradually and saving at the end, accumulate new stats; then, when saving, load old stats, add new stats to them, save the sum. This means that stats saving/loading is much more "atomic": luacov doesn't care at all if another instance updates the stats file, unless they happen to try to save stats at the same time. That can't happen in the most common case when parent Lua with luacov enabled starts another one using os.execute. Even with luacov.tick parent may only save after the child exits. luacov.pause and luacov.resume are now only needed for pausing stats saving, which may be useful for performance when using luacov.tick.
-rw-r--r--src/luacov/runner.lua85
-rw-r--r--tests/nested/test.lua4
2 files changed, 32 insertions, 57 deletions
diff --git a/src/luacov/runner.lua b/src/luacov/runner.lua
index e9b87b7..7671862 100644
--- a/src/luacov/runner.lua
+++ b/src/luacov/runner.lua
@@ -20,7 +20,7 @@ local function on_exit_wrap(fn)
return anchor
end
-local data
+local data = {}
local tick
local paused = true
local initialized = false
@@ -80,6 +80,22 @@ function runner.update_stats(old_stats, extra_stats)
end
end
+-- Adds accumulated stats to existing stats file or writes a new one, then resets data.
+local function save_stats()
+ local loaded = stats.load(runner.configuration.statsfile) or {}
+
+ for name, file_data in pairs(data) do
+ if loaded[name] then
+ runner.update_stats(loaded[name], file_data)
+ else
+ loaded[name] = file_data
+ end
+ end
+
+ stats.save(runner.configuration.statsfile, loaded)
+ data = {}
+end
+
--------------------------------------------------
-- Debug hook set by LuaCov.
-- Acknowledges that a line is executed, but does nothing
@@ -144,7 +160,7 @@ function runner.debug_hook(_, line_nr, level)
ctr = 0
if not paused then
- stats.save(runner.configuration.statsfile, data)
+ save_stats()
end
end
end
@@ -175,10 +191,11 @@ local function on_exit()
-- so this method could be called twice
if on_exit_run_once then return end
on_exit_run_once = true
+ save_stats()
- runner.pause()
-
- if runner.configuration.runreport then runner.run_report(runner.configuration) end
+ if runner.configuration.runreport then
+ runner.run_report(runner.configuration)
+ end
end
-- Returns true if the given filename exists.
@@ -376,58 +393,16 @@ function runner.load_config(configuration)
end
--------------------------------------------------
--- Pauses LuaCov's runner.
--- Saves collected data and stops, allowing other processes to write to
--- the same stats file. Data is still collected during pause.
+-- Pauses saving data collected by LuaCov's runner.
+-- Allows other processes to write to the same stats file.
+-- Data is still collected during pause.
function runner.pause()
- if paused then
- return
- end
-
paused = true
- stats.save(runner.configuration.statsfile, data)
- -- Reset data, so that after resuming it could be added to data loaded
- -- from the stats file, possibly updated from another process.
- data = {}
end
--------------------------------------------------
--- Resumes LuaCov's runner.
--- Reloads stats file, possibly updated from other processes,
--- and continues saving collected data.
+-- Resumes saving data collected by LuaCov's runner.
function runner.resume()
- if not paused then
- return
- end
-
- local loaded = stats.load(runner.configuration.statsfile) or {}
-
- if data then
- for name, file in pairs(loaded) do
- if data[name] then
- runner.update_stats(data[name], file)
- else
- data[name] = file
- end
- end
- else
- data = loaded
- end
-
- if not tick then
- -- As __gc hooks are called in reverse order of their creation,
- -- and stats file has a __gc hook closing it,
- -- the exit __gc hook writing data to stats file must be recreated
- -- after stats file is reopened.
-
- if runner.on_exit_trick then
- -- Deactivate previous handler.
- getmetatable(runner.on_exit_trick).__gc = nil
- end
-
- runner.on_exit_trick = on_exit_wrap(on_exit)
- end
-
paused = false
end
@@ -476,7 +451,6 @@ end
function runner.init(configuration)
runner.configuration = runner.load_config(configuration)
tick = runner.tick
- runner.resume()
-- metatable trick on filehandle won't work if Lua exits through
-- os.exit() hence wrap that with exit code as well
@@ -517,7 +491,12 @@ function runner.init(configuration)
end
end
+ if not tick then
+ runner.on_exit_trick = on_exit_wrap(on_exit)
+ end
+
initialized = true
+ paused = false
end
--------------------------------------------------
@@ -525,7 +504,7 @@ end
-- This should only be called from daemon processes or sandboxes which have
-- disabled os.exit and other hooks that are used to determine shutdown.
function runner.shutdown()
- on_exit()
+ on_exit()
end
-- Gets the sourcefilename from a function.
diff --git a/tests/nested/test.lua b/tests/nested/test.lua
index 67a5e72..d9d4e49 100644
--- a/tests/nested/test.lua
+++ b/tests/nested/test.lua
@@ -3,8 +3,6 @@ local luacov = require "luacov.runner"
testlib.f1()
-luacov.pause()
-
local cmd = arg[-5] or "lua"
local slash = cmd:find("/")
@@ -21,6 +19,4 @@ cmd = cmd .. " -e 'dofile([[script.lua]])'"
local ok = os.execute("cd subdir && " .. cmd)
assert(ok == 0 or ok == true)
-luacov.resume()
-
testlib.f2()