123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- 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 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
- 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
- skynet.ret(skynet.pack(t.handle))
- else
- assert(not t.monitor[source])
- t.monitor[source]=skynet.response()
- end
- end
- -- from customers, release handle , ref count - 1
- function datasheet.release(source, handle)
- -- send message, don't ret
- releasehandle(source, handle)
- 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
|