view src/event/modules/ngx_rtsig_module.c @ 485:4ebe09b07e30 release-0.1.17

nginx-0.1.17-RELEASE import *) Change: the ngx_http_rewrite_module was rewritten from the scratch. Now it is possible to redirect, to return the error codes, to check the variables and referrers. The directives can be used inside locations. The redirect directive was canceled. *) Feature: the ngx_http_geo_module. *) Feature: the proxy_set_x_var and fastcgi_set_var directives. *) Bugfix: the location configuration with "=" modifier may be used in another location. *) Bugfix: the correct content type was set only for requests that use small caps letters in extension. *) Bugfix: if the proxy_pass or fastcgi_pass directives were set in the location, and access was denied, and the error was redirected to a static page, then the segmentation fault occurred. *) Bugfix: if in a proxied "Location" header was a relative URL, then a host name and a slash were added to them; the bug had appeared in 0.1.14. *) Bugfix: the system error message was not logged on Linux.
author Igor Sysoev <igor@sysoev.ru>
date Thu, 03 Feb 2005 19:33:37 +0000
parents 2ff194b74f1e
children 975f62e77f02
line wrap: on
line source


/*
 * Copyright (C) Igor Sysoev
 */


#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>


#if (NGX_TEST_BUILD_RTSIG)

#define F_SETSIG       10
#define SIGRTMIN       33
#define si_fd          __spare__[0]
#define KERN_RTSIGNR   30
#define KERN_RTSIGMAX  31

int sigtimedwait(const sigset_t *set, siginfo_t *info,
                 const struct timespec *timeout)
{
    return -1;
}

int ngx_linux_rtsig_max;

#endif


typedef struct {
    int        signo;
    ngx_int_t  overflow_events;
    ngx_int_t  overflow_test;
    ngx_int_t  overflow_threshold;
} ngx_rtsig_conf_t;


extern ngx_event_module_t  ngx_poll_module_ctx;

static ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle);
static void ngx_rtsig_done(ngx_cycle_t *cycle);
static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c);
static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c, u_int flags);
static ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle);
static ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle);

static void *ngx_rtsig_create_conf(ngx_cycle_t *cycle);
static char *ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf);
static char *ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf,
                                                     void *post, void *data);


static sigset_t        set;
static ngx_uint_t      overflow, overflow_current;
static struct pollfd  *overflow_list;


static ngx_str_t      rtsig_name = ngx_string("rtsig");

static ngx_conf_num_bounds_t  ngx_overflow_threshold_bounds = {
    ngx_check_ngx_overflow_threshold_bounds, 2, 10
};


static ngx_command_t  ngx_rtsig_commands[] = {

    {ngx_string("rtsig_signo"),
     NGX_EVENT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_num_slot,
     0,
     offsetof(ngx_rtsig_conf_t, signo),
     NULL},

    {ngx_string("rtsig_overflow_events"),
     NGX_EVENT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_num_slot,
     0,
     offsetof(ngx_rtsig_conf_t, overflow_events),
     NULL},

    {ngx_string("rtsig_overflow_test"),
     NGX_EVENT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_num_slot,
     0,
     offsetof(ngx_rtsig_conf_t, overflow_test),
     NULL},

    {ngx_string("rtsig_overflow_threshold"),
     NGX_EVENT_CONF|NGX_CONF_TAKE1,
     ngx_conf_set_num_slot,
     0,
     offsetof(ngx_rtsig_conf_t, overflow_threshold),
     &ngx_overflow_threshold_bounds},

    ngx_null_command
};


ngx_event_module_t  ngx_rtsig_module_ctx = {
    &rtsig_name,
    ngx_rtsig_create_conf,               /* create configuration */
    ngx_rtsig_init_conf,                 /* init configuration */

    {
        NULL,                            /* add an event */
        NULL,                            /* delete an event */
        NULL,                            /* enable an event */
        NULL,                            /* disable an event */
        ngx_rtsig_add_connection,        /* add an connection */
        ngx_rtsig_del_connection,        /* delete an connection */
        NULL,                            /* process the changes */
        ngx_rtsig_process_events,        /* process the events */
        ngx_rtsig_init,                  /* init the events */
        ngx_rtsig_done,                  /* done the events */
    }

};

