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