win32 use hashtable for fd management
[platform/upstream/libwebsockets.git] / lib / pollfd.c
1 /*
2  * libwebsockets - small server side websockets and web server implementation
3  *
4  * Copyright (C) 2010-2014 Andy Green <andy@warmcat.com>
5  *
6  *  This library is free software; you can redistribute it and/or
7  *  modify it under the terms of the GNU Lesser General Public
8  *  License as published by the Free Software Foundation:
9  *  version 2.1 of the License.
10  *
11  *  This library is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  *  Lesser General Public License for more details.
15  *
16  *  You should have received a copy of the GNU Lesser General Public
17  *  License along with this library; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19  *  MA  02110-1301  USA
20  */
21
22 #include "private-libwebsockets.h"
23
24 int
25 insert_wsi_socket_into_fds(struct libwebsocket_context *context,
26                                                        struct libwebsocket *wsi)
27 {
28         struct libwebsocket_pollargs pa = { wsi->sock, LWS_POLLIN, 0 };
29
30         if (context->fds_count >= context->max_fds) {
31                 lwsl_err("Too many fds (%d)\n", context->max_fds);
32                 return 1;
33         }
34
35 #ifndef _WIN32
36         if (wsi->sock >= context->max_fds) {
37                 lwsl_err("Socket fd %d is too high (%d)\n",
38                                                 wsi->sock, context->max_fds);
39                 return 1;
40         }
41 #endif
42
43         assert(wsi);
44         assert(wsi->sock >= 0);
45
46         lwsl_info("insert_wsi_socket_into_fds: wsi=%p, sock=%d, fds pos=%d\n",
47                                             wsi, wsi->sock, context->fds_count);
48
49         context->protocols[0].callback(context, wsi,
50                 LWS_CALLBACK_LOCK_POLL,
51                 wsi->user_space, (void *) &pa, 0);
52
53         insert_wsi(context, wsi);
54         wsi->position_in_fds_table = context->fds_count;
55         context->fds[context->fds_count].fd = wsi->sock;
56         context->fds[context->fds_count].events = LWS_POLLIN;
57         
58         lws_plat_insert_socket_into_fds(context, wsi);
59
60         /* external POLL support via protocol 0 */
61         context->protocols[0].callback(context, wsi,
62                 LWS_CALLBACK_ADD_POLL_FD,
63                 wsi->user_space, (void *) &pa, 0);
64
65         context->protocols[0].callback(context, wsi,
66                 LWS_CALLBACK_UNLOCK_POLL,
67                 wsi->user_space, (void *)&pa, 0);
68
69         return 0;
70 }
71
72 int
73 remove_wsi_socket_from_fds(struct libwebsocket_context *context,
74                                                       struct libwebsocket *wsi)
75 {
76         int m;
77         struct libwebsocket_pollargs pa = { wsi->sock, 0, 0 };
78
79         lws_libev_io(context, wsi, LWS_EV_STOP | LWS_EV_READ | LWS_EV_WRITE);
80
81         if (!--context->fds_count) {
82                 context->protocols[0].callback(context, wsi,
83                         LWS_CALLBACK_LOCK_POLL,
84                         wsi->user_space, (void *) &pa, 0);
85                 goto do_ext;
86         }
87
88         if (wsi->sock > context->max_fds) {
89                 lwsl_err("Socket fd %d too high (%d)\n",
90                                                    wsi->sock, context->max_fds);
91                 return 1;
92         }
93
94         lwsl_info("%s: wsi=%p, sock=%d, fds pos=%d\n", __func__,
95                                     wsi, wsi->sock, wsi->position_in_fds_table);
96
97         context->protocols[0].callback(context, wsi,
98                 LWS_CALLBACK_LOCK_POLL,
99                 wsi->user_space, (void *)&pa, 0);
100
101         m = wsi->position_in_fds_table; /* replace the contents for this */
102
103         /* have the last guy take up the vacant slot */
104         context->fds[m] = context->fds[context->fds_count];
105
106         lws_plat_delete_socket_from_fds(context, wsi, m);
107
108         /*
109          * end guy's fds_lookup entry remains unchanged
110          * (still same fd pointing to same wsi)
111          */
112         /* end guy's "position in fds table" changed */
113         wsi_from_fd(context,context->fds[context->fds_count].fd)-> 
114                                         position_in_fds_table = m;
115         /* deletion guy's lws_lookup entry needs nuking */
116         delete_from_fd(context,wsi->sock);
117         /* removed wsi has no position any more */
118         wsi->position_in_fds_table = -1;
119
120 do_ext:
121         /* remove also from external POLL support via protocol 0 */
122         if (wsi->sock) {
123                 context->protocols[0].callback(context, wsi,
124                     LWS_CALLBACK_DEL_POLL_FD, wsi->user_space,
125                     (void *) &pa, 0);
126         }
127         context->protocols[0].callback(context, wsi,
128                                        LWS_CALLBACK_UNLOCK_POLL,
129                                        wsi->user_space, (void *) &pa, 0);
130         return 0;
131 }
132
133 int
134 lws_change_pollfd(struct libwebsocket *wsi, int _and, int _or)
135 {
136         struct libwebsocket_context *context;
137         int tid;
138         int sampled_tid;
139         struct libwebsocket_pollfd *pfd;
140         struct libwebsocket_pollargs pa;
141
142         if (!wsi || !wsi->protocol || wsi->position_in_fds_table < 0)
143                 return 1;
144         
145         context = wsi->protocol->owning_server;
146         if (!context)
147                 return 1;
148
149         pfd = &context->fds[wsi->position_in_fds_table];
150         pa.fd = wsi->sock;
151
152         context->protocols[0].callback(context, wsi,
153                 LWS_CALLBACK_LOCK_POLL, wsi->user_space,  (void *) &pa, 0);
154
155         pa.prev_events = pfd->events;
156         pa.events = pfd->events = (pfd->events & ~_and) | _or;
157
158         context->protocols[0].callback(context, wsi,
159                         LWS_CALLBACK_CHANGE_MODE_POLL_FD,
160                                 wsi->user_space, (void *) &pa, 0);
161
162         /*
163          * if we changed something in this pollfd...
164          *   ... and we're running in a different thread context
165          *     than the service thread...
166          *       ... and the service thread is waiting ...
167          *         then cancel it to force a restart with our changed events
168          */
169         if (pa.prev_events != pa.events) {
170                 
171                 if (lws_plat_change_pollfd(context, wsi, pfd)) {
172                         lwsl_info("%s failed\n", __func__);
173                         return 1;
174                 }
175
176                 sampled_tid = context->service_tid;
177                 if (sampled_tid) {
178                         tid = context->protocols[0].callback(context, NULL,
179                                      LWS_CALLBACK_GET_THREAD_ID, NULL, NULL, 0);
180                         if (tid != sampled_tid)
181                                 libwebsocket_cancel_service(context);
182                 }
183         }
184
185         context->protocols[0].callback(context, wsi,
186                 LWS_CALLBACK_UNLOCK_POLL, wsi->user_space, (void *) &pa, 0);
187         
188         return 0;
189 }
190
191
192 /**
193  * libwebsocket_callback_on_writable() - Request a callback when this socket
194  *                                       becomes able to be written to without
195  *                                       blocking
196  *
197  * @context:    libwebsockets context
198  * @wsi:        Websocket connection instance to get callback for
199  */
200
201 LWS_VISIBLE int
202 libwebsocket_callback_on_writable(struct libwebsocket_context *context,
203                                                       struct libwebsocket *wsi)
204 {
205 #ifdef LWS_USE_HTTP2
206         struct libwebsocket *network_wsi, *wsi2;
207         int already;
208
209         lwsl_info("%s: %p\n", __func__, wsi);
210         
211         if (wsi->mode != LWS_CONNMODE_HTTP2_SERVING)
212                 goto network_sock;
213         
214         if (wsi->u.http2.requested_POLLOUT) {
215                 lwsl_info("already pending writable\n");
216                 return 1;
217         }
218         
219         if (wsi->u.http2.tx_credit <= 0) {
220                 /*
221                  * other side is not able to cope with us sending
222                  * anything so no matter if we have POLLOUT on our side.
223                  * 
224                  * Delay waiting for our POLLOUT until peer indicates he has
225                  * space for more using tx window command in http2 layer
226                  */
227                 lwsl_info("%s: %p: waiting_tx_credit (%d)\n", __func__, wsi, wsi->u.http2.tx_credit);
228                 wsi->u.http2.waiting_tx_credit = 1;
229                 return 0;
230         }
231         
232         network_wsi = lws_http2_get_network_wsi(wsi);
233         already = network_wsi->u.http2.requested_POLLOUT;
234         
235         /* mark everybody above him as requesting pollout */
236         
237         wsi2 = wsi;
238         while (wsi2) {
239                 wsi2->u.http2.requested_POLLOUT = 1;
240                 lwsl_info("mark %p pending writable\n", wsi2);
241                 wsi2 = wsi2->u.http2.parent_wsi;
242         }
243         
244         /* for network action, act only on the network wsi */
245         
246         wsi = network_wsi;
247         if (already)
248                 return 1;
249 network_sock:
250 #endif
251
252         if (lws_ext_callback_for_each_active(wsi,
253                                 LWS_EXT_CALLBACK_REQUEST_ON_WRITEABLE, NULL, 0))
254                 return 1;
255
256         if (wsi->position_in_fds_table < 0) {
257                 lwsl_err("%s: failed to find socket %d\n", __func__, wsi->sock);
258                 return -1;
259         }
260
261         if (lws_change_pollfd(wsi, 0, LWS_POLLOUT))
262                 return -1;
263
264         lws_libev_io(context, wsi, LWS_EV_START | LWS_EV_WRITE);
265
266         return 1;
267 }
268
269 /**
270  * libwebsocket_callback_on_writable_all_protocol() - Request a callback for
271  *                      all connections using the given protocol when it
272  *                      becomes possible to write to each socket without
273  *                      blocking in turn.
274  *
275  * @protocol:   Protocol whose connections will get callbacks
276  */
277
278 LWS_VISIBLE int
279 libwebsocket_callback_on_writable_all_protocol(
280                                   const struct libwebsocket_protocols *protocol)
281 {
282         struct libwebsocket_context *context = protocol->owning_server;
283         int n;
284         struct libwebsocket *wsi;
285
286         for (n = 0; n < context->fds_count; n++) {
287                 wsi = wsi_from_fd(context,context->fds[n].fd);
288                 if (!wsi)
289                         continue;
290                 if (wsi->protocol == protocol)
291                         libwebsocket_callback_on_writable(context, wsi);
292         }
293
294         return 0;
295 }