coroutine.lua 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. -- You should use this module (skynet.coroutine) instead of origin lua coroutine in skynet framework
  2. local coroutine = coroutine
  3. -- origin lua coroutine module
  4. local coroutine_resume = coroutine.resume
  5. local coroutine_yield = coroutine.yield
  6. local coroutine_status = coroutine.status
  7. local coroutine_running = coroutine.running
  8. local select = select
  9. local skynetco = {}
  10. skynetco.isyieldable = coroutine.isyieldable
  11. skynetco.running = coroutine.running
  12. skynetco.status = coroutine.status
  13. local skynet_coroutines = setmetatable({}, { __mode = "kv" })
  14. function skynetco.create(f)
  15. local co = coroutine.create(f)
  16. -- mark co as a skynet coroutine
  17. skynet_coroutines[co] = true
  18. return co
  19. end
  20. do -- begin skynetco.resume
  21. local profile = require "skynet.profile"
  22. -- skynet use profile.resume_co/yield_co instead of coroutine.resume/yield
  23. local skynet_resume = profile.resume_co
  24. local skynet_yield = profile.yield_co
  25. local function unlock(co, ...)
  26. skynet_coroutines[co] = true
  27. return ...
  28. end
  29. local function skynet_yielding(co, from, ...)
  30. skynet_coroutines[co] = false
  31. return unlock(co, skynet_resume(co, from, skynet_yield(from, ...)))
  32. end
  33. local function resume(co, from, ok, ...)
  34. if not ok then
  35. return ok, ...
  36. elseif coroutine_status(co) == "dead" then
  37. -- the main function exit
  38. skynet_coroutines[co] = nil
  39. return true, ...
  40. elseif (...) == "USER" then
  41. return true, select(2, ...)
  42. else
  43. -- blocked in skynet framework, so raise the yielding message
  44. return resume(co, from, skynet_yielding(co, from, ...))
  45. end
  46. end
  47. -- record the root of coroutine caller (It should be a skynet thread)
  48. local coroutine_caller = setmetatable({} , { __mode = "kv" })
  49. function skynetco.resume(co, ...)
  50. local co_status = skynet_coroutines[co]
  51. if not co_status then
  52. if co_status == false then
  53. -- is running
  54. return false, "cannot resume a skynet coroutine suspend by skynet framework"
  55. end
  56. if coroutine_status(co) == "dead" then
  57. -- always return false, "cannot resume dead coroutine"
  58. return coroutine_resume(co, ...)
  59. else
  60. return false, "cannot resume none skynet coroutine"
  61. end
  62. end
  63. local from = coroutine_running()
  64. local caller = coroutine_caller[from] or from
  65. coroutine_caller[co] = caller
  66. return resume(co, caller, coroutine_resume(co, ...))
  67. end
  68. function skynetco.thread(co)
  69. co = co or coroutine_running()
  70. if skynet_coroutines[co] ~= nil then
  71. return coroutine_caller[co] , false
  72. else
  73. return co, true
  74. end
  75. end
  76. end -- end of skynetco.resume
  77. function skynetco.status(co)
  78. local status = coroutine.status(co)
  79. if status == "suspended" then
  80. if skynet_coroutines[co] == false then
  81. return "blocked"
  82. else
  83. return "suspended"
  84. end
  85. else
  86. return status
  87. end
  88. end
  89. function skynetco.yield(...)
  90. return coroutine_yield("USER", ...)
  91. end
  92. do -- begin skynetco.wrap
  93. local function wrap_co(ok, ...)
  94. if ok then
  95. return ...
  96. else
  97. error(...)
  98. end
  99. end
  100. function skynetco.wrap(f)
  101. local co = skynetco.create(function(...)
  102. return f(...)
  103. end)
  104. return function(...)
  105. return wrap_co(skynetco.resume(co, ...))
  106. end
  107. end
  108. end -- end of skynetco.wrap
  109. return skynetco