201 lines
4.4 KiB
Lua
201 lines
4.4 KiB
Lua
local skynet = require "skynet"
|
|
local dump = require "skynet.datasheet.dump"
|
|
local core = require "skynet.datasheet.core"
|
|
local service = require "skynet.service"
|
|
|
|
local builder = {}
|
|
|
|
local cache = {}
|
|
local dataset = {}
|
|
local address
|
|
|
|
local unique_id = 0
|
|
local function unique_string(str)
|
|
unique_id = unique_id + 1
|
|
return str .. tostring(unique_id)
|
|
end
|
|
|
|
local function monitor(pointer)
|
|
skynet.fork(function()
|
|
skynet.call(address, "lua", "collect", pointer)
|
|
for k,v in pairs(cache) do
|
|
if v == pointer then
|
|
cache[k] = nil
|
|
return
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
|
|
local function dumpsheet(v)
|
|
if type(v) == "string" then
|
|
return v
|
|
else
|
|
return dump.dump(v)
|
|
end
|
|
end
|
|
|
|
function builder.new(name, v)
|
|
assert(dataset[name] == nil)
|
|
local datastring = unique_string(dumpsheet(v))
|
|
local pointer = core.stringpointer(datastring)
|
|
skynet.call(address, "lua", "update", name, pointer)
|
|
cache[datastring] = pointer
|
|
dataset[name] = datastring
|
|
monitor(pointer)
|
|
end
|
|
|
|
function builder.update(name, v)
|
|
local lastversion = assert(dataset[name])
|
|
local newversion = dumpsheet(v)
|
|
local diff = unique_string(dump.diff(lastversion, newversion))
|
|
local pointer = core.stringpointer(diff)
|
|
skynet.call(address, "lua", "update", name, pointer)
|
|
cache[diff] = pointer
|
|
local lp = assert(cache[lastversion])
|
|
skynet.send(address, "lua", "release", lp)
|
|
dataset[name] = diff
|
|
monitor(pointer)
|
|
end
|
|
|
|
function builder.compile(v)
|
|
return dump.dump(v)
|
|
end
|
|
|
|
local function datasheet_service()
|
|
|
|
local skynet = require "skynet"
|
|
|
|
local datasheet = {}
|
|
local handles = {} -- handle:{ ref:count , name:name , collect:resp }
|
|
local dataset = {} -- name:{ handle:handle, monitor:{monitors queue} }
|
|
local customers = {} -- source: { handle:true }
|
|
|
|
setmetatable(customers, { __index = function(c, source)
|
|
local v = {}
|
|
c[source] = v
|
|
return v
|
|
end } )
|
|
|
|
local function releasehandle(source, handle)
|
|
local h = handles[handle]
|
|
h.ref = h.ref - 1
|
|
if h.ref == 0 and h.collect then
|
|
h.collect(true)
|
|
h.collect = nil
|
|
handles[handle] = nil
|
|
end
|
|
local t=dataset[h.name]
|
|
t.monitor[source]=nil
|
|
end
|
|
|
|
-- from builder, create or update handle
|
|
function datasheet.update(source, name, handle)
|
|
local t = dataset[name]
|
|
if not t then
|
|
-- new datasheet
|
|
t = { handle = handle, monitor = {} }
|
|
dataset[name] = t
|
|
handles[handle] = { ref = 1, name = name }
|
|
else
|
|
-- report update to customers
|
|
handles[handle] = { ref = handles[t.handle].ref, name = name }
|
|
t.handle = handle
|
|
|
|
for k,v in pairs(t.monitor) do
|
|
v(true, handle)
|
|
t.monitor[k] = nil
|
|
end
|
|
end
|
|
skynet.ret()
|
|
end
|
|
|
|
-- from customers
|
|
function datasheet.query(source, name)
|
|
local t = assert(dataset[name], "create data first")
|
|
local handle = t.handle
|
|
local h = handles[handle]
|
|
h.ref = h.ref + 1
|
|
customers[source][handle] = true
|
|
skynet.ret(skynet.pack(handle))
|
|
end
|
|
|
|
-- from customers, monitor handle change
|
|
function datasheet.monitor(source, handle)
|
|
local h = assert(handles[handle], "Invalid data handle")
|
|
local t = dataset[h.name]
|
|
if t.handle ~= handle then -- already changes
|
|
customers[source][t.handle] = true
|
|
skynet.ret(skynet.pack(t.handle))
|
|
else
|
|
assert(not t.monitor[source])
|
|
local resp = skynet.response()
|
|
t.monitor[source]= function(ok, handle)
|
|
if ok then
|
|
customers[source][handle] = true
|
|
end
|
|
resp(ok, handle)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- from customers, release handle , ref count - 1
|
|
function datasheet.release(source, handle)
|
|
-- send message, don't ret
|
|
customers[source][handle] = nil
|
|
releasehandle(source, handle)
|
|
end
|
|
|
|
-- customer closed, clear all handles it queried
|
|
function datasheet.close(source)
|
|
for handle in pairs(customers[source]) do
|
|
releasehandle(source, handle)
|
|
end
|
|
customers[source] = nil
|
|
end
|
|
|
|
-- from builder, monitor handle release
|
|
function datasheet.collect(source, handle)
|
|
local h = assert(handles[handle], "Invalid data handle")
|
|
if h.ref == 0 then
|
|
handles[handle] = nil
|
|
skynet.ret()
|
|
else
|
|
assert(h.collect == nil, "Only one collect allows")
|
|
h.collect = skynet.response()
|
|
end
|
|
end
|
|
|
|
skynet.dispatch("lua", function(_,source,cmd,...)
|
|
datasheet[cmd](source,...)
|
|
end)
|
|
|
|
skynet.info_func(function()
|
|
local info = {}
|
|
local tmp = {}
|
|
for k,v in pairs(handles) do
|
|
tmp[k] = v
|
|
end
|
|
for k,v in pairs(dataset) do
|
|
local h = handles[v.handle]
|
|
tmp[v.handle] = nil
|
|
info[k] = {
|
|
handle = v.handle,
|
|
monitors = h.ref,
|
|
}
|
|
end
|
|
for k,v in pairs(tmp) do
|
|
info[k] = v.ref
|
|
end
|
|
|
|
return info
|
|
end)
|
|
|
|
end
|
|
|
|
skynet.init(function()
|
|
address=service.new("datasheet", datasheet_service)
|
|
end)
|
|
|
|
return builder
|