ngx_module_t  ngx_rtsig_module = {
    NGX_MODULE,
    &ngx_rtsig_module_ctx,               /* module context */
    ngx_rtsig_commands,                  /* module directives */
    NGX_EVENT_MODULE,                      /* module type */
    NULL,                                  /* init module */
    NULL                                   /* init process */
};


static ngx_int_t ngx_rtsig_init(ngx_cycle_t *cycle)
{
    ngx_rtsig_conf_t  *rtscf;

    rtscf = ngx_event_get_conf(cycle->conf_ctx, ngx_rtsig_module);

    sigemptyset(&set);
    sigaddset(&set, rtscf->signo);
    sigaddset(&set, rtscf->signo + 1);
    sigaddset(&set, SIGIO);

    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {
        ngx_log_error(NGX_LOG_EMERG, cycle->log, ngx_errno,
                      "sigprocmask() failed");
        return NGX_ERROR;
    }

    if (overflow_list) {
        ngx_free(overflow_list);
    }

    overflow_list = ngx_alloc(sizeof(struct pollfd) * rtscf->overflow_events,
                              cycle->log);
    if (overflow_list == NULL) {
        return NGX_ERROR;
    }

    ngx_io = ngx_os_io;

    ngx_event_actions = ngx_rtsig_module_ctx.actions;

    ngx_event_flags = NGX_USE_RTSIG_EVENT|NGX_USE_GREEDY_EVENT;

    return NGX_OK;
}


static void ngx_rtsig_done(ngx_cycle_t *cycle)
{
    ngx_free(overflow_list);

    overflow_list = NULL;
}


static ngx_int_t ngx_rtsig_add_connection(ngx_connection_t *c)
{
    int                signo;
    ngx_rtsig_conf_t  *rtscf;

    if (c->read->accept && c->read->disabled) {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "rtsig enable connection: fd:%d", c->fd);

        if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {
            ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                          "fcntl(F_SETOWN) failed");
            return NGX_ERROR;
        }

        c->read->active = 1;
        c->read->disabled = 0;
    }

    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);

    signo = rtscf->signo + c->read->instance;

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "rtsig add connection: fd:%d signo:%d", c->fd, signo);

    if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK|O_ASYNC) == -1) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      "fcntl(O_RDWR|O_NONBLOCK|O_ASYNC) failed");
        return NGX_ERROR;
    }

    if (fcntl(c->fd, F_SETSIG, signo) == -1) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      "fcntl(F_SETSIG) failed");
        return NGX_ERROR;
    }

    if (fcntl(c->fd, F_SETOWN, ngx_pid) == -1) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      "fcntl(F_SETOWN) failed");
        return NGX_ERROR;
    }

#if (NGX_HAVE_ONESIGFD)
    if (fcntl(c->fd, F_SETAUXFL, O_ONESIGFD) == -1) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      "fcntl(F_SETAUXFL) failed");
        return NGX_ERROR;
    }
#endif

    c->read->active = 1;
    c->write->active = 1;

    return NGX_OK;
}


static ngx_int_t ngx_rtsig_del_connection(ngx_connection_t *c, u_int flags)
{
    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                   "rtsig del connection: fd:%d", c->fd);

    if ((flags & NGX_DISABLE_EVENT) && c->read->accept) {

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
                       "rtsig disable connection: fd:%d", c->fd);

        c->read->active = 0;
        c->read->disabled = 1;
        return NGX_OK;
    }

    if (flags & NGX_CLOSE_EVENT) {
        c->read->active = 0;
        c->write->active = 0;
        return NGX_OK;
    }

    if (fcntl(c->fd, F_SETFL, O_RDWR|O_NONBLOCK) == -1) {
        ngx_log_error(NGX_LOG_ALERT, c->log, ngx_errno,
                      "fcntl(O_RDWR|O_NONBLOCK) failed");
        return NGX_ERROR;
    }

    c->read->active = 0;
    c->write->active = 0;

    return NGX_OK;
}


