Mercurial > hg > nginx
changeset 2128:345a014436d4
*) move Darwin support to separate files
*) Darwin sendfile() support
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Wed, 30 Jul 2008 12:18:07 +0000 |
parents | 05e8de8fcfbb |
children | 25add486e7aa |
files | auto/os/conf auto/os/darwin auto/os/features auto/sources src/core/ngx_config.h src/os/unix/ngx_darwin.h src/os/unix/ngx_darwin_config.h src/os/unix/ngx_darwin_init.c src/os/unix/ngx_darwin_sendfile_chain.c src/os/unix/ngx_os.h |
diffstat | 10 files changed, 756 insertions(+), 48 deletions(-) [+] |
line wrap: on
line diff
--- a/auto/os/conf Wed Jul 30 06:12:30 2008 +0000 +++ b/auto/os/conf Wed Jul 30 12:18:07 2008 +0000 @@ -18,6 +18,10 @@ . auto/os/solaris ;; + Darwin:*) + . auto/os/darwin + ;; + win32) . auto/os/win32 ;; @@ -36,24 +40,6 @@ ' ;; - Darwin:*) - have=NGX_DARWIN . auto/have_headers - have=NGX_HAVE_INHERITED_NONBLOCK . auto/have - CORE_INCS="$UNIX_INCS" - CORE_DEPS="$UNIX_DEPS $POSIX_DEPS" - CORE_SRCS="$UNIX_SRCS" - - ngx_feature="atomic(3)" - ngx_feature_name=NGX_DARWIN_ATOMIC - ngx_feature_run=no - ngx_feature_incs="#include <libkern/OSAtomic.h>" - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test="int32_t lock, n; - n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)" - . auto/feature - ;; - HP-UX:*) # HP/UX have=NGX_HPUX . auto/have_headers
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/auto/os/darwin Wed Jul 30 12:18:07 2008 +0000 @@ -0,0 +1,115 @@ + +# Copyright (C) Igor Sysoev + + +have=NGX_DARWIN . auto/have_headers + +CORE_INCS="$UNIX_INCS" +CORE_DEPS="$UNIX_DEPS $DARWIN_DEPS" +CORE_SRCS="$UNIX_SRCS $DARWIN_SRCS" + + + +ngx_spacer=' +' + +# kqueue + +echo " + kqueue found" +have=NGX_HAVE_KQUEUE . auto/have +have=NGX_HAVE_CLEAR_EVENT . auto/have +EVENT_MODULES="$EVENT_MODULES $KQUEUE_MODULE" +CORE_SRCS="$CORE_SRCS $KQUEUE_SRCS" +EVENT_FOUND=YES +NGX_KQUEUE_CHECKED=YES + +ngx_feature="kqueue's EVFILT_TIMER" +ngx_feature_name="NGX_HAVE_TIMER_EVENT" +ngx_feature_run=yes +ngx_feature_incs="#include <sys/event.h> + #include <sys/time.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int kq; + struct kevent kev; + struct timespec ts; + + if ((kq = kqueue()) == -1) return 1; + + kev.ident = 0; + kev.filter = EVFILT_TIMER; + kev.flags = EV_ADD|EV_ENABLE; + kev.fflags = 0; + kev.data = 1000; + kev.udata = 0; + + ts.tv_sec = 0; + ts.tv_nsec = 0; + + if (kevent(kq, &kev, 1, &kev, 1, &ts) == -1) return 1; + + if (kev.flags & EV_ERROR) return 1;" + +. auto/feature + + +ngx_feature="Darwin 64-bit kqueue millisecond timeout bug" +ngx_feature_name=NGX_DARWIN_KEVENT_BUG +ngx_feature_run=bug +ngx_feature_incs="#include <sys/event.h> + #include <sys/time.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int kq; + struct kevent kev; + struct timespec ts; + struct timeval tv, tv0; + + kq = kqueue(); + + ts.tv_sec = 0; + ts.tv_nsec = 999000000; + + gettimeofday(&tv, 0); + kevent(kq, NULL, 0, &kev, 1, &ts); + gettimeofday(&tv0, 0); + timersub(&tv0, &tv, &tv); + + if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;" + +. auto/feature + + +# sendfile() + +CC_AUX_FLAGS="$CC_AUX_FLAGS" +ngx_feature="sendfile()" +ngx_feature_name="NGX_HAVE_SENDFILE" +ngx_feature_run=yes +ngx_feature_incs="#include <sys/types.h> + #include <sys/socket.h> + #include <sys/uio.h> + #include <sys/errno.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int s = 0, fd = 1; + off_t n; off_t off = 0; + n = sendfile(s, fd, off, &n, NULL, 0); + if (n == -1 && errno == ENOSYS) return 1" +. auto/feature + +if [ $ngx_found = yes ]; then + have=NGX_HAVE_SENDFILE . auto/have + CORE_SRCS="$CORE_SRCS $DARWIN_SENDFILE_SRCS" +fi + + +ngx_feature="atomic(3)" +ngx_feature_name=NGX_DARWIN_ATOMIC +ngx_feature_run=no +ngx_feature_incs="#include <libkern/OSAtomic.h>" +ngx_feature_path= +ngx_feature_libs= +ngx_feature_test="int32_t lock, n; + n = OSAtomicCompareAndSwap32Barrier(0, 1, lock)" +. auto/feature
--- a/auto/os/features Wed Jul 30 06:12:30 2008 +0000 +++ b/auto/os/features Wed Jul 30 12:18:07 2008 +0000 @@ -122,36 +122,6 @@ if (kev.flags & EV_ERROR) return 1;" . auto/feature - - - if [ "$NGX_SYSTEM" = "Darwin" ]; then - - ngx_feature="Darwin 64-bit kqueue millisecond timeout bug" - ngx_feature_name=NGX_DARWIN_KEVENT_BUG - ngx_feature_run=bug - ngx_feature_incs="#include <sys/event.h> -#include <sys/time.h>" - ngx_feature_path= - ngx_feature_libs= - ngx_feature_test="int kq; - struct kevent kev; - struct timespec ts; - struct timeval tv, tv0; - - kq = kqueue(); - - ts.tv_sec = 0; - ts.tv_nsec = 999000000; - - gettimeofday(&tv, 0); - kevent(kq, NULL, 0, &kev, 1, &ts); - gettimeofday(&tv0, 0); - timersub(&tv0, &tv, &tv); - - if (tv.tv_sec * 1000000 + tv.tv_usec < 900000) return 1;" - - . auto/feature - fi fi fi
--- a/auto/sources Wed Jul 30 06:12:30 2008 +0000 +++ b/auto/sources Wed Jul 30 12:18:07 2008 +0000 @@ -198,6 +198,11 @@ SOLARIS_SENDFILEV_SRCS=src/os/unix/ngx_solaris_sendfilev_chain.c +DARWIN_DEPS="src/os/unix/ngx_darwin_config.h src/os/unix/ngx_darwin.h" +DARWIN_SRCS=src/os/unix/ngx_darwin_init.c +DARWIN_SENDFILE_SRCS=src/os/unix/ngx_darwin_sendfile_chain.c + + WIN32_INCS="$CORE_INCS $EVENT_INCS src/os/win32" WIN32_DEPS="$CORE_DEPS $EVENT_DEPS \
--- a/src/core/ngx_config.h Wed Jul 30 06:12:30 2008 +0000 +++ b/src/core/ngx_config.h Wed Jul 30 12:18:07 2008 +0000 @@ -29,6 +29,10 @@ #include <ngx_solaris_config.h> +#elif (NGX_DARWIN) +#include <ngx_darwin_config.h> + + #elif (NGX_WIN32) #include <ngx_win32_config.h>
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/unix/ngx_darwin.h Wed Jul 30 12:18:07 2008 +0000 @@ -0,0 +1,19 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_DARWIN_H_INCLUDED_ +#define _NGX_DARWIN_H_INCLUDED_ + + +ngx_chain_t *ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, + off_t limit); + +extern int ngx_darwin_kern_osreldate; +extern int ngx_darwin_hw_ncpu; +extern u_long ngx_darwin_net_inet_tcp_sendspace; + + +#endif /* _NGX_DARWIN_H_INCLUDED_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/unix/ngx_darwin_config.h Wed Jul 30 12:18:07 2008 +0000 @@ -0,0 +1,87 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#ifndef _NGX_DARWIN_CONFIG_H_INCLUDED_ +#define _NGX_DARWIN_CONFIG_H_INCLUDED_ + + + +#include <sys/types.h> +#include <sys/time.h> +#include <unistd.h> +#include <inttypes.h> +#include <stdarg.h> +#include <stddef.h> /* offsetof() */ +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <signal.h> +#include <pwd.h> +#include <grp.h> +#include <dirent.h> +#include <glob.h> + +#include <sys/filio.h> /* FIONBIO */ +#include <sys/ioctl.h> +#include <sys/uio.h> +#include <sys/stat.h> +#include <fcntl.h> + +#include <sys/wait.h> +#include <sys/mman.h> +#include <sys/resource.h> +#include <sched.h> + +#include <sys/socket.h> +#include <netinet/in.h> +#include <netinet/tcp.h> /* TCP_NODELAY */ +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/un.h> + +#include <sys/sysctl.h> +#include <xlocale.h> + + +#ifndef IOV_MAX +#define IOV_MAX 64 +#endif + + +#include <ngx_auto_config.h> + + +#if (NGX_HAVE_POLL) +#include <poll.h> +#endif + + +#if (NGX_HAVE_KQUEUE) +#include <sys/event.h> +#endif + + +#define NGX_LISTEN_BACKLOG -1 + + +#ifndef NGX_HAVE_INHERITED_NONBLOCK +#define NGX_HAVE_INHERITED_NONBLOCK 1 +#endif + + +#ifndef NGX_HAVE_CASELESS_FILESYSTEM +#define NGX_HAVE_CASELESS_FILESYSTEM 1 +#endif + + +#define NGX_HAVE_OS_SPECIFIC_INIT 1 + + +extern char **environ; + + +#endif /* _NGX_DARWIN_CONFIG_H_INCLUDED_ */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/unix/ngx_darwin_init.c Wed Jul 30 12:18:07 2008 +0000 @@ -0,0 +1,155 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> + + +char ngx_darwin_kern_ostype[16]; +char ngx_darwin_kern_osrelease[128]; +int ngx_darwin_hw_ncpu; +int ngx_darwin_kern_ipc_somaxconn; +u_long ngx_darwin_net_inet_tcp_sendspace; + + +static ngx_os_io_t ngx_darwin_io = { + ngx_unix_recv, + ngx_readv_chain, + ngx_udp_unix_recv, + ngx_unix_send, +#if (NGX_HAVE_SENDFILE) + ngx_darwin_sendfile_chain, + NGX_IO_SENDFILE +#else + ngx_writev_chain, + 0 +#endif +}; + + +typedef struct { + char *name; + void *value; + size_t size; + ngx_uint_t exists; +} sysctl_t; + + +sysctl_t sysctls[] = { + { "hw.ncpu", + &ngx_darwin_hw_ncpu, + sizeof(ngx_darwin_hw_ncpu), 0 }, + + { "net.inet.tcp.sendspace", + &ngx_darwin_net_inet_tcp_sendspace, + sizeof(ngx_darwin_net_inet_tcp_sendspace), 0 }, + + { "kern.ipc.somaxconn", + &ngx_darwin_kern_ipc_somaxconn, + sizeof(ngx_darwin_kern_ipc_somaxconn), 0 }, + + { NULL, NULL, 0, 0 } +}; + + +ngx_int_t +ngx_os_specific_init(ngx_log_t *log) +{ + int somaxconn; + size_t size; + ngx_err_t err; + ngx_uint_t i; + + size = sizeof(ngx_darwin_kern_ostype); + if (sysctlbyname("kern.ostype", + ngx_darwin_kern_ostype, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.ostype) failed"); + + if (ngx_errno != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_ostype[size - 1] = '\0'; + } + + size = sizeof(ngx_darwin_kern_osrelease); + if (sysctlbyname("kern.osrelease", + ngx_darwin_kern_osrelease, &size, NULL, 0) == -1) { + ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, + "sysctlbyname(kern.osrelease) failed"); + + if (ngx_errno != NGX_ENOMEM) { + return NGX_ERROR; + } + + ngx_darwin_kern_osrelease[size - 1] = '\0'; + } + + + for (i = 0; sysctls[i].name; i++) { + size = sysctls[i].size; + + if (sysctlbyname(sysctls[i].name, sysctls[i].value, &size, NULL, 0) + == 0) + { + sysctls[i].exists = 1; + continue; + } + + err = ngx_errno; + + if (err == NGX_ENOENT) { + continue; + } + + ngx_log_error(NGX_LOG_ALERT, log, err, + "sysctlbyname(%s) failed", sysctls[i].name); + return NGX_ERROR; + } + + ngx_ncpu = ngx_darwin_hw_ncpu; + + somaxconn = 32676; + + if (ngx_darwin_kern_ipc_somaxconn > somaxconn) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "sysctl kern.ipc.somaxconn must be no more than %d", + somaxconn); + return NGX_ERROR; + } + + ngx_tcp_nodelay_and_tcp_nopush = 1; + + ngx_os_io = ngx_darwin_io; + + return NGX_OK; +} + + +void +ngx_os_specific_status(ngx_log_t *log) +{ + u_long value; + ngx_uint_t i; + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s", + ngx_darwin_kern_ostype, ngx_darwin_kern_osrelease); + + for (i = 0; sysctls[i].name; i++) { + if (sysctls[i].exists) { + if (sysctls[i].size == sizeof(long)) { + value = *(long *) sysctls[i].value; + + } else { + value = *(int *) sysctls[i].value; + } + + ngx_log_error(NGX_LOG_NOTICE, log, 0, "%s: %l", + sysctls[i].name, value); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/os/unix/ngx_darwin_sendfile_chain.c Wed Jul 30 12:18:07 2008 +0000 @@ -0,0 +1,363 @@ + +/* + * Copyright (C) Igor Sysoev + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> + + +/* + * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same + * old bug as early FreeBSD sendfile() syscall: + * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771 + * + * Besides sendfile() has another bug: if one calls sendfile() + * with both a header and a trailer, then sendfile() ignores a file part + * at all and sends only the header and the trailer together. + * For this reason we send a trailer only if there is no a header. + * + * Although sendfile() allows to pass a header or a trailer, + * it may send the header or the trailer and a part of the file + * in different packets. And FreeBSD workaround (TCP_NOPUSH option) + * does not help. + */ + + +#if (IOV_MAX > 64) +#define NGX_HEADERS 64 +#define NGX_TRAILERS 64 +#else +#define NGX_HEADERS IOV_MAX +#define NGX_TRAILERS IOV_MAX +#endif + + +ngx_chain_t * +ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int rc; + u_char *prev; + off_t size, send, prev_send, aligned, sent, fprev; + off_t header_size, file_size; + ngx_uint_t eintr, eagain, complete; + ngx_err_t err; + ngx_buf_t *file; + ngx_array_t header, trailer; + ngx_event_t *wev; + ngx_chain_t *cl; + struct sf_hdtr hdtr; + struct iovec *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + eagain = 0; + + header.elts = headers; + header.size = sizeof(struct iovec); + header.nalloc = NGX_HEADERS; + header.pool = c->pool; + + trailer.elts = trailers; + trailer.size = sizeof(struct iovec); + trailer.nalloc = NGX_TRAILERS; + trailer.pool = c->pool; + + for ( ;; ) { + file = NULL; + file_size = 0; + header_size = 0; + eintr = 0; + complete = 0; + prev_send = send; + + header.nelts = 0; + trailer.nelts = 0; + + /* create the header iovec and coalesce the neighbouring bufs */ + + prev = NULL; + iov = NULL; + + for (cl = in; + cl && header.nelts < IOV_MAX && send < limit; + cl = cl->next) + { + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (!ngx_buf_in_memory_only(cl->buf)) { + break; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + iov->iov_len += (size_t) size; + + } else { + iov = ngx_array_push(&header); + if (iov == NULL) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = (size_t) size; + } + + prev = cl->buf->pos + (size_t) size; + header_size += size; + send += size; + } + + + if (cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + do { + size = cl->buf->file_last - cl->buf->file_pos; + + if (send + size > limit) { + size = limit - send; + + aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) + & ~((off_t) ngx_pagesize - 1); + + if (aligned <= cl->buf->file_last) { + size = aligned - cl->buf->file_pos; + } + } + + file_size += size; + send += size; + fprev = cl->buf->file_pos + size; + cl = cl->next; + + } while (cl + && cl->buf->in_file + && send < limit + && file->file->fd == cl->buf->file->fd + && fprev == cl->buf->file_pos); + } + + if (file && header.nelts == 0) { + + /* create the tailer iovec and coalesce the neighbouring bufs */ + + prev = NULL; + iov = NULL; + + while (cl && header.nelts < IOV_MAX && send < limit) { + + if (ngx_buf_special(cl->buf)) { + cl = cl->next; + continue; + } + + if (!ngx_buf_in_memory_only(cl->buf)) { + break; + } + + size = cl->buf->last - cl->buf->pos; + + if (send + size > limit) { + size = limit - send; + } + + if (prev == cl->buf->pos) { + iov->iov_len += (size_t) size; + + } else { + iov = ngx_array_push(&trailer); + if (iov == NULL) { + return NGX_CHAIN_ERROR; + } + + iov->iov_base = (void *) cl->buf->pos; + iov->iov_len = (size_t) size; + } + + prev = cl->buf->pos + (size_t) size; + send += size; + cl = cl->next; + } + } + + if (file) { + + /* + * sendfile() returns EINVAL if sf_hdtr's count is 0, + * but corresponding pointer is not NULL + */ + + hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL; + hdtr.hdr_cnt = header.nelts; + hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL; + hdtr.trl_cnt = trailer.nelts; + + sent = header_size + file_size; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: @%O %O h:%O", + file->file_pos, sent, header_size); + + rc = sendfile(file->file->fd, c->fd, file->file_pos, + &sent, &hdtr, 0); + + if (rc == -1) { + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + if (err == NGX_EINTR) { + eintr = 1; + + } else { + eagain = 1; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() sent only %O bytes", sent); + + } else { + wev->error = 1; + (void) ngx_connection_error(c, err, "sendfile() failed"); + return NGX_CHAIN_ERROR; + } + } + + if (rc == 0 && sent == 0) { + + /* + * if rc and sent equal to zero, then someone + * has truncated the file, so the offset became beyond + * the end of the file + */ + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile() reported that \"%s\" was truncated", + file->file->name.data); + + return NGX_CHAIN_ERROR; + } + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: %d, @%O %O:%O", + rc, file->file_pos, sent, file_size + header_size); + + } else { + rc = writev(c->fd, header.elts, header.nelts); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "writev: %d of %uz", rc, send); + + if (rc == -1) { + err = ngx_errno; + + if (err == NGX_EAGAIN || err == NGX_EINTR) { + if (err == NGX_EINTR) { + eintr = 1; + } + + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "writev() not ready"); + + } else { + wev->error = 1; + ngx_connection_error(c, err, "writev() failed"); + return NGX_CHAIN_ERROR; + } + } + + sent = rc > 0 ? rc : 0; + } + + if (send - prev_send == sent) { + complete = 1; + } + + c->sent += sent; + + for (cl = in; cl; cl = cl->next) { + + if (ngx_buf_special(cl->buf)) { + continue; + } + + if (sent == 0) { + break; + } + + size = ngx_buf_size(cl->buf); + + if (sent >= size) { + sent -= size; + + if (ngx_buf_in_memory(cl->buf)) { + cl->buf->pos = cl->buf->last; + } + + if (cl->buf->in_file) { + cl->buf->file_pos = cl->buf->file_last; + } + + continue; + } + + if (ngx_buf_in_memory(cl->buf)) { + cl->buf->pos += (size_t) sent; + } + + if (cl->buf->in_file) { + cl->buf->file_pos += sent; + } + + break; + } + + if (eintr) { + continue; + } + + if (!complete) { + wev->ready = 0; + return cl; + } + + if (send >= limit || cl == NULL) { + return cl; + } + + in = cl; + } +}