Hi,

first of all, thanks for providing APR :-)

I think I've found a bug in the kqueue implementation of apr_pollset_poll().
I am using FreeBSD -current with apr-1.2.7, but the SVN trunk
version of kqueue.c has the same bug:


    232  APR_DECLARE(apr_status_t) apr_pollset_poll(apr_pollset_t *pollset,
    233                                             apr_interval_time_t timeout,
    234                                             apr_int32_t *num,
    235                                             const apr_pollfd_t 
**descriptors)
    236  {
    237      int ret, i;
    238      struct timespec tv, *tvptr;
    239      apr_status_t rv = APR_SUCCESS;
    240  
    241      if (timeout < 0) {
    242          tvptr = NULL;
    243      }
    244      else {
    245          tv.tv_sec = (long) apr_time_sec(timeout);
    246          tv.tv_nsec = (long) apr_time_msec(timeout);
    247          tvptr = &tv;
    248      }


lines 245,246 convert from timeout (usec) to struct timespec, but line 246
assigns milliseconds to nanoseconds, with a wrong magnitude of 10^6.

Net effect is that a timeout of say 999 msec becomes a timeout of 999 nsec.

Please find attached a proposed patch and a test program to expose the bug.

thanks
marco


--- kqueue.c.orig	Fri Aug 18 16:44:49 2006
+++ kqueue.c	Fri Aug 18 16:51:06 2006
@@ -240,13 +240,13 @@ APR_DECLARE(apr_status_t) apr_pollset_po
 
     if (timeout < 0) {
         tvptr = NULL;
     }
     else {
         tv.tv_sec = (long) apr_time_sec(timeout);
-        tv.tv_nsec = (long) apr_time_msec(timeout);
+        tv.tv_nsec = (long) apr_time_usec(timeout) * 1000;
         tvptr = &tv;
     }
 
     ret = kevent(pollset->kqueue_fd, NULL, 0, pollset->ke_set, pollset->nalloc,
                 tvptr);
     (*num) = ret;
/* apr_kqueue_bug.c
   Expose a bug in the kqueue version of apr_pollset_poll().

   compile with:
   cc -W -Wall -Werror -o apr_kqueue_bug apr_kqueue_bug.c \
       -I/usr/local/include/apr-1 -L /usr/local/lib -lapr-1
 */

#include <stdio.h>
#include <stdlib.h>
#include <apr_poll.h>

#define CHECK(x) do { \
    rv = x; \
    if (rv != APR_SUCCESS) { printf("%s %s", #x, errmsg(rv)); exit(1); } \
    } while (0)

const char *
errmsg(apr_status_t rv)
{
    static char buf[80];

    if (rv == APR_SUCCESS) {
        return "success";
    } else {
        apr_strerror(rv, buf, sizeof buf);
        return buf;
    }
}

void mywait(apr_pollset_t *pollset, apr_time_t timeout_us)
{
    apr_status_t rv;
    apr_time_t   start_us, stop_us;
    apr_pollfd_t const *ret_pfd;
    int          num_pfd = 0;

    start_us = apr_time_now();
    rv = apr_pollset_poll(pollset, timeout_us, &num_pfd, &ret_pfd);
    stop_us = apr_time_now();
    if (! APR_STATUS_IS_TIMEUP(rv)) {
        printf("unexpected apr_pollset_poll %s", errmsg(rv));
    }
    printf("wait: asked %lld ms, waited %lld ms\n",
        apr_time_as_msec(timeout_us), apr_time_as_msec(stop_us - start_us));
}

int main()
{
    apr_status_t   rv;
    apr_pollset_t *pollset;
    apr_pool_t    *pool;
    apr_pollfd_t   pfd;
    apr_socket_t  *sock;

    CHECK(apr_initialize());
    CHECK(apr_pool_create(&pool, NULL));
    CHECK(apr_pollset_create(&pollset, 512, pool, 0));
    CHECK(apr_socket_create(&sock, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool));

    pfd.desc_type = APR_POLL_SOCKET;
    pfd.reqevents = APR_POLLIN;
    pfd.desc.s = sock;
    CHECK(apr_pollset_add(pollset, &pfd));

    mywait(pollset,  999 * 1000); // 999 ms  => will wait 0 ms
    mywait(pollset, 1999 * 1000); // 1999 ms => will wait 1000 ms

    return 0;
}

Reply via email to