Merge 0.10->trunk
[prosody.git] / net / cqueues.lua
1 -- Prosody IM
2 -- Copyright (C) 2014 Daurnimator
3 --
4 -- This project is MIT/X11 licensed. Please see the
5 -- COPYING file in the source package for more information.
6 --
7 -- This module allows you to use cqueues with a net.server mainloop
8 --
9
10 local server = require "net.server";
11 local cqueues = require "cqueues";
12
13 -- Create a single top level cqueue
14 local cq;
15
16 if server.cq then -- server provides cqueues object
17         cq = server.cq;
18 elseif server.get_backend() == "select" and server._addtimer then -- server_select
19         cq = cqueues.new();
20         local function step()
21                 assert(cq:loop(0));
22         end
23
24         -- Use wrapclient (as wrapconnection isn't exported) to get server_select to watch cq fd
25         local handler = server.wrapclient({
26                 getfd = function() return cq:pollfd(); end;
27                 settimeout = function() end; -- Method just needs to exist
28                 close = function() end; -- Need close method for 'closeall'
29         }, nil, nil, {});
30
31         -- Only need to listen for readable; cqueues handles everything under the hood
32         -- readbuffer is called when `select` notes an fd as readable
33         handler.readbuffer = step;
34
35         -- Use server_select low lever timer facility,
36         -- this callback gets called *every* time there is a timeout in the main loop
37         server._addtimer(function(current_time)
38                 -- This may end up in extra step()'s, but cqueues handles it for us.
39                 step();
40                 return cq:timeout();
41         end);
42 elseif server.event and server.base then -- server_event
43         cq = cqueues.new();
44         -- Only need to listen for readable; cqueues handles everything under the hood
45         local EV_READ = server.event.EV_READ;
46         server.base:addevent(cq:pollfd(), EV_READ, function(e)
47                         assert(cq:loop(0));
48                         -- Convert a cq timeout to an acceptable timeout for luaevent
49                         local t = cq:timeout();
50                         if t == 0 then -- if you give luaevent 0, it won't call this callback again
51                                 t = 0.000001; -- 1 microsecond is the smallest that works (goes into a `struct timeval`)
52                         elseif t == nil then -- you always need to give a timeout, pick something big if we don't have one
53                                 t = 0x7FFFFFFF; -- largest 32bit int
54                         end
55                         return EV_READ, t;
56                 end,
57                 -- Schedule the callback to fire on first tick to ensure any cq:wrap calls that happen during start-up are serviced.
58                 0.000001);
59 else
60         error "NYI"
61 end
62
63 return {
64         cq = cq;
65 }