ngx_int_t ngx_rtsig_process_events(ngx_cycle_t *cycle)
{
    int                 signo;
    ngx_int_t           instance, i;
    ngx_uint_t          expire;
    size_t              n;
    ngx_msec_t          timer;
    ngx_err_t           err;
    siginfo_t           si;
    ngx_event_t        *rev, *wev;
    struct timeval      tv;
    struct timespec     ts, *tp;
    struct sigaction    sa;
    ngx_epoch_msec_t    delta;
    ngx_connection_t   *c;
    ngx_rtsig_conf_t   *rtscf;

    if (overflow) {
        timer = 0;
        expire = 0;

    } else {
        for ( ;; ) {
            timer = ngx_event_find_timer();

#if (NGX_THREADS)

            if (timer == NGX_TIMER_ERROR) {
                return NGX_ERROR;
            }

            if (timer == NGX_TIMER_INFINITE || timer > 500) {
                timer = 500;
                break;
            }

#endif

            if (timer != 0) {
                break;
            }

            ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "rtsig expired timer");

            ngx_event_expire_timers((ngx_msec_t)
                                    (ngx_elapsed_msec - ngx_old_elapsed_msec));

            if (ngx_posted_events && ngx_threaded) {
                ngx_wakeup_worker_thread(cycle);
            }
        }

        expire = 1;

        if (ngx_accept_mutex) {
            if (ngx_accept_disabled > 0) {
                ngx_accept_disabled--;

            } else {
                ngx_accept_mutex_held = 0;

                if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                    return NGX_ERROR;
                }

                if (ngx_accept_mutex_held == 0
                    && (timer == NGX_TIMER_INFINITE
                        || timer > ngx_accept_mutex_delay))
                {
                    timer = ngx_accept_mutex_delay;
                    expire = 0;
                } 
            }
        }
    }

    if (timer == NGX_TIMER_INFINITE) {
        tp = NULL;
        expire = 0;

    } else {
        ts.tv_sec = timer / 1000;
        ts.tv_nsec = (timer % 1000) * 1000000;
        tp = &ts;
    }

    ngx_old_elapsed_msec = ngx_elapsed_msec;

    ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                   "rtsig timer: %d", timer);

    /* Linux's sigwaitinfo() is sigtimedwait() with the NULL timeout pointer */

    signo = sigtimedwait(&set, &si, tp);

    if (signo == -1) {
        err = ngx_errno;

        ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, err,
                       "rtsig signo:%d", signo);

        if (err == NGX_EAGAIN) {

            if (timer == NGX_TIMER_INFINITE) {
                ngx_accept_mutex_unlock();
                ngx_log_error(NGX_LOG_ALERT, cycle->log, err,
                              "sigtimedwait() returned EAGAIN without timeout");
                return NGX_ERROR;
            }

            err = 0;
        }

    } else {
        err = 0;
        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "rtsig signo:%d fd:%d band:%04Xd",
                       signo, si.si_fd, si.si_band);
    }

    ngx_gettimeofday(&tv);
    ngx_time_update(tv.tv_sec);

    delta = ngx_elapsed_msec;
    ngx_elapsed_msec = (ngx_epoch_msec_t) tv.tv_sec * 1000
                                          + tv.tv_usec / 1000 - ngx_start_msec;

    if (err) {
        ngx_accept_mutex_unlock();
        ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
                      cycle->log, err, "sigtimedwait() failed");
        return NGX_ERROR;
    }

    if (timer != NGX_TIMER_INFINITE) {
        delta = ngx_elapsed_msec - delta;

        ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "rtsig timer: %d, delta: %d", timer, (int) delta);
    }

    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);

    if (signo == rtscf->signo || signo == rtscf->signo + 1) {

        if (overflow && (ngx_uint_t) si.si_fd > overflow_current) {
            return NGX_OK;
        }

        /* TODO: old_cycles */

        c = &ngx_cycle->connections[si.si_fd];

        instance = signo - rtscf->signo;

        rev = c->read;

        if (c->read->instance != instance) {

            /*
             * the stale event from a file descriptor
             * that was just closed in this iteration
             */

            ngx_accept_mutex_unlock();

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "rtsig: stale event %p", c);

            return NGX_OK;
        }

        if (si.si_band & (POLLIN|POLLHUP|POLLERR)) {
            if (rev->active) {

                if (ngx_threaded && !rev->accept) {
                    if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) {
                        ngx_accept_mutex_unlock();
                        return NGX_ERROR;
                    }

                    rev->posted_ready = 1;
                    ngx_post_event(rev);

                    ngx_mutex_unlock(ngx_posted_events_mutex);

                } else {
                    rev->ready = 1;

                    if (!ngx_threaded && !ngx_accept_mutex_held) {
                        rev->event_handler(rev);

                    } else if (rev->accept) {
                        if (ngx_accept_disabled <= 0) {
                            rev->event_handler(rev);
                        }

                    } else {
                        ngx_post_event(rev); 
                    }
                }
            }
        }

        wev = c->write;

        if (si.si_band & (POLLOUT|POLLHUP|POLLERR)) {
            if (wev->active) {

                if (ngx_threaded) {
                    if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) {
                        ngx_accept_mutex_unlock();
                        return NGX_ERROR;
                    }

                    wev->posted_ready = 1;
                    ngx_post_event(wev);

                    ngx_mutex_unlock(ngx_posted_events_mutex);

                } else {
                    wev->ready = 1;

                    if (!ngx_threaded && !ngx_accept_mutex_held) {
                        wev->event_handler(wev);

                    } else {
                        ngx_post_event(wev);
                    }
                }
            }
        }

    } else if (signo == SIGIO) {
        ngx_accept_mutex_unlock();

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                      "rt signal queue overflowed");

        /* flush the RT signal queue */

        ngx_memzero(&sa, sizeof(struct sigaction));
        sa.sa_handler = SIG_DFL;
        sigemptyset(&sa.sa_mask);

        if (sigaction(rtscf->signo, &sa, NULL) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "sigaction(%d, SIG_DFL) failed", rtscf->signo);
        }

        if (sigaction(rtscf->signo + 1, &sa, NULL) == -1) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                          "sigaction(%d, SIG_DFL) failed", rtscf->signo + 1);
        }

        overflow = 1;
        overflow_current = 0;
        ngx_event_actions.process_events = ngx_rtsig_process_overflow;

        return NGX_ERROR;

    } else if (signo != -1) {
        ngx_accept_mutex_unlock();

        ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                      "sigtimedwait() returned unexpected signal: %d", signo);

        return NGX_ERROR;
    }

    ngx_accept_mutex_unlock();

    if (expire && delta) {
        ngx_event_expire_timers((ngx_msec_t) delta);
    }

    if (ngx_posted_events) {
        if (ngx_threaded) {
            ngx_wakeup_worker_thread(cycle);

        } else {
            ngx_event_process_posted(cycle);
        }
    }

    if (signo == -1) {
        return NGX_AGAIN;
    } else {
        return NGX_OK;
    }
}


