Advertisement
Guest User

Extended example of Lua coroutines

a guest
Aug 21st, 2013
146
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 6.43 KB | None | 0 0
  1. -- ***************************************************************
  2. --
  3. -- Copyright 2013 by Sean Conner.  All Rights Reserved.
  4. --
  5. -- This library is free software; you can redistribute it and/or modify it
  6. -- under the terms of the GNU Lesser General Public License as published by
  7. -- the Free Software Foundation; either version 3 of the License, or (at your
  8. -- option) any later version.
  9. --
  10. -- This library is distributed in the hope that it will be useful, but
  11. -- WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  12. -- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
  13. -- License for more details.
  14. --
  15. -- You should have received a copy of the GNU Lesser General Public License
  16. -- along with this library; if not, see <http://www.gnu.org/licenses/>.
  17. --
  18. -- Comments, questions and criticisms can be sent to: [email protected]
  19. --
  20. -- ********************************************************************
  21.  
  22. local proc    = require "org.conman.process"
  23. local net     = require "org.conman.net"
  24. local pollset = require "org.conman.pollset"
  25. local lpeg    = require "lpeg"
  26.  
  27. -- **********************************************************************
  28.  
  29. FILE       = arg[1] or "/home/spc/source/lua/daemon/lua/silly.lua"
  30. setqueue   = pollset()
  31. runqueue   = {}
  32. sleepqueue = {}
  33.  
  34. -- **********************************************************************
  35.  
  36. function wsleep(sock,amount)
  37.   sock.awake = os.time() + amount
  38.   table.insert(sleepqueue,sock)
  39.   coroutine.yield('sleep')
  40. end
  41.  
  42. -- **********************************************************************
  43.  
  44. local eoln      = lpeg.P"\r"^-1 * lpeg.P"\n"
  45. local lineparse = lpeg.C((lpeg.P(1) - eoln)^0) * eoln * lpeg.C(lpeg.P(1)^0)
  46. local integer   = lpeg.R"09"^1
  47. local frac      = lpeg.P"."  * lpeg.R"09"^1
  48. local exp       = lpeg.S"Ee" * lpeg.S"-+"^-1 * lpeg.R"09"^1
  49. local number    = ((lpeg.P"-"^-1 * integer * frac^-1 * exp^-1) / tonumber)
  50.                 * lpeg.locale().space^1 * lpeg.C(lpeg.P(1)^0)
  51.  
  52. function wread(sock,amount)
  53.   local amount = amount or "*l"
  54.  
  55.   if type(amount) == 'number' then
  56.     if #sock.readbuf < amount then
  57.       coroutine.yield('read')
  58.       return wread(sock,amount)
  59.     else
  60.       local d      = sock.readbuf:sub(1,amount)
  61.       sock.readbuf = sock.readbuf:sub(amount+1,-1)
  62.       return d
  63.     end
  64.    
  65.   elseif amount == '*l' then
  66.     local line,rest = lineparse:match(sock.readbuf)
  67.     if line == nil then
  68.       coroutine.yield('read')
  69.       return wread(sock,amount)
  70.     else
  71.       sock.readbuf = rest
  72.       return line
  73.     end
  74.  
  75.   elseif amount == '*a' then
  76.     if #sock.readbuf == 0 then
  77.       coroutine.yield('read')
  78.       return wread(sock,amount)
  79.     else
  80.       local d = sock.readbuf
  81.       sock.readbuf = ""
  82.       return d
  83.     end
  84.   elseif amount == '*n' then
  85.     local num,rest = number:match(sock.readbuf)
  86.     if num == nil then
  87.       coroutine.yield('read')
  88.       return read(sock,amount)
  89.     else
  90.       sock.readbuf = rest
  91.       return num
  92.     end
  93.   end
  94. end
  95.  
  96. -- **********************************************************************
  97.  
  98. function wwrite(sock,data)
  99.   if #sock.writebuf == 0 then
  100.     local bytes = sock.rawsock:send(sock.raddr,data)
  101.     data  = data:sub(bytes + 1,-1)
  102.   end
  103.   sock.writebuf = sock.writebuf .. data
  104.   if #sock.writebuf > 0 then
  105.     setqueue:update(sock.rawsock:fd(),"rw")
  106.   end
  107. end
  108.  
  109. -- **********************************************************************
  110.  
  111. function check_signals()
  112.   if proc.sig.caught(proc.sig.INT)
  113.   or proc.sig.caught(proc.sig.QUIT)
  114.   or proc.sig.caught(proc.sig.TERM) then
  115.     os.exit(0)
  116.   elseif proc.sig.caught(proc.sig.USR1) then
  117.     dofile(FILE)
  118.   end
  119. end
  120.  
  121. -- **********************************************************************
  122.  
  123. function mainloop()
  124.   local timeout
  125.   local events
  126.   local now
  127.   local socket
  128.   local okay
  129.   local newsleep
  130.  
  131.   while true do    
  132.     check_signals()
  133.    
  134.     if #runqueue > 0 then
  135.       timeout = 0
  136.     elseif #sleepqueue > 0 then
  137.       timeout = 1
  138.     else
  139.       timeout = -1
  140.     end
  141.    
  142.     events,err = setqueue:events(timeout)
  143.     if err ~= 0 then
  144.       return mainloop()
  145.     end
  146.    
  147.     for i = 1 , #events do
  148.       events[i].obj(events[i])
  149.     end
  150.    
  151.     newsleep = {}
  152.     now      = os.time()
  153.    
  154.     while #sleepqueue > 0 do
  155.       obj = table.remove(sleepqueue,1)
  156.       if obj.awake > now then
  157.         table.insert(newsleep,obj)
  158.       else
  159.         table.insert(runqueue,obj)
  160.       end
  161.     end
  162.    
  163.     sleepqueue = newsleep
  164.  
  165.     socket = table.remove(runqueue,1)    
  166.     if socket then
  167.       okay,socket.state = coroutine.resume(socket.f,socket)
  168.       if coroutine.status(socket.f) == 'dead' then
  169.         setqueue:remove(socket.rawsock:fd())
  170.         socket.rawsock:close()
  171.       end
  172.     end    
  173.   end
  174. end
  175.  
  176. -- **********************************************************************
  177.  
  178. proc.sig.catch(proc.sig.INT)
  179. proc.sig.catch(proc.sig.QUIT)
  180. proc.sig.catch(proc.sig.TERM)
  181. proc.sig.catch(proc.sig.USR1)
  182.  
  183. laddr = net.address('192.168.1.10','tcp',22222)
  184. sock  = net.socket('ip','tcp')
  185.  
  186. sock.reuseaddr = true
  187. sock.nonblock  = true
  188. sock:bind(laddr)
  189. sock:listen()
  190.  
  191. dofile(FILE)
  192.  
  193. setqueue:insert(sock:fd(),"r",function(event)
  194.   local conn,rem = sock:accept()
  195.   conn.nonblock  = true
  196.  
  197.   setqueue:insert(conn:fd(),"r",(function(conn,raddr)
  198.     local socket =
  199.     {
  200.       read     = wread,
  201.       write    = wwrite,
  202.       sleep    = wsleep,
  203.       readbuf  = "",
  204.       writebuf = "",
  205.       state    = 'run',
  206.       rawsock  = conn,
  207.       raddr    = raddr,
  208.       f        = coroutine.create(main)
  209.     }
  210.  
  211.     table.insert(runqueue,socket)    
  212.    
  213.     return function(event)
  214.       if event.read then
  215.         local rem,data = conn:recv()
  216.        
  217.         if #data == 0 then
  218.           setqueue:remove(conn:fd())
  219.           conn:close()
  220.           if fini then fini(socket) end
  221.        end
  222.        
  223.         socket.readbuf = socket.readbuf .. data
  224.         if socket.state == 'read' then
  225.           socket.state = 'run'
  226.           table.insert(runqueue,socket)
  227.         end
  228.       end
  229.      
  230.       if event.write then
  231.         if #socket.writebuf > 0 then
  232.           local bytes = conn:send(raddr,socket.writebuf)
  233.           socket.writebuf = socket.writebuf:sub(bytes+1,-1)
  234.         end
  235.        
  236.         if #socket.writebuf == 0 then
  237.           setqueue:update(conn:fd(),"r")
  238.         end        
  239.       end
  240.     end
  241.   end)(conn,rem))
  242. end)
  243.  
  244. mainloop()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement