├── example ├── bank.lua ├── shop.lua └── testtransaction.lua ├── lualib └── transaction.lua └── service └── transactiond.lua /example/bank.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local transaction = require "transaction" 3 | 4 | local balance = 0 5 | 6 | local function dispatch(ta , gold) 7 | balance = balance + gold 8 | skynet.error(string.format("%s balance (%d) change %d gold", ta, balance, gold)) 9 | end 10 | 11 | skynet.start(function() 12 | skynet.dispatch("lua" , function (_,_, session, ...) 13 | transaction.dispatch(dispatch, session, ...) 14 | end) 15 | end) 16 | -------------------------------------------------------------------------------- /example/shop.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local transaction = require "transaction" 3 | 4 | local bank 5 | 6 | local function dispatch(ta , what, gold) 7 | skynet.error(string.format("%s shop buy %s by %d gold", ta, what, gold)) 8 | ta:call(bank, - gold) 9 | end 10 | 11 | skynet.start(function() 12 | bank = skynet.uniqueservice "bank" 13 | skynet.dispatch("lua" , function (_,_, session, ...) 14 | transaction.dispatch(dispatch, session, ...) 15 | end) 16 | end) 17 | -------------------------------------------------------------------------------- /example/testtransaction.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local transaction = require "transaction" 3 | 4 | local service = {} 5 | 6 | local function session_a() 7 | local t = transaction.create() 8 | t:call(service.bank, 100) -- add 100 gold to bank 9 | skynet.sleep(100) -- wait 1s 10 | t:call(service.shop, "A", 50) 11 | t:release() 12 | end 13 | 14 | local function session_b() 15 | local t = transaction.create() 16 | skynet.sleep(100) -- wait 1s 17 | t:call(service.shop, "B", 30) 18 | t:release() 19 | end 20 | 21 | local function session_c() 22 | local t = transaction.create() 23 | t:call(service.bank, 20) 24 | t:release() 25 | end 26 | 27 | local master = ... 28 | 29 | skynet.start(function() 30 | service.shop = skynet.uniqueservice "shop" 31 | service.bank = skynet.uniqueservice "bank" 32 | if master ~= "slave" then 33 | skynet.newservice(SERVICE_NAME, "slave") -- fork self 34 | end 35 | skynet.fork(session_a) 36 | skynet.fork(session_b) 37 | skynet.fork(session_c) 38 | end) 39 | -------------------------------------------------------------------------------- /lualib/transaction.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | local queue = require "skynet.queue" 3 | 4 | local transaction = {} 5 | transaction.__index = transaction 6 | 7 | function transaction:__tostring() 8 | return "[transaction:"..self._session.."]" 9 | end 10 | 11 | local service 12 | local session_pool = {} 13 | local PROTO = "lua" -- you can change it 14 | 15 | skynet.init(function() 16 | service = skynet.uniqueservice "transactiond" 17 | end) 18 | 19 | local function new_session(session) 20 | return setmetatable({ _session = session } , transaction) 21 | end 22 | 23 | function transaction.create(proto) 24 | local session = skynet.call(service, "lua", "create") 25 | return new_session(session) 26 | end 27 | 28 | function transaction:call(s, ...) 29 | return skynet.call(s, PROTO, self._session, ...) 30 | end 31 | 32 | function transaction:release() 33 | skynet.call(service, "lua", "release", self._session) 34 | end 35 | 36 | local function query_lock(self, session) 37 | skynet.call(service, "lua", "query", self._session) 38 | end 39 | 40 | local function watch_session(session) 41 | skynet.call(service, "lua", "link", session) 42 | session_pool[session] = nil 43 | end 44 | 45 | local function query_session(session) 46 | local t = session_pool[session] 47 | if t then 48 | return t 49 | end 50 | t = new_session(session) 51 | session_pool[session] = t 52 | t._queue = queue() 53 | t._queue(query_lock, t, session) 54 | skynet.fork(watch_session, session) 55 | return t 56 | end 57 | 58 | local function queue_action(f, resp, t, ...) 59 | resp(pcall(f, t, ...)) 60 | end 61 | 62 | function transaction.dispatch(f, session, ...) 63 | local t = query_session(session) 64 | local resp = skynet.response() 65 | t._queue(queue_action, f, resp, t, ...) 66 | end 67 | 68 | return transaction 69 | -------------------------------------------------------------------------------- /service/transactiond.lua: -------------------------------------------------------------------------------- 1 | local skynet = require "skynet" 2 | 3 | local transaction = {} 4 | local session_pool = { 5 | id = 0, 6 | active = nil, 7 | session = {}, 8 | queue = {}, 9 | link = {}, 10 | } 11 | 12 | function transaction.create() 13 | local id = session_pool.id + 1 14 | session_pool.id = id 15 | skynet.ret(skynet.pack(id)) 16 | end 17 | 18 | function transaction.release(session) 19 | if session_pool.active == session then 20 | local session = table.remove(session_pool.queue, 1) 21 | session_pool.active = session 22 | if session then 23 | local q = session_pool.session[session] 24 | for _, resp in ipairs(q) do 25 | resp(true) 26 | end 27 | end 28 | end 29 | local q = session_pool.link[session] 30 | if q then 31 | for _, resp in ipairs(q) do 32 | resp(true) 33 | end 34 | session_pool.link[session] = nil 35 | end 36 | skynet.ret() 37 | end 38 | 39 | function transaction.link(session) 40 | local q = session_pool.link[session] 41 | local resp = skynet.response() 42 | if q then 43 | table.insert(q, resp) 44 | else 45 | session_pool.link[session] = { resp } 46 | end 47 | end 48 | 49 | function transaction.query(session) 50 | local active = session_pool.active 51 | if active == nil then 52 | session_pool.active = session 53 | skynet.ret() 54 | return 55 | elseif active == session then 56 | skynet.ret() 57 | return 58 | end 59 | local q = session_pool.session[session] 60 | local resp = skynet.response() 61 | if q then 62 | table.insert(q, resp) 63 | else 64 | session_pool.session[session] = { resp } 65 | end 66 | table.insert(session_pool.queue, session) 67 | end 68 | 69 | skynet.start(function() 70 | skynet.dispatch("lua", function (_, source, cmd, session) 71 | transaction[cmd](session) 72 | end) 73 | end) 74 | --------------------------------------------------------------------------------