pollset.c revision 269847
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifdef WIN32
18/* POSIX defines 1024 for the FD_SETSIZE */
19#define FD_SETSIZE 1024
20#endif
21
22#include "apr.h"
23#include "apr_poll.h"
24#include "apr_time.h"
25#include "apr_portable.h"
26#include "apr_arch_file_io.h"
27#include "apr_arch_networkio.h"
28#include "apr_arch_poll_private.h"
29#include "apr_arch_inherit.h"
30
31static apr_pollset_method_e pollset_default_method = POLLSET_DEFAULT_METHOD;
32
33#if !APR_FILES_AS_SOCKETS
34#if defined (WIN32)
35
36/* Create a dummy wakeup socket pipe for interrupting the poller
37 */
38static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset)
39{
40    apr_status_t rv;
41
42    if ((rv = apr_file_socket_pipe_create(&pollset->wakeup_pipe[0],
43                                          &pollset->wakeup_pipe[1],
44                                          pollset->pool)) != APR_SUCCESS)
45        return rv;
46
47    pollset->wakeup_pfd.p = pollset->pool;
48    pollset->wakeup_pfd.reqevents = APR_POLLIN;
49    pollset->wakeup_pfd.desc_type = APR_POLL_FILE;
50    pollset->wakeup_pfd.desc.f = pollset->wakeup_pipe[0];
51
52    return apr_pollset_add(pollset, &pollset->wakeup_pfd);
53}
54
55#else  /* !WIN32 */
56static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset)
57{
58    return APR_ENOTIMPL;
59}
60
61static apr_status_t apr_file_socket_pipe_close(apr_file_t *file)
62{
63    return APR_ENOTIMPL;
64}
65
66#endif /* WIN32 */
67#else  /* APR_FILES_AS_SOCKETS */
68
69/* Create a dummy wakeup pipe for interrupting the poller
70 */
71static apr_status_t create_wakeup_pipe(apr_pollset_t *pollset)
72{
73    apr_status_t rv;
74
75    if ((rv = apr_file_pipe_create(&pollset->wakeup_pipe[0],
76                                   &pollset->wakeup_pipe[1],
77                                   pollset->pool)) != APR_SUCCESS)
78        return rv;
79
80    pollset->wakeup_pfd.p = pollset->pool;
81    pollset->wakeup_pfd.reqevents = APR_POLLIN;
82    pollset->wakeup_pfd.desc_type = APR_POLL_FILE;
83    pollset->wakeup_pfd.desc.f = pollset->wakeup_pipe[0];
84
85    {
86        int flags;
87
88        if ((flags = fcntl(pollset->wakeup_pipe[0]->filedes, F_GETFD)) == -1)
89            return errno;
90
91        flags |= FD_CLOEXEC;
92        if (fcntl(pollset->wakeup_pipe[0]->filedes, F_SETFD, flags) == -1)
93            return errno;
94    }
95    {
96        int flags;
97
98        if ((flags = fcntl(pollset->wakeup_pipe[1]->filedes, F_GETFD)) == -1)
99            return errno;
100
101        flags |= FD_CLOEXEC;
102        if (fcntl(pollset->wakeup_pipe[1]->filedes, F_SETFD, flags) == -1)
103            return errno;
104    }
105
106    return apr_pollset_add(pollset, &pollset->wakeup_pfd);
107}
108#endif /* !APR_FILES_AS_SOCKETS */
109
110/* Read and discard what's ever in the wakeup pipe.
111 */
112void apr_pollset_drain_wakeup_pipe(apr_pollset_t *pollset)
113{
114    char rb[512];
115    apr_size_t nr = sizeof(rb);
116
117    while (apr_file_read(pollset->wakeup_pipe[0], rb, &nr) == APR_SUCCESS) {
118        /* Although we write just one byte to the other end of the pipe
119         * during wakeup, multiple threads could call the wakeup.
120         * So simply drain out from the input side of the pipe all
121         * the data.
122         */
123        if (nr != sizeof(rb))
124            break;
125    }
126}
127
128static apr_status_t pollset_cleanup(void *p)
129{
130    apr_pollset_t *pollset = (apr_pollset_t *) p;
131    if (pollset->provider->cleanup) {
132        (*pollset->provider->cleanup)(pollset);
133    }
134    if (pollset->flags & APR_POLLSET_WAKEABLE) {
135        /* Close both sides of the wakeup pipe */
136        if (pollset->wakeup_pipe[0]) {
137#if APR_FILES_AS_SOCKETS
138            apr_file_close(pollset->wakeup_pipe[0]);
139#else
140            apr_file_socket_pipe_close(pollset->wakeup_pipe[0]);
141#endif
142            pollset->wakeup_pipe[0] = NULL;
143        }
144        if (pollset->wakeup_pipe[1]) {
145#if APR_FILES_AS_SOCKETS
146            apr_file_close(pollset->wakeup_pipe[1]);
147#else
148            apr_file_socket_pipe_close(pollset->wakeup_pipe[1]);
149#endif
150            pollset->wakeup_pipe[1] = NULL;
151        }
152    }
153
154    return APR_SUCCESS;
155}
156
157#if defined(HAVE_KQUEUE)
158extern apr_pollset_provider_t *apr_pollset_provider_kqueue;
159#endif
160#if defined(HAVE_PORT_CREATE)
161extern apr_pollset_provider_t *apr_pollset_provider_port;
162#endif
163#if defined(HAVE_EPOLL)
164extern apr_pollset_provider_t *apr_pollset_provider_epoll;
165#endif
166#if defined(HAVE_AIO_MSGQ)
167extern apr_pollset_provider_t *apr_pollset_provider_aio_msgq;
168#endif
169#if defined(HAVE_POLL)
170extern apr_pollset_provider_t *apr_pollset_provider_poll;
171#endif
172extern apr_pollset_provider_t *apr_pollset_provider_select;
173
174static apr_pollset_provider_t *pollset_provider(apr_pollset_method_e method)
175{
176    apr_pollset_provider_t *provider = NULL;
177    switch (method) {
178        case APR_POLLSET_KQUEUE:
179#if defined(HAVE_KQUEUE)
180            provider = apr_pollset_provider_kqueue;
181#endif
182        break;
183        case APR_POLLSET_PORT:
184#if defined(HAVE_PORT_CREATE)
185            provider = apr_pollset_provider_port;
186#endif
187        break;
188        case APR_POLLSET_EPOLL:
189#if defined(HAVE_EPOLL)
190            provider = apr_pollset_provider_epoll;
191#endif
192        break;
193        case APR_POLLSET_AIO_MSGQ:
194#if defined(HAVE_AIO_MSGQ)
195            provider = apr_pollset_provider_aio_msgq;
196#endif
197        break;
198        case APR_POLLSET_POLL:
199#if defined(HAVE_POLL)
200            provider = apr_pollset_provider_poll;
201#endif
202        break;
203        case APR_POLLSET_SELECT:
204            provider = apr_pollset_provider_select;
205        break;
206        case APR_POLLSET_DEFAULT:
207        break;
208    }
209    return provider;
210}
211
212APR_DECLARE(apr_status_t) apr_pollset_create_ex(apr_pollset_t **ret_pollset,
213                                                apr_uint32_t size,
214                                                apr_pool_t *p,
215                                                apr_uint32_t flags,
216                                                apr_pollset_method_e method)
217{
218    apr_status_t rv;
219    apr_pollset_t *pollset;
220    apr_pollset_provider_t *provider = NULL;
221
222    *ret_pollset = NULL;
223
224 #ifdef WIN32
225    /* Favor WSAPoll if supported.
226     * This will work only if ws2_32.dll has WSAPoll funtion.
227     * In other cases it will fall back to select() method unless
228     * the APR_POLLSET_NODEFAULT is added to the flags.
229     */
230    if (method == APR_POLLSET_DEFAULT) {
231        method = APR_POLLSET_POLL;
232    }
233 #endif
234
235    if (method == APR_POLLSET_DEFAULT)
236        method = pollset_default_method;
237    while (provider == NULL) {
238        provider = pollset_provider(method);
239        if (!provider) {
240            if ((flags & APR_POLLSET_NODEFAULT) == APR_POLLSET_NODEFAULT)
241                return APR_ENOTIMPL;
242            if (method == pollset_default_method)
243                return APR_ENOTIMPL;
244            method = pollset_default_method;
245        }
246    }
247    if (flags & APR_POLLSET_WAKEABLE) {
248        /* Add room for wakeup descriptor */
249        size++;
250    }
251
252    pollset = apr_palloc(p, sizeof(*pollset));
253    pollset->nelts = 0;
254    pollset->nalloc = size;
255    pollset->pool = p;
256    pollset->flags = flags;
257    pollset->provider = provider;
258
259    rv = (*provider->create)(pollset, size, p, flags);
260    if (rv == APR_ENOTIMPL) {
261        if (method == pollset_default_method) {
262            return rv;
263        }
264        provider = pollset_provider(pollset_default_method);
265        if (!provider) {
266            return APR_ENOTIMPL;
267        }
268        rv = (*provider->create)(pollset, size, p, flags);
269        if (rv != APR_SUCCESS) {
270            return rv;
271        }
272        pollset->provider = provider;
273    }
274    else if (rv != APR_SUCCESS) {
275        return rv;
276    }
277    if (flags & APR_POLLSET_WAKEABLE) {
278        /* Create wakeup pipe */
279        if ((rv = create_wakeup_pipe(pollset)) != APR_SUCCESS) {
280            return rv;
281        }
282    }
283    if ((flags & APR_POLLSET_WAKEABLE) || provider->cleanup)
284        apr_pool_cleanup_register(p, pollset, pollset_cleanup,
285                                  apr_pool_cleanup_null);
286
287    *ret_pollset = pollset;
288    return APR_SUCCESS;
289}
290
291APR_DECLARE(const char *) apr_pollset_method_name(apr_pollset_t *pollset)
292{
293    return pollset->provider->name;
294}
295
296APR_DECLARE(const char *) apr_poll_method_defname()
297{
298    apr_pollset_provider_t *provider = NULL;
299
300    provider = pollset_provider(pollset_default_method);
301    if (provider)
302        return provider->name;
303    else
304        return "unknown";
305}
306
307APR_DECLARE(apr_status_t) apr_pollset_create(apr_pollset_t **pollset,
308                                             apr_uint32_t size,
309                                             apr_pool_t *p,
310                                             apr_uint32_t flags)
311{
312    apr_pollset_method_e method = APR_POLLSET_DEFAULT;
313    return apr_pollset_create_ex(pollset, size, p, flags, method);
314}
315
316APR_DECLARE(apr_status_t) apr_pollset_destroy(apr_pollset_t * pollset)
317{
318    if (pollset->flags & APR_POLLSET_WAKEABLE ||
319        pollset->provider->cleanup)
320        return apr_pool_cleanup_run(pollset->pool, pollset,
321                                    pollset_cleanup);
322    else
323        return APR_SUCCESS;
324}
325
326APR_DECLARE(apr_status_t) apr_pollset_wakeup(apr_pollset_t *pollset)
327{
328    if (pollset->flags & APR_POLLSET_WAKEABLE)
329        return apr_file_putc(1, pollset->wakeup_pipe[1]);
330    else
331        return APR_EINIT;
332}
333
334APR_DECLARE(apr_status_t) apr_pollset_add(apr_pollset_t *pollset,
335                                          const apr_pollfd_t *descriptor)
336{
337    return (*pollset->provider->add)(pollset, descriptor);
338}
339
340APR_DECLARE(apr_status_t) apr_pollset_remove(apr_pollset_t *pollset,
341                                             const apr_pollfd_t *descriptor)
342{
343    return (*pollset->provider->remove)(pollset, descriptor);
344}
345
346APR_DECLARE(apr_status_t) apr_pollset_poll(apr_pollset_t *pollset,
347                                           apr_interval_time_t timeout,
348                                           apr_int32_t *num,
349                                           const apr_pollfd_t **descriptors)
350{
351    return (*pollset->provider->poll)(pollset, timeout, num, descriptors);
352}
353