simplifying runners Notifies implementation

This commit is contained in:
Axel Kittenberger 2010-11-20 13:10:52 +00:00
parent 1baed5c949
commit ec336e0ed1
2 changed files with 140 additions and 135 deletions

View File

@ -115,5 +115,5 @@ convert = {
end, end,
} }
sync{convert, source="magicdir", recursive=false} sync{convert, source="magicdir", subdirs=false}

View File

@ -1401,6 +1401,24 @@ local Syncs = (function()
end)() end)()
-----
-- Utility function, returns the relative part of absolute path if it
-- begins with root
--
local function splitPath(path, root)
local rl = #root
local sp = string.sub(path, 1, rl)
print("split", path, root, sp)
if sp == root then
print("splited", path, root, string.sub(path, rl, -1))
return string.sub(path, rl, -1)
else
return nil
end
end
----- -----
-- Interface to inotify, watches recursively subdirs and -- Interface to inotify, watches recursively subdirs and
-- sends events. -- sends events.
@ -1410,64 +1428,57 @@ end)()
-- by changing this. -- by changing this.
-- --
local Inotifies = (function() local Inotifies = (function()
-----
-- A list indexed by inotifies watch descriptor. ---- XXX wdlist, syncpaths
-- Contains a list of all syncs observing this directory
-- (directly or by recurse)
local wdlist = CountArray.new()
----- -----
-- A list indexed by sync's containing a list of all paths -- A list indexed by inotifies watch descriptor yielding the
-- watches by this sync pointing to the watch descriptor. -- directories absolute paths.
local syncpaths = {}
-----
-- Adds watches for a directory including all subdirectories.
-- --
-- @param root+path directory to observe local wdpaths = CountArray.new()
-- @param recurse true if recursing into subdirs or
-- the relative path to root for recursed inotifies
-- @param sync link to the observer to be notified.
-- Note: Inotifies should handle this opaquely
-- @param raise if not nil creates Create events for all files/dirs
-- in this directory. Value is time for event.
local function addSync(root, path, recurse, sync, raise)
log("Function",
"Inotifies.addSync(",root,", ",path,", ",recurse,", ",sync,")")
-----
-- The same vice versa, all watch descriptors by its
-- absolute path.
--
local pathwds = CountArray.new()
-----
-- A list indexed by sync's containing the root path this
-- sync is interested in.
--
local syncRoots = {}
-----
-- Adds watches for a directory (optionally) including all subdirectories.
--
-- @param path absolute path of directory to observe
-- @param recurse true if recursing into subdirs
-- @param raiseSync --X --
-- raiseTime if not nil sends create Events for all files/dirs
-- to this sync.
--
local function addWatch(path, recurse, raiseSync, raiseTime)
log("Function",
"Inotifies.addWatch(",path,", ",recurse,", ",sync,", ",raise")")
local wd = pathwds[path]
if not wd then
-- lets the core registers watch with the kernel -- lets the core registers watch with the kernel
local wd = lsyncd.inotifyadd(root .. path); local wd = lsyncd.inotifyadd(path);
if wd < 0 then if wd < 0 then
log("Error","Failure adding watch ",root,path," -> ignored ") log("Error","Failure adding watch ",path," -> ignored ")
return return
end end
pathwds[path] = wd
-- creates a sublist in wdlist wdpaths[wd] = path
if not wdlist[wd] then
wdlist[wd] = Array.new()
end end
-- and adds this sync to wdlist.
table.insert(wdlist[wd], {
root = root,
path = path,
recurse = recurse,
sync = sync
})
-- creates an entry in syncpaths to quickly retrieve
-- all entries of this sync for a path.
local sp = syncpaths[sync]
if not sp then
sp = {}
syncpaths[sync] = sp
end
sp[path] = wd
-- registers and adds watches for all subdirectories -- registers and adds watches for all subdirectories
-- and/or raises create events for all entries -- and/or raises create events for all entries
if recurse or raise then if recurse or raise then
local entries = lsyncd.readdir(root .. path) local entries = lsyncd.readdir(path)
for dirname, isdir in pairs(entries) do for dirname, isdir in pairs(entries) do
local pd = path .. dirname local pd = path .. dirname
if isdir then if isdir then
@ -1475,50 +1486,42 @@ local Inotifies = (function()
end end
-- creates a Create event for entry. -- creates a Create event for entry.
if raise then if raiseSync then
sync:delay("Create", raise, pd, nil) raiseSync:delay("Create", raiseTime, pd, nil)
end end
-- adds syncs for subdirs -- adds syncs for subdirs
if isdir and recurse then if isdir and recurse then
addSync(root, pd, true, sync, raise) addWatch(pd, true, raiseSync, raiseTime)
end end
end end
end end
end end
----- -----
-- Removes one event receiver from a directory. -- Stops watching a directory
-- --
local function removeSync(sync, path) local function removeWatch(path)
local sp = syncpaths[sync] local wd = pathwds[path]
if not sp then
error("internal fail, removeSync, nonexisting sync: ")
end
local wd = sp[path]
if not wd then if not wd then
error("internal fail, removeSync, nonexisting wd.") return
end end
local ilist = wdlist[wd]
if not ilist then
error("internal fail, removeSync, nonexisting ilist.")
end
-- TODO optimize for 1 entry only case
local i, found
for i, v in ipairs(ilist) do
if v.sync == sync then
found = true
break
end
end
if not found then
error("internal fail, removeSync, nonexisiting i.")
end
table.remove(ilist, i)
if #ilist == 0 then
wdlist[wd] = nil
lsyncd.inotifyrm(wd) lsyncd.inotifyrm(wd)
wdpaths[wd] = nil
pathwids[path] = nil
end end
sp[path] = nil
-----
-- adds a Sync to receive events
--
-- @param root root dir to watch
-- @param sync Object to receive events
--
local function addSync(root, sync)
if syncRoots[sync] then
error("internal fail, duplicate sync in Inotifies()")
end
syncRoots[sync] = root
addWatch(root, true)
end end
----- -----
@ -1533,6 +1536,7 @@ local Inotifies = (function()
-- --
local function event(etype, wd, isdir, time, filename, wd2, filename2) local function event(etype, wd, isdir, time, filename, wd2, filename2)
local ftype; local ftype;
if isdir then if isdir then
ftype = "directory" ftype = "directory"
filename = filename .. "/" filename = filename .. "/"
@ -1540,6 +1544,7 @@ local Inotifies = (function()
filename2 = filename2 .. "/" filename2 = filename2 .. "/"
end end
end end
if filename2 then if filename2 then
log("Inotify", "got event ", etype, " ", filename, log("Inotify", "got event ", etype, " ", filename,
" to ", filename2) " to ", filename2)
@ -1548,80 +1553,80 @@ local Inotifies = (function()
end end
-- looks up the watch descriptor id -- looks up the watch descriptor id
local ilist = wdlist[wd] local path = wdlist[wd]
if not ilist then if not path then
-- this is normal in case of deleted subdirs -- this is normal in case of deleted subdirs
log("Inotify", "event belongs to unknown watch descriptor.") log("Inotify", "event belongs to unknown watch descriptor.")
return return
end end
local ilist2 = wd2 and wdlist[wd2]
-- works through all observers interested in this directory local path2 = wd2 and wdpaths[wd2]
for _, inotify in ipairs(ilist) do -- set to true if at least one sync wants recursive data
local path = inotify.path .. filename local recurse = false
local path2
local etype2 = etype
if filename2 and ilist2 then
local inotify2
-- finds the target directory inotify/watch
for _2, i2 in ipairs(ilist2) do
if inotify.sync == i2.sync then
inotify2 = i2
break
end
end
if not inotify2 then
log("Normal", "Transformed move to Create for ",
inotify.sync.config.name)
etype2 = "Create"
else
path2 = inotify2.path .. filename2
end
end
inotify.sync:delay(etype2, time, path, path2)
-- adds subdirs for new directories for sync, root in pairs(syncRoots) do repeat
if isdir and inotify.recurse then local relative = splitPath(path, root)
if etype2 == "Create" then local relative2
addSync(inotify.root, path, true, inotify.sync, time) if path2 then
elseif etype2 == "Delete" then relative2 = splitPath(path2, root)
removeSync(inotify.sync, path)
elseif etype2 == "Move" then
removeSync(inotify.sync, path)
addSync(inotify.root, path2, true, inotify.sync, time)
end
end end
if not relative and not relative2 then
-- sync is not interested in this dir
break -- continue
end end
-- TODO handle cases where a sync watches target only. XXX --- checks if this sync is interested in subdirs
if isdir then
recurse = recurse or
sync.config.subdirs or
sync.config.subdirs == nil
end
-- makes a copy of etype to possibly change it
local etyped = etype
if etyped == 'Move' then
if not relative2 then
log("Normal", "Transformed Move to Create for ",
sync.config.name)
etyped = 'Create'
elseif not relative then
relative = relative2
relative2 = nil
log("Normal", "Transformed Move to Delete for ",
sync.config.name)
etyped = 'Delete'
end
end
sync:delay(etyped, time, relative, relative2)
if isdir and
(sync.config.subdirs or sync.config.subdirs == nil)
then
if etyped == "Create" then
addWatch(path, true, sync, time)
elseif etyped == "Delete" then
removeWatch(path)
elseif etyped == "Move" then
removeWatch(path)
addWatch(path2, true, sync, time)
end
end
until true end
end end
----- -----
-- Writes a status report about inotifies to a filedescriptor -- Writes a status report about inotifies to a filedescriptor
-- --
local function statusReport(f) local function statusReport(f)
f:write("Watching ",wdlist:size()," directories\n") f:write("Watching ",wdpaths:size()," directories\n")
for wd, v in wdlist:walk() do for wd, path in wdpaths:walk() do
f:write(" ",wd,": ") f:write(" ",wd,": ",path,"\n")
local sep = ""
for _, v in ipairs(v) do
f:write(v.root,"/",v.path or "",sep)
sep = ", "
end end
f:write("\n")
end
end
-----
-- Returns the number of directories watched in total.
local function size()
return wdlist:size()
end end
-- public interface -- public interface
return { return {
addSync = addSync, addSync = addSync,
size = size,
event = event, event = event,
statusReport = statusReport statusReport = statusReport
} }