/* TODO: old cylces */

static ngx_int_t ngx_rtsig_process_overflow(ngx_cycle_t *cycle)
{
    int                name[2], rtsig_max, rtsig_nr, events, ready;
    size_t             len;
    ngx_int_t          tested, n, i;
    ngx_err_t          err;
    ngx_event_t       *rev, *wev;
    ngx_connection_t  *c;
    ngx_rtsig_conf_t  *rtscf;

    rtscf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_rtsig_module);

    tested = 0;

    for ( ;; ) {

        n = 0;
        while (n < rtscf->overflow_events) {

            if (overflow_current == cycle->connection_n) {
                break;
            }

            c = &cycle->connections[overflow_current++];

            if (c->fd == -1) {
                continue;
            }

            events = 0;

            if (c->read->active && c->read->event_handler) {
                events |= POLLIN;
            }

            if (c->write->active && c->write->event_handler) {
                events |= POLLOUT;
            }

            if (events == 0) {
                continue;
            }

            overflow_list[n].fd = c->fd;
            overflow_list[n].events = events;
            overflow_list[n].revents = 0;
            n++;
        }

        if (n == 0) {
            break;
        }

        for ( ;; ) {
            ready = poll(overflow_list, n, 0);

            if (ready == -1) {
                err = ngx_errno;
                ngx_log_error((err == NGX_EINTR) ? NGX_LOG_INFO : NGX_LOG_ALERT,
                              cycle->log, 0,
                              "poll() failed while the overflow recover");

                if (err == NGX_EINTR) {
                    continue;
                }
            }

            break;
        }

        if (ready <= 0) {
            continue;
        }

        if (ngx_mutex_lock(ngx_posted_events_mutex) == NGX_ERROR) {
            return NGX_ERROR;
        }

        for (i = 0; i < n; i++) {
            c = &cycle->connections[overflow_list[i].fd];

            rev = c->read;

            if (rev->active
                && !rev->closed
                && rev->event_handler
                && (overflow_list[i].revents
                                          & (POLLIN|POLLERR|POLLHUP|POLLNVAL)))
            {
                tested++;

                if (ngx_threaded) {
                    rev->posted_ready = 1;
                    ngx_post_event(rev);

                } else {
                    rev->ready = 1;
                    rev->event_handler(rev); 
                }
            }

            wev = c->write;

            if (wev->active
                && !wev->closed
                && wev->event_handler
                && (overflow_list[i].revents
                                         & (POLLOUT|POLLERR|POLLHUP|POLLNVAL)))
            {
                tested++;

                if (ngx_threaded) {
                    wev->posted_ready = 1;
                    ngx_post_event(wev);

                } else {
                    wev->ready = 1;
                    wev->event_handler(wev); 
                }
            }
        }

        ngx_mutex_unlock(ngx_posted_events_mutex);

        if (tested >= rtscf->overflow_test) {

            if (ngx_linux_rtsig_max) {

                /*
                 * Check the current rt queue length to prevent
                 * the new overflow.
                 *
                 * Learn the /proc/sys/kernel/rtsig-max value because
                 * it can be changed since the last checking.
                 */

                name[0] = CTL_KERN;
                name[1] = KERN_RTSIGMAX;
                len = sizeof(rtsig_max);
                if (sysctl(name, sizeof(name), &rtsig_max, &len, NULL, 0) == -1)
                {
                    ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,
                                  "sysctl(KERN_RTSIGMAX) failed");
                    return NGX_ERROR;
                }

                name[0] = CTL_KERN;
                name[1] = KERN_RTSIGNR;
                len = sizeof(rtsig_nr);
                if (sysctl(name, sizeof(name), &rtsig_nr, &len, NULL, 0) == -1)
                {
                    ngx_log_error(NGX_LOG_ALERT, cycle->log, errno,
                                  "sysctl(KERN_RTSIGNR) failed");
                    return NGX_ERROR;
                }

                /*
                 * drain the rt signal queue if the /proc/sys/kernel/rtsig-nr
                 * is bigger than
                 *    /proc/sys/kernel/rtsig-max / rtsig_overflow_threshold
                 */

                if (rtsig_max / rtscf->overflow_threshold < rtsig_nr) {
                    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                                   "rtsig queue state: %d/%d",
                                   rtsig_nr, rtsig_max);
                    while (ngx_rtsig_process_events(cycle) == NGX_OK) {
                        /* void */
                    }
                }

            } else {

                /*
                 * Linux has not KERN_RTSIGMAX since 2.6.6-mm2
                 * so drain the rt signal queue unconditionally
                 */

                while (ngx_rtsig_process_events(cycle) == NGX_OK) { /* void */ }
            }

            tested = 0;
        }
    }

    if (ngx_posted_events) {
        if (ngx_threaded) {
            ngx_wakeup_worker_thread(cycle);

        } else {
            ngx_event_process_posted(cycle);
        }
    }

    ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
                  "rt signal queue overflow recovered");

    overflow = 0;
    ngx_event_actions.process_events = ngx_rtsig_process_events;

    return NGX_OK;
}


