Thread pool for server
[yaz-moved-to-github.git] / src / sock_man.c
1
2 #include <yaz/sock_man.h>
3 #include <yaz/log.h>
4 #include <yaz/nmem.h>
5 #include <assert.h>
6 #include <sys/epoll.h>
7 #include <unistd.h>
8
9 struct yaz_sock_man_s {
10     yaz_sock_chan_t chan_list;
11     yaz_sock_chan_t free_list;
12     yaz_sock_chan_t timeout_list;
13     NMEM nmem;
14     int epoll_handle;
15     int maxevents;
16     int event_no;
17     int event_ret;
18     int timeout;
19     int rescan;
20     struct epoll_event *events;
21 };
22
23 struct yaz_sock_chan_s {
24     yaz_sock_chan_t next;
25     yaz_sock_chan_t prev;
26     int fd;
27     unsigned input_mask;
28     unsigned output_mask;
29     int max_idle;
30     void *data;
31     yaz_sock_man_t man;
32 };
33
34 yaz_sock_man_t yaz_sock_man_new(void)
35 {
36     NMEM nmem = nmem_create();
37     yaz_sock_man_t man = nmem_malloc(nmem, sizeof(*man));
38     man->nmem = nmem;
39     man->chan_list = 0;
40     man->free_list = 0;
41     man->timeout_list = 0;
42     man->epoll_handle = epoll_create(100);
43     man->maxevents = 30;
44     man->event_no = 0;
45     man->event_ret = 0;
46     man->timeout = -1;
47     man->rescan = 0;
48     man->events = nmem_malloc(nmem, man->maxevents * sizeof(*man->events));
49     if (man->epoll_handle == -1)
50     {
51         yaz_sock_man_destroy(man);
52         return 0;
53     }
54     return man;
55 }
56
57 void yaz_sock_man_destroy(yaz_sock_man_t man)
58 {
59     if (man)
60     {
61         while (man->chan_list)
62         {
63             yaz_log(YLOG_WARN, "yaz_sock_man_destroy: closing %p",
64                     man->chan_list);
65             yaz_sock_chan_destroy(man->chan_list);
66         }
67         if (man->epoll_handle != -1)
68             close(man->epoll_handle);
69         nmem_destroy(man->nmem);
70     }
71 }
72
73 static void poll_ctl(int op, yaz_sock_chan_t p)
74 {
75     struct epoll_event event;
76
77     event.events = 0;
78     if (p->input_mask & yaz_poll_read)
79         event.events |= EPOLLIN;
80     if (p->input_mask & yaz_poll_write)
81         event.events |= EPOLLOUT;
82     if (p->input_mask & yaz_poll_except)
83         event.events |= EPOLLERR;
84
85     event.data.ptr = p;
86     epoll_ctl(p->man->epoll_handle, op, p->fd, &event);
87
88 }
89
90 yaz_sock_chan_t yaz_sock_chan_new(yaz_sock_man_t srv, int fd, void *data,
91                                   unsigned mask)
92 {
93     yaz_sock_chan_t p;
94     if (srv->free_list)
95     {
96         p = srv->free_list;
97         srv->free_list = p->next;
98     }
99     else
100         p = nmem_malloc(srv->nmem, sizeof(*p));
101     p->next = srv->chan_list;
102     if (p->next)
103         p->next->prev = p;
104     p->prev = 0;
105     srv->chan_list = p;
106
107     p->fd = fd;
108     p->input_mask = mask;
109     p->output_mask = 0;
110     p->data = data;
111     p->max_idle = -1;
112     p->man = srv;
113
114     poll_ctl(EPOLL_CTL_ADD, p);
115     return p;
116 }
117
118 static void rescan_timeout(yaz_sock_man_t man)
119 {
120     if (man->rescan)
121     {
122         int timeout = -1;
123         yaz_sock_chan_t p;
124         for (p = man->chan_list; p; p = p->next)
125             if (p->max_idle != -1 && (timeout == -1 || p->max_idle < timeout))
126                 timeout = p->max_idle;
127         man->timeout = timeout;
128         man->rescan = 0;
129     }
130 }
131
132 void yaz_sock_chan_destroy(yaz_sock_chan_t p)
133 {
134     yaz_sock_man_t srv = p->man;
135     if (p->prev)
136         p->prev->next = p->next;
137     else
138     {
139         assert(srv->chan_list == p);
140         srv->chan_list = p->next;
141     }
142
143     if (p->next)
144         p->next->prev = p->prev;
145
146     poll_ctl(EPOLL_CTL_DEL, p);
147
148     p->next = srv->free_list;
149     p->prev = 0;
150     srv->free_list = p;
151
152     srv->rescan = 1;
153 }
154
155 yaz_sock_chan_t yaz_sock_man_wait(yaz_sock_man_t man)
156 {
157     struct epoll_event *ev;
158     yaz_sock_chan_t chan = 0;
159
160     if (man->timeout_list)
161     { /* possibly timeout events not returned */
162         for (chan = man->timeout_list; chan; chan = chan->next)
163             if (chan->max_idle == man->timeout)
164                 break;
165         if (chan)
166         {
167             man->timeout_list = chan->next;
168             chan->output_mask = yaz_poll_timeout;
169             return chan;
170         }
171         man->timeout_list = 0; /* no more timeout events */
172     }
173     assert(man->timeout_list == 0);
174     assert(man->event_no <= man->event_ret);
175     if (man->event_no == man->event_ret)
176     { /* must wait again */
177         rescan_timeout(man);
178         man->event_no = 0;
179         man->event_ret = epoll_wait(man->epoll_handle, man->events,
180                                     man->maxevents, man->timeout);
181         if (man->event_ret == 0)
182         {
183             /* timeout */
184             for (chan = man->chan_list; chan; chan = chan->next)
185                 if (chan->max_idle == man->timeout)
186                     break;
187             assert(chan); /* there must be one chan in a timeout state */
188             man->timeout_list = chan->next;
189             chan->output_mask = yaz_poll_timeout;
190             return chan;
191         }
192         else if (man->event_ret < 0)
193         {
194             /* error */
195             return 0;
196         }
197     }
198     ev = man->events + man->event_no;
199     chan = ev->data.ptr;
200     chan->output_mask = 0;
201     if (ev->events & EPOLLIN)
202         chan->output_mask |= yaz_poll_read;
203     if (ev->events & EPOLLOUT)
204         chan->output_mask |= yaz_poll_write;
205     if (ev->events & EPOLLERR)
206         chan->output_mask |= yaz_poll_except;
207     man->event_no++;
208     return chan;
209 }
210
211 void yaz_sock_chan_set_mask(yaz_sock_chan_t chan, unsigned mask)
212 {
213     if (chan->input_mask != mask)
214     {
215         chan->input_mask = mask;
216         poll_ctl(EPOLL_CTL_MOD, chan);
217     }
218 }
219
220 void yaz_sock_chan_set_max_idle(yaz_sock_chan_t chan, int max_idle)
221 {
222     if (chan->max_idle != max_idle)
223     {
224         chan->max_idle = max_idle;
225         chan->man->rescan = 1;
226     }
227 }
228
229 unsigned yaz_sock_get_mask(yaz_sock_chan_t chan)
230 {
231     return chan->output_mask;
232 }
233
234 void *yaz_sock_chan_get_data(yaz_sock_chan_t chan)
235 {
236     return chan->data;
237 }
238
239
240 /*
241  * Local variables:
242  * c-basic-offset: 4
243  * c-file-style: "Stroustrup"
244  * indent-tabs-mode: nil
245  * End:
246  * vim: shiftwidth=4 tabstop=8 expandtab
247  */
248