static void *ngx_rtsig_create_conf(ngx_cycle_t *cycle)
{
    ngx_rtsig_conf_t  *rtscf;

    ngx_test_null(rtscf, ngx_palloc(cycle->pool, sizeof(ngx_rtsig_conf_t)),
                  NGX_CONF_ERROR);

    rtscf->signo = NGX_CONF_UNSET;
    rtscf->overflow_events = NGX_CONF_UNSET;
    rtscf->overflow_test = NGX_CONF_UNSET;
    rtscf->overflow_threshold = NGX_CONF_UNSET;

    return rtscf;
}


static char *ngx_rtsig_init_conf(ngx_cycle_t *cycle, void *conf)
{
    ngx_rtsig_conf_t  *rtscf = conf;

    /* LinuxThreads use the first 3 RT signals */
    ngx_conf_init_value(rtscf->signo, SIGRTMIN + 10);

    ngx_conf_init_value(rtscf->overflow_events, 16);
    ngx_conf_init_value(rtscf->overflow_test, 32);
    ngx_conf_init_value(rtscf->overflow_threshold, 10);

    return NGX_CONF_OK;
}


static char *ngx_check_ngx_overflow_threshold_bounds(ngx_conf_t *cf,
                                                     void *post, void *data)
{
    if (ngx_linux_rtsig_max) {
        return ngx_conf_check_num_bounds(cf, post, data);
    }

    ngx_conf_log_error(NGX_LOG_WARN, cf, 0,
                       "\"rtsig_overflow_threshold\" is not supported "
                       "since Linux 2.6.6-mm2, ignored");

    return NGX_CONF_OK;
}