kgio-2.8.1/0000755000004100000410000000000012214142252012476 5ustar www-datawww-datakgio-2.8.1/README0000644000004100000410000000416612214142252013365 0ustar www-datawww-data= kgio - kinder, gentler I/O for Ruby kgio provides non-blocking I/O methods for Ruby without raising exceptions on EAGAIN and EINPROGRESS. It is intended for use with the Unicorn and Rainbows! Rack servers, but may be used by other applications (that run on Unix-like platforms). == Features * Can avoid expensive exceptions on common EAGAIN/EINPROGRESS errors, returning :wait_readable or :wait_writable instead. These exceptions got more expensive to hit under Ruby 1.9.2 (but should be fixed in Ruby 1.9.3 to 1.9.1 performance levels) * Returns the unwritten portion of the string on partial writes, making it ideal for buffering unwritten data. * May call any method defined to be "kgio_wait_writable" or "kgio_wait_readable" methods to allow socket/pipe objects to make custom callbacks (such as adding the file descriptor to a poll set and yielding the current Fiber). * Uses {accept4}[http://kernel.org/doc/man-pages/online/pages/man2/accept4.2.html] on new GNU/Linux systems to avoid unnecessary fcntl() calls * Uses MSG_DONTWAIT on GNU/Linux to further avoid unnecessary fcntl() calls * Compatible with existing Ruby IO objects and Ruby threading. == Install The library consists of a C extension so you'll need a Unix-like system with a C compiler and Ruby development libraries/headers. http://rubyforge.org/frs/?group_id=8977 You may also install it via RubyGems.org: gem install kgio You can get the latest source via git from the following locations (these versions may not be stable): git://bogomips.org/kgio.git git://repo.or.cz/kgio.git (mirror) You may browse the code from the web and download the latest snapshot tarballs here: * http://bogomips.org/kgio.git (cgit) * http://repo.or.cz/w/kgio.git (gitweb) See the HACKING guide on how to contribute and build prerelease gems from git. == Contact All feedback (bug reports, user/development dicussion, patches, pull requests) go to the mailing list/newsgroup. See the ISSUES document for information on the {kgio mailing list}[mailto:kgio@librelist.org] For the latest on kgio releases, you may check our NEWS page (and subscribe to our Atom feed). kgio-2.8.1/ext/0000755000004100000410000000000012214142252013276 5ustar www-datawww-datakgio-2.8.1/ext/kgio/0000755000004100000410000000000012214142252014227 5ustar www-datawww-datakgio-2.8.1/ext/kgio/extconf.rb0000644000004100000410000000357312214142252016232 0ustar www-datawww-datarequire 'mkmf' $CPPFLAGS << ' -D_GNU_SOURCE' $CPPFLAGS << ' -DPOSIX_C_SOURCE=1' $CPPFLAGS += '-D_POSIX_C_SOURCE=200112L' unless have_macro('CLOCK_MONOTONIC', 'time.h') have_func('CLOCK_MONOTONIC', 'time.h') end have_type('clockid_t', 'time.h') have_library('rt', 'clock_gettime', 'time.h') # taken from ext/socket/extconf.rb in ruby/trunk: # OpenSolaris: have_library("nsl", "t_open") have_library("socket", "socket") have_func("poll", "poll.h") have_func("getaddrinfo", %w(sys/types.h sys/socket.h netdb.h)) or abort "getaddrinfo required" have_func("getnameinfo", %w(sys/types.h sys/socket.h netdb.h)) or abort "getnameinfo required" have_type("struct sockaddr_storage", %w(sys/types.h sys/socket.h)) or abort "struct sockaddr_storage required" have_func('accept4', %w(sys/socket.h)) have_header("sys/select.h") have_func("writev", "sys/uio.h") if have_header('ruby/io.h') rubyio = %w(ruby.h ruby/io.h) have_struct_member("rb_io_t", "fd", rubyio) have_struct_member("rb_io_t", "mode", rubyio) have_struct_member("rb_io_t", "pathv", rubyio) else rubyio = %w(ruby.h rubyio.h) rb_io_t = have_type("OpenFile", rubyio) ? "OpenFile" : "rb_io_t" have_struct_member(rb_io_t, "f", rubyio) have_struct_member(rb_io_t, "f2", rubyio) have_struct_member(rb_io_t, "mode", rubyio) have_struct_member(rb_io_t, "path", rubyio) have_func('rb_fdopen') end have_type("struct RFile", rubyio) and check_sizeof("struct RFile", rubyio) have_type("struct RObject") and check_sizeof("struct RObject") check_sizeof("int") have_func('rb_io_ascii8bit_binmode') have_func('rb_update_max_fd') have_func('rb_fd_fix_cloexec') have_func('rb_cloexec_open') have_func('rb_thread_blocking_region') have_func('rb_thread_io_blocking_region') have_func('rb_str_set_len') have_func('rb_time_interval') have_func('rb_wait_for_single_fd') have_func('rb_str_subseq') have_func('rb_ary_subseq') create_makefile('kgio_ext') kgio-2.8.1/ext/kgio/connect.c0000644000004100000410000002557312214142252016040 0ustar www-datawww-data#include "kgio.h" #include "my_fileno.h" #include "sock_for_fd.h" #include "blocking_io_region.h" static void close_fail(int fd, const char *msg) { int saved_errno = errno; (void)close(fd); errno = saved_errno; rb_sys_fail(msg); } static int MY_SOCK_STREAM = #if defined(SOCK_NONBLOCK) && defined(SOCK_CLOEXEC) # ifdef HAVE_RB_FD_FIX_CLOEXEC (SOCK_STREAM|SOCK_NONBLOCK|SOCK_CLOEXEC) # else (SOCK_STREAM|SOCK_NONBLOCK) # endif #else SOCK_STREAM #endif /* ! SOCK_NONBLOCK */ ; /* do not set close-on-exec by default on Ruby <2.0.0 */ #ifndef HAVE_RB_FD_FIX_CLOEXEC # define rb_fd_fix_cloexec(fd) for (;0;) #endif /* HAVE_RB_FD_FIX_CLOEXEC */ /* try to use SOCK_NONBLOCK and SOCK_CLOEXEC */ static int my_socket(int domain) { int fd; retry: fd = socket(domain, MY_SOCK_STREAM, 0); if (fd < 0) { switch (errno) { case EMFILE: case ENFILE: #ifdef ENOBUFS case ENOBUFS: #endif /* ENOBUFS */ errno = 0; rb_gc(); fd = socket(domain, MY_SOCK_STREAM, 0); break; case EINVAL: if (MY_SOCK_STREAM != SOCK_STREAM) { MY_SOCK_STREAM = SOCK_STREAM; goto retry; } } if (fd < 0) rb_sys_fail("socket"); } if (MY_SOCK_STREAM == SOCK_STREAM) { if (fcntl(fd, F_SETFL, O_RDWR | O_NONBLOCK) < 0) close_fail(fd, "fcntl(F_SETFL, O_RDWR | O_NONBLOCK)"); rb_fd_fix_cloexec(fd); } return fd; } static VALUE my_connect(VALUE klass, int io_wait, int domain, void *addr, socklen_t addrlen) { int fd = my_socket(domain); if (connect(fd, addr, addrlen) < 0) { if (errno == EINPROGRESS) { VALUE io = sock_for_fd(klass, fd); if (io_wait) { errno = EAGAIN; (void)kgio_call_wait_writable(io); } return io; } close_fail(fd, "connect"); } return sock_for_fd(klass, fd); } static void tcp_getaddr(struct addrinfo *hints, struct sockaddr_storage *addr, VALUE ip, VALUE port) { int rc; struct addrinfo *res; const char *ipname = StringValuePtr(ip); char ipport[6]; unsigned uport; if (TYPE(port) != T_FIXNUM) rb_raise(rb_eTypeError, "port must be a non-negative integer"); uport = FIX2UINT(port); rc = snprintf(ipport, sizeof(ipport), "%u", uport); if (rc >= (int)sizeof(ipport) || rc <= 0) rb_raise(rb_eArgError, "invalid TCP port: %u", uport); memset(hints, 0, sizeof(struct addrinfo)); hints->ai_family = AF_UNSPEC; hints->ai_socktype = SOCK_STREAM; hints->ai_protocol = IPPROTO_TCP; /* disallow non-deterministic DNS lookups */ hints->ai_flags = AI_NUMERICHOST; rc = getaddrinfo(ipname, ipport, hints, &res); if (rc != 0) rb_raise(rb_eArgError, "getaddrinfo(%s:%s): %s", ipname, ipport, gai_strerror(rc)); /* copy needed data and free ASAP to avoid needing rb_ensure */ hints->ai_family = res->ai_family; hints->ai_addrlen = res->ai_addrlen; memcpy(addr, res->ai_addr, res->ai_addrlen); freeaddrinfo(res); } static VALUE tcp_connect(VALUE klass, VALUE ip, VALUE port, int io_wait) { struct addrinfo hints; struct sockaddr_storage addr; tcp_getaddr(&hints, &addr, ip, port); return my_connect(klass, io_wait, hints.ai_family, &addr, hints.ai_addrlen); } static struct sockaddr *sockaddr_from(socklen_t *addrlen, VALUE addr) { if (TYPE(addr) == T_STRING) { *addrlen = (socklen_t)RSTRING_LEN(addr); return (struct sockaddr *)(RSTRING_PTR(addr)); } rb_raise(rb_eTypeError, "invalid address"); return NULL; } #if defined(MSG_FASTOPEN) && defined(HAVE_RB_THREAD_BLOCKING_REGION) #ifndef HAVE_RB_STR_SUBSEQ #define rb_str_subseq rb_str_substr #endif struct tfo_args { int fd; void *buf; size_t buflen; struct sockaddr *addr; socklen_t addrlen; }; static VALUE tfo_sendto(void *_a) { struct tfo_args *a = _a; ssize_t w; w = sendto(a->fd, a->buf, a->buflen, MSG_FASTOPEN, a->addr, a->addrlen); return (VALUE)w; } /* * call-seq: * * s = Kgio::Socket.new(:INET, :STREAM) * addr = Socket.pack_sockaddr_in(80, "example.com") * s.kgio_fastopen("hello world", addr) -> nil * * Starts a TCP connection using TCP Fast Open. This uses a blocking * sendto() syscall and is only available on Ruby 1.9 or later. * This raises exceptions (including Errno::EINPROGRESS/Errno::EAGAIN) * on errors. Using this is only recommended for blocking sockets. * * Timeouts may be set with setsockopt: * * s.setsockopt(:SOCKET, :SNDTIMEO, [1,0].pack("l_l_")) */ static VALUE fastopen(VALUE sock, VALUE buf, VALUE addr) { struct tfo_args a; VALUE str = (TYPE(buf) == T_STRING) ? buf : rb_obj_as_string(buf); ssize_t w; a.fd = my_fileno(sock); a.buf = RSTRING_PTR(str); a.buflen = (size_t)RSTRING_LEN(str); a.addr = sockaddr_from(&a.addrlen, addr); /* n.b. rb_thread_blocking_region preserves errno */ w = (ssize_t)rb_thread_io_blocking_region(tfo_sendto, &a, a.fd); if (w < 0) rb_sys_fail("sendto"); if ((size_t)w == a.buflen) return Qnil; return rb_str_subseq(str, w, a.buflen - w); } #endif /* MSG_FASTOPEN */ /* * call-seq: * * Kgio::TCPSocket.new('127.0.0.1', 80) -> socket * * Creates a new Kgio::TCPSocket object and initiates a * non-blocking connection. * * This may block and call any method defined to +kgio_wait_writable+ * for the class. * * Unlike the TCPSocket.new in Ruby, this does NOT perform DNS * lookups (which is subject to a different set of timeouts and * best handled elsewhere). */ static VALUE kgio_tcp_connect(VALUE klass, VALUE ip, VALUE port) { return tcp_connect(klass, ip, port, 1); } /* * call-seq: * * Kgio::TCPSocket.start('127.0.0.1', 80) -> socket * * Creates a new Kgio::TCPSocket object and initiates a * non-blocking connection. The caller should select/poll * on the socket for writability before attempting to write * or optimistically attempt a write and handle :wait_writable * or Errno::EAGAIN. * * Unlike the TCPSocket.new in Ruby, this does NOT perform DNS * lookups (which is subject to a different set of timeouts and * best handled elsewhere). */ static VALUE kgio_tcp_start(VALUE klass, VALUE ip, VALUE port) { return tcp_connect(klass, ip, port, 0); } static VALUE unix_connect(VALUE klass, VALUE path, int io_wait) { struct sockaddr_un addr = { 0 }; long len; StringValue(path); len = RSTRING_LEN(path); if ((long)sizeof(addr.sun_path) <= len) rb_raise(rb_eArgError, "too long unix socket path (max: %dbytes)", (int)sizeof(addr.sun_path)-1); memcpy(addr.sun_path, RSTRING_PTR(path), len); addr.sun_family = AF_UNIX; return my_connect(klass, io_wait, PF_UNIX, &addr, sizeof(addr)); } /* * call-seq: * * Kgio::UNIXSocket.new("/path/to/unix/socket") -> socket * * Creates a new Kgio::UNIXSocket object and initiates a * non-blocking connection. * * This may block and call any method defined to +kgio_wait_writable+ * for the class. */ static VALUE kgio_unix_connect(VALUE klass, VALUE path) { return unix_connect(klass, path, 1); } /* * call-seq: * * Kgio::UNIXSocket.start("/path/to/unix/socket") -> socket * * Creates a new Kgio::UNIXSocket object and initiates a * non-blocking connection. The caller should select/poll * on the socket for writability before attempting to write * or optimistically attempt a write and handle :wait_writable * or Errno::EAGAIN. */ static VALUE kgio_unix_start(VALUE klass, VALUE path) { return unix_connect(klass, path, 0); } static VALUE stream_connect(VALUE klass, VALUE addr, int io_wait) { int domain; socklen_t addrlen; struct sockaddr *sockaddr = sockaddr_from(&addrlen, addr); switch (((struct sockaddr_storage *)(sockaddr))->ss_family) { case AF_UNIX: domain = PF_UNIX; break; case AF_INET: domain = PF_INET; break; case AF_INET6: domain = PF_INET6; break; default: rb_raise(rb_eArgError, "invalid address family"); } return my_connect(klass, io_wait, domain, sockaddr, addrlen); } /* call-seq: * * addr = Socket.pack_sockaddr_in(80, 'example.com') * Kgio::Socket.connect(addr) -> socket * * addr = Socket.pack_sockaddr_un("/path/to/unix/socket") * Kgio::Socket.connect(addr) -> socket * * Creates a generic Kgio::Socket object and initiates a * non-blocking connection. * * This may block and call any method defined to +kgio_wait_writable+ * for the class. */ static VALUE kgio_connect(VALUE klass, VALUE addr) { return stream_connect(klass, addr, 1); } /* * If passed one argument, this is identical to Kgio::Socket.connect. * If passed two or three arguments, it uses its superclass method: * * Socket.new(domain, socktype [, protocol ]) */ static VALUE kgio_new(int argc, VALUE *argv, VALUE klass) { if (argc == 1) /* backwards compat, the only way for kgio <= 2.7.4 */ return stream_connect(klass, argv[0], 1); return rb_call_super(argc, argv); } /* call-seq: * * addr = Socket.pack_sockaddr_in(80, 'example.com') * Kgio::Socket.start(addr) -> socket * * addr = Socket.pack_sockaddr_un("/path/to/unix/socket") * Kgio::Socket.start(addr) -> socket * * Creates a generic Kgio::Socket object and initiates a * non-blocking connection. The caller should select/poll * on the socket for writability before attempting to write * or optimistically attempt a write and handle :wait_writable * or Errno::EAGAIN. */ static VALUE kgio_start(VALUE klass, VALUE addr) { return stream_connect(klass, addr, 0); } void init_kgio_connect(void) { VALUE mKgio = rb_define_module("Kgio"); VALUE cSocket = rb_const_get(rb_cObject, rb_intern("Socket")); VALUE mSocketMethods = rb_const_get(mKgio, rb_intern("SocketMethods")); VALUE cKgio_Socket, cTCPSocket, cUNIXSocket; /* * Document-class: Kgio::Socket * * A generic socket class with Kgio::SocketMethods included. * This is returned by all Kgio methods that accept(2) a connected * stream socket. */ cKgio_Socket = rb_define_class_under(mKgio, "Socket", cSocket); rb_include_module(cKgio_Socket, mSocketMethods); rb_define_singleton_method(cKgio_Socket, "new", kgio_new, -1); rb_define_singleton_method(cKgio_Socket, "connect", kgio_connect, 1); rb_define_singleton_method(cKgio_Socket, "start", kgio_start, 1); #if defined(MSG_FASTOPEN) && defined(HAVE_RB_THREAD_BLOCKING_REGION) rb_define_method(cKgio_Socket, "kgio_fastopen", fastopen, 2); #endif /* * Document-class: Kgio::TCPSocket * * Kgio::TCPSocket should be used in place of the plain TCPSocket * when kgio_* methods are needed. */ cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket")); cTCPSocket = rb_define_class_under(mKgio, "TCPSocket", cTCPSocket); rb_include_module(cTCPSocket, mSocketMethods); rb_define_singleton_method(cTCPSocket, "new", kgio_tcp_connect, 2); rb_define_singleton_method(cTCPSocket, "start", kgio_tcp_start, 2); /* * Document-class: Kgio::UNIXSocket * * Kgio::UNIXSocket should be used in place of the plain UNIXSocket * when kgio_* methods are needed. */ cUNIXSocket = rb_const_get(rb_cObject, rb_intern("UNIXSocket")); cUNIXSocket = rb_define_class_under(mKgio, "UNIXSocket", cUNIXSocket); rb_include_module(cUNIXSocket, mSocketMethods); rb_define_singleton_method(cUNIXSocket, "new", kgio_unix_connect, 1); rb_define_singleton_method(cUNIXSocket, "start", kgio_unix_start, 1); init_sock_for_fd(); } kgio-2.8.1/ext/kgio/accept.c0000644000004100000410000003471512214142252015644 0ustar www-datawww-data#include "kgio.h" #include "missing_accept4.h" #include "sock_for_fd.h" #include "my_fileno.h" #include "nonblock.h" static VALUE localhost; static VALUE cClientSocket; static VALUE cKgio_Socket; static VALUE mSocketMethods; static VALUE iv_kgio_addr; #if defined(__linux__) && defined(HAVE_RB_THREAD_BLOCKING_REGION) static int accept4_flags = SOCK_CLOEXEC; #else /* ! linux */ static int accept4_flags = SOCK_CLOEXEC | SOCK_NONBLOCK; #endif /* ! linux */ struct accept_args { int fd; int flags; struct sockaddr *addr; socklen_t *addrlen; VALUE accept_io; VALUE accepted_class; }; /* * Sets the default class for newly accepted sockets. This is * legacy behavior, kgio_accept and kgio_tryaccept now take optional * class arguments to override this value. */ static VALUE set_accepted(VALUE klass, VALUE aclass) { VALUE tmp; if (NIL_P(aclass)) aclass = cKgio_Socket; tmp = rb_funcall(aclass, rb_intern("included_modules"), 0, 0); tmp = rb_funcall(tmp, rb_intern("include?"), 1, mSocketMethods); if (tmp != Qtrue) rb_raise(rb_eTypeError, "class must include Kgio::SocketMethods"); cClientSocket = aclass; return aclass; } /* * Returns the default class for newly accepted sockets when kgio_accept * or kgio_tryaccept are not passed arguments */ static VALUE get_accepted(VALUE klass) { return cClientSocket; } /* * accept() wrapper that'll fall back on accept() if we were built on * a system with accept4() but run on a system without accept4() */ static VALUE xaccept(void *ptr) { struct accept_args *a = ptr; int rv; rv = accept_fn(a->fd, a->addr, a->addrlen, a->flags); if (rv < 0 && errno == ENOSYS && accept_fn != my_accept4) { accept_fn = my_accept4; rv = accept_fn(a->fd, a->addr, a->addrlen, a->flags); } return (VALUE)rv; } #ifdef HAVE_RB_THREAD_BLOCKING_REGION # include # include "blocking_io_region.h" static int thread_accept(struct accept_args *a, int force_nonblock) { if (force_nonblock) set_nonblocking(a->fd); return (int)rb_thread_io_blocking_region(xaccept, a, a->fd); } #else /* ! HAVE_RB_THREAD_BLOCKING_REGION */ # include static int thread_accept(struct accept_args *a, int force_nonblock) { int rv; /* always use non-blocking accept() under 1.8 for green threads */ set_nonblocking(a->fd); /* created sockets are always non-blocking under 1.8, too */ a->flags |= SOCK_NONBLOCK; TRAP_BEG; rv = (int)xaccept(a); TRAP_END; return rv; } #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */ static void prepare_accept(struct accept_args *a, VALUE self, int argc, const VALUE *argv) { a->fd = my_fileno(self); a->accept_io = self; switch (argc) { case 2: a->flags = NUM2INT(argv[1]); a->accepted_class = NIL_P(argv[0]) ? cClientSocket : argv[0]; return; case 0: /* default, legacy behavior */ a->flags = accept4_flags; a->accepted_class = cClientSocket; return; case 1: a->flags = accept4_flags; a->accepted_class = NIL_P(argv[0]) ? cClientSocket : argv[0]; return; } rb_raise(rb_eArgError, "wrong number of arguments (%d for 1)", argc); } static VALUE in_addr_set(VALUE io, struct sockaddr_storage *addr, socklen_t len) { VALUE host; int host_len, rc; char *host_ptr; switch (addr->ss_family) { case AF_INET: host_len = (long)INET_ADDRSTRLEN; break; case AF_INET6: host_len = (long)INET6_ADDRSTRLEN; break; default: rb_raise(rb_eRuntimeError, "unsupported address family: ss_family=%lu (socklen=%ld)", (unsigned long)addr->ss_family, (long)len); } host = rb_str_new(NULL, host_len); host_ptr = RSTRING_PTR(host); rc = getnameinfo((struct sockaddr *)addr, len, host_ptr, host_len, NULL, 0, NI_NUMERICHOST); if (rc != 0) rb_raise(rb_eRuntimeError, "getnameinfo: %s", gai_strerror(rc)); rb_str_set_len(host, strlen(host_ptr)); return rb_ivar_set(io, iv_kgio_addr, host); } #if defined(__linux__) # define post_accept kgio_autopush_accept #else # define post_accept(a,b) for(;0;) #endif static VALUE my_accept(struct accept_args *a, int force_nonblock) { int client_fd; VALUE client_io; int retried = 0; retry: client_fd = thread_accept(a, force_nonblock); if (client_fd < 0) { switch (errno) { case EAGAIN: if (force_nonblock) return Qnil; a->fd = my_fileno(a->accept_io); errno = EAGAIN; (void)rb_io_wait_readable(a->fd); /* fall-through to EINTR case */ #ifdef ECONNABORTED case ECONNABORTED: #endif /* ECONNABORTED */ #ifdef EPROTO case EPROTO: #endif /* EPROTO */ case EINTR: /* raise IOError if closed during sleep */ a->fd = my_fileno(a->accept_io); goto retry; case ENOMEM: case EMFILE: case ENFILE: #ifdef ENOBUFS case ENOBUFS: #endif /* ENOBUFS */ if (!retried) { retried = 1; errno = 0; rb_gc(); goto retry; } default: rb_sys_fail("accept"); } } client_io = sock_for_fd(a->accepted_class, client_fd); post_accept(a->accept_io, client_io); if (a->addr) in_addr_set(client_io, (struct sockaddr_storage *)a->addr, *a->addrlen); else rb_ivar_set(client_io, iv_kgio_addr, localhost); return client_io; } /* * call-seq: * * io.kgio_addr! => refreshes the given sock address */ static VALUE addr_bang(VALUE io) { int fd = my_fileno(io); struct sockaddr_storage addr; socklen_t len = sizeof(struct sockaddr_storage); if (getpeername(fd, (struct sockaddr *)&addr, &len) != 0) rb_sys_fail("getpeername"); if (addr.ss_family == AF_UNIX) return rb_ivar_set(io, iv_kgio_addr, localhost); return in_addr_set(io, &addr, len); } /* * call-seq: * * server = Kgio::TCPServer.new('0.0.0.0', 80) * server.kgio_tryaccept -> Kgio::Socket or nil * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil * server.kgio_tryaccept(nil, flags) -> Kgio::Socket or nil * * Initiates a non-blocking accept and returns a generic Kgio::Socket * object with the kgio_addr attribute set to the IP address of the * connected client on success. * * Returns nil on EAGAIN, and raises on other errors. * * An optional +klass+ argument may be specified to override the * Kgio::Socket-class on a successful return value. * * An optional +flags+ argument may also be specified. * +flags+ is a bitmask that may contain any combination of: * * - Kgio::SOCK_CLOEXEC - close-on-exec flag (enabled by default) * - Kgio::SOCK_NONBLOCK - non-blocking flag (unimportant) */ static VALUE tcp_tryaccept(int argc, VALUE *argv, VALUE self) { struct sockaddr_storage addr; socklen_t addrlen = sizeof(struct sockaddr_storage); struct accept_args a; a.addr = (struct sockaddr *)&addr; a.addrlen = &addrlen; prepare_accept(&a, self, argc, argv); return my_accept(&a, 1); } /* * call-seq: * * server = Kgio::TCPServer.new('0.0.0.0', 80) * server.kgio_accept -> Kgio::Socket or nil * server.kgio_tryaccept -> Kgio::Socket or nil * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil * * Initiates a blocking accept and returns a generic Kgio::Socket * object with the kgio_addr attribute set to the IP address of * the client on success. * * On Ruby implementations using native threads, this can use a blocking * accept(2) (or accept4(2)) system call to avoid thundering herds. * * An optional +klass+ argument may be specified to override the * Kgio::Socket-class on a successful return value. * * An optional +flags+ argument may also be specified. * +flags+ is a bitmask that may contain any combination of: * * - Kgio::SOCK_CLOEXEC - close-on-exec flag (enabled by default) * - Kgio::SOCK_NONBLOCK - non-blocking flag (unimportant) */ static VALUE tcp_accept(int argc, VALUE *argv, VALUE self) { struct sockaddr_storage addr; socklen_t addrlen = sizeof(struct sockaddr_storage); struct accept_args a; a.addr = (struct sockaddr *)&addr; a.addrlen = &addrlen; prepare_accept(&a, self, argc, argv); return my_accept(&a, 0); } /* * call-seq: * * server = Kgio::UNIXServer.new("/path/to/unix/socket") * server.kgio_tryaccept -> Kgio::Socket or nil * server.kgio_tryaccept(klass = MySocket) -> MySocket or nil * server.kgio_tryaccept(nil, flags) -> Kgio::Socket or nil * * Initiates a non-blocking accept and returns a generic Kgio::Socket * object with the kgio_addr attribute set (to the value of * Kgio::LOCALHOST) on success. * * An optional +klass+ argument may be specified to override the * Kgio::Socket-class on a successful return value. * * An optional +flags+ argument may also be specified. * +flags+ is a bitmask that may contain any combination of: * * - Kgio::SOCK_CLOEXEC - close-on-exec flag (enabled by default) * - Kgio::SOCK_NONBLOCK - non-blocking flag (unimportant) */ static VALUE unix_tryaccept(int argc, VALUE *argv, VALUE self) { struct accept_args a; a.addr = NULL; a.addrlen = NULL; prepare_accept(&a, self, argc, argv); return my_accept(&a, 1); } /* * call-seq: * * server = Kgio::UNIXServer.new("/path/to/unix/socket") * server.kgio_accept -> Kgio::Socket or nil * server.kgio_accept(klass = MySocket) -> MySocket or nil * server.kgio_accept(nil, flags) -> Kgio::Socket or nil * * Initiates a blocking accept and returns a generic Kgio::Socket * object with the kgio_addr attribute set (to the value of * Kgio::LOCALHOST) on success. * * On Ruby implementations using native threads, this can use a blocking * accept(2) (or accept4(2)) system call to avoid thundering herds. * * An optional +klass+ argument may be specified to override the * Kgio::Socket-class on a successful return value. * * An optional +flags+ argument may also be specified. * +flags+ is a bitmask that may contain any combination of: * * - Kgio::SOCK_CLOEXEC - close-on-exec flag (enabled by default) * - Kgio::SOCK_NONBLOCK - non-blocking flag (unimportant) */ static VALUE unix_accept(int argc, VALUE *argv, VALUE self) { struct accept_args a; a.addr = NULL; a.addrlen = NULL; prepare_accept(&a, self, argc, argv); return my_accept(&a, 0); } /* * call-seq: * * Kgio.accept_cloexec? -> true or false * * Returns true if newly accepted Kgio::Sockets are created with the * FD_CLOEXEC file descriptor flag, false if not. * * Deprecated, use the per-socket flags for kgio_*accept instead. */ static VALUE get_cloexec(VALUE mod) { return (accept4_flags & SOCK_CLOEXEC) == SOCK_CLOEXEC ? Qtrue : Qfalse; } /* * * call-seq: * * Kgio.accept_nonblock? -> true or false * * Returns true if newly accepted Kgio::Sockets are created with the * O_NONBLOCK file status flag, false if not. * * Deprecated, use the per-socket flags for kgio_*accept instead. */ static VALUE get_nonblock(VALUE mod) { return (accept4_flags & SOCK_NONBLOCK)==SOCK_NONBLOCK ? Qtrue : Qfalse; } /* * call-seq: * * Kgio.accept_cloexec = true * Kgio.accept_cloexec = false * * Sets whether or not Kgio::Socket objects created by * TCPServer#kgio_accept, * TCPServer#kgio_tryaccept, * UNIXServer#kgio_accept, * and UNIXServer#kgio_tryaccept * default to being created with the FD_CLOEXEC file descriptor flag. * * This is on by default, as there is little reason to deal to enable * it for client sockets on a socket server. * * Deprecated, use the per-socket flags for kgio_*accept instead. */ static VALUE set_cloexec(VALUE mod, VALUE boolean) { switch (TYPE(boolean)) { case T_TRUE: accept4_flags |= SOCK_CLOEXEC; return boolean; case T_FALSE: accept4_flags &= ~SOCK_CLOEXEC; return boolean; } rb_raise(rb_eTypeError, "not true or false"); return Qnil; } /* * call-seq: * * Kgio.accept_nonblock = true * Kgio.accept_nonblock = false * * Sets whether or not Kgio::Socket objects created by * TCPServer#kgio_accept, * TCPServer#kgio_tryaccept, * UNIXServer#kgio_accept, * and UNIXServer#kgio_tryaccept * are created with the O_NONBLOCK file status flag. * * This defaults to +false+ for GNU/Linux where MSG_DONTWAIT is * available (and on newer GNU/Linux, accept4() may also set * the non-blocking flag. This defaults to +true+ on non-GNU/Linux * systems. * * This is always true on Ruby implementations using user-space threads. * * Deprecated, use the per-socket flags for kgio_*accept instead. */ static VALUE set_nonblock(VALUE mod, VALUE boolean) { switch (TYPE(boolean)) { case T_TRUE: accept4_flags |= SOCK_NONBLOCK; return boolean; case T_FALSE: accept4_flags &= ~SOCK_NONBLOCK; return boolean; } rb_raise(rb_eTypeError, "not true or false"); return Qnil; } void init_kgio_accept(void) { VALUE cUNIXServer, cTCPServer; VALUE mKgio = rb_define_module("Kgio"); /* * Maps to the SOCK_NONBLOCK constant in Linux for setting * the non-blocking flag on newly accepted sockets. This is * usually unnecessary as sockets are made non-blocking * whenever non-blocking methods are used. */ rb_define_const(mKgio, "SOCK_NONBLOCK", INT2NUM(SOCK_NONBLOCK)); /* * Maps to the SOCK_CLOEXEC constant in Linux for setting * the close-on-exec flag on newly accepted descriptors. This * is enabled by default, and there is usually no reason to * disable close-on-exec for accepted sockets. */ rb_define_const(mKgio, "SOCK_CLOEXEC", INT2NUM(SOCK_CLOEXEC)); localhost = rb_const_get(mKgio, rb_intern("LOCALHOST")); cKgio_Socket = rb_const_get(mKgio, rb_intern("Socket")); cClientSocket = cKgio_Socket; mSocketMethods = rb_const_get(mKgio, rb_intern("SocketMethods")); rb_define_method(mSocketMethods, "kgio_addr!", addr_bang, 0); rb_define_singleton_method(mKgio, "accept_cloexec?", get_cloexec, 0); rb_define_singleton_method(mKgio, "accept_cloexec=", set_cloexec, 1); rb_define_singleton_method(mKgio, "accept_nonblock?", get_nonblock, 0); rb_define_singleton_method(mKgio, "accept_nonblock=", set_nonblock, 1); rb_define_singleton_method(mKgio, "accept_class=", set_accepted, 1); rb_define_singleton_method(mKgio, "accept_class", get_accepted, 0); /* * Document-class: Kgio::UNIXServer * * Kgio::UNIXServer should be used in place of the plain UNIXServer * when kgio_accept and kgio_tryaccept methods are needed. */ cUNIXServer = rb_const_get(rb_cObject, rb_intern("UNIXServer")); cUNIXServer = rb_define_class_under(mKgio, "UNIXServer", cUNIXServer); rb_define_method(cUNIXServer, "kgio_tryaccept", unix_tryaccept, -1); rb_define_method(cUNIXServer, "kgio_accept", unix_accept, -1); /* * Document-class: Kgio::TCPServer * * Kgio::TCPServer should be used in place of the plain TCPServer * when kgio_accept and kgio_tryaccept methods are needed. */ cTCPServer = rb_const_get(rb_cObject, rb_intern("TCPServer")); cTCPServer = rb_define_class_under(mKgio, "TCPServer", cTCPServer); rb_define_method(cTCPServer, "kgio_tryaccept", tcp_tryaccept, -1); rb_define_method(cTCPServer, "kgio_accept", tcp_accept, -1); init_sock_for_fd(); iv_kgio_addr = rb_intern("@kgio_addr"); } kgio-2.8.1/ext/kgio/nonblock.h0000644000004100000410000000065712214142252016215 0ustar www-datawww-data#include #include #include static void set_nonblocking(int fd) { int flags = fcntl(fd, F_GETFL); /* * do not check < 0 here, one day we may have enough FD flags * to require negative bit */ if (flags == -1) rb_sys_fail("fcntl(F_GETFL)"); if ((flags & O_NONBLOCK) == O_NONBLOCK) return; flags = fcntl(fd, F_SETFL, flags | O_NONBLOCK); if (flags < 0) rb_sys_fail("fcntl(F_SETFL)"); } kgio-2.8.1/ext/kgio/kgio.h0000644000004100000410000000332312214142252015332 0ustar www-datawww-data#ifndef KGIO_H #define KGIO_H #include #ifdef HAVE_RUBY_IO_H # include #else # include #endif #include #include #include #include #include #include #include #include #include #include #include "ancient_ruby.h" struct io_args { VALUE io; VALUE buf; char *ptr; long len; int fd; }; void init_kgio_wait(void); void init_kgio_read_write(void); void init_kgio_accept(void); void init_kgio_connect(void); void init_kgio_autopush(void); void init_kgio_poll(void); void init_kgio_tryopen(void); void kgio_autopush_accept(VALUE, VALUE); void kgio_autopush_recv(VALUE); void kgio_autopush_send(VALUE); VALUE kgio_call_wait_writable(VALUE io); VALUE kgio_call_wait_readable(VALUE io); #if defined(HAVE_RB_THREAD_BLOCKING_REGION) && defined(HAVE_POLL) # define USE_KGIO_POLL #endif /* USE_KGIO_POLL */ #ifndef HAVE_RB_UPDATE_MAX_FD # define rb_update_max_fd(fd) for (;0;) #endif /* * 2012/12/13 - Linux 3.7 was released on 2012/12/10 with TFO. * Headers distributed with glibc will take some time to catch up and * be officially released. Most GNU/Linux distros will take a few months * to a year longer. "Enterprise" distros will probably take 5-7 years. * So keep these until 2017 at least... */ #ifdef __linux__ # ifndef MSG_FASTOPEN # define MSG_FASTOPEN 0x20000000 /* for clients */ # endif # ifndef TCP_FASTOPEN # define TCP_FASTOPEN 23 /* for listeners */ # endif /* we _may_ have TFO support */ # define KGIO_TFO_MAYBE (1) #else /* rely entirely on standard system headers */ # define KGIO_TFO_MAYBE (0) #endif extern unsigned kgio_tfo; #endif /* KGIO_H */ kgio-2.8.1/ext/kgio/wait.c0000644000004100000410000000714112214142252015342 0ustar www-datawww-data#include "kgio.h" #include "my_fileno.h" static ID id_wait_rd, id_wait_wr; #if defined(HAVE_RB_TIME_INTERVAL) && defined(HAVE_RB_WAIT_FOR_SINGLE_FD) static int kgio_timedwait(VALUE self, VALUE timeout, int write_p) { struct timeval tv = rb_time_interval(timeout); int events = write_p ? RB_WAITFD_OUT : RB_WAITFD_IN; return rb_wait_for_single_fd(my_fileno(self), events, &tv); } #else /* ! (HAVE_RB_TIME_INTERVAL && HAVE_RB_WAIT_FOR_SINGLE_FD) */ static int kgio_timedwait(VALUE self, VALUE timeout, int write_p) { VALUE argv[4]; VALUE set = rb_ary_new3(1, self); argv[0] = write_p ? Qnil : set; argv[1] = write_p ? set : Qnil; argv[2] = Qnil; argv[3] = timeout; set = rb_funcall2(rb_cIO, rb_intern("select"), 4, argv); return NIL_P(set) ? 0 : 1; } #endif /* ! (HAVE_RB_TIME_INTERVAL && HAVE_RB_WAIT_FOR_SINGLE_FD) */ static int kgio_wait(int argc, VALUE *argv, VALUE self, int write_p) { int fd; VALUE timeout; if (rb_scan_args(argc, argv, "01", &timeout) == 1 && !NIL_P(timeout)) return kgio_timedwait(self, timeout, write_p); fd = my_fileno(self); errno = EAGAIN; write_p ? rb_io_wait_writable(fd) : rb_io_wait_readable(fd); return 1; } /* * call-seq: * * io.kgio_wait_readable -> IO * io.kgio_wait_readable(timeout) -> IO or nil * * Blocks the running Thread indefinitely until the IO object is readable * or if +timeout+ expires. If +timeout+ is specified and expires, +nil+ * is returned. * * This method is automatically called (without timeout argument) by default * whenever kgio_read needs to block on input. * * Users of alternative threading/fiber libraries are * encouraged to override this method in their subclasses or modules to * work with their threading/blocking methods. */ static VALUE kgio_wait_readable(int argc, VALUE *argv, VALUE self) { int r = kgio_wait(argc, argv, self, 0); if (r < 0) rb_sys_fail("kgio_wait_readable"); return r == 0 ? Qnil : self; } /* * call-seq: * * io.kgio_wait_writable -> IO * io.kgio_wait_writable(timeout) -> IO or nil * * Blocks the running Thread indefinitely until the IO object is writable * or if +timeout+ expires. If +timeout+ is specified and expires, +nil+ * is returned. * * This method is automatically called (without timeout argument) by default * whenever kgio_write needs to block on output. * * Users of alternative threading/fiber libraries are * encouraged to override this method in their subclasses or modules to * work with their threading/blocking methods. */ static VALUE kgio_wait_writable(int argc, VALUE *argv, VALUE self) { int r = kgio_wait(argc, argv, self, 1); if (r < 0) rb_sys_fail("kgio_wait_writable"); return r == 0 ? Qnil : self; } VALUE kgio_call_wait_writable(VALUE io) { return rb_funcall(io, id_wait_wr, 0, 0); } VALUE kgio_call_wait_readable(VALUE io) { return rb_funcall(io, id_wait_rd, 0, 0); } void init_kgio_wait(void) { VALUE mKgio = rb_define_module("Kgio"); /* * Document-module: Kgio::DefaultWaiters * * This module contains default kgio_wait_readable and * kgio_wait_writable methods that block indefinitely (in a * thread-safe manner) until an IO object is read or writable. * This module is included in the Kgio::PipeMethods and * Kgio::SocketMethods modules used by all bundled IO-derived * objects. */ VALUE mWaiters = rb_define_module_under(mKgio, "DefaultWaiters"); id_wait_rd = rb_intern("kgio_wait_readable"); id_wait_wr = rb_intern("kgio_wait_writable"); rb_define_method(mWaiters, "kgio_wait_readable", kgio_wait_readable, -1); rb_define_method(mWaiters, "kgio_wait_writable", kgio_wait_writable, -1); } kgio-2.8.1/ext/kgio/kgio_ext.c0000644000004100000410000000210612214142252016203 0ustar www-datawww-data#include "kgio.h" #include #include /* true if TCP Fast Open is usable */ unsigned kgio_tfo; static void tfo_maybe(void) { VALUE mKgio = rb_define_module("Kgio"); /* Deal with the case where system headers have not caught up */ if (KGIO_TFO_MAYBE) { /* Ensure Linux 3.7 or later for TCP_FASTOPEN */ struct utsname buf; unsigned maj, min; if (uname(&buf) != 0) rb_sys_fail("uname"); if (sscanf(buf.release, "%u.%u", &maj, &min) != 2) return; if (maj < 3 || (maj == 3 && min < 7)) return; } /* * KGIO_TFO_MAYBE will be false if a distro backports TFO * to a pre-3.7 kernel, but includes the necessary constants * in system headers */ #if defined(MSG_FASTOPEN) && defined(TCP_FASTOPEN) rb_define_const(mKgio, "TCP_FASTOPEN", INT2NUM(TCP_FASTOPEN)); rb_define_const(mKgio, "MSG_FASTOPEN", INT2NUM(MSG_FASTOPEN)); kgio_tfo = 1; #endif } void Init_kgio_ext(void) { tfo_maybe(); init_kgio_wait(); init_kgio_read_write(); init_kgio_connect(); init_kgio_accept(); init_kgio_autopush(); init_kgio_poll(); init_kgio_tryopen(); } kgio-2.8.1/ext/kgio/sock_for_fd.h0000644000004100000410000000332412214142252016660 0ustar www-datawww-data#ifndef SOCK_FOR_FD_H #define SOCK_FOR_FD_H #include #ifdef HAVE_RUBY_IO_H # include #else # include # include #endif #if defined(MakeOpenFile) && \ defined(HAVE_RB_IO_T) && (HAVE_RB_IO_T == 1) && \ defined(HAVE_RB_IO_ASCII8BIT_BINMODE) && \ defined(HAVE_ST_FD) && \ defined(HAVE_ST_MODE) # define SOCK_FOR_FD (19) # define FMODE_NOREVLOOKUP 0x100 #elif defined(MakeOpenFile) && \ (defined(OpenFile) || defined(HAVE_RB_IO_T)) && \ defined(HAVE_RB_FDOPEN) && \ defined(HAVE_ST_F) && \ defined(HAVE_ST_F2) && \ defined(HAVE_ST_MODE) # define SOCK_FOR_FD (18) #else # define SOCK_FOR_FD (-1) #endif #if SOCK_FOR_FD == 19 /* modeled after ext/socket/init.c */ static VALUE sock_for_fd(VALUE klass, int fd) { VALUE sock; rb_io_t *fp; rb_update_max_fd(fd); /* 1.9.3+ API */ sock = rb_obj_alloc(klass); MakeOpenFile(sock, fp); fp->fd = fd; fp->mode = FMODE_READWRITE|FMODE_DUPLEX|FMODE_NOREVLOOKUP; rb_io_ascii8bit_binmode(sock); rb_io_synchronized(fp); return sock; } #elif SOCK_FOR_FD == 18 /* modeled after init_sock() in ext/socket/socket.c */ static VALUE sock_for_fd(VALUE klass, int fd) { VALUE sock = rb_obj_alloc(klass); OpenFile *fp; MakeOpenFile(sock, fp); fp->f = rb_fdopen(fd, "r"); fp->f2 = rb_fdopen(fd, "w"); fp->mode = FMODE_READWRITE; rb_io_synchronized(fp); return sock; } #else /* Rubinius, et al. */ static ID id_for_fd; static VALUE sock_for_fd(VALUE klass, int fd) { return rb_funcall(klass, id_for_fd, 1, INT2NUM(fd)); } static void init_sock_for_fd(void) { id_for_fd = rb_intern("for_fd"); } #endif /* sock_for_fd */ #if SOCK_FOR_FD > 0 # define init_sock_for_fd() for (;0;) #endif #endif /* SOCK_FOR_FD_H */ kgio-2.8.1/ext/kgio/ancient_ruby.h0000644000004100000410000000131712214142252017064 0ustar www-datawww-data#ifndef MISSING_ANCIENT_RUBY_H #define MISSING_ANCIENT_RUBY_H #ifndef HAVE_RB_STR_SET_LEN static void my_str_set_len(VALUE str, long len) { RSTRING(str)->len = len; RSTRING(str)->ptr[len] = '\0'; } #define rb_str_set_len(str,len) my_str_set_len((str),(len)) #endif /* ! HAVE_RB_STR_SET_LEN */ #ifndef RSTRING_PTR # define RSTRING_PTR(s) (RSTRING(s)->ptr) #endif /* !defined(RSTRING_PTR) */ #ifndef RSTRING_LEN # define RSTRING_LEN(s) (RSTRING(s)->len) #endif /* !defined(RSTRING_LEN) */ #ifndef RARRAY_PTR # define RARRAY_PTR(s) (RARRAY(s)->ptr) #endif /* !defined(RARRAY_PTR) */ #ifndef RARRAY_LEN # define RARRAY_LEN(s) (RARRAY(s)->len) #endif /* !defined(RARRAY_LEN) */ #endif /* MISSING_ANCIENT_RUBY_H */ kgio-2.8.1/ext/kgio/read_write.c0000644000004100000410000005261012214142252016524 0ustar www-datawww-data#include "kgio.h" #include "my_fileno.h" #include "nonblock.h" #ifdef HAVE_WRITEV # include # define USE_WRITEV 1 #else # define USE_WRITEV 0 static ssize_t assert_writev(int fd, void* iov, int len) { assert(0 && "you should not try to call writev"); return -1; } # define writev assert_writev #endif static VALUE sym_wait_readable, sym_wait_writable; static VALUE eErrno_EPIPE, eErrno_ECONNRESET; static ID id_set_backtrace; #ifndef HAVE_RB_STR_SUBSEQ #define rb_str_subseq rb_str_substr #endif #ifndef HAVE_RB_ARY_SUBSEQ static inline VALUE my_ary_subseq(VALUE ary, long idx, long len) { VALUE args[2] = {LONG2FIX(idx), LONG2FIX(len)}; return rb_ary_aref(2, args, ary); } #define rb_ary_subseq my_ary_subseq #endif /* * we know MSG_DONTWAIT works properly on all stream sockets under Linux * we can define this macro for other platforms as people care and * notice. */ #if defined(__linux__) && ! defined(USE_MSG_DONTWAIT) # define USE_MSG_DONTWAIT static const int peek_flags = MSG_DONTWAIT|MSG_PEEK; /* we don't need these variants, we call kgio_autopush_send/recv directly */ static inline void kgio_autopush_read(VALUE io) { } static inline void kgio_autopush_write(VALUE io) { } #else static const int peek_flags = MSG_PEEK; static inline void kgio_autopush_read(VALUE io) { kgio_autopush_recv(io); } static inline void kgio_autopush_write(VALUE io) { kgio_autopush_send(io); } #endif NORETURN(static void raise_empty_bt(VALUE, const char *)); NORETURN(static void my_eof_error(void)); NORETURN(static void wr_sys_fail(const char *)); NORETURN(static void rd_sys_fail(const char *)); static void raise_empty_bt(VALUE err, const char *msg) { VALUE exc = rb_exc_new2(err, msg); VALUE bt = rb_ary_new(); rb_funcall(exc, id_set_backtrace, 1, bt); rb_exc_raise(exc); } static void my_eof_error(void) { raise_empty_bt(rb_eEOFError, "end of file reached"); } static void wr_sys_fail(const char *msg) { switch (errno) { case EPIPE: errno = 0; raise_empty_bt(eErrno_EPIPE, msg); case ECONNRESET: errno = 0; raise_empty_bt(eErrno_ECONNRESET, msg); } rb_sys_fail(msg); } static void rd_sys_fail(const char *msg) { if (errno == ECONNRESET) { errno = 0; raise_empty_bt(eErrno_ECONNRESET, msg); } rb_sys_fail(msg); } static void prepare_read(struct io_args *a, int argc, VALUE *argv, VALUE io) { VALUE length; a->io = io; a->fd = my_fileno(io); rb_scan_args(argc, argv, "11", &length, &a->buf); a->len = NUM2LONG(length); if (NIL_P(a->buf)) { a->buf = rb_str_new(NULL, a->len); } else { StringValue(a->buf); rb_str_modify(a->buf); rb_str_resize(a->buf, a->len); } a->ptr = RSTRING_PTR(a->buf); } static int read_check(struct io_args *a, long n, const char *msg, int io_wait) { if (n < 0) { if (errno == EINTR) { a->fd = my_fileno(a->io); return -1; } rb_str_set_len(a->buf, 0); if (errno == EAGAIN) { if (io_wait) { (void)kgio_call_wait_readable(a->io); /* buf may be modified in other thread/fiber */ rb_str_modify(a->buf); rb_str_resize(a->buf, a->len); a->ptr = RSTRING_PTR(a->buf); return -1; } else { a->buf = sym_wait_readable; return 0; } } rd_sys_fail(msg); } rb_str_set_len(a->buf, n); if (n == 0) a->buf = Qnil; return 0; } static VALUE my_read(int io_wait, int argc, VALUE *argv, VALUE io) { struct io_args a; long n; prepare_read(&a, argc, argv, io); kgio_autopush_read(io); if (a.len > 0) { set_nonblocking(a.fd); retry: n = (long)read(a.fd, a.ptr, a.len); if (read_check(&a, n, "read", io_wait) != 0) goto retry; } return a.buf; } /* * call-seq: * * io.kgio_read(maxlen) -> buffer * io.kgio_read(maxlen, buffer) -> buffer * * Reads at most maxlen bytes from the stream socket. Returns with a * newly allocated buffer, or may reuse an existing buffer if supplied. * * This may block and call any method defined to +kgio_wait_readable+ * for the class. * * Returns nil on EOF. * * This behaves like read(2) and IO#readpartial, NOT fread(3) or * IO#read which possess read-in-full behavior. */ static VALUE kgio_read(int argc, VALUE *argv, VALUE io) { return my_read(1, argc, argv, io); } /* * Same as Kgio::PipeMethods#kgio_read, except EOFError is raised * on EOF without a backtrace. This method is intended as a * drop-in replacement for places where IO#readpartial is used, and * may be aliased as such. */ static VALUE kgio_read_bang(int argc, VALUE *argv, VALUE io) { VALUE rv = my_read(1, argc, argv, io); if (NIL_P(rv)) my_eof_error(); return rv; } /* * call-seq: * * io.kgio_tryread(maxlen) -> buffer * io.kgio_tryread(maxlen, buffer) -> buffer * * Reads at most maxlen bytes from the stream socket. Returns with a * newly allocated buffer, or may reuse an existing buffer if supplied. * * Returns nil on EOF. * * Returns :wait_readable if EAGAIN is encountered. */ static VALUE kgio_tryread(int argc, VALUE *argv, VALUE io) { return my_read(0, argc, argv, io); } #ifdef USE_MSG_DONTWAIT static VALUE my_recv(int io_wait, int argc, VALUE *argv, VALUE io) { struct io_args a; long n; prepare_read(&a, argc, argv, io); kgio_autopush_recv(io); if (a.len > 0) { retry: n = (long)recv(a.fd, a.ptr, a.len, MSG_DONTWAIT); if (read_check(&a, n, "recv", io_wait) != 0) goto retry; } return a.buf; } /* * This method may be optimized on some systems (e.g. GNU/Linux) to use * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl. * Otherwise this is the same as Kgio::PipeMethods#kgio_read */ static VALUE kgio_recv(int argc, VALUE *argv, VALUE io) { return my_recv(1, argc, argv, io); } /* * Same as Kgio::SocketMethods#kgio_read, except EOFError is raised * on EOF without a backtrace */ static VALUE kgio_recv_bang(int argc, VALUE *argv, VALUE io) { VALUE rv = my_recv(1, argc, argv, io); if (NIL_P(rv)) my_eof_error(); return rv; } /* * This method may be optimized on some systems (e.g. GNU/Linux) to use * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl. * Otherwise this is the same as Kgio::PipeMethods#kgio_tryread */ static VALUE kgio_tryrecv(int argc, VALUE *argv, VALUE io) { return my_recv(0, argc, argv, io); } #else /* ! USE_MSG_DONTWAIT */ # define kgio_recv kgio_read # define kgio_recv_bang kgio_read_bang # define kgio_tryrecv kgio_tryread #endif /* USE_MSG_DONTWAIT */ static VALUE my_peek(int io_wait, int argc, VALUE *argv, VALUE io) { struct io_args a; long n; prepare_read(&a, argc, argv, io); kgio_autopush_recv(io); if (a.len > 0) { if (peek_flags == MSG_PEEK) set_nonblocking(a.fd); retry: n = (long)recv(a.fd, a.ptr, a.len, peek_flags); if (read_check(&a, n, "recv(MSG_PEEK)", io_wait) != 0) goto retry; } return a.buf; } /* * call-seq: * * socket.kgio_trypeek(maxlen) -> buffer * socket.kgio_trypeek(maxlen, buffer) -> buffer * * Like kgio_tryread, except it uses MSG_PEEK so it does not drain the * socket buffer. A subsequent read of any type (including another peek) * will return the same data. */ static VALUE kgio_trypeek(int argc, VALUE *argv, VALUE io) { return my_peek(0, argc, argv, io); } /* * call-seq: * * socket.kgio_peek(maxlen) -> buffer * socket.kgio_peek(maxlen, buffer) -> buffer * * Like kgio_read, except it uses MSG_PEEK so it does not drain the * socket buffer. A subsequent read of any type (including another peek) * will return the same data. */ static VALUE kgio_peek(int argc, VALUE *argv, VALUE io) { return my_peek(1, argc, argv, io); } /* * call-seq: * * Kgio.trypeek(socket, maxlen) -> buffer * Kgio.trypeek(socket, maxlen, buffer) -> buffer * * Like Kgio.tryread, except it uses MSG_PEEK so it does not drain the * socket buffer. This can only be used on sockets and not pipe objects. * Maybe used in place of SocketMethods#kgio_trypeek for non-Kgio objects */ static VALUE s_trypeek(int argc, VALUE *argv, VALUE mod) { if (argc <= 1) rb_raise(rb_eArgError, "wrong number of arguments"); return my_peek(0, argc - 1, &argv[1], argv[0]); } static void prepare_write(struct io_args *a, VALUE io, VALUE str) { a->buf = (TYPE(str) == T_STRING) ? str : rb_obj_as_string(str); a->ptr = RSTRING_PTR(a->buf); a->len = RSTRING_LEN(a->buf); a->io = io; a->fd = my_fileno(io); } static int write_check(struct io_args *a, long n, const char *msg, int io_wait) { if (a->len == n) { done: a->buf = Qnil; } else if (n < 0) { if (errno == EINTR) { a->fd = my_fileno(a->io); return -1; } if (errno == EAGAIN) { long written = RSTRING_LEN(a->buf) - a->len; if (io_wait) { (void)kgio_call_wait_writable(a->io); /* buf may be modified in other thread/fiber */ a->len = RSTRING_LEN(a->buf) - written; if (a->len <= 0) goto done; a->ptr = RSTRING_PTR(a->buf) + written; return -1; } else if (written > 0) { a->buf = rb_str_subseq(a->buf, written, a->len); } else { a->buf = sym_wait_writable; } return 0; } wr_sys_fail(msg); } else { assert(n >= 0 && n < a->len && "write/send syscall broken?"); a->ptr += n; a->len -= n; return -1; } return 0; } static VALUE my_write(VALUE io, VALUE str, int io_wait) { struct io_args a; long n; prepare_write(&a, io, str); set_nonblocking(a.fd); retry: n = (long)write(a.fd, a.ptr, a.len); if (write_check(&a, n, "write", io_wait) != 0) goto retry; if (TYPE(a.buf) != T_SYMBOL) kgio_autopush_write(io); return a.buf; } /* * call-seq: * * io.kgio_write(str) -> nil * * Returns nil when the write completes. * * This may block and call any method defined to +kgio_wait_writable+ * for the class. */ static VALUE kgio_write(VALUE io, VALUE str) { return my_write(io, str, 1); } /* * call-seq: * * io.kgio_trywrite(str) -> nil, String or :wait_writable * * Returns nil if the write was completed in full. * * Returns a String containing the unwritten portion if EAGAIN * was encountered, but some portion was successfully written. * * Returns :wait_writable if EAGAIN is encountered and nothing * was written. */ static VALUE kgio_trywrite(VALUE io, VALUE str) { return my_write(io, str, 0); } #ifndef HAVE_WRITEV #define iovec my_iovec struct my_iovec { void *iov_base; size_t iov_len; }; #endif /* tests for choosing following constants were done on Linux 3.0 x86_64 * (Ubuntu 12.04) Core i3 i3-2330M slowed to 1600MHz * testing script https://gist.github.com/2850641 * fill free to make more thorough testing and choose better value */ /* test shows that its meaningless to set WRITEV_MEMLIMIT more that 1M * even when tcp_wmem set to relatively high value (2M) (in fact, it becomes * even slower). 512K performs a bit better in average case. */ #define WRITEV_MEMLIMIT (512*1024) /* same test shows that custom_writev is faster than glibc writev when * average string is smaller than ~500 bytes and slower when average strings * is greater then ~600 bytes. 512 bytes were choosen cause current compilers * turns x/512 into x>>9 */ #define WRITEV_IMPL_THRESHOLD 512 static unsigned int iov_max = 1024; /* this could be overriden in init */ struct io_args_v { VALUE io; VALUE buf; VALUE vec_buf; struct iovec *vec; unsigned long iov_cnt; size_t batch_len; int something_written; int fd; }; static ssize_t custom_writev(int fd, const struct iovec *vec, unsigned int iov_cnt, size_t total_len) { unsigned int i; ssize_t result; char *buf, *curbuf; const struct iovec *curvec = vec; /* we do not want to use ruby's xmalloc because * it can fire GC, and we'll free buffer shortly anyway */ curbuf = buf = malloc(total_len); if (buf == NULL) return -1; for (i = 0; i < iov_cnt; i++, curvec++) { memcpy(curbuf, curvec->iov_base, curvec->iov_len); curbuf += curvec->iov_len; } result = write(fd, buf, total_len); /* well, it seems that `free` could not change errno * but lets save it anyway */ i = errno; free(buf); errno = i; return result; } static void prepare_writev(struct io_args_v *a, VALUE io, VALUE ary) { a->io = io; a->fd = my_fileno(io); a->something_written = 0; if (TYPE(ary) == T_ARRAY) /* rb_ary_subseq will not copy array unless it modified */ a->buf = rb_ary_subseq(ary, 0, RARRAY_LEN(ary)); else a->buf = rb_Array(ary); a->vec_buf = rb_str_new(0, 0); a->vec = NULL; } static void fill_iovec(struct io_args_v *a) { unsigned long i; struct iovec *curvec; a->iov_cnt = RARRAY_LEN(a->buf); a->batch_len = 0; if (a->iov_cnt == 0) return; if (a->iov_cnt > iov_max) a->iov_cnt = iov_max; rb_str_resize(a->vec_buf, sizeof(struct iovec) * a->iov_cnt); curvec = a->vec = (struct iovec*)RSTRING_PTR(a->vec_buf); for (i=0; i < a->iov_cnt; i++, curvec++) { /* rb_ary_store could reallocate array, * so that ought to use RARRAY_PTR */ VALUE str = RARRAY_PTR(a->buf)[i]; long str_len, next_len; if (TYPE(str) != T_STRING) { str = rb_obj_as_string(str); rb_ary_store(a->buf, i, str); } str_len = RSTRING_LEN(str); /* lets limit total memory to write, * but always take first string */ next_len = a->batch_len + str_len; if (i && next_len > WRITEV_MEMLIMIT) { a->iov_cnt = i; break; } a->batch_len = next_len; curvec->iov_base = RSTRING_PTR(str); curvec->iov_len = str_len; } } static long trim_writev_buffer(struct io_args_v *a, long n) { long i; long ary_len = RARRAY_LEN(a->buf); VALUE *elem = RARRAY_PTR(a->buf); if (n == (long)a->batch_len) { i = a->iov_cnt; n = 0; } else { for (i = 0; n && i < ary_len; i++, elem++) { n -= RSTRING_LEN(*elem); if (n < 0) break; } } /* all done */ if (i == ary_len) { assert(n == 0 && "writev system call is broken"); a->buf = Qnil; return 0; } /* partially done, remove fully-written buffers */ if (i > 0) a->buf = rb_ary_subseq(a->buf, i, ary_len - i); /* setup+replace partially written buffer */ if (n < 0) { VALUE str = RARRAY_PTR(a->buf)[0]; long str_len = RSTRING_LEN(str); str = rb_str_subseq(str, str_len + n, -n); rb_ary_store(a->buf, 0, str); } return RARRAY_LEN(a->buf); } static int writev_check(struct io_args_v *a, long n, const char *msg, int io_wait) { if (n >= 0) { if (n > 0) a->something_written = 1; return trim_writev_buffer(a, n); } else if (n < 0) { if (errno == EINTR) { a->fd = my_fileno(a->io); return -1; } if (errno == EAGAIN) { if (io_wait) { (void)kgio_call_wait_writable(a->io); return -1; } else if (!a->something_written) { a->buf = sym_wait_writable; } return 0; } wr_sys_fail(msg); } return 0; } static VALUE my_writev(VALUE io, VALUE str, int io_wait) { struct io_args_v a; long n; prepare_writev(&a, io, str); set_nonblocking(a.fd); do { fill_iovec(&a); if (a.iov_cnt == 0) n = 0; else if (a.iov_cnt == 1) n = (long)write(a.fd, a.vec[0].iov_base, a.vec[0].iov_len); /* for big strings use library function */ else if (USE_WRITEV && a.batch_len / WRITEV_IMPL_THRESHOLD > a.iov_cnt) n = (long)writev(a.fd, a.vec, a.iov_cnt); else n = (long)custom_writev(a.fd, a.vec, a.iov_cnt, a.batch_len); } while (writev_check(&a, n, "writev", io_wait) != 0); rb_str_resize(a.vec_buf, 0); if (TYPE(a.buf) != T_SYMBOL) kgio_autopush_write(io); return a.buf; } /* * call-seq: * * io.kgio_writev(array) -> nil * * Returns nil when the write completes. * * This may block and call any method defined to +kgio_wait_writable+ * for the class. * * Note: it uses +Array()+ semantic for converting argument, so that * it will succeed if you pass something else. */ static VALUE kgio_writev(VALUE io, VALUE ary) { return my_writev(io, ary, 1); } /* * call-seq: * * io.kgio_trywritev(array) -> nil, Array or :wait_writable * * Returns nil if the write was completed in full. * * Returns an Array of strings containing the unwritten portion * if EAGAIN was encountered, but some portion was successfully written. * * Returns :wait_writable if EAGAIN is encountered and nothing * was written. * * Note: it uses +Array()+ semantic for converting argument, so that * it will succeed if you pass something else. */ static VALUE kgio_trywritev(VALUE io, VALUE ary) { return my_writev(io, ary, 0); } #ifdef USE_MSG_DONTWAIT /* * This method behaves like Kgio::PipeMethods#kgio_write, except * it will use send(2) with the MSG_DONTWAIT flag on sockets to * avoid unnecessary calls to fcntl(2). */ static VALUE my_send(VALUE io, VALUE str, int io_wait) { struct io_args a; long n; prepare_write(&a, io, str); retry: n = (long)send(a.fd, a.ptr, a.len, MSG_DONTWAIT); if (write_check(&a, n, "send", io_wait) != 0) goto retry; if (TYPE(a.buf) != T_SYMBOL) kgio_autopush_send(io); return a.buf; } /* * This method may be optimized on some systems (e.g. GNU/Linux) to use * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl. * Otherwise this is the same as Kgio::PipeMethods#kgio_write */ static VALUE kgio_send(VALUE io, VALUE str) { return my_send(io, str, 1); } /* * This method may be optimized on some systems (e.g. GNU/Linux) to use * MSG_DONTWAIT to avoid explicitly setting the O_NONBLOCK flag via fcntl. * Otherwise this is the same as Kgio::PipeMethods#kgio_trywrite */ static VALUE kgio_trysend(VALUE io, VALUE str) { return my_send(io, str, 0); } #else /* ! USE_MSG_DONTWAIT */ # define kgio_send kgio_write # define kgio_trysend kgio_trywrite #endif /* ! USE_MSG_DONTWAIT */ /* * call-seq: * * Kgio.tryread(io, maxlen) -> buffer * Kgio.tryread(io, maxlen, buffer) -> buffer * * Returns nil on EOF. * Returns :wait_readable if EAGAIN is encountered. * * Maybe used in place of PipeMethods#kgio_tryread for non-Kgio objects */ static VALUE s_tryread(int argc, VALUE *argv, VALUE mod) { if (argc <= 1) rb_raise(rb_eArgError, "wrong number of arguments"); return my_read(0, argc - 1, &argv[1], argv[0]); } /* * call-seq: * * Kgio.trywrite(io, str) -> nil, String or :wait_writable * * Returns nil if the write was completed in full. * * Returns a String containing the unwritten portion if EAGAIN * was encountered, but some portion was successfully written. * * Returns :wait_writable if EAGAIN is encountered and nothing * was written. * * Maybe used in place of PipeMethods#kgio_trywrite for non-Kgio objects */ static VALUE s_trywrite(VALUE mod, VALUE io, VALUE str) { return my_write(io, str, 0); } /* * call-seq: * * Kgio.trywritev(io, array) -> nil, Array or :wait_writable * * Returns nil if the write was completed in full. * * Returns a Array of strings containing the unwritten portion if EAGAIN * was encountered, but some portion was successfully written. * * Returns :wait_writable if EAGAIN is encountered and nothing * was written. * * Maybe used in place of PipeMethods#kgio_trywritev for non-Kgio objects */ static VALUE s_trywritev(VALUE mod, VALUE io, VALUE ary) { return kgio_trywritev(io, ary); } void init_kgio_read_write(void) { VALUE mPipeMethods, mSocketMethods; VALUE mKgio = rb_define_module("Kgio"); VALUE mWaiters = rb_const_get(mKgio, rb_intern("DefaultWaiters")); sym_wait_readable = ID2SYM(rb_intern("wait_readable")); sym_wait_writable = ID2SYM(rb_intern("wait_writable")); rb_define_singleton_method(mKgio, "tryread", s_tryread, -1); rb_define_singleton_method(mKgio, "trywrite", s_trywrite, 2); rb_define_singleton_method(mKgio, "trywritev", s_trywritev, 2); rb_define_singleton_method(mKgio, "trypeek", s_trypeek, -1); /* * Document-module: Kgio::PipeMethods * * This module may be used used to create classes that respond to * various Kgio methods for reading and writing. This is included * in Kgio::Pipe by default. */ mPipeMethods = rb_define_module_under(mKgio, "PipeMethods"); rb_define_method(mPipeMethods, "kgio_read", kgio_read, -1); rb_define_method(mPipeMethods, "kgio_read!", kgio_read_bang, -1); rb_define_method(mPipeMethods, "kgio_write", kgio_write, 1); rb_define_method(mPipeMethods, "kgio_writev", kgio_writev, 1); rb_define_method(mPipeMethods, "kgio_tryread", kgio_tryread, -1); rb_define_method(mPipeMethods, "kgio_trywrite", kgio_trywrite, 1); rb_define_method(mPipeMethods, "kgio_trywritev", kgio_trywritev, 1); /* * Document-module: Kgio::SocketMethods * * This method behaves like Kgio::PipeMethods, but contains * optimizations for sockets on certain operating systems * (e.g. GNU/Linux). */ mSocketMethods = rb_define_module_under(mKgio, "SocketMethods"); rb_define_method(mSocketMethods, "kgio_read", kgio_recv, -1); rb_define_method(mSocketMethods, "kgio_read!", kgio_recv_bang, -1); rb_define_method(mSocketMethods, "kgio_write", kgio_send, 1); rb_define_method(mSocketMethods, "kgio_writev", kgio_writev, 1); rb_define_method(mSocketMethods, "kgio_tryread", kgio_tryrecv, -1); rb_define_method(mSocketMethods, "kgio_trywrite", kgio_trysend, 1); rb_define_method(mSocketMethods, "kgio_trywritev", kgio_trywritev, 1); rb_define_method(mSocketMethods, "kgio_trypeek", kgio_trypeek, -1); rb_define_method(mSocketMethods, "kgio_peek", kgio_peek, -1); /* * Returns the client IP address of the socket as a string * (e.g. "127.0.0.1" or "::1"). * This is always the value of the Kgio::LOCALHOST constant * for UNIX domain sockets. */ rb_define_attr(mSocketMethods, "kgio_addr", 1, 1); id_set_backtrace = rb_intern("set_backtrace"); eErrno_EPIPE = rb_const_get(rb_mErrno, rb_intern("EPIPE")); eErrno_ECONNRESET = rb_const_get(rb_mErrno, rb_intern("ECONNRESET")); rb_include_module(mPipeMethods, mWaiters); rb_include_module(mSocketMethods, mWaiters); #ifdef HAVE_WRITEV { # ifdef IOV_MAX unsigned int sys_iov_max = IOV_MAX; # else unsigned int sys_iov_max = sysconf(_SC_IOV_MAX); # endif if (sys_iov_max < iov_max) iov_max = sys_iov_max; } #endif } kgio-2.8.1/ext/kgio/set_file_path.h0000644000004100000410000000117012214142252017205 0ustar www-datawww-data#if defined(HAVE_RB_IO_T) && \ defined(HAVE_TYPE_STRUCT_RFILE) && \ defined(HAVE_ST_PATHV) /* MRI 1.9 */ static void set_file_path(VALUE io, VALUE path) { rb_io_t *fptr = RFILE(io)->fptr; fptr->pathv = rb_str_new4(path); } #elif defined(HAVE_TYPE_OPENFILE) && \ defined(HAVE_TYPE_STRUCT_RFILE) && \ defined(HAVE_ST_PATH) /* MRI 1.8 */ #include "util.h" static void set_file_path(VALUE io, VALUE path) { OpenFile *fptr = RFILE(io)->fptr; fptr->path = ruby_strdup(RSTRING_PTR(path)); } #else /* Rubinius */ static void set_file_path(VALUE io, VALUE path) { rb_iv_set(io, "@path", rb_str_new4(path)); } #endif kgio-2.8.1/ext/kgio/autopush.c0000644000004100000410000001424012214142252016244 0ustar www-datawww-data/* * We use a very basic strategy to use TCP_CORK semantics optimally * in most TCP servers: On corked sockets, we will uncork on recv() * if there was a previous send(). Otherwise we do not fiddle * with TCP_CORK at all. * * Under Linux, we can rely on TCP_CORK being inherited in an * accept()-ed client socket so we can avoid syscalls for each * accept()-ed client if we know the accept() socket corks. * * This module does NOTHING for client TCP sockets, we only deal * with accept()-ed sockets right now. */ #include "kgio.h" #include "my_fileno.h" #include /* * As of FreeBSD 4.5, TCP_NOPUSH == TCP_CORK * ref: http://dotat.at/writing/nopush.html * We won't care for older FreeBSD since nobody runs Ruby on them... */ #ifdef TCP_CORK # define KGIO_NOPUSH TCP_CORK #elif defined(TCP_NOPUSH) # define KGIO_NOPUSH TCP_NOPUSH #endif #ifdef KGIO_NOPUSH static ID id_autopush_state; static int enabled = 1; enum autopush_state { AUTOPUSH_STATE_ACCEPTOR_IGNORE = -1, AUTOPUSH_STATE_IGNORE = 0, AUTOPUSH_STATE_WRITER = 1, AUTOPUSH_STATE_WRITTEN = 2, AUTOPUSH_STATE_ACCEPTOR = 3 }; #if defined(R_CAST) && \ defined(HAVE_TYPE_STRUCT_RFILE) && \ defined(HAVE_TYPE_STRUCT_ROBJECT) && \ ((SIZEOF_STRUCT_RFILE + SIZEOF_INT) <= (SIZEOF_STRUCT_ROBJECT)) struct AutopushSocket { struct RFile rfile; enum autopush_state autopush_state; }; static enum autopush_state state_get(VALUE io) { return ((struct AutopushSocket *)(io))->autopush_state; } static void state_set(VALUE io, enum autopush_state state) { ((struct AutopushSocket *)(io))->autopush_state = state; } #else static enum autopush_state state_get(VALUE io) { VALUE val; if (rb_ivar_defined(io, id_autopush_state) == Qfalse) return AUTOPUSH_STATE_IGNORE; val = rb_ivar_get(io, id_autopush_state); return (enum autopush_state)NUM2INT(val); } static void state_set(VALUE io, enum autopush_state state) { rb_ivar_set(io, id_autopush_state, INT2NUM(state)); } #endif /* IVAR fallback */ static enum autopush_state detect_acceptor_state(VALUE io); static void push_pending_data(VALUE io); /* * call-seq: * Kgio.autopush? -> true or false * * Returns whether or not autopush is enabled. * * Only available on systems with TCP_CORK (Linux) or * TCP_NOPUSH (FreeBSD, and maybe other *BSDs). */ static VALUE s_get_autopush(VALUE self) { return enabled ? Qtrue : Qfalse; } /* * call-seq: * Kgio.autopush = true * Kgio.autopush = false * * Enables or disables autopush for sockets created with kgio_accept * and kgio_tryaccept methods. Autopush relies on TCP_CORK/TCP_NOPUSH * being enabled on the listen socket. * * Only available on systems with TCP_CORK (Linux) or * TCP_NOPUSH (FreeBSD, and maybe other *BSDs). */ static VALUE s_set_autopush(VALUE self, VALUE val) { enabled = RTEST(val); return val; } /* * call-seq: * * io.kgio_autopush? -> true or false * * Returns the current autopush state of the Kgio::SocketMethods-enabled * socket. * * Only available on systems with TCP_CORK (Linux) or * TCP_NOPUSH (FreeBSD, and maybe other *BSDs). */ static VALUE autopush_get(VALUE io) { return state_get(io) <= 0 ? Qfalse : Qtrue; } /* * call-seq: * * io.kgio_autopush = true * io.kgio_autopush = false * * Enables or disables autopush on any given Kgio::SocketMethods-capable * IO object. This does NOT enable or disable TCP_NOPUSH/TCP_CORK right * away, that must be done with IO.setsockopt * * Only available on systems with TCP_CORK (Linux) or * TCP_NOPUSH (FreeBSD, and maybe other *BSDs). */ static VALUE autopush_set(VALUE io, VALUE vbool) { if (RTEST(vbool)) state_set(io, AUTOPUSH_STATE_WRITER); else state_set(io, AUTOPUSH_STATE_IGNORE); return vbool; } void init_kgio_autopush(void) { VALUE mKgio = rb_define_module("Kgio"); VALUE tmp; rb_define_singleton_method(mKgio, "autopush?", s_get_autopush, 0); rb_define_singleton_method(mKgio, "autopush=", s_set_autopush, 1); tmp = rb_define_module_under(mKgio, "SocketMethods"); rb_define_method(tmp, "kgio_autopush=", autopush_set, 1); rb_define_method(tmp, "kgio_autopush?", autopush_get, 0); id_autopush_state = rb_intern("@kgio_autopush_state"); } /* * called after a successful write, just mark that we've put something * in the skb and will need to uncork on the next write. */ void kgio_autopush_send(VALUE io) { if (state_get(io) == AUTOPUSH_STATE_WRITER) state_set(io, AUTOPUSH_STATE_WRITTEN); } /* called on successful accept() */ void kgio_autopush_accept(VALUE accept_io, VALUE client_io) { enum autopush_state acceptor_state; if (!enabled) return; acceptor_state = state_get(accept_io); if (acceptor_state == AUTOPUSH_STATE_IGNORE) acceptor_state = detect_acceptor_state(accept_io); if (acceptor_state == AUTOPUSH_STATE_ACCEPTOR) state_set(client_io, AUTOPUSH_STATE_WRITER); else state_set(client_io, AUTOPUSH_STATE_IGNORE); } void kgio_autopush_recv(VALUE io) { if (enabled && (state_get(io) == AUTOPUSH_STATE_WRITTEN)) { push_pending_data(io); state_set(io, AUTOPUSH_STATE_WRITER); } } static enum autopush_state detect_acceptor_state(VALUE io) { int corked = 0; int fd = my_fileno(io); socklen_t optlen = sizeof(int); enum autopush_state state; if (getsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &corked, &optlen) != 0) { if (errno != EOPNOTSUPP) rb_sys_fail("getsockopt(TCP_CORK/TCP_NOPUSH)"); errno = 0; state = AUTOPUSH_STATE_ACCEPTOR_IGNORE; } else if (corked) { state = AUTOPUSH_STATE_ACCEPTOR; } else { state = AUTOPUSH_STATE_ACCEPTOR_IGNORE; } state_set(io, state); return state; } /* * checks to see if we've written anything since the last recv() * If we have, uncork the socket and immediately recork it. */ static void push_pending_data(VALUE io) { int optval = 0; const socklen_t optlen = sizeof(int); const int fd = my_fileno(io); if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0) rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 0)"); /* immediately recork */ optval = 1; if (setsockopt(fd, IPPROTO_TCP, KGIO_NOPUSH, &optval, optlen) != 0) rb_sys_fail("setsockopt(TCP_CORK/TCP_NOPUSH, 1)"); } #else /* !KGIO_NOPUSH */ void kgio_autopush_recv(VALUE io){} void kgio_autopush_send(VALUE io){} void init_kgio_autopush(void) { } #endif /* ! KGIO_NOPUSH */ kgio-2.8.1/ext/kgio/my_fileno.h0000644000004100000410000000126312214142252016363 0ustar www-datawww-data#include #ifdef HAVE_RUBY_IO_H # include #else # include # include #endif #if ! HAVE_RB_IO_T # define rb_io_t OpenFile #endif #ifdef GetReadFile # define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr))) #else # if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8) # define FPTR_TO_FD(fptr) fileno(fptr->f) # else # define FPTR_TO_FD(fptr) fptr->fd # endif #endif static int my_fileno(VALUE io) { rb_io_t *fptr; int fd; if (TYPE(io) != T_FILE) io = rb_convert_type(io, T_FILE, "IO", "to_io"); GetOpenFile(io, fptr); fd = FPTR_TO_FD(fptr); if (fd < 0) rb_raise(rb_eIOError, "closed stream"); return fd; } kgio-2.8.1/ext/kgio/poll.c0000644000004100000410000001253712214142252015351 0ustar www-datawww-data#include "kgio.h" #if defined(USE_KGIO_POLL) #include "my_fileno.h" #include #include "broken_system_compat.h" #include #include #ifdef HAVE_RUBY_ST_H # include #else # include #endif static VALUE sym_wait_readable, sym_wait_writable; static ID id_clear; struct poll_args { struct pollfd *fds; nfds_t nfds; int timeout; VALUE ios; st_table *fd_to_io; struct timespec start; }; static int interrupted(void) { switch (errno) { case EINTR: #ifdef ERESTART case ERESTART: #endif return 1; } return 0; } static int retryable(struct poll_args *a) { struct timespec ts; if (a->timeout < 0) return 1; clock_gettime(hopefully_CLOCK_MONOTONIC, &ts); ts.tv_sec -= a->start.tv_sec; ts.tv_nsec -= a->start.tv_nsec; if (ts.tv_nsec < 0) { ts.tv_sec--; ts.tv_nsec += 1000000000; } a->timeout -= ts.tv_sec * 1000; a->timeout -= ts.tv_nsec / 1000000; if (a->timeout < 0) a->timeout = 0; return 1; } static int num2timeout(VALUE timeout) { switch (TYPE(timeout)) { case T_NIL: return -1; case T_FIXNUM: return FIX2INT(timeout); case T_BIGNUM: return NUM2INT(timeout); } rb_raise(rb_eTypeError, "timeout must be integer or nil"); return 0; } static VALUE poll_free(VALUE args) { struct poll_args *a = (struct poll_args *)args; if (a->fds) xfree(a->fds); if (a->fd_to_io) st_free_table(a->fd_to_io); return Qnil; } static short value2events(VALUE event) { if (event == sym_wait_readable) return POLLIN; if (event == sym_wait_writable) return POLLOUT; if (TYPE(event) == T_FIXNUM) return (short)FIX2INT(event); rb_raise(rb_eArgError, "unrecognized event"); } static int io_to_pollfd_i(VALUE key, VALUE value, VALUE args) { struct poll_args *a = (struct poll_args *)args; struct pollfd *pollfd = &a->fds[a->nfds++]; pollfd->fd = my_fileno(key); pollfd->events = value2events(value); st_insert(a->fd_to_io, (st_data_t)pollfd->fd, (st_data_t)key); return ST_CONTINUE; } static void hash2pollfds(struct poll_args *a) { a->nfds = 0; a->fds = xmalloc(sizeof(struct pollfd) * RHASH_SIZE(a->ios)); a->fd_to_io = st_init_numtable(); rb_hash_foreach(a->ios, io_to_pollfd_i, (VALUE)a); } static VALUE nogvl_poll(void *ptr) { struct poll_args *a = ptr; if (a->timeout > 0) clock_gettime(hopefully_CLOCK_MONOTONIC, &a->start); return (VALUE)poll(a->fds, a->nfds, a->timeout); } static VALUE poll_result(int nr, struct poll_args *a) { struct pollfd *fds = a->fds; VALUE io; int rc; if ((nfds_t)nr != a->nfds) rb_funcall(a->ios, id_clear, 0); for (; nr > 0; fds++) { if (fds->revents == 0) continue; --nr; rc = st_lookup(a->fd_to_io, (st_data_t)fds->fd, &io); assert(rc == 1 && "fd => IO mapping failed"); rb_hash_aset(a->ios, io, INT2FIX((int)fds->revents)); } return a->ios; } static VALUE do_poll(VALUE args) { struct poll_args *a = (struct poll_args *)args; int nr; Check_Type(a->ios, T_HASH); retry: hash2pollfds(a); nr = (int)rb_thread_blocking_region(nogvl_poll, a, RUBY_UBF_IO, NULL); if (nr < 0) { if (interrupted()) { if (retryable(a)) { poll_free(args); goto retry; } return Qnil; } rb_sys_fail("poll"); } if (nr == 0) return Qnil; return poll_result(nr, a); } /* * call-seq: * * Kgio.poll({ $stdin => :wait_readable }, 100) -> hash or nil * Kgio.poll({ $stdin => Kgio::POLLIN }, 100) -> hash or nil * * Accepts an input hash with IO objects to wait for as the key and * the events to wait for as its value. The events may either be * +:wait_readable+ or +:wait_writable+ symbols or a Fixnum mask of * Kgio::POLL* constants: * * Kgio::POLLIN - there is data to read * Kgio::POLLPRI - there is urgent data to read * Kgio::POLLOUT - writing will not block * Kgio::POLLRDHUP - peer has shutdown writes (Linux 2.6.17+ only) * * Timeout is specified in Integer milliseconds just like the underlying * poll(2), not in seconds like IO.select. A nil timeout means to wait * forever. It must be an Integer or nil. * * Kgio.poll modifies and returns its input hash on success with the * IO-like object as the key and an Integer mask of events as the hash * value. It can return any of the events specified in the input * above, along with the following events: * * Kgio::POLLERR - error condition occurred on the descriptor * Kgio::POLLHUP - hang up * Kgio::POLLNVAL - invalid request (bad file descriptor) * * This method is only available under Ruby 1.9 or any other * implementations that uses native threads and rb_thread_blocking_region() */ static VALUE s_poll(int argc, VALUE *argv, VALUE self) { VALUE timeout; struct poll_args a; rb_scan_args(argc, argv, "11", &a.ios, &timeout); a.timeout = num2timeout(timeout); a.fds = NULL; a.fd_to_io = NULL; return rb_ensure(do_poll, (VALUE)&a, poll_free, (VALUE)&a); } void init_kgio_poll(void) { VALUE mKgio = rb_define_module("Kgio"); if (check_clock() < 0) return; rb_define_singleton_method(mKgio, "poll", s_poll, -1); sym_wait_readable = ID2SYM(rb_intern("wait_readable")); sym_wait_writable = ID2SYM(rb_intern("wait_writable")); id_clear = rb_intern("clear"); #define c(x) rb_define_const(mKgio,#x,INT2NUM((int)x)) /* standard types */ c(POLLIN); c(POLLPRI); c(POLLOUT); #ifdef POLLRDHUP c(POLLRDHUP); #endif /* outputs */ c(POLLERR); c(POLLHUP); c(POLLNVAL); } #else /* ! USE_KGIO_POLL */ void init_kgio_poll(void) { } #endif /* ! USE_KGIO_POLL */ kgio-2.8.1/ext/kgio/tryopen.c0000644000004100000410000001130412214142252016072 0ustar www-datawww-data#include #ifdef HAVE_RUBY_IO_H # include #else # include #endif #ifdef HAVE_RUBY_ST_H # include #else # include #endif #include #include #include #include #include "set_file_path.h" #include "ancient_ruby.h" static ID id_for_fd, id_to_path, id_path; static st_table *errno2sym; struct open_args { const char *pathname; int flags; mode_t mode; }; #ifndef HAVE_RB_CLOEXEC_OPEN # define rb_cloexec_open(p,f,m) open((p),(f),(m)) #endif static VALUE nogvl_open(void *ptr) { struct open_args *o = ptr; return (VALUE)rb_cloexec_open(o->pathname, o->flags, o->mode); } #ifndef HAVE_RB_THREAD_BLOCKING_REGION # define RUBY_UBF_IO ((void *)(-1)) # include "rubysig.h" typedef void rb_unblock_function_t(void *); typedef VALUE rb_blocking_function_t(void *); static VALUE my_thread_blocking_region( rb_blocking_function_t *fn, void *data1, rb_unblock_function_t *ubf, void *data2) { VALUE rv; TRAP_BEG; /* for FIFO */ rv = fn(data1); TRAP_END; return rv; } #define rb_thread_blocking_region(fn,data1,ubf,data2) \ my_thread_blocking_region((fn),(data1),(ubf),(data2)) #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */ /* * call-seq: * * Kgio::File.tryopen(filename, [, mode [, perm]]) -> Kgio::File or Symbol * * Returns a Kgio::File object on a successful open. +filename+ is a * path to any file on the filesystem. If specified, +mode+ is a bitmask * of flags (see IO.sysopen) and +perm+ should be an octal number. * * This does not raise errors for most failures, but installs returns a * Ruby symbol for the constant in the Errno::* namespace. * * Common error symbols are: * * - :ENOENT * - :EACCES * * See your open(2) manpage for more information on open(2) errors. */ static VALUE s_tryopen(int argc, VALUE *argv, VALUE klass) { int fd; VALUE pathname, flags, mode; struct open_args o; int retried = 0; VALUE rv; rb_scan_args(argc, argv, "12", &pathname, &flags, &mode); if (rb_respond_to(pathname, id_to_path)) pathname = rb_funcall(pathname, id_to_path, 0); o.pathname = StringValueCStr(pathname); switch (TYPE(flags)) { case T_NIL: o.flags = O_RDONLY; break; case T_FIXNUM: o.flags = FIX2INT(flags); break; case T_BIGNUM: o.flags = NUM2INT(flags); break; default: rb_raise(rb_eArgError, "flags must be an Integer"); } switch (TYPE(mode)) { case T_NIL: o.mode = 0666; break; case T_FIXNUM: o.mode = FIX2INT(mode); break; case T_BIGNUM: o.mode = NUM2INT(mode); break; default: rb_raise(rb_eArgError, "mode must be an Integer"); } retry: fd = (int)rb_thread_blocking_region(nogvl_open, &o, RUBY_UBF_IO, 0); if (fd < 0) { if (errno == EMFILE || errno == ENFILE || errno == ENOMEM) { rb_gc(); if (retried) rb_sys_fail(o.pathname); retried = 1; goto retry; } if (fd < 0) { int saved_errno = errno; if (!st_lookup(errno2sym, (st_data_t)errno, &rv)) { errno = saved_errno; rb_sys_fail(o.pathname); } return rv; } } rv = rb_funcall(klass, id_for_fd, 1, INT2FIX(fd)); set_file_path(rv, pathname); return rv; } void init_kgio_tryopen(void) { VALUE mKgio = rb_define_module("Kgio"); VALUE mPipeMethods = rb_const_get(mKgio, rb_intern("PipeMethods")); VALUE cFile; VALUE tmp; VALUE *ptr; long len; id_path = rb_intern("path"); id_for_fd = rb_intern("for_fd"); id_to_path = rb_intern("to_path"); /* * Document-class: Kgio::File * * This subclass of the core File class adds the "tryopen" singleton * method for opening files. A single "tryopen" and check for the * return value may be used to avoid unnecessary stat(2) syscalls * or File.open exceptions when checking for the existence of a file * and opening it. */ cFile = rb_define_class_under(mKgio, "File", rb_cFile); rb_define_singleton_method(cFile, "tryopen", s_tryopen, -1); rb_include_module(cFile, mPipeMethods); if (!rb_funcall(cFile, rb_intern("method_defined?"), 1, ID2SYM(id_to_path))) rb_define_alias(cFile, "to_path", "path"); errno2sym = st_init_numtable(); tmp = rb_funcall(rb_mErrno, rb_intern("constants"), 0); ptr = RARRAY_PTR(tmp); len = RARRAY_LEN(tmp); for (; --len >= 0; ptr++) { VALUE error; ID const_id; switch (TYPE(*ptr)) { case T_SYMBOL: const_id = SYM2ID(*ptr); break; case T_STRING: const_id = rb_intern(RSTRING_PTR(*ptr)); break; default: rb_bug("constant not a symbol or string"); } error = rb_const_get(rb_mErrno, const_id); if ((TYPE(error) != T_CLASS) || !rb_const_defined(error, rb_intern("Errno"))) continue; error = rb_const_get(error, rb_intern("Errno")); switch (TYPE(error)) { case T_FIXNUM: case T_BIGNUM: st_insert(errno2sym, (st_data_t)NUM2INT(error), ID2SYM(const_id)); } } } kgio-2.8.1/ext/kgio/broken_system_compat.h0000644000004100000410000000267512214142252020641 0ustar www-datawww-data/* * this header includes functions to support broken systems * without clock_gettime() or CLOCK_MONOTONIC */ #ifndef HAVE_TYPE_CLOCKID_T typedef int clockid_t; #endif #ifndef HAVE_CLOCK_GETTIME # ifndef CLOCK_REALTIME # define CLOCK_REALTIME 0 /* whatever */ # endif static int fake_clock_gettime(clockid_t clk_id, struct timespec *res) { struct timeval tv; int r = gettimeofday(&tv, NULL); assert(0 == r && "gettimeofday() broke!?"); res->tv_sec = tv.tv_sec; res->tv_nsec = tv.tv_usec * 1000; return r; } # define clock_gettime fake_clock_gettime #endif /* broken systems w/o clock_gettime() */ /* * UGH * CLOCK_MONOTONIC is not guaranteed to be a macro, either */ #ifndef CLOCK_MONOTONIC # if (!defined(_POSIX_MONOTONIC_CLOCK) || !defined(HAVE_CLOCK_MONOTONIC)) # define CLOCK_MONOTONIC CLOCK_REALTIME # endif #endif /* * Availability of a monotonic clock needs to be detected at runtime * since we could've been built on a different system than we're run * under. */ static clockid_t hopefully_CLOCK_MONOTONIC; static int check_clock(void) { struct timespec now; hopefully_CLOCK_MONOTONIC = CLOCK_MONOTONIC; /* we can't check this reliably at compile time */ if (clock_gettime(CLOCK_MONOTONIC, &now) == 0) return 1; if (clock_gettime(CLOCK_REALTIME, &now) == 0) { hopefully_CLOCK_MONOTONIC = CLOCK_REALTIME; rb_warn("CLOCK_MONOTONIC not available, " "falling back to CLOCK_REALTIME"); return 2; } return -1; } kgio-2.8.1/ext/kgio/blocking_io_region.h0000644000004100000410000000051712214142252020225 0ustar www-datawww-data#ifdef HAVE_RB_THREAD_BLOCKING_REGION # ifdef HAVE_RB_THREAD_IO_BLOCKING_REGION /* temporary API for Ruby 1.9.3 */ VALUE rb_thread_io_blocking_region(rb_blocking_function_t *, void *, int); # else # define rb_thread_io_blocking_region(fn,data,fd) \ rb_thread_blocking_region((fn),(data),RUBY_UBF_IO,0) # endif #endif kgio-2.8.1/ext/kgio/missing_accept4.h0000644000004100000410000000302712214142252017456 0ustar www-datawww-data#if !defined(HAVE_ACCEPT4) || !defined(SOCK_CLOEXEC) || !defined(SOCK_NONBLOCK) # ifndef _GNU_SOURCE # define _GNU_SOURCE # endif # include # include # ifndef SOCK_CLOEXEC # if (02000000 == O_NONBLOCK) # define SOCK_CLOEXEC 1 # define SOCK_NONBLOCK 2 # else # define SOCK_CLOEXEC 02000000 # define SOCK_NONBLOCK O_NONBLOCK # endif # endif #endif /* !HAVE_ACCEPT4 */ /* accept4() is currently a Linux-only goodie */ static int my_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags) { int fd = accept(sockfd, addr, addrlen); if (fd >= 0) { if ((flags & SOCK_CLOEXEC) == SOCK_CLOEXEC) (void)fcntl(fd, F_SETFD, FD_CLOEXEC); /* * Some systems inherit O_NONBLOCK across accept(). * We also expect our users to use MSG_DONTWAIT under * Linux, so fcntl() is completely unnecessary * in most cases... */ if ((flags & SOCK_NONBLOCK) == SOCK_NONBLOCK) { int fl = fcntl(fd, F_GETFL); /* * unconditional, OSX 10.4 (and maybe other *BSDs) * F_GETFL returns a false O_NONBLOCK with TCP sockets * (but not UNIX sockets) [ruby-talk:274079] */ (void)fcntl(fd, F_SETFL, fl | O_NONBLOCK); } /* * nothing we can do about fcntl() errors in this wrapper * function, let the user (Ruby) code figure it out */ errno = 0; } return fd; } typedef int accept_fn_t(int, struct sockaddr *, socklen_t *, int); #ifdef HAVE_ACCEPT4 static accept_fn_t *accept_fn = accept4; #else static accept_fn_t *accept_fn = my_accept4; #endif kgio-2.8.1/test/0000755000004100000410000000000012214142252013455 5ustar www-datawww-datakgio-2.8.1/test/test_socketpair_read_write.rb0000644000004100000410000000025712214142252021416 0ustar www-datawww-datarequire './test/lib_read_write.rb' class TestKgioUNIXSocketPair < Test::Unit::TestCase def setup @rd, @wr = Kgio::UNIXSocket.pair end include LibReadWriteTest end kgio-2.8.1/test/lib_read_write.rb0000644000004100000410000002660312214142252016764 0ustar www-datawww-data# -*- encoding: binary -*- require 'test/unit' require 'io/nonblock' require 'digest/sha1' require 'fileutils' $-w = true require 'kgio' module LibReadWriteTest RANDOM_BLOB = File.open("/dev/urandom") { |fp| fp.read(10 * 1024 * 1024) } def teardown @rd.close if defined?(@rd) && ! @rd.closed? @wr.close if defined?(@wr) && ! @wr.closed? FileUtils.remove_entry_secure(@tmpdir) if defined?(@tmpdir) end def test_write_empty assert_nil @wr.kgio_write("") end def test_trywrite_empty assert_nil @wr.kgio_trywrite("") end def test_writev_empty assert_nil @wr.kgio_writev([]) end def test_trywritev_empty assert_nil @wr.kgio_trywritev([]) end def test_read_zero assert_equal "", @rd.kgio_read(0) buf = "foo" assert_equal buf.object_id, @rd.kgio_read(0, buf).object_id assert_equal "", buf end def test_read_shared a = "." * 0x1000 b = a.dup @wr.syswrite "a" assert_equal "a", @rd.kgio_read(0x1000, a) assert_equal "a", a assert_equal "." * 0x1000, b end def test_read_shared_2 a = "." * 0x1000 b = a.dup @wr.syswrite "a" assert_equal "a", @rd.kgio_read(0x1000, b) assert_equal "a", b assert_equal "." * 0x1000, a end def test_tryread_zero assert_equal "", @rd.kgio_tryread(0) buf = "foo" assert_equal buf.object_id, @rd.kgio_tryread(0, buf).object_id assert_equal "", buf end def test_tryread_shared a = "." * 0x1000 b = a.dup @wr.syswrite("a") IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_equal "a", @rd.kgio_tryread(0x1000, b) assert_equal "a", b assert_equal "." * 0x1000, a end def test_tryread_shared_2 a = "." * 0x1000 b = a.dup @wr.syswrite("a") IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_equal "a", @rd.kgio_tryread(0x1000, a) assert_equal "a", a assert_equal "." * 0x1000, b end def test_read_eof @wr.close assert_nil @rd.kgio_read(5) end def test_read_bang_eof @wr.close begin @rd.kgio_read!(5) assert false, "should never get here (line:#{__LINE__})" rescue EOFError => e assert_equal [], e.backtrace end end def test_tryread_eof @wr.close IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_nil @rd.kgio_tryread(5) end def test_write_closed @rd.close begin loop { @wr.kgio_write "HI" } rescue Errno::EPIPE, Errno::ECONNRESET => e assert_equal [], e.backtrace return end assert false, "should never get here (line:#{__LINE__})" end def test_trywrite_closed @rd.close begin loop { @wr.kgio_trywrite "HI" } rescue Errno::EPIPE, Errno::ECONNRESET => e assert_equal [], e.backtrace return end assert false, "should never get here (line:#{__LINE__})" end def test_writev_closed @rd.close begin loop { @wr.kgio_writev ["HI"] } rescue Errno::EPIPE, Errno::ECONNRESET => e assert_equal [], e.backtrace return end assert false, "should never get here (line:#{__LINE__})" end def test_trywritev_closed @rd.close begin loop { @wr.kgio_trywritev ["HI"] } rescue Errno::EPIPE, Errno::ECONNRESET => e assert_equal [], e.backtrace return end assert false, "should never get here (line:#{__LINE__})" end def test_trywrite_full buf = "\302\251" * 1024 * 1024 buf2 = "" dig = Digest::SHA1.new t = Thread.new do sleep 1 nr = 0 begin dig.update(@rd.readpartial(4096, buf2)) nr += buf2.size rescue EOFError break rescue => e end while true dig.hexdigest end 50.times do wr = buf begin rv = @wr.kgio_trywrite(wr) case rv when String wr = rv when :wait_readable assert false, "should never get here line=#{__LINE__}" when :wait_writable IO.select(nil, [ @wr ]) else wr = false end end while wr end @wr.close t.join assert_equal '8ff79d8115f9fe38d18be858c66aa08a1cc27a66', t.value end def test_trywritev_full buf = ["\302\251" * 128] * 8 * 1024 buf2 = "" dig = Digest::SHA1.new t = Thread.new do sleep 1 nr = 0 begin dig.update(@rd.readpartial(4096, buf2)) nr += buf2.size rescue EOFError break rescue => e end while true dig.hexdigest end 50.times do wr = buf begin rv = @wr.kgio_trywritev(wr) case rv when Array wr = rv when :wait_readable assert false, "should never get here line=#{__LINE__}" when :wait_writable IO.select(nil, [ @wr ]) else wr = false end end while wr end @wr.close t.join assert_equal '8ff79d8115f9fe38d18be858c66aa08a1cc27a66', t.value end def test_write_conv assert_equal nil, @wr.kgio_write(10) assert_equal "10", @rd.kgio_read(2) end def test_trywrite_conv assert_equal nil, @wr.kgio_trywrite(10) IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_equal "10", @rd.kgio_tryread(2) end def test_tryread_empty assert_equal :wait_readable, @rd.kgio_tryread(1) end def test_read_too_much assert_equal nil, @wr.kgio_write("hi") assert_equal "hi", @rd.kgio_read(4) end def test_tryread_too_much assert_equal nil, @wr.kgio_trywrite("hi") assert_equal @rd, @rd.kgio_wait_readable assert_equal "hi", @rd.kgio_tryread(4) end def test_read_short assert_equal nil, @wr.kgio_write("hi") assert_equal "h", @rd.kgio_read(1) assert_equal "i", @rd.kgio_read(1) end def test_tryread_short assert_equal nil, @wr.kgio_trywrite("hi") IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_equal "h", @rd.kgio_tryread(1) assert_equal "i", @rd.kgio_tryread(1) end def test_read_extra_buf tmp = "" tmp_object_id = tmp.object_id assert_equal nil, @wr.kgio_write("hi") rv = @rd.kgio_read(2, tmp) assert_equal "hi", rv assert_equal rv.object_id, tmp.object_id assert_equal tmp_object_id, rv.object_id end def test_trywrite_return_wait_writable tmp = [] tmp << @wr.kgio_trywrite("HI") until tmp[-1] == :wait_writable assert :wait_writable === tmp[-1] assert(!(:wait_readable === tmp[-1])) assert_equal :wait_writable, tmp.pop assert tmp.size > 0 penultimate = tmp.pop assert(penultimate == "I" || penultimate == nil) assert tmp.size > 0 tmp.each { |count| assert_equal nil, count } end def test_trywritev_return_wait_writable tmp = [] tmp << @wr.kgio_trywritev(["HI"]) until tmp[-1] == :wait_writable assert :wait_writable === tmp[-1] assert(!(:wait_readable === tmp[-1])) assert_equal :wait_writable, tmp.pop assert tmp.size > 0 penultimate = tmp.pop assert(penultimate == "I" || penultimate == nil) assert tmp.size > 0 tmp.each { |count| assert_equal nil, count } end def test_tryread_extra_buf_eagain_clears_buffer tmp = "hello world" rv = @rd.kgio_tryread(2, tmp) assert_equal :wait_readable, rv assert_equal "", tmp end def test_tryread_extra_buf_eof_clears_buffer tmp = "hello world" @wr.close IO.select([@rd]) # this seems needed on FreeBSD 9.0 assert_nil @rd.kgio_tryread(2, tmp) assert_equal "", tmp end def test_monster_trywrite buf = RANDOM_BLOB.dup rv = @wr.kgio_trywrite(buf) assert_kind_of String, rv assert rv.size < buf.size @rd.nonblock = false assert_equal(buf, @rd.read(buf.size - rv.size) + rv) end def test_monster_write buf = RANDOM_BLOB.dup thr = Thread.new { @wr.kgio_write(buf) } @rd.nonblock = false readed = @rd.read(buf.size) thr.join assert_nil thr.value assert_equal buf, readed end def test_monster_trywritev buf, start = [], 0 while start < RANDOM_BLOB.size s = RANDOM_BLOB[start, 1000] start += s.size buf << s end rv = @wr.kgio_trywritev(buf) assert_kind_of Array, rv rv = rv.join assert rv.size < RANDOM_BLOB.size @rd.nonblock = false assert_equal(RANDOM_BLOB, @rd.read(RANDOM_BLOB.size - rv.size) + rv) end def test_monster_writev buf, start = [], 0 while start < RANDOM_BLOB.size s = RANDOM_BLOB[start, 10000] start += s.size buf << s end thr = Thread.new { @wr.kgio_writev(buf) } @rd.nonblock = false readed = @rd.read(RANDOM_BLOB.size) thr.join assert_nil thr.value e = (RANDOM_BLOB == readed) assert e end def test_monster_write_wait_writable @wr.instance_variable_set :@nr, 0 def @wr.kgio_wait_writable @nr += 1 IO.select(nil, [self]) end buf = "." * 1024 * 1024 * 10 thr = Thread.new { @wr.kgio_write(buf) } Thread.pass until thr.stop? readed = @rd.read(buf.size) thr.join assert_nil thr.value assert_equal buf, readed assert @wr.instance_variable_get(:@nr) > 0 end def test_monster_writev_wait_writable @wr.instance_variable_set :@nr, 0 def @wr.kgio_wait_writable @nr += 1 IO.select(nil, [self]) end buf = ["." * 1024] * 1024 * 10 buf_size = buf.inject(0){|c, s| c + s.size} thr = Thread.new { @wr.kgio_writev(buf) } Thread.pass until thr.stop? readed = @rd.read(buf_size) thr.join assert_nil thr.value e = (buf.join == readed) assert e assert @wr.instance_variable_get(:@nr) > 0 end def test_wait_readable_ruby_default elapsed = 0 foo = nil t0 = Time.now thr = Thread.new { sleep 1; @wr.write "HELLO" } foo = @rd.kgio_read(5) elapsed = Time.now - t0 assert elapsed >= 1.0, "elapsed: #{elapsed}" assert_equal "HELLO", foo thr.join assert_equal 5, thr.value end def test_wait_writable_ruby_default buf = "." * 512 nr = 0 begin nr += @wr.write_nonblock(buf) rescue Errno::EAGAIN break end while true elapsed = 0 foo = nil t0 = Time.now thr = Thread.new { sleep 1; @rd.read(nr) } foo = @wr.kgio_write("HELLO") elapsed = Time.now - t0 assert_nil foo if @wr.stat.pipe? assert elapsed >= 1.0, "elapsed: #{elapsed}" end assert(String === foo || foo == nil) assert_kind_of String, thr.value end def test_wait_readable_method def @rd.kgio_wait_readable defined?(@z) ? raise(RuntimeError, "Hello") : @z = "HI" end foo = nil begin foo = @rd.kgio_read(5) assert false rescue RuntimeError => e assert_equal("Hello", e.message) end assert_equal "HI", @rd.instance_variable_get(:@z) assert_nil foo end def test_tryread_wait_readable_method def @rd.kgio_wait_readable raise "Hello" end assert_equal :wait_readable, @rd.kgio_tryread(5) end def test_trywrite_wait_readable_method def @wr.kgio_wait_writable raise "Hello" end buf = "." * 4096 rv = nil until rv == :wait_writable rv = @wr.kgio_trywrite(buf) end assert_equal :wait_writable, rv end def test_wait_writable_method def @wr.kgio_wait_writable defined?(@z) ? raise(RuntimeError, "Hello") : @z = "HI" end n = [] begin loop { n << @wr.kgio_write("HIHIHIHIHIHI") } assert false rescue RuntimeError => e assert_equal("Hello", e.message) end assert n.size > 0 assert_equal "HI", @wr.instance_variable_get(:@z) end end kgio-2.8.1/test/test_tcp_server_read_client_write.rb0000644000004100000410000000050112214142252022754 0ustar www-datawww-datarequire './test/lib_read_write' class TesTcpServerReadClientWrite < Test::Unit::TestCase def setup @host = ENV["TEST_HOST"] || '127.0.0.1' @srv = Kgio::TCPServer.new(@host, 0) @port = @srv.addr[1] @wr = Kgio::TCPSocket.new(@host, @port) @rd = @srv.kgio_accept end include LibReadWriteTest end kgio-2.8.1/test/test_accept_class.rb0000644000004100000410000000330212214142252017463 0ustar www-datawww-datarequire 'test/unit' require 'io/nonblock' $-w = true require 'kgio' class TestAcceptClass < Test::Unit::TestCase class FooSocket < Kgio::Socket end def setup assert_equal Kgio::Socket, Kgio.accept_class end def teardown Kgio.accept_class = nil assert_equal Kgio::Socket, Kgio.accept_class end def test_tcp_socket Kgio.accept_class = Kgio::TCPSocket assert_equal Kgio::TCPSocket, Kgio.accept_class end def test_invalid assert_raises(TypeError) { Kgio.accept_class = TCPSocket } assert_equal Kgio::Socket, Kgio.accept_class end def test_accepted_class @host = ENV["TEST_HOST"] || '127.0.0.1' @srv = Kgio::TCPServer.new(@host, 0) @port = @srv.addr[1] Kgio.accept_class = Kgio::TCPSocket client = TCPSocket.new(@host, @port) assert_instance_of Kgio::TCPSocket, @srv.kgio_accept client = TCPSocket.new(@host, @port) IO.select([@srv]) assert_instance_of Kgio::TCPSocket, @srv.kgio_tryaccept Kgio.accept_class = nil client = TCPSocket.new(@host, @port) assert_instance_of Kgio::Socket, @srv.kgio_accept client = TCPSocket.new(@host, @port) IO.select([@srv]) assert_instance_of Kgio::Socket, @srv.kgio_tryaccept Kgio.accept_class = Kgio::UNIXSocket client = TCPSocket.new(@host, @port) assert_instance_of Kgio::UNIXSocket, @srv.kgio_accept client = TCPSocket.new(@host, @port) IO.select([@srv]) assert_instance_of Kgio::UNIXSocket, @srv.kgio_tryaccept client = TCPSocket.new(@host, @port) assert_instance_of FooSocket, @srv.kgio_accept(FooSocket) client = TCPSocket.new(@host, @port) IO.select([@srv]) assert_instance_of FooSocket, @srv.kgio_tryaccept(FooSocket) end end kgio-2.8.1/test/test_unix_server.rb0000644000004100000410000000063412214142252017415 0ustar www-datawww-datarequire 'tempfile' require 'tmpdir' require './test/lib_server_accept' class TestKgioUNIXServer < Test::Unit::TestCase def setup @tmpdir = Dir.mktmpdir('kgio_unix_2') tmp = Tempfile.new('kgio_unix_2', @tmpdir) @path = tmp.path tmp.close! @srv = Kgio::UNIXServer.new(@path) @host = '127.0.0.1' end def client_connect UNIXSocket.new(@path) end include LibServerAccept end kgio-2.8.1/test/test_cross_thread_close.rb0000644000004100000410000000113112214142252020702 0ustar www-datawww-datarequire 'test/unit' $-w = true require 'kgio' class TestCrossThreadClose < Test::Unit::TestCase def test_cross_thread_close host = ENV["TEST_HOST"] || '127.0.0.1' srv = Kgio::TCPServer.new(host, 0) thr = Thread.new do begin srv.kgio_accept rescue => e e end end sleep(0.1) until thr.stop? srv.close unless defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" && RUBY_VERSION == "1.9.3" thr.run rescue nil end thr.join assert_kind_of IOError, thr.value end end if defined?(RUBY_ENGINE) && RUBY_ENGINE == "ruby" kgio-2.8.1/test/test_tcp_client_read_server_write.rb0000644000004100000410000000050112214142252022754 0ustar www-datawww-datarequire './test/lib_read_write' class TesTcpClientReadServerWrite < Test::Unit::TestCase def setup @host = ENV["TEST_HOST"] || '127.0.0.1' @srv = Kgio::TCPServer.new(@host, 0) @port = @srv.addr[1] @wr = Kgio::TCPSocket.new(@host, @port) @rd = @srv.kgio_accept end include LibReadWriteTest end kgio-2.8.1/test/test_connect_fd_leak.rb0000644000004100000410000000055012214142252020137 0ustar www-datawww-datarequire 'test/unit' require 'io/nonblock' $-w = true require 'kgio' class TestConnectFDLeak < Test::Unit::TestCase def test_unix_socket nr = 0 path = "/non/existent/path" assert(! File.exist?(path), "#{path} should not exist") begin sock = Kgio::UNIXSocket.new(path) rescue Errno::ENOENT end while (nr += 1) < 10000 end end kgio-2.8.1/test/test_tcp_server.rb0000644000004100000410000000045712214142252017223 0ustar www-datawww-datarequire './test/lib_server_accept' class TestKgioTCPServer < Test::Unit::TestCase def setup @host = ENV["TEST_HOST"] || '127.0.0.1' @srv = Kgio::TCPServer.new(@host, 0) @port = @srv.addr[1] end def client_connect TCPSocket.new(@host, @port) end include LibServerAccept end kgio-2.8.1/test/test_tcp6_client_read_server_write.rb0000644000004100000410000000116712214142252023053 0ustar www-datawww-datarequire './test/lib_read_write' begin tmp = TCPServer.new(ENV["TEST_HOST6"] || '::1', 0) ipv6_enabled = true rescue => e warn "skipping IPv6 tests, host does not seem to be IPv6 enabled:" warn " #{e.class}: #{e}" ipv6_enabled = false end class TestTcp6ClientReadServerWrite < Test::Unit::TestCase def setup @host = ENV["TEST_HOST6"] || '::1' @srv = Kgio::TCPServer.new(@host, 0) @port = @srv.addr[1] @wr = Kgio::TCPSocket.new(@host, @port) @rd = @srv.kgio_accept assert_equal Socket.unpack_sockaddr_in(@rd.getpeername)[-1], @rd.kgio_addr end include LibReadWriteTest end if ipv6_enabled kgio-2.8.1/test/test_poll.rb0000644000004100000410000000625112214142252016013 0ustar www-datawww-datarequire 'test/unit' $-w = true require 'kgio' class TestPoll < Test::Unit::TestCase def teardown [ @rd, @wr ].each { |io| io.close unless io.closed? } end def setup @rd, @wr = IO.pipe end def test_constants assert_kind_of Integer, Kgio::POLLIN assert_kind_of Integer, Kgio::POLLOUT assert_kind_of Integer, Kgio::POLLPRI assert_kind_of Integer, Kgio::POLLHUP assert_kind_of Integer, Kgio::POLLERR assert_kind_of Integer, Kgio::POLLNVAL end def test_poll_symbol set = { @rd => :wait_readable, @wr => :wait_writable } res = Kgio.poll(set) assert_equal({@wr => Kgio::POLLOUT}, res) assert_equal set.object_id, res.object_id end def test_poll_integer set = { @wr => Kgio::POLLOUT|Kgio::POLLHUP } res = Kgio.poll(set) assert_equal({@wr => Kgio::POLLOUT}, res) assert_equal set.object_id, res.object_id end def test_poll_timeout t0 = Time.now res = Kgio.poll({}, 10) diff = Time.now - t0 assert diff >= 0.010, "diff=#{diff}" assert_nil res end def test_poll_close foo = nil thr = Thread.new { sleep 0.100; @wr.close } t0 = Time.now res = Kgio.poll({@rd => Kgio::POLLIN}) diff = Time.now - t0 thr.join assert_equal([ @rd ], res.keys) assert diff >= 0.010, "diff=#{diff}" end def test_signal_close orig = trap(:USR1) { @rd.close } res = nil thr = Thread.new { sleep 0.100; Process.kill(:USR1, $$) } t0 = Time.now assert_raises(IOError) do result = Kgio.poll({@rd => Kgio::POLLIN}) result.each_key { |io| io.read_nonblock(1) } end diff = Time.now - t0 thr.join assert diff >= 0.010, "diff=#{diff}" ensure trap(:USR1, orig) end def test_poll_EINTR ok = false orig = trap(:USR1) { ok = true } thr = Thread.new do sleep 0.100 Process.kill(:USR1, $$) end t0 = Time.now res = Kgio.poll({@rd => Kgio::POLLIN}, 1000) diff = Time.now - t0 thr.join assert_nil res assert diff >= 1.0, "diff=#{diff}" assert ok ensure trap(:USR1, orig) end def test_poll_EINTR_changed ok = false pollset = { @rd => Kgio::POLLIN } orig = trap(:USR1) do pollset[@wr] = Kgio::POLLOUT ok = true end thr = Thread.new do sleep 0.100 100.times do Process.kill(:USR1, $$) Thread.pass end end t0 = Time.now res = Kgio.poll(pollset, 1000) diff = Time.now - t0 thr.join assert_equal({@wr => Kgio::POLLOUT}, res) assert diff < 1.0, "diff=#{diff}" assert ok ensure trap(:USR1, orig) end def test_poll_signal_torture usr1 = 0 empty = 0 nr = 100 set = { @rd => Kgio::POLLIN } orig = trap(:USR1) { usr1 += 1 } pid = fork do trap(:USR1, "DEFAULT") sleep 0.1 ppid = Process.ppid nr.times { Process.kill(:USR1, ppid); sleep 0.05 } @wr.syswrite('.') exit!(0) end empty += 1 until Kgio.poll(set.dup, 100) _, status = Process.waitpid2(pid) assert status.success?, status.inspect assert usr1 > 0, "usr1: #{usr1}" ensure trap(:USR1, orig) end unless RUBY_PLATFORM =~ /kfreebsd-gnu/ end if Kgio.respond_to?(:poll) kgio-2.8.1/test/test_unix_connect.rb0000644000004100000410000000401712214142252017537 0ustar www-datawww-datarequire 'test/unit' require 'io/nonblock' $-w = true require 'kgio' require 'tempfile' require 'tmpdir' class SubSocket < Kgio::Socket attr_accessor :foo def kgio_wait_writable @foo = "waited" end end class TestKgioUnixConnect < Test::Unit::TestCase def setup @tmpdir = Dir.mktmpdir('kgio_unix_1') tmp = Tempfile.new('kgio_unix_1', @tmpdir) @path = tmp.path tmp.close! @srv = Kgio::UNIXServer.new(@path) @addr = Socket.pack_sockaddr_un(@path) end def teardown @srv.close unless @srv.closed? File.unlink(@path) FileUtils.remove_entry_secure(@tmpdir) Kgio.accept_cloexec = true end def test_unix_socket_new_invalid assert_raises(ArgumentError) { Kgio::UNIXSocket.new('*' * 1024 * 1024) } end def test_unix_socket_new sock = Kgio::UNIXSocket.new(@path) sock.respond_to?(:close_on_exec?) and assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?) assert_instance_of Kgio::UNIXSocket, sock ready = IO.select(nil, [ sock ]) assert_equal sock, ready[1][0] assert_equal nil, sock.kgio_write("HELLO") end def test_new sock = Kgio::Socket.new(@addr) sock.respond_to?(:close_on_exec?) and assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?) assert_instance_of Kgio::Socket, sock ready = IO.select(nil, [ sock ]) assert_equal sock, ready[1][0] assert_equal nil, sock.kgio_write("HELLO") end def test_start sock = Kgio::Socket.start(@addr) assert_instance_of Kgio::Socket, sock ready = IO.select(nil, [ sock ]) assert_equal sock, ready[1][0] assert_equal nil, sock.kgio_write("HELLO") end def test_socket_start sock = SubSocket.start(@addr) assert_nil sock.foo ready = IO.select(nil, [ sock ]) assert_equal sock, ready[1][0] assert_equal nil, sock.kgio_write("HELLO") end def test_wait_writable_set sock = SubSocket.new(@addr) assert_kind_of Kgio::Socket, sock assert_instance_of SubSocket, sock assert_equal nil, sock.kgio_write("HELLO") end end kgio-2.8.1/test/test_tcp_connect.rb0000644000004100000410000000442112214142252017341 0ustar www-datawww-datarequire 'test/unit' require 'io/nonblock' $-w = true require 'kgio' class SubSocket < Kgio::Socket attr_accessor :foo def kgio_wait_writable @foo = "waited" end end class TestKgioTcpConnect < Test::Unit::TestCase def setup @host = ENV["TEST_HOST"] || '127.0.0.1' @srv = Kgio::TCPServer.new(@host, 0) @port = @srv.addr[1] @addr = Socket.pack_sockaddr_in(@port, @host) end def teardown @srv.close unless @srv.closed? Kgio.accept_cloexec = true Kgio.accept_nonblock = false end def test_new sock = Kgio::Socket.new(@addr) assert_kind_of Kgio::Socket, sock ready = IO.select(nil, [ sock ]) assert_equal sock, ready[1][0] assert_equal nil, sock.kgio_write("HELLO") sock.respond_to?(:close_on_exec?) and assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?) end def test_start sock = Kgio::Socket.start(@addr) sock.respond_to?(:close_on_exec?) and assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?) assert_kind_of Kgio::Socket, sock ready = IO.select(nil, [ sock ]) assert_equal sock, ready[1][0] assert_equal nil, sock.kgio_write("HELLO") end def test_tcp_socket_new_invalid assert_raises(ArgumentError) { Kgio::TCPSocket.new('example.com', 80) } assert_raises(ArgumentError) { Kgio::TCPSocket.new('999.999.999.999', 80) } assert_raises(TypeError) { Kgio::TCPSocket.new("127.0.0.1", "http") } assert_raises(TypeError) { Kgio::TCPSocket.new('example.com', "http") } end def test_tcp_socket_new sock = Kgio::TCPSocket.new(@host, @port) sock.respond_to?(:close_on_exec?) and assert_equal(RUBY_VERSION.to_f >= 2.0, sock.close_on_exec?) assert_instance_of Kgio::TCPSocket, sock ready = IO.select(nil, [ sock ]) assert_equal sock, ready[1][0] assert_equal nil, sock.kgio_write("HELLO") end def test_socket_start sock = SubSocket.start(@addr) assert_nil sock.foo ready = IO.select(nil, [ sock ]) assert_equal sock, ready[1][0] assert_equal nil, sock.kgio_write("HELLO") end def test_wait_writable_set sock = SubSocket.new(@addr) assert_equal "waited", sock.foo if RUBY_PLATFORM =~ /linux/ IO.select(nil, [sock]) if RUBY_PLATFORM !~ /linux/ assert_equal nil, sock.kgio_write("HELLO") end end kgio-2.8.1/test/test_autopush.rb0000644000004100000410000001070112214142252016710 0ustar www-datawww-datarequire 'tempfile' require 'test/unit' begin $-w = false RUBY_PLATFORM =~ /linux/ and require 'strace' rescue LoadError end $-w = true require 'kgio' class TestAutopush < Test::Unit::TestCase TCP_CORK = 3 TCP_NOPUSH = 4 def setup Kgio.autopush = false assert_equal false, Kgio.autopush? @host = ENV["TEST_HOST"] || '127.0.0.1' @srv = Kgio::TCPServer.new(@host, 0) RUBY_PLATFORM =~ /linux/ and @srv.setsockopt(Socket::IPPROTO_TCP, TCP_CORK, 1) RUBY_PLATFORM =~ /freebsd/ and @srv.setsockopt(Socket::IPPROTO_TCP, TCP_NOPUSH, 1) @port = @srv.addr[1] end def test_autopush_accessors Kgio.autopush = true opt = RUBY_PLATFORM =~ /freebsd/ ? TCP_NOPUSH : TCP_CORK s = Kgio::TCPSocket.new(@host, @port) assert_equal 0, s.getsockopt(Socket::IPPROTO_TCP, opt).unpack('i')[0] assert ! s.kgio_autopush? s.kgio_autopush = true assert s.kgio_autopush? s.kgio_write 'asdf' assert_equal :wait_readable, s.kgio_tryread(1) assert s.kgio_autopush? val = s.getsockopt(Socket::IPPROTO_TCP, opt).unpack('i')[0] assert_operator val, :>, 0, "#{opt}=#{val} (#{RUBY_PLATFORM})" end def test_autopush_true_unix Kgio.autopush = true tmp = Tempfile.new('kgio_unix') @path = tmp.path tmp.close! @srv = Kgio::UNIXServer.new(@path) @rd = Kgio::UNIXSocket.new(@path) t0 = nil if defined?(Strace) io, err = Strace.me { @wr = @srv.kgio_accept } assert_nil err rc = nil io, err = Strace.me { t0 = Time.now @wr.kgio_write "HI\n" rc = @wr.kgio_tryread 666 } assert_nil err lines = io.readlines assert lines.grep(/TCP_CORK/).empty?, lines.inspect else @wr = @srv.kgio_accept t0 = Time.now @wr.kgio_write "HI\n" rc = @wr.kgio_tryread 666 end assert_equal "HI\n", @rd.kgio_read(3) diff = Time.now - t0 assert(diff < 0.200, "nopush on UNIX sockets? diff=#{diff} > 200ms") assert_equal :wait_readable, rc ensure File.unlink(@path) rescue nil end def test_autopush_false Kgio.autopush = nil assert_equal false, Kgio.autopush? @wr = Kgio::TCPSocket.new(@host, @port) if defined?(Strace) io, err = Strace.me { @rd = @srv.kgio_accept } assert_nil err lines = io.readlines assert lines.grep(/TCP_CORK/).empty?, lines.inspect assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0] else @rd = @srv.kgio_accept end rbuf = "..." t0 = Time.now @rd.kgio_write "HI\n" @wr.kgio_read(3, rbuf) diff = Time.now - t0 assert(diff >= 0.190, "nopush broken? diff=#{diff} > 200ms") assert_equal "HI\n", rbuf end def test_autopush_true Kgio.autopush = true assert_equal true, Kgio.autopush? @wr = Kgio::TCPSocket.new(@host, @port) if defined?(Strace) io, err = Strace.me { @rd = @srv.kgio_accept } assert_nil err lines = io.readlines assert_equal 1, lines.grep(/TCP_CORK/).size, lines.inspect assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0] else @rd = @srv.kgio_accept end @wr.write "HI\n" rbuf = "" if defined?(Strace) io, err = Strace.me { @rd.kgio_read(3, rbuf) } assert_nil err lines = io.readlines assert lines.grep(/TCP_CORK/).empty?, lines.inspect assert_equal "HI\n", rbuf else assert_equal "HI\n", @rd.kgio_read(3, rbuf) end t0 = Time.now @rd.kgio_write "HI2U2\n" @rd.kgio_write "HOW\n" rc = false if defined?(Strace) io, err = Strace.me { rc = @rd.kgio_tryread(666) } else rc = @rd.kgio_tryread(666) end @wr.readpartial(666, rbuf) rbuf == "HI2U2\nHOW\n" or warn "rbuf=#{rbuf.inspect} looking bad?" diff = Time.now - t0 assert(diff < 0.200, "time diff=#{diff} >= 200ms") assert_equal :wait_readable, rc if defined?(Strace) assert_nil err lines = io.readlines assert_equal 2, lines.grep(/TCP_CORK/).size, lines.inspect end @wr.close @rd.close @wr = Kgio::TCPSocket.new(@host, @port) if defined?(Strace) io, err = Strace.me { @rd = @srv.kgio_accept } assert_nil err lines = io.readlines assert lines.grep(/TCP_CORK/).empty?,"optimization fail: #{lines.inspect}" assert_equal 1, @rd.getsockopt(Socket::SOL_TCP, TCP_CORK).unpack("i")[0] end end def teardown Kgio.autopush = false end end if RUBY_PLATFORM =~ /linux|freebsd/ kgio-2.8.1/test/test_default_wait.rb0000644000004100000410000000175712214142252017523 0ustar www-datawww-datarequire 'test/unit' require 'io/nonblock' $-w = true require 'kgio' class TestDefaultWait < Test::Unit::TestCase def test_socket_pair a, b = Kgio::UNIXSocket.pair assert_equal a, a.kgio_wait_writable a.syswrite('.') assert_equal b, b.kgio_wait_readable end def test_pipe a, b = Kgio::Pipe.new assert_equal b, b.kgio_wait_writable b.syswrite('.') assert_equal a, a.kgio_wait_readable end def test_wait_readable_timed a, b = Kgio::Pipe.new t0 = Time.now assert_nil a.kgio_wait_readable(1.1) diff = Time.now - t0 assert_in_delta diff, 1.1, 0.2 b.kgio_write '.' assert_equal a, a.kgio_wait_readable(1.1) end def test_wait_writable_timed a, b = Kgio::Pipe.new buf = "*" * 65536 true until Symbol === b.kgio_trywrite(buf) t0 = Time.now assert_nil b.kgio_wait_writable(1.1) diff = Time.now - t0 assert_in_delta diff, 1.1, 0.2 a.kgio_read(16384) assert_equal b, b.kgio_wait_writable(1.1) end end kgio-2.8.1/test/test_accept_flags.rb0000644000004100000410000000277312214142252017465 0ustar www-datawww-datarequire 'test/unit' require 'fcntl' require 'io/nonblock' $-w = true require 'kgio' class TestAcceptFlags < Test::Unit::TestCase def test_accept_flags @host = ENV["TEST_HOST"] || '127.0.0.1' @srv = Kgio::TCPServer.new(@host, 0) @port = @srv.addr[1] client = TCPSocket.new(@host, @port) accepted = @srv.kgio_accept(nil, Kgio::SOCK_NONBLOCK) assert_instance_of Kgio::Socket, accepted flags = accepted.fcntl(Fcntl::F_GETFD) assert_equal 0, flags & Fcntl::FD_CLOEXEC assert_nil client.close assert_nil accepted.close client = TCPSocket.new(@host, @port) accepted = @srv.kgio_accept(nil, Kgio::SOCK_CLOEXEC) assert_instance_of Kgio::Socket, accepted flags = accepted.fcntl(Fcntl::F_GETFD) assert_equal Fcntl::FD_CLOEXEC, flags & Fcntl::FD_CLOEXEC assert_nil client.close assert_nil accepted.close client = TCPSocket.new(@host, @port) accepted = @srv.kgio_accept(nil, Kgio::SOCK_CLOEXEC|Kgio::SOCK_NONBLOCK) assert_instance_of Kgio::Socket, accepted flags = accepted.fcntl(Fcntl::F_GETFD) assert_equal Fcntl::FD_CLOEXEC, flags & Fcntl::FD_CLOEXEC assert_nil client.close assert_nil accepted.close client = TCPSocket.new(@host, @port) accepted = @srv.kgio_accept(nil, Kgio::SOCK_CLOEXEC|Kgio::SOCK_NONBLOCK) assert_instance_of Kgio::Socket, accepted flags = accepted.fcntl(Fcntl::F_GETFD) assert_equal Fcntl::FD_CLOEXEC, flags & Fcntl::FD_CLOEXEC assert_nil client.close assert_nil accepted.close end end kgio-2.8.1/test/lib_server_accept.rb0000644000004100000410000000433112214142252017456 0ustar www-datawww-datarequire 'test/unit' require 'fcntl' require 'io/nonblock' require 'fileutils' $-w = true require 'kgio' module LibServerAccept def teardown @srv.close unless @srv.closed? FileUtils.remove_entry_secure(@tmpdir) if defined?(@tmpdir) Kgio.accept_cloexec = true Kgio.accept_nonblock = false end def test_tryaccept_success a = client_connect IO.select([@srv]) b = @srv.kgio_tryaccept assert_kind_of Kgio::Socket, b assert_equal @host, b.kgio_addr end def test_tryaccept_flags a = client_connect IO.select([@srv]) b = @srv.kgio_tryaccept nil, 0 assert_kind_of Kgio::Socket, b assert_equal 0, b.fcntl(Fcntl::F_GETFD) end def test_blocking_accept_flags a = client_connect IO.select([@srv]) b = @srv.kgio_accept nil, 0 assert_kind_of Kgio::Socket, b assert_equal 0, b.fcntl(Fcntl::F_GETFD) end def test_tryaccept_fail assert_equal nil, @srv.kgio_tryaccept end def test_blocking_accept t0 = Time.now pid = fork { sleep 1; a = client_connect; sleep } b = @srv.kgio_accept elapsed = Time.now - t0 assert_kind_of Kgio::Socket, b assert_equal @host, b.kgio_addr Process.kill(:KILL, pid) Process.waitpid(pid) assert elapsed >= 1, "elapsed: #{elapsed}" end def test_blocking_accept_with_nonblock_socket @srv.nonblock = true t0 = Time.now pid = fork { sleep 1; a = client_connect; sleep } b = @srv.kgio_accept elapsed = Time.now - t0 assert_kind_of Kgio::Socket, b assert_equal @host, b.kgio_addr Process.kill(:KILL, pid) Process.waitpid(pid) assert elapsed >= 1, "elapsed: #{elapsed}" t0 = Time.now pid = fork { sleep 6; a = client_connect; sleep } b = @srv.kgio_accept elapsed = Time.now - t0 assert_kind_of Kgio::Socket, b assert_equal @host, b.kgio_addr Process.kill(:KILL, pid) Process.waitpid(pid) assert elapsed >= 6, "elapsed: #{elapsed}" t0 = Time.now pid = fork { sleep 1; a = client_connect; sleep } b = @srv.kgio_accept elapsed = Time.now - t0 assert_kind_of Kgio::Socket, b assert_equal @host, b.kgio_addr Process.kill(:KILL, pid) Process.waitpid(pid) assert elapsed >= 1, "elapsed: #{elapsed}" end end kgio-2.8.1/test/test_singleton_read_write.rb0000644000004100000410000000102712214142252021250 0ustar www-datawww-datarequire 'test/unit' $-w = true require 'kgio' class TestSingletonReadWrite < Test::Unit::TestCase def test_unix_socketpair a, b = UNIXSocket.pair Kgio.trywrite(a, "HELLO") buf = "" assert_equal "HELLO", Kgio.tryread(b, 5, buf) assert_equal "HELLO", buf assert_equal :wait_readable, Kgio.tryread(b, 5) end def test_arg_error assert_raises(ArgumentError) { Kgio.tryread } assert_raises(ArgumentError) { Kgio.tryread($stdin) } assert_raises(ArgumentError) { Kgio.trywrite($stdout) } end end kgio-2.8.1/test/test_tfo.rb0000644000004100000410000000345512214142252015640 0ustar www-datawww-datarequire 'test/unit' require 'kgio' class TestTFO < Test::Unit::TestCase def test_constants if `uname -s`.chomp == "Linux" && `uname -r`.to_f >= 3.7 assert_equal 23, Kgio::TCP_FASTOPEN assert_equal 0x20000000, Kgio::MSG_FASTOPEN end end def fastopen_ok? if RUBY_PLATFORM =~ /linux/ tfo = File.read("/proc/sys/net/ipv4/tcp_fastopen").to_i client_enable = 1 server_enable = 2 enable = client_enable | server_enable (tfo & enable) == enable else false end end def test_tfo_client_server unless fastopen_ok? warn "TCP Fast Open not enabled on this system (check kernel docs)" return end addr = '127.0.0.1' qlen = 1024 s = Kgio::TCPServer.new(addr, 0) s.setsockopt(:TCP, Kgio::TCP_FASTOPEN, qlen) port = s.local_address.ip_port addr = Socket.pack_sockaddr_in(port, addr) c = Kgio::Socket.new(:INET, :STREAM) assert_nil c.kgio_fastopen("HELLO", addr) a = s.accept assert_equal "HELLO", a.read(5) c.close a.close # ensure empty sends work c = Kgio::Socket.new(:INET, :STREAM) assert_nil c.kgio_fastopen("", addr) a = s.accept Thread.new { c.close } assert_nil a.read(1) a.close # try a monster packet buf = 'x' * (1024 * 1024 * 320) c = Kgio::Socket.new(:INET, :STREAM) thr = Thread.new do a = s.accept assert_equal buf.size, a.read(buf.size).size a.close end assert_nil c.kgio_fastopen(buf, addr) thr.join c.close # allow timeouts c = Kgio::Socket.new(:INET, :STREAM) c.setsockopt(:SOCKET, :SNDTIMEO, [ 0, 10 ].pack("l_l_")) unsent = c.kgio_fastopen(buf, addr) c.close assert_equal s.accept.read.size + unsent.size, buf.size end if defined?(Addrinfo) && defined?(Kgio::TCP_FASTOPEN) end kgio-2.8.1/test/test_unix_client_read_server_write.rb0000644000004100000410000000063312214142252023157 0ustar www-datawww-datarequire './test/lib_read_write' require 'tempfile' require 'tmpdir' class TestUnixClientReadServerWrite < Test::Unit::TestCase def setup @tmpdir = Dir.mktmpdir('kgio_unix_0') tmp = Tempfile.new('kgio_unix_0', @tmpdir) @path = tmp.path tmp.close! @srv = Kgio::UNIXServer.new(@path) @rd = Kgio::UNIXSocket.new(@path) @wr = @srv.kgio_tryaccept end include LibReadWriteTest end kgio-2.8.1/test/test_pipe_read_write.rb0000644000004100000410000000023612214142252020204 0ustar www-datawww-datarequire './test/lib_read_write.rb' class TestKgioPipe < Test::Unit::TestCase def setup @rd, @wr = Kgio::Pipe.new end include LibReadWriteTest end kgio-2.8.1/test/test_peek.rb0000644000004100000410000000150512214142252015766 0ustar www-datawww-datarequire 'test/unit' $-w = true require 'kgio' class TestPeek < Test::Unit::TestCase class EIEIO < Errno::EIO end def teardown @rd.close @wr.close end def test_peek @rd, @wr = Kgio::UNIXSocket.pair @wr.kgio_write "HELLO" assert_equal "HELLO", @rd.kgio_peek(5) assert_equal "HELLO", @rd.kgio_trypeek(5) assert_equal "HELLO", @rd.kgio_read(5) assert_equal :wait_readable, @rd.kgio_trypeek(5) def @rd.kgio_wait_readable raise EIEIO end assert_raises(EIEIO) { @rd.kgio_peek(5) } end def test_peek_singleton @rd, @wr = UNIXSocket.pair @wr.syswrite "HELLO" assert_equal "HELLO", Kgio.trypeek(@rd, 666) assert_equal "HELLO", Kgio.trypeek(@rd, 666) assert_equal "HELLO", Kgio.tryread(@rd, 666) assert_equal :wait_readable, Kgio.trypeek(@rd, 5) end end kgio-2.8.1/test/test_pipe_popen.rb0000644000004100000410000000050512214142252017177 0ustar www-datawww-datarequire 'test/unit' require 'io/nonblock' $-w = true require 'kgio' class TestPipePopen < Test::Unit::TestCase def test_popen io = Kgio::Pipe.popen("sleep 1 && echo HI") assert_equal :wait_readable, io.kgio_tryread(2) sleep 1.5 assert_equal "HI\n", io.kgio_read(3) assert_nil io.kgio_read(5) end end kgio-2.8.1/test/test_no_dns_on_tcp_connect.rb0000644000004100000410000000050212214142252021371 0ustar www-datawww-datarequire 'test/unit' $-w = true require 'kgio' class TestNoDnsOnTcpConnect < Test::Unit::TestCase def test_connect_remote assert_raises(ArgumentError) { Kgio::TCPSocket.new("example.com", 666) } end def test_connect_localhost assert_raises(ArgumentError) { Kgio::TCPSocket.new("localhost", 666) } end end kgio-2.8.1/test/test_kgio_addr.rb0000644000004100000410000000102412214142252016761 0ustar www-datawww-data# -*- encoding: binary -*- require 'test/unit' $-w = true require 'kgio' class TestKgioAddr < Test::Unit::TestCase def test_tcp addr = ENV["TEST_HOST"] || '127.0.0.1' tcp = TCPServer.new(addr, 0) port = tcp.addr[1] client = Kgio::TCPSocket.new(addr, port) accepted = tcp.accept assert ! accepted.instance_eval { defined?(@kgio_addr) } accepted.extend Kgio::SocketMethods s = accepted.kgio_addr! assert_equal addr, s assert_equal addr, accepted.instance_variable_get(:@kgio_addr) end end kgio-2.8.1/test/test_tryopen.rb0000644000004100000410000000430512214142252016543 0ustar www-datawww-datarequire 'tempfile' require 'test/unit' $-w = true require 'kgio' class TestTryopen < Test::Unit::TestCase def test_tryopen_success tmp = Kgio::File.tryopen(__FILE__) tmp.respond_to?(:close_on_exec?) and assert_equal(RUBY_VERSION.to_f >= 2.0, tmp.close_on_exec?) assert_kind_of File, tmp assert_equal File.read(__FILE__), tmp.read assert_equal __FILE__, tmp.path assert_equal __FILE__, tmp.to_path tmp.close end def test_tryopen_ENOENT tmp = Tempfile.new "tryopen" path = tmp.path tmp.close! tmp = Kgio::File.tryopen(path) assert_equal :ENOENT, tmp end def test_tryopen_EACCES tmp = Tempfile.new "tryopen" File.chmod 0000, tmp.path tmp = Kgio::File.tryopen(tmp.path) if Process.euid == 0 assert_kind_of Kgio::File, tmp warn "cannot test EACCES when euid == 0" else assert_equal(:EACCES, tmp) end end def test_tryopen_readwrite tmp = Tempfile.new "tryopen" file = Kgio::File.tryopen(tmp.path, IO::RDWR) file.syswrite "FOO" assert_equal "FOO", tmp.sysread(3) end def test_tryopen_try_readwrite tmp = Tempfile.new "tryopen" file = Kgio::File.tryopen(tmp.path, IO::RDWR) assert_nil file.kgio_trywrite("FOO") file.rewind assert_equal "FOO", file.kgio_tryread(3) end def test_tryopen_mode tmp = Tempfile.new "tryopen" path = tmp.path tmp.close! file = Kgio::File.tryopen(path, IO::RDWR|IO::CREAT, 0000) assert_equal 0100000, File.stat(path).mode ensure File.unlink path end require "benchmark" def test_benchmark nr = 1000000 tmp = Tempfile.new('tryopen') file = tmp.path Benchmark.bmbm do |x| x.report("tryopen (OK)") do nr.times { Kgio::File.tryopen(file).close } end x.report("open (OK)") do nr.times { File.readable?(file) && File.open(file).close } end end tmp.close! assert_equal :ENOENT, Kgio::File.tryopen(file) Benchmark.bmbm do |x| x.report("tryopen (ENOENT)") do nr.times { Kgio::File.tryopen(file) } end x.report("open (ENOENT)") do nr.times { File.readable?(file) && File.open(file) } end end end if ENV["BENCHMARK"] end kgio-2.8.1/test/test_unix_server_read_client_write.rb0000644000004100000410000000063312214142252023157 0ustar www-datawww-datarequire './test/lib_read_write' require 'tempfile' require 'tmpdir' class TestUnixServerReadClientWrite < Test::Unit::TestCase def setup @tmpdir = Dir.mktmpdir('kgio_unix_3') tmp = Tempfile.new('kgio_unix_3', @tmpdir) @path = tmp.path tmp.close! @srv = Kgio::UNIXServer.new(@path) @wr = Kgio::UNIXSocket.new(@path) @rd = @srv.kgio_tryaccept end include LibReadWriteTest end kgio-2.8.1/test/test_socket.rb0000644000004100000410000000057612214142252016341 0ustar www-datawww-datarequire 'test/unit' require 'kgio' class TestKgioSocket < Test::Unit::TestCase def test_socket_args s = Kgio::Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0) assert_kind_of Socket, s assert_instance_of Kgio::Socket, s s = Kgio::Socket.new(Socket::AF_UNIX, Socket::SOCK_STREAM, 0) assert_kind_of Socket, s assert_instance_of Kgio::Socket, s end end kgio-2.8.1/.wrongdoc.yml0000644000004100000410000000026612214142252015125 0ustar www-datawww-data--- cgit_url: http://bogomips.org/kgio.git git_url: git://bogomips.org/kgio.git rdoc_url: http://bogomips.org/kgio/ public_email: kgio@librelist.org private_email: kgio@bogomips.org kgio-2.8.1/kgio.gemspec0000644000004100000410000000176512214142252015005 0ustar www-datawww-dataENV["VERSION"] or abort "VERSION= must be specified" manifest = File.readlines('.manifest').map! { |x| x.chomp! } require 'wrongdoc' extend Wrongdoc::Gemspec name, summary, title = readme_metadata Gem::Specification.new do |s| s.name = %q{kgio} s.version = ENV["VERSION"].dup s.homepage = Wrongdoc.config[:rdoc_url] s.authors = ["#{name} hackers"] s.date = Time.now.utc.strftime('%Y-%m-%d') s.description = readme_description s.email = %q{kgio@librelist.org} s.extra_rdoc_files = extra_rdoc_files(manifest) s.files = manifest s.rdoc_options = rdoc_options s.rubyforge_project = %q{rainbows} s.summary = summary s.test_files = Dir['test/test_*.rb'] s.extensions = %w(ext/kgio/extconf.rb) # development dependencies commented out for folks stuck on # old Ruby/RubyGems versions # s.add_development_dependency('wrongdoc', '~> 1.5') # s.add_development_dependency('strace_me', '~> 1.0') # Linux only # s.license = %w(LGPL) # disabled for compatibility with older RubyGems end kgio-2.8.1/LICENSE0000644000004100000410000000166712214142252013515 0ustar www-datawww-datakgio is copyrighted Free Software by all contributors, see logs in revision control for names and email addresses of all of them. You can redistribute it and/or modify it under the terms of the GNU Lesser General Public License (LGPL) as published by the Free Software Foundation, version {2.1}[http://www.gnu.org/licenses/lgpl-2.1.txt] or or {3}[http://www.gnu.org/licenses/lgpl-3.0.txt] (see link:COPYING). The kgio project leader (Eric Wong) reserves the right to relicense kgio under future versions of the LGPL. kgio is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with kgio; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 kgio-2.8.1/HACKING0000644000004100000410000000461412214142252013472 0ustar www-datawww-data= kgio Hacker's Guide === Documentation We use the latest version of {wrongdoc}[http://bogomips.org/wrongdoc] as much as possible. Please wrap documentation at 72 characters-per-line or less (long URLs are exempt) so it is comfortably readable from terminals. When referencing mailing list posts, use "http://mid.gmane.org/$MESSAGE_ID" if possible since the Message-ID remains searchable even if Gmane becomes unavailable. === Code Compatibility We target Ruby 1.8.6+, 1.9.1+ and Rubinius 1.1+ and their respective C APIs. All of our C code should be compatible with all reasonably modern Unices and should run on compilers supported by the versions of Ruby we target. We will NEVER directly support non-Free platforms under any circumstances. Our C code follows K&R indentation style (hard tabs, tabs are always 8 characters wide) and NOT the indentation style of Matz Ruby. == Contributing Contributions are welcome in the form of patches, pull requests, code review, testing, documentation, user support or any other feedback. The {kgio mailing list}[mailto:kgio@librelist.org] is the central coordination point for all user and developer feedback and bug reports. === Submitting Patches Follow conventions already established in the code and do not exceed 80 characters per line. Inline patches (from "git format-patch -M") to the mailing list are preferred because they allow code review and comments in the reply to the patch. We will adhere to mostly the same conventions for patch submissions as git itself. See the Documentation/SubmittingPatches document distributed with git on on patch submission guidelines to follow. Just don't email the git mailing list or maintainer with kgio patches :) == Running Development Versions It is easy to install the contents of your git working directory: Via RubyGems (RubyGems 1.3.5+ recommended for prerelease versions): gmake install-gem Without RubyGems (via setup.rb): gmake install It is not at all recommended to mix a RubyGems installation with an installation done without RubyGems, however. === Tests We use GNU make to run tests in parallel. test/unit/parallel didn't exist for old versions of Ruby before 1.9.3. Users of GNU-based systems (such as GNU/Linux) usually have GNU make installed as "make" instead of "gmake". Running the entire test suite with 4 tests in parallel: gmake -j4 test Running just one unit test: gmake test/test_poll.rb kgio-2.8.1/ISSUES0000644000004100000410000000217612214142252013442 0ustar www-datawww-data= Issues The kgio {mailing list}[mailto:kgio@librelist.org] is the best place to report bugs, submit patches and/or obtain support after you have searched the mailing list archives and {documentation}[http://bogomips.org/kgio]. * Do not {top post}[http://catb.org/jargon/html/T/top-post.html] in replies * Quote only the relevant portions of the message you're replying to * Do not send any HTML mail at all If your issue is of a sensitive nature or you're just shy in public, then feel free to email us privately at mailto:kgio@bogomips.org instead and your issue will be handled discreetly. If you don't get a response within a few days, we may have forgotten about it so feel free to ask again. == Submitting Patches See the HACKING document (and additionally, the Documentation/SubmittingPatches document distributed with git) on guidelines for patch submission. == Mailing List Info * subscribe: send a message to the mailing list * post: mailto:kgio@librelist.org * private: mailto:kgio@bogomips.org == Mailing List Archives (coming soon) * http://bogomips.org/kgio/archives/ * nntp://news.gmane.org/gmane.comp.lang.ruby.kgio.general kgio-2.8.1/Rakefile0000644000004100000410000000352312214142252014146 0ustar www-datawww-data# -*- encoding: binary -*- cgit_url = "http://bogomips.org/kgio.git" git_url = 'git://bogomips.org/kgio.git' desc "post news article to rubyforge" task :publish_news do require 'rubyforge' spec = Gem::Specification.load('kgio.gemspec') tmp = Tempfile.new('rf-news') _, subject, body = `git cat-file tag v#{spec.version}`.split(/\n\n/, 3) tmp.puts subject tmp.puts tmp.puts spec.description.strip tmp.puts "" tmp.puts "* #{spec.homepage}" tmp.puts "* #{spec.email}" tmp.puts "* #{git_url}" tmp.print "\nChanges:\n\n" tmp.puts body tmp.flush system(ENV["VISUAL"], tmp.path) or abort "#{ENV["VISUAL"]} failed: #$?" msg = File.readlines(tmp.path) subject = msg.shift blank = msg.shift blank == "\n" or abort "no newline after subject!" subject.strip! body = msg.join("").strip! rf = RubyForge.new.configure rf.login rf.post_news('rainbows', subject, body) end desc "post to RAA" task :raa_update do require 'net/http' require 'net/netrc' rc = Net::Netrc.locate('kgio-raa') or abort "~/.netrc not found" password = rc.password s = Gem::Specification.load('kgio.gemspec') desc = [ s.description.strip ] desc << "" desc << "* #{s.email}" desc << "* #{git_url}" desc << "* #{cgit_url}" desc = desc.join("\n") uri = URI.parse('http://raa.ruby-lang.org/regist.rhtml') form = { :name => s.name, :short_description => s.summary, :version => s.version.to_s, :status => 'experimental', :owner => s.authors.first, :email => s.email, :category_major => 'Library', :category_minor => 'System', :url => s.homepage, :download => 'http://rubyforge.org/frs/?group_id=8977', :license => "LGPL", :description_style => 'Plain', :description => desc, :pass => password, :submit => 'Update', } res = Net::HTTP.post_form(uri, form) p res puts res.body end kgio-2.8.1/ChangeLog0000644000004100000410000022362112214142252014256 0ustar www-datawww-dataChangeLog from http://bogomips.org/kgio.git commit 9bde3ab9a7e6e1776ba43bd0e7a3e9202f1026f6 Author: Eric Wong Date: Wed Sep 11 00:01:21 2013 +0000 kgio 2.8.1 - minor improvements and test fixes Improved error reporting for kgio_accept/kgio_tryaccept. Minor size reduction throughout. There are also several test case fixes for race conditions. Thanks to Hleb Valoshka and the Debian project for all the help with this release! Eric Wong (7): check syscall returns against < 0 instead of == -1 accept: more informative exception on unknown family test_tryopen: skip EACCES test when euid == 0 test/lib_read_write: account for larger-than-normal pipes test_poll: avoid potentially thread-unsafe test test_poll: preserve original trap(:USR1) handler test_poll: be less dependent on signal handler ordering Hleb Valoshka (4): Change prefix of temporary sockets to prevent races Don't dump 20M in case of failure Create own directory for every unix socket in unit tests Close tempfile and unlink it immediately. commit c3be428c452fd9c91d31b5b7d5b697175b6652df Author: Hleb Valoshka <375gnu@gmail.com> Date: Sat Sep 7 23:07:36 2013 +0300 Close tempfile and unlink it immediately. When a Tempfile object is garbage collected, or when the Ruby interpreter exits, its associated temporary file is automatically deleted. This may lead to race condition when Tempfile is used like in these tests. Signed-off-by: Eric Wong commit ff940137dc5098c9d8094e041e9be053cb3e139f Author: Eric Wong Date: Thu Sep 5 23:11:59 2013 +0000 test_poll: be less dependent on signal handler ordering Just spam the thread several times to force the wakeup to happen and not be as dependent on signal handler execution ordering of different Ruby VMs. Tested-by: Hleb Valoshka <375gnu@gmail.com> commit 90340eb8f2c5bf820eabd3c25fb39e45285b1c40 Author: Hleb Valoshka <375gnu@gmail.com> Date: Wed Sep 4 15:07:19 2013 +0300 Create own directory for every unix socket in unit tests [ew: this avoids a TOCTOU issue for multiple test invocations] Signed-off-by: Eric Wong commit 71f656e40de99d240c873ebb23451f76e46e5a20 Author: Eric Wong Date: Tue Sep 3 22:47:20 2013 +0000 test_poll: preserve original trap(:USR1) handler The existing SIGUSR1 handler may not always be "DEFAULT", so restore the original one. commit 377028d998f32c9bf8926a275019445103328c3d Author: Eric Wong Date: Tue Sep 3 22:45:26 2013 +0000 test_poll: avoid potentially thread-unsafe test Modifying pollset in a different pollset is thread-unsafe, so just do that inside the signal handler as that should fire before restarting poll(). commit 259347346169d4a2ab5b33719fe0985edc64bd3c Author: Eric Wong Date: Tue Sep 3 22:43:04 2013 +0000 test/lib_read_write: account for larger-than-normal pipes Linux pipe buffers may use larger pages and increase the capacity of pipes. Thus the 10000 write attempt is not sufficient; just infinite loop until we hit :wait_writable. commit dae148a9d6de7501acf8a68218b3e9d2c5ff6ff7 Author: Eric Wong Date: Tue Sep 3 19:35:46 2013 +0000 test_tryopen: skip EACCES test when euid == 0 This fails when the test is run as root (which may be the case of some Ruby installations) or fakeroot (which is the case of Debian build systems). commit 5c99abe6108d8d6a52c07b969afbc556c59b33be Author: Hleb Valoshka <375GNU@Gmail.COM> Date: Sat Aug 24 17:31:42 2013 +0300 Don't dump 20M in case of failure Signed-off-by: Eric Wong commit fc88b648ca5c2ba6c845c357192d7e36f9dedc5c Author: Hleb Valoshka <375GNU@Gmail.COM> Date: Sat Aug 24 16:50:17 2013 +0300 Change prefix of temporary sockets to prevent races Note: this is an incomplete fix and the race can still happen. A proper fix would involve using a temporary directory for each test and placing the Unix socket in there. Signed-off-by: Eric Wong commit 30c12374f8d23f0e635946fc38546820216457bf Author: Eric Wong Date: Fri Aug 16 02:00:21 2013 +0000 accept: more informative exception on unknown family This may help us diagnose issues on strange OSes. commit 9f30805bc4ff65ad4ca0be9dfb26b1a9bdc70c51 Author: Eric Wong Date: Fri Aug 16 01:52:15 2013 +0000 check syscall returns against < 0 instead of == -1 This may help us avoid errors in case of C library bugs, and also results in smaller code: $ ~/linux/scripts/bloat-o-meter before.so after.so add/remove: 0/0 grow/shrink: 0/9 up/down: 0/-66 (-66) function old new delta s_trywrite 160 159 -1 kgio_write 160 159 -1 kgio_trywrite 160 159 -1 my_recv 616 610 -6 my_peek 616 610 -6 stream_connect 456 448 -8 my_socket 222 213 -9 my_writev 1703 1687 -16 write_check 427 409 -18 commit 8be51237720fd18cb45188f29c717bbac0ca1964 Author: Eric Wong Date: Fri Jan 18 10:25:20 2013 +0000 kgio 2.8.0 - TCP Fast Open, writev/trywritev TCP Fast Open in Linux 3.7 and later is now supported in the client via Kgio::Socket#kgio_fastopen. This release also adds the kgio_writev and kgio_trywritev methods, thanks to funny-falcon commit 3e555a62c75406d15199fd7bdb287704e5738352 Author: Eric Wong Date: Fri Jan 18 10:50:28 2013 +0000 rename fastopen => kgio_fastopen in Kgio::Socket In the unlikely case the Ruby Socket class implements its own "fastopen" method, we will avoid conflicting. commit c751f42f5f6a5e54a399df472015ab6d2ffc3f7a Author: Eric Wong Date: Sun Dec 30 11:43:10 2012 +0000 accept: do not set blocking if non-blocking is set This is prone to race conditions in multiprocess situations where one process is relying on non-blocking operation while another (likely newer process) relies on blocking operation. Since the blocking process can always fall back to calling rb_io_wait_readable(), use that instead and give up some scalability for higher reliability. Those interested in avoiding thundering herds will have to stop/start their processes using blocking sockets (and tolerate some downtime). commit c63ad2b2e0e25f0765605e8ba2d7038b5e28d878 Author: Eric Wong Date: Thu Dec 27 02:16:26 2012 +0000 fastopen: fix argument order in RDoc example Oops :x commit 5f696156e097a1e66cb0c5c2a7cf62b38fd97605 Author: Eric Wong Date: Thu Dec 27 01:29:01 2012 +0000 read_write: remove unused variable commit f61cef65b8a8816160c622324b4f1aad55034e4a Author: Eric Wong Date: Thu Dec 27 01:16:56 2012 +0000 implement TCP Fast Open support (client + server) Server support just requires exposing one constant for setsockopt: Kgio::TCP_FASTOPEN Client support implements a new Kgio::Socket#fastopen method. This new method wraps the the sendto() syscall. With TCP Fast Open, the sendto() syscall is overloaded for stream sockets to implement the functionality of both connect() + write() Since it only makes sense to use _blocking_ I/O for sendto(), TFO clients are only supported in Ruby implementations with native threads. commit 7a3fc55424338ad458cc719d4cb3c4e28802d5cb Author: Eric Wong Date: Thu Dec 13 00:02:50 2012 +0000 Kgio::Socket.new retains compatibility with Socket.new This allows us to create an unconnected socket, just like the normal Socket class it inherits from. commit 48fc432a3b9dfd2b0435f0975556d4a321a5239b Author: Eric Wong Date: Wed Dec 12 21:40:50 2012 +0000 connect: factor out tcp_getaddr() function This will be reused for TCP fast open support. commit 9ddd17b0e296eb279f05d418da6ad46319bcf0b5 Author: Eric Wong Date: Wed Dec 12 21:21:29 2012 +0000 connect: split out my_socket() function This makes the retry logic for mismatched libc headers/kernel versions easier to understand and follow. commit 8b4df8ece93ddc4e2fb685905461c1ed27b22295 Author: Eric Wong Date: Wed Nov 21 23:16:00 2012 +0000 tryopen: include errno.h header just in case errno.h is not guaranteed to be included in existing headers, so we need to #include it to ensure errno and friends are usable. Thanks to stuart on the kgio mailing list for noticing ref: <062571308.133355.1353536890665.JavaMail.sas1@172.29.251.236> commit f020550fc802f299fdcdec695ac80d53ef3d24d9 Author: Eric Wong Date: Mon Jul 2 04:21:40 2012 +0000 test workaround for platforms with unreliable signals Ruby may not respond well to signals on all platforms, especially not after fork()-ing in the face of a running pthread (timer thread on 1.9.2). SIGKILL bypasses Ruby (and all userspace) signal handling on Debian GNU/kFreeBSD. commit 488a148d8b172e152e3450062b172ba516ab84b3 Author: Eric Wong Date: Mon Jul 2 04:20:20 2012 +0000 test/lib_read_write: wait for readability before tryread On FreeBSD, writing to a loopback TCP socket does not guarantee immediate readability on the other end. Tested on Debian GNU/kFreeBSD 6.0 commit c79babfd175aa7b4be9d4d1a10a64c17b93730a0 Author: Eric Wong Date: Mon Jul 2 03:16:09 2012 +0000 test_poll: skip signal torture on Debian GNU/kfreebsd This cascades test failures on a platform with questionable signal/fork handling. Tested on: Debian GNU/kFreeBSD 6.0 commit ff27e74a49bf6746ffe74cfc865430221f0bafe0 Author: Sokolov Yura 'funny-falcon Date: Fri Jun 1 13:42:58 2012 +0400 add `#kgio_writev` and `#kgio_trywritev` Add methods for using writev(2) syscall for sending array of string in a single syscall. This is more efficient than concatenating strings on Ruby side or sending them one by one. `#kgio_trywritev` returns array of strings which are not sent to the socket. If there were objects other than string, they could be converted using `#to_s` method, but this is not strictly applied, cause `#kgio_*writev` tries to write at most `sysconf(_SC_IOV_MAX)` items at once (for Linux its value is 1024). First string of returned array could be part of string from array, so that you should assume it is not in consistent state. `#kgio_writev` semantic differs a bit from `#kgio_write` in term of buffers mutability: currently `#kgio_write` tries to follow string changes made concurrently, but `#kgio_writev` works with array's lightweight copy. Signed-off-by: Eric Wong commit fa52cc5d0ef7d04b844868e08e2e7ec3c9e3396e Author: Eric Wong Date: Wed May 30 12:31:19 2012 -0700 tryopen: avoid ambiguous name for subst function Define rb_thread_blocking_region as a macro for MRI 1.8 to prevent confusing output in tools such as valgrind/gdb. commit a72e6cd0dd3038ae2a1b5ef94780143f5ab041c0 Author: Sokolov Yura 'funny-falcon Date: Wed May 30 17:56:55 2012 +0400 use rb_str_subseq for tail string on write Use rb_str_subseq for taking string's tail. rb_str_subseq do not allocate additional memory in this case. And although it prevents from collecting original string, it seems that tests wins both in performance and in memory usage. Use fallback to rb_str_substr on ruby1.8 Signed-off-by: Eric Wong commit 021eaddbfb41d82c0082657f60021bad52b3a6dc Author: Sokolov Yura 'funny-falcon Date: Wed May 30 17:56:54 2012 +0400 Fix UnixClientReadServerWrite test class name Signed-off-by: Eric Wong commit ab3fa8e85c02227985c56261d4898339f85d2b20 Author: Eric Wong Date: Fri Mar 23 21:10:22 2012 +0000 kgio 2.7.4 - small fixes and cleanups Fix build for platforms lacking both TCP_CORK _and_ TCP_NOPUSH There are many test case fixes and cleanups, too. commit 49f28a5257d20a7f4b0aa790424ca207287aa7b6 Author: Eric Wong Date: Sat Mar 24 00:13:40 2012 +0000 test: more workaround for FreeBSD 9.0 Followup-to: e26358413c9d87e1ce8f6cda5cf0b8dd53979ed2 commit e26358413c9d87e1ce8f6cda5cf0b8dd53979ed2 Author: Eric Wong Date: Tue Mar 20 22:00:47 2012 +0000 test/lib_read_write: test workarounds for TCP in FreeBSD 9.0 Under load, TCP sockets may not register as readable right away after the writer finishes. This can be expected for implementations where loopback TCP is a closer simulation of non-local TCP traffic. These test failures were noticed under FreeBSD 9.0. commit 2cab4f2fa642241dbcaf8881d39bd275a59dc67b Author: Eric Wong Date: Fri Mar 23 11:56:04 2012 -0700 test_tryopen: fix horribly-named test for EACCES We can't actually test for EPERM without changing permissions/ownership, and we can't do that without root... commit ce62ddbef053ad31af2f3ec6fcb7d2488859383d Author: Eric Wong Date: Thu Mar 22 06:18:41 2012 +0000 test/lib_read_write: increase test reliability IO#readpartial may not drain the socket buffers enough for kgio_write to succeed on some platforms. So use IO#read for read-in-full behavior. commit 74b9f78e11b915439555290dc3bdd4331303561c Author: Eric Wong Date: Mon Mar 19 06:05:06 2012 +0000 test/*: remove assert_nothing_raised It makes test failures hard to track down, tests will already fail if exceptions are thrown and we'll get nice backtraces. commit 85ae255f73b9b81ae3d17e6420dbb95a29dbe8b7 Author: Eric Wong Date: Mon Mar 19 05:53:03 2012 +0000 fix for non-Linux, non-TCP_NOPUSH platforms We don't need to care for TCP_NOPUSH in read_write.c, it's entirely in autopush.c and no-op on platforms without TCP_CORK/TCP_NOPUSH. TCP_CORK/TCP_NOPUSH are non-POSIX, so it's entirely possible some Free systems will lack them. Reported-by: Edho Arief commit 5ea4cdd1275c0f862bf9dcd1d344dc57d70e5392 Author: Eric Wong Date: Thu Mar 15 07:04:33 2012 +0000 kgio 2.7.3 - compatibility fixes Fixed build and autopush support under Debian GNU/kFreeBSD. Test case fixes for timing-sensitive tests. commit 2c2befb1caa47fe3bf2e6d31dd0733956d178c87 Author: Eric Wong Date: Wed Mar 7 10:14:17 2012 +0000 HACKING: add instructions for running tests Unlike most Rubyists, I prefer GNU make to Rake. commit 92dca5e5554e056f892fcb6cae20693b39b4044b Author: Eric Wong Date: Wed Mar 7 08:37:11 2012 +0000 test_poll: workaround for timing-sensitive test on slow hosts poll(2) may return successfully before it gets interrupted by the signal. Found and fix confirmed by 375gnu on the kgio mailing list. ref: commit 3a847e231d494829077a300912588f499c0bc2af Author: Eric Wong Date: Mon Mar 5 23:19:30 2012 +0000 test: increase delta range for timing-sensitive test This appears to be needed for Debian GNU/kFreeBSD under KVM. commit 1129029ab1bf886979a66a69b04d244dba8b63cf Author: Eric Wong Date: Mon Mar 5 23:19:29 2012 +0000 autopush: fix/enable under Debian GNU/kFreeBSD It seems autopush support in our autopush code has always been broken outside of Linux-based systems, as we never marked the socket as having pending data. commit 56cce133d979c22bbef80fdba1881d8f40876e2f Author: Eric Wong Date: Mon Mar 5 22:37:20 2012 +0000 accept4: require SOCK_NONBLOCK/SOCK_CLOEXEC macros The check for the accept4() function actually succeeds on a stock installation of Debian GNU/kFreeBSD 6.0, but the eglibc headers fail to define the necessary flags. commit 3e7bd918153cd09dd3bdd4e6963e173ae050ae68 Author: Eric Wong Date: Sun Jan 8 03:38:17 2012 +0000 kgio 2.7.2 - for older, and older Rubies Fix a missing #include for Ruby 1.8.5 users. No need to upgrade to this (nor 2.7.1) if you're on a modern version of Ruby. commit d9fe99171c191c55240d756fbc498d82e419f13a Author: Eric Wong Date: Sun Jan 8 03:35:30 2012 +0000 tryopen: remember to include ancient_ruby.h for 1.8.5 Apparently the old Ruby 1.8.6 installation lying around isn't old enough. commit 0dc3909d2ac0f711f038c7a387a9a1da2d5fcb62 Author: Eric Wong Date: Sun Jan 8 01:58:44 2012 +0000 kgio 2.7.1 - compatibility with older Rubies This release fixes some compatibility issues with people stuck on older versions of Ruby/RubyGems. * define RARRAY_PTR/RARRAY_LEN macros for Ruby 1.8.6 * test/test_autopush: skip strace tests if not available * gemspec: disable development dependencies for old systems commit fd1ea5a73155f577f7b77b18c2d6d23af287c123 Author: Eric Wong Date: Sun Jan 8 01:52:52 2012 +0000 gemspec: disable development dependencies for old systems "Enterprise" users are sometimes stuck on older Rubies/RubyGems and this is still required for them. commit e9b90724d8a2ee8c405acdf112adfabcb5cc3159 Author: Eric Wong Date: Sun Jan 8 01:50:21 2012 +0000 test/test_autopush: skip strace tests if not available No need to completely fail on a test. commit 77775e385fe92d1309c65c585b4643712c58e5ba Author: Eric Wong Date: Sat Jan 7 11:15:37 2012 +0000 define RARRAY_PTR/RARRAY_LEN macros for Ruby 1.8.6 Apparently Ruby 1.8.6 is still in use... commit 86f703bfd872536902e7f5293acea3ed0ba0f495 Author: Eric Wong Date: Tue Dec 13 06:09:48 2011 +0000 kgio 2.7.0 - minor updates When running under Ruby trunk/2.0.0dev, all IO objects created by kgio will be close-on-exec by default to match the (future) 2.0.0 behavior. accept()ed sockets in kgio have always been close-on-exec by default.. Singleton Kgio.accept_* methods are deprecated as the kgio_accept/kgio_tryaccept methods all take an additional flags argument. There are various, test, documentation, and error message improvements. commit a40c1d4b30253c68d7997324bcebceb68018bc37 Author: Eric Wong Date: Wed Nov 16 18:42:47 2011 -0800 doc: update documentation regarding accept method flags There's no reason for SOCK_NONBLOCK with Ruby, and SOCK_CLOEXEC has always been on-by-default with accept() wrappers. commit be3672501ecde716dae723e887d4a9e4d731240c Author: Eric Wong Date: Wed Nov 16 18:37:37 2011 -0800 connect,tryopen: set close-on-exec flag for new fds on Ruby 2.0+ All IO objects created by Kgio will have FD_CLOEXEC descriptor flag set on it when run under Ruby 2.0.0dev. This matches the upcoming behavior of Ruby 2.0.0dev for IO objects in the core and standard library. This change does not affect users on Ruby 1.9.3 and earlier. accept()-ed sockets in kgio have _always_ had FD_CLOEXEC set by default. commit 48dc3c5a1943801311567e72a8e69fcb0cd8cf8d Author: Eric Wong Date: Tue Nov 15 13:30:26 2011 -0800 tests: remove tests for IO#nonblock? after accept There's no point in testing a Ruby implementation detail and these tests fail under OpenBSD where the accept()-ed socket inherits the O_NONBLOCK flag from the parent. commit a1a648fe905808ffa902c44ba7626e3b3eeda627 Author: Eric Wong Date: Tue Nov 15 13:20:25 2011 -0800 accept: deprecate singleton Kgio.accept_* methods The kgio_accept and kgio_tryaccept methods now take an additional flags argument, so there's no reason to set global flags anywhere. commit 246cfe96f12ce06a5b504873789ada2efd288885 Author: Eric Wong Date: Tue Nov 15 13:18:32 2011 -0800 accept: always set O_NONBLOCK for accept()-ed sockets in 1.8 This is mostly an implementation detail, but it's already true on OpenBSD (and maybe other BSDs), and also requires no additional syscalls on newer Linux systems. commit 4cef568a9d06033c295c1f4920918c6fed36a24d Author: Eric Wong Date: Mon Aug 29 17:33:43 2011 -0700 test_poll: test for closing a polled IO in sighandler This needs to work similarly to IO.select. commit bb37d358b3326a03a69f65e12c775bb9861b3ad5 Author: Eric Wong Date: Wed Aug 24 17:36:46 2011 -0700 .wrongdoc.yml: add public/private email addresses We want feedback! commit 71eefc0f191d2dde969e6f05f65ec1e80a305e11 Author: Eric Wong Date: Wed Aug 24 17:34:55 2011 -0700 wait.c: fix misspelling in rdoc eye kan spel! commit 7d70f24ac40c984a91f3709b4bd277aaa781746e Author: Eric Wong Date: Tue Aug 9 12:35:18 2011 -0700 LICENSE: s/GNU C Library/kgio/ This is not glibc and I'm not Ulrich Drepper. commit 4ed70d1c840172631e4347e0d0b86a7149f4c8bb Author: Eric Wong Date: Tue Aug 9 12:34:25 2011 -0700 TODO: update SSL/TLS support status :) Monkeys! commit b42164253fa0f4c7c5749eab651c64ddd6cc906a Author: Eric Wong Date: Thu Jul 21 15:57:10 2011 -0700 use rb_update_max_fd() under MRI 1.9.3+ This helps exec() and similar methods close descriptors on shutdown. commit d064ac9334be079d0e830bc2361c065cbaa52a64 Author: Eric Wong Date: Fri Jul 15 11:12:09 2011 -0700 connect: more descriptive error for TCP port We want more descriptive error messages and don't want crazy stuff like floats. commit dcaa9f9be83bfd59503033ae8f8eeca79c68c9df Author: Eric Wong Date: Thu Jul 14 18:54:37 2011 -0700 kgio 2.6.0 - minor feature update and cleanups We now export SOCK_NONBLOCK, SOCK_CLOEXEC constants in the Kgio namespace to make kgio_tryaccept/kgio_accept easier-to-use. There are also some minor internal cleanups. commit 8baed92a7c02adcc913bcc7760b77a240b529c63 Author: Eric Wong Date: Thu Jul 14 17:32:10 2011 -0700 add prototype for rb_thread_blocking_io_region() It's no in the public headers, but Ruby 1.9.3 will have it and it's still superior to rb_thread_blocking_region() even though it's not ideal. commit 823f41d0f86dda497c166a839d8215275f5d48a0 Author: Eric Wong Date: Thu Jul 14 17:29:57 2011 -0700 missing_accept4: don't use FD_CLOEXEC for SOCK_CLOEXEC emulation It's too confusing and may break binary compatibility if the system is later upgraded. commit 142beefba460685fea5b6646e1ba629f9ee207b3 Author: Eric Wong Date: Thu Jul 14 17:29:05 2011 -0700 accept.c: fix RDoc for new Kgio::SOCK_* constants We don't want people using the compatibility constants since they're actually broken on systems with real accept4(). commit c22f2b5ebccbca8e04aa22821964f67c4a81c675 Author: Eric Wong Date: Wed Jul 6 19:11:47 2011 -0700 export SOCK_NONBLOCK, SOCK_CLOEXEC constants in Kgio It's more reliable than relying on IO::NONBLOCK and Fcntl::FD_CLOEXEC constants. The existing constants are not guaranteed to be equivalent to what accept4() takes even though the current Linux implementation does it this way. commit 8e1a53f99a752d8ccba324560a9e52bf6e80680d Author: Eric Wong Date: Wed Jul 6 18:32:39 2011 -0700 accept.c: reinstate errno after calling my_fileno() my_fileno() may change errno on some Rubies commit e720827b48c3318627d1471ad4ec90d6166520fd Author: Eric Wong Date: Fri Jun 17 19:01:06 2011 -0700 doc: update documentation for kgio_addr attribute We support IPv6, not just IPv4 commit ce08273658b14dc3d53273b514e46b6e65882bf6 Author: Eric Wong Date: Fri Jun 17 18:35:38 2011 -0700 doc: call-seq for kgio_wait_writable It's there for kgio_wait_readable commit 72ab71667b2a9c27d1eda73aacb8b86187f317d0 Author: Eric Wong Date: Fri Jun 17 18:10:44 2011 -0700 ancient_ruby.h: avoid symbol clobbering In case the toolchain can't test the feature properly or Ruby is upgraded and the symbol is added. commit 079f9227dce2e69bd3460c783e12fa05c687b7d3 Author: Eric Wong Date: Wed Jun 15 22:16:15 2011 -0700 fix misc compiler warnings Some installations of Ruby clobbered my usual CFLAGS=-Wall commit 8fe21f6758bb877efacce1fa6573e72625252585 Author: Eric Wong Date: Wed Jun 15 08:30:03 2011 +0000 make timed kgio_wait_* implementation safer IO.select can handle fd >= 1024 safely in some Rubies while fd_set may not. We could use rb_thread_fd_select(), but rb_wait_for_single_fd() is available now so the former is not worth the maintenance hassle. commit 9159f70862e3e6a76d821c4a70bc68a603793a49 Author: Eric Wong Date: Wed Jun 15 02:39:13 2011 +0000 Kgio::File includes Kgio::PipeMethods module Kgio::File may be used to open FIFOs, so non-blocking I/O is still useful in that context. commit 6d6f704e29d7bf1e95f1c9c60cbab82ec3d430d2 Author: Eric Wong Date: Wed Jun 15 02:34:49 2011 +0000 Kgio::File.tryopen runs GC on ENOMEM It is possible but unlikely to get ENOMEM on open(2), so try to GC away some files. commit e693b871567119345c2c567bfa2ad46e210d655b Author: Eric Wong Date: Wed Jun 15 02:27:06 2011 +0000 doc: add rdoc for Kgio::File.tryopen New feature in 2.5, we now have 100% documentation again. commit 5550222b389c2971ee98bdd62c749ce228efda06 Author: Eric Wong Date: Wed Jun 15 02:12:31 2011 +0000 kgio_wait_*able: documentation for optional timeout New features are better if they're documentated. commit 3fe0ad91d7a81a84ecc9e75ba8f5162bad30b2ac Author: Eric Wong Date: Wed Jun 15 02:06:32 2011 +0000 doc: use librelist.org instead of librelist.com Non-profit TLD is better sounding for a Free Software project. commit 2e20f8b1a1b74ae5442e44de70196a5e3121c642 Author: Eric Wong Date: Wed Jun 15 02:04:00 2011 +0000 GIT-VERSION-GEN: bump version for new API commit aa9594b0d38b012729a46fd519fcc369600f4c3e Merge: bbf9a3b fdde0a2 Author: Eric Wong Date: Wed Jun 15 02:05:43 2011 +0000 Merge branch '2.4-stable' * 2.4-stable: kgio 2.4.2 - OpenSolaris build fix extconf: -lnsl and -lsocket checks for OpenSolaris commit fdde0a27c51134d7a6a9cd1d66d93d1ac6640940 Author: Eric Wong Date: Tue Jun 14 18:40:07 2011 +0000 kgio 2.4.2 - OpenSolaris build fix * adds -lnsl and -lsocket checks for OpenSolaris commit 1a7eed4c69abb7bafd3e3dc2acd13e243995e98e Author: Eric Wong Date: Mon Jun 13 14:26:10 2011 -0700 extconf: -lnsl and -lsocket checks for OpenSolaris Reported via private email. (cherry picked from commit d224563823accca63fd871260e3f0dad6758c8d4) commit bbf9a3bc0ca2d91705e27ad8bfb5c0ed9651a2ef Author: Eric Wong Date: Tue Jun 14 08:41:32 2011 +0000 add timeout to kgio_wait_{read,writ}able io/wait doesn't have an IO#wait_writable method, yet[1] and IO#wait checks FIONREAD which makes it unsuitable for certain descriptors. This method uses the new rb_wait_for_single_fd() function in Ruby 1.9.r. This internally uses ppoll() under Linux, meaning it performs the same regardless of the FD used. [1] http://redmine.ruby-lang.org/issues/4647 [2] http://redmine.ruby-lang.org/issues/4849 commit d224563823accca63fd871260e3f0dad6758c8d4 Author: Eric Wong Date: Mon Jun 13 14:26:10 2011 -0700 extconf: -lnsl and -lsocket checks for OpenSolaris Reported via private email. commit 83c6584be53d6863e647067ba385b42ed5347cdb Author: Eric Wong Date: Mon May 23 11:28:29 2011 -0700 README: clarify that we only work on Unix-like systems We won't waste time with inferior, non-Free platforms. commit 2ade0059c67a0ae4fa1b416c500169e3ac66bfff Author: Eric Wong Date: Fri May 20 20:00:15 2011 -0700 pkg.mk: update to the latest version Fixes locale issues with grep and adds check-warnings commit 8184059bf16d73f0a386ddbf68c4949d1dec3bdf Merge: a5357ad f656c49 Author: Eric Wong Date: Fri May 20 19:54:48 2011 -0700 Merge branch '2.4-stable' * 2.4-stable: kgio 2.4.1 - Kgio.poll avoids EINTR, really Kgio.poll: ensure EINTR never gets raised commit f656c49f77d896cbbb1e684d826472c09dcc2253 Author: Eric Wong Date: Fri May 20 19:53:03 2011 -0700 kgio 2.4.1 - Kgio.poll avoids EINTR, really This release fixes a race condition that could allow Errno::EINTR to be raised even though the 2.4.0 release was supposed to stop that. Nobody uses Kgio.poll, really, so this shouldn't be an issue for real code, yet. commit f809a87f70f0937a87b5d3a83704847daceef4dd Author: Eric Wong Date: Fri May 20 19:50:34 2011 -0700 Kgio.poll: ensure EINTR never gets raised Retry on a zero timeout if we get interrupted even if the timeout expired. This is also what IO.select does in Ruby itself. commit a5357ad014d2eacc99dd7ee46040686cbf1d871c Author: Eric Wong Date: Fri May 20 19:50:34 2011 -0700 Kgio.poll: ensure EINTR never gets raised Retry on a zero timeout if we get interrupted even if the timeout expired. This is also what IO.select does in Ruby itself. commit 605765ded31c784727077dfca573092ba725f717 Author: Eric Wong Date: Fri May 13 17:57:10 2011 -0700 Kgio.tryopen => Kgio::File.tryopen This will allow users to subclass Kgio::File and override certain behavior (e.g. overriding "#each"). commit c8bd876fb5086e5b79299869b4c29f1f7f020b4d Author: Eric Wong Date: Fri May 13 17:48:56 2011 -0700 return Kgio::File for Kgio.tryopen This also allows us to return/override #to_path and #path if necessary, but so far everything works with MRI 1.8, MRI 1.9, and Rubinius. commit 6cefcff5889cceaa001f76f4be1a1c5e513b241d Author: Eric Wong Date: Fri May 13 15:55:50 2011 -0700 add Kgio.tryopen method For the case where a file is readable, it's faster to just call open() instead of stat()-ing and then calling open(). open() failures are still relatively cheap, too, they're still more expensive than a failed stat() but cheaper than raising an exception. commit ab732113e13f1690fd2c1a18d1c66beb7864d847 Author: Eric Wong Date: Thu May 5 15:58:17 2011 -0700 kgio 2.4.0 - portability fixes and more == All supported platforms (*nix + MRI 1.8+, Rubinius) * OpenBSD (and possibly other *BSD) fixes, thanks to Jeremy Evans. * kgio_accept and kgio_tryaccept now take an optional second argument for flags (like the accept4() flags argument). == Ruby 1.9-only things * Kgio.poll no longer raises Errno::EINTR to match IO.select. == Ruby 1.9 trunk things * close() on an active FD in a different thread is better handled/detected. * copy-on-write for strings is properly triggered commit 43190caf48309fa6aa5423d2d1ae5c320ad07fb5 Author: Eric Wong Date: Thu May 5 15:38:00 2011 -0700 test_cross_thread_close: disable on RUBY_ENGINE != "ruby" These aren't well-defined semantics... commit 9c98bbd79f152fd72b257f9c37ca185587e56225 Author: Eric Wong Date: Thu May 5 13:24:36 2011 -0700 read_write: call rb_str_modify() before rb_str_resize() This is needed under Ruby trunk if the string is not actually resized. commit c821ebeb851807840f74c4cb4f1a10e691bf222a Author: Eric Wong Date: Thu May 5 13:11:53 2011 -0700 poll: deal with pollset changes on EINTR This allows callers to modify the pollset, interrupt the polling thread, and then poll with the modified pollset. This is also important for dealing with closed IO objects that may have been invalidated due to GC, too. commit f589a9ae18216e1220bea8f905f33051e88f1ae7 Author: Eric Wong Date: Thu May 5 12:30:45 2011 -0700 test_tcp_connect: disable wait_writable_set test on non-Linux OpenBSD seems to make connect() return success immediately even on a non-blocking socket, so it's hard to test for. Thanks to Jeremy Evans to reporting the issue under OpenBSD. ref: http://mid.gmane.org/20110505181846.GB9693@jeremyevans.local commit 9900efecb60635ad97b5c00c76eb60252839b1c1 Author: Jeremy Evans Date: Thu May 5 11:18:46 2011 -0700 fix kgio_peek for !KGIO_NOPUSH systems Fix kgio_peek by adding an empty kgio_autopush_recv if !KGIO_NOPUSH. ref: http://mid.gmane.org/20110505181846.GB9693@jeremyevans.local Acked-by: Eric Wong commit 577cf3056d9f3088145aea51bbc09a0c90a7695e Author: Jeremy Evans Date: Thu May 5 11:49:01 2011 -0700 connect: zero out hints argument for getaddrinfo Some systems like OpenBSD are stricter about irrelevant fields than GNU/Linux. [ew: commit message] ref: http://mid.gmane.org/20110505181846.GB9693@jeremyevans.local Acked-by: Eric Wong commit 537e4c341137a45330e28376e8f29da7df44808f Author: Eric Wong Date: Wed May 4 19:26:38 2011 -0700 Kgio.poll restarts on interrupt This changes our semantics, but it's unlikely anybody relies on EINTR right now... commit f2a3998e0c0f63ad14acf5ccc0141fc6cdce24e3 Author: Eric Wong Date: Wed Apr 27 11:44:51 2011 -0700 extconf: remove unnecessary dir_config statement I didn't know this about mkmf at the time... commit 3033f2e8f178c0f150cfd3e2a070570154a27430 Author: Eric Wong Date: Thu Apr 21 20:08:01 2011 +0000 doc: improve kgio_accept/kgio_tryaccept docs Documenting the new flags options and also improving style of the existing class overrides. commit b885cf9a2ef0864dcebb9bba7b1fcf3eb08f9ae8 Author: Eric Wong Date: Thu Apr 21 20:06:08 2011 +0000 test_autopush: attempting to fix a timing test... Not fun on slow systems, unfortunately... commit be46333541acd72bde3544a3e86f6ead0bb364d0 Author: Eric Wong Date: Wed Apr 20 18:36:09 2011 -0700 doc: fix trywrite call-seq commit 3b48d6c823a7da34c0b37d8eb3c11964c4a3ba89 Author: Eric Wong Date: Thu Apr 14 02:31:50 2011 +0000 test_autopush: use assert_in_delta for test Some systems are slower than others... commit f36f7584b766cb5d558fcfa94ea639e6090bcb54 Author: Eric Wong Date: Mon Apr 11 18:21:09 2011 +0000 gemspec: bump wrongdoc version commit 212f6fdfe51e2167d362686bb2622ce04e6e5de5 Author: Eric Wong Date: Mon Apr 11 18:20:27 2011 +0000 gemspec: remove unnecessary require_paths RubyGems handles it already commit fefd652d6cc5825bebbe164a360c4a06b1399dcb Author: Eric Wong Date: Sun Apr 10 07:27:40 2011 +0000 add test for cross thread close Ruby 1.9.3dev has better support for it commit 983e8ff915118d00b683109df0834733b485086d Author: Eric Wong Date: Sun Apr 10 07:04:41 2011 +0000 read_write: detect closed fd on EINTR Another thread may have killed the descriptor. commit 01949865e2715b487e098dbc60ba3ae7a01a3b54 Author: Eric Wong Date: Sun Apr 10 06:07:55 2011 +0000 accept: better detect closed files on EINTR/EAGAIN Another thread may have killed the acceptor object. commit 59782a15d0be87130934cbecb34ed639be68b44a Author: Eric Wong Date: Sun Mar 20 16:27:55 2011 -0700 *accept methods can take flags argument, like accept4() This allows applications to not rely on global accept4_flags commit 0ef079617b7d71cc26574247918c4a3e18454b21 Author: Eric Wong Date: Sat Mar 19 18:29:15 2011 -0700 accept: prepare optional flags argument to accept() wrappers Don't force people to rely on global flags, there may be blocking parts of an otherwise non-blocking program... commit f8b8b3f73d238d4c29368b4a0f768c5afb03d43d Author: Eric Wong Date: Thu Mar 17 14:06:45 2011 -0700 HACKING: updates for wrongdoc vs rdoc commit c79b5f9037ce69fb3ebce470a14af505aa1c8f5a Author: Eric Wong Date: Tue Mar 15 12:00:52 2011 +0000 kgio 2.3.3 - minor fixes We no longer over-allocate memory for Kgio.poll (1.9.x-only). Under Ruby 1.9.3dev, we also use rb_thread_io_blocking_region to properly deal with cross-thread IO#close. commit 82a3e7d24e3cd51f15df593590986b7c5d0834aa Author: Eric Wong Date: Mon Mar 14 19:35:01 2011 +0000 poll: fix over-allocation for poll(2) Oops, fortunately we allocated too much and not too little. commit 6299ef55e20454eaca0b9860ac4a9bd6ddd143a7 Author: Eric Wong Date: Mon Mar 14 19:35:01 2011 +0000 use rb_thread_blocking_io_region if possible It's in Ruby 1.9.3dev and tracks waiting FDs on blocked threads. commit 59ed57abf542b89babf595e5508cba42ceb9fd47 Author: Eric Wong Date: Fri Feb 25 16:48:11 2011 +0000 doc: more consistent references for kgio_wait_*able methods Much nicer this way... commit 88ae3fb48de345a3a102ac4d17bb71e8a4691230 Author: Eric Wong Date: Tue Feb 15 08:55:25 2011 -0800 kgio 2.3.2 - OpenBSD build fix Thanks to Jeremy Evans, this release fixes the build under OpenBSD. commit 8556c2ccf64840a080a928312d8feed2834d4d29 Author: Jeremy Evans Date: Mon Feb 14 17:03:53 2011 -0800 Fix build on OpenBSD OpenBSD's getnameinfo(3) requires the sys/types.h header file, and including it should not cause a problem for other OSes. Acked-by: Eric Wong commit 6d19ebb3a917b566830f8d33e95b1eea2e99658d Author: Eric Wong Date: Sun Feb 13 16:50:13 2011 -0800 kgio 2.3.1 - compatibility fix * connect.c: disable AI_NUMERICSERV It's not needed since we already verify the service is a numeric port. AI_NUMERICSERV is not available in older glibc (<2.3.4) and probably other old systems. commit b30aa658d7b4d946427119b23cfc264cc7172149 Author: Eric Wong Date: Sun Feb 13 16:33:29 2011 -0800 connect.c: disable AI_NUMERICSERV It's not needed since we already verify the service is a numeric port. AI_NUMERICSERV is not available in older glibc (<2.3.4) and probably other old systems. commit f74bcf37ed31611d14aba2d4b4518c6a3dea0f40 Author: Eric Wong Date: Wed Feb 9 10:10:36 2011 +0000 kgio 2.3.0 - MSG_PEEK and poll(2) support recv() with MSG_PEEK for sockets is added with the try* interface. SocketMethods#kgio_trypeek and SocketMethods#kgio_peek or Kgio.trypeek for non-Kgio-enabled sockets. For Ruby 1.9 only: poll(2) is exposed via the Kgio.poll singleton method and should provide an alternative for IO.select users. Both of these new features should work well on modern Unix-like operating systems. commit 00d1bc89ed3811701195bf5ddb400a0a0067126f Author: Eric Wong Date: Tue Feb 8 14:11:45 2011 -0800 move poll support checks to kgio.h We may use poll elsewhere... commit 257b090af54e6a1cecd44325f8664c4c682a6740 Author: Eric Wong Date: Tue Feb 8 13:56:39 2011 -0800 pkg.mk: update to the latest * Fixes Ruby 1.9.3dev deprecation warnings * Fixes some documentation dependency issues * Allows RUBY_TEST_OPTS to be passed to unit tests commit d8616b605ad4d83b69e2679e1c210e476cc18e00 Author: Eric Wong Date: Tue Feb 8 13:50:55 2011 -0800 doc: fully RDoc all methods and classes Whee! commit 3f6bffb4c4297df48a69d146243fbe5ba8040cb5 Author: Eric Wong Date: Tue Feb 8 13:50:01 2011 -0800 doc: fix accept -> tryaccept copy+paste error Oops commit 823e8978b38dcfb642e5059e879af4209ebba0f1 Author: Eric Wong Date: Tue Feb 8 13:36:56 2011 -0800 README: fix download link/ref commit 5c480aee3067006b5da6d45b7de41d8401b70848 Author: Eric Wong Date: Tue Feb 8 10:41:51 2011 +0000 preliminary poll(2) support It's a nice alternative to IO.select for higher-numbered file descriptors, especially sparse ones. Our interface also generates less garbage than IO.select does. commit 47653194bf6ad53b9f5fca1b266c30855df5ebbd Author: Eric Wong Date: Sun Feb 6 21:56:35 2011 +0000 add support for recv() with MSG_PEEK Kgio.trypeek, kgio_trypeek and kgio_peek methods are added for using with sockets. commit 75a7da2bd757617995f5492df1205e4a3459618b Author: Eric Wong Date: Thu Feb 3 18:46:21 2011 -0800 kgio 2.2.0 - kinder, gentler I/O for the Internets * sockets accept()ed by a TCP_NOPUSH/TCP_CORK listener automatically flush on kgio_*read calls if there is pending data. "Kgio.autopush = false" disables this globally, and Kgio::Socket also get "kgio_autopush=" to enable/disable on a per-object individual basis. * ECONNRESET exceptions get empty backtraces for kgio_*read. There's nothing a programmer can do about these, so there's no point in going through the expensive backtrace generation process. * Kgio.try* singleton methods added for working with non-Kgio enhanced objects. No more needing to use Object#extend and blowing away your method cache to make existing I/O objects kinder and gentler. * IPv6 support should be complete, systems without a native getaddrinfo(3) are now unsupported (and will remain so unless somebody complains). There should be no other backwards-incompatible changes other than requiring getaddrinfo(3) and friends for IPv6 support. commit c8fb5aa33262a455997ff6a57659a8d125f36d66 Author: Eric Wong Date: Thu Feb 3 14:28:11 2011 -0800 add SocketMethods#kgio_addr! This refreshes (or sets) the @kgio_addr ivar for sockets that didn't go through kgio_accept or kgio_tryaccept. commit cff0dd2f73acc73f721b2a589af9e37baedd2489 Author: Eric Wong Date: Wed Feb 2 13:56:31 2011 -0800 fix typos in ipv6 test case Oops, RTFE :P commit 17abe6ce8f01810022b948c71de0026b4ac89597 Author: Eric Wong Date: Wed Feb 2 21:33:28 2011 +0000 add proper IPv6 support No extra #ifdefs, we just won't support old systems without getaddrinfo() and friends anymore. I doubt anybody still has them... commit 879f2f0ee9133f34ec3e24141bdb4936e3408d3a Author: Eric Wong Date: Tue Feb 1 14:00:07 2011 -0800 avoid re-interning if GCC is not used (or under 1.8) Needless calls to rb_intern are wasteful in even semi-frequently used code. commit 499f158c74b7c455dca08fc30be88cb699ee24c6 Author: Eric Wong Date: Tue Feb 1 13:58:20 2011 -0800 kgio_*read: empty backtrace for ECONNRESET There's nothing a programmer can do about ECONNRESET so just make the exception cheaper to raise so it can still be logged. commit 36f69750cd69cbf892580da04be6675e23d92f6f Author: Eric Wong Date: Mon Jan 31 18:39:31 2011 -0800 add singleton methods for non-Kgio objects This allows people to more easily use Kgio in existing apps. commit 37e50a9a5fcd45242373379c0dc61ebf8ff609af Author: Eric Wong Date: Mon Jan 31 18:27:24 2011 -0800 autopush: enable accessors for client sockets Might as well allow clients to efficiently handle TCP_CORK/TCP_NOPUSH, too. commit d4773fc63a847119004c17a1b8803a815f99d98a Author: Eric Wong Date: Mon Jan 31 17:34:07 2011 -0800 autopush: enable this by default TCP_CORK (and presuably TCP_NOPUSH) aren't remotely useful in Rainbows! without this and there's almost no overhead for MRI, either. commit 8a1fc65c88dee174940735bb46074c72ac47ce61 Author: Eric Wong Date: Mon Jan 31 17:05:48 2011 -0800 autopush: optimize away ivar usage under MRI We know that all versions of MRI have a small RFile structure that is allocated in the same object slots as other Ruby types and also zeroed on allocation. This optimization enables us to fall back to using ivars in case MRI changes or if we're used on other Rubies. commit 6479b6d3934b8930910e0057f516aa019dd7a8c7 Author: Eric Wong Date: Mon Jan 31 15:18:33 2011 -0800 autopush: enable for TCP_NOPUSH under FreeBSD Hopefully it works for people who use TCP_NOPUSH... commit 15744a90cda72e9007914cd2a78b0b2949193479 Author: Eric Wong Date: Mon Jan 31 13:58:53 2011 -0800 autopush: simplify implementation and just use ivars Duh... commit 313d2bb8d37dbc5602e464def90b3e7fa9f60924 Author: Eric Wong Date: Mon Jan 31 13:03:02 2011 -0800 rename nopush_smart to autopush This is probably a better name for it, libautocork is a nice name even though we won't use it directly. commit 910f6f3df099c04fcd55bd6b20785cce69cb36ae Author: Eric Wong Date: Thu Jan 27 19:43:39 2011 -0800 preliminary implementation of "smart_nopush" It only supports TCP_CORK under Linux right now. We use a very basic strategy to use TCP_CORK semantics optimally in most TCP servers: On corked sockets, we will uncork on recv() if there was a previous send(). Otherwise we do not fiddle with TCP_CORK at all. Under Linux, we can rely on TCP_CORK being inherited in an accept()-ed client socket so we can avoid syscalls for each accept()-ed client if we already know the accept() socket corks. This module does NOTHING for client TCP sockets, we only deal with accept()-ed sockets right now. commit ec91ac3d8c8d9236ba0cd01794c9c4a3ee3f7eeb Author: Eric Wong Date: Thu Jan 27 14:11:16 2011 -0800 revamp packaging makefile, update URLs More common code that's still GNU make is better for my sanity. Also, bogomips.org went on a URL diet recently. commit f6c79438ed195bb706903d104cce850bfbfbac41 Author: Eric Wong Date: Tue Jan 18 15:52:25 2011 -0800 add tests for empty writes, too There could be some platforms that dislike it... commit 9c81cc3fd8d2b3dce68d69d8e0c56a4c5d89ebf0 Author: Eric Wong Date: Thu Jan 13 14:46:02 2011 -0800 Makefile: remove non-existent target reference Oops commit fb8104e1f2a5d1cdcb99a19b6a4bdabf0b1c2643 Author: Eric Wong Date: Sat Dec 25 18:06:47 2010 -0800 kgio 2.1.1 - one small Rubinius fix We now avoid errno side-effects in kgio_wait_*able methods. This affects Rubinius, but may affect other Ruby platforms (particularly those that use stdio) as well. commit 6ab4331f8137e949ab57f014f96ff3918a315044 Author: Eric Wong Date: Sat Dec 25 18:02:16 2010 -0800 avoid errno side-effects in kgio_wait_*able Retrieving the file descriptor may have side-effects on certain Ruby implementations (e.g. Rubinius), so ensure our errno is preserved before calling rb_io_wait_*able(). commit 3a0323b642ee054319a5e64ffe28e089bbd013e4 Author: Eric Wong Date: Sun Dec 26 01:08:58 2010 +0000 gemspec: point folks to the public mailing list It's more useful that way. commit 9d5c9e6c9975cb5c10e7384aed9ed22ae0ee57c8 Author: Eric Wong Date: Sun Dec 26 01:02:00 2010 +0000 kgio 2.1.0 - accept improvements and fixes kgio_accept and kgio_tryaccept now take an optional argument to override the default Kgio::Socket class that is returned. These methods also fall back to using regular accept() if kgio was built on a system with accept4() and later run on a system without accept4(). commit dcd5eff7dd5d5861b67667f48424979be9bcabc8 Author: Eric Wong Date: Sun Dec 26 00:21:25 2010 +0000 quiet down some harmless compiler warnings Less noise means we'll notice real bugs sooner. commit 5280f35f131d88f90afffff0e10f7900530728aa Author: Eric Wong Date: Sat Dec 25 23:00:05 2010 +0000 accept4: fall back to regular accept() on ENOSYS kgio may occasionally be built on a system with accept4() and then deployed on one without it. Handle this case gracefully since it unfortunately happens on production systems. commit b859c4a12905cbd71d19cde2aaa9f88ec0374cc5 Author: Eric Wong Date: Sat Dec 25 22:44:53 2010 +0000 accept methods may take an optional argument This is preferred as we no longer have to rely on a global constant. commit ef069ece624906b3946248421620d8458bcef605 Author: Eric Wong Date: Fri Dec 24 09:21:19 2010 +0000 Rakefile: fix RAA license Oops, we were never Ruby licensed. commit 472240687caf3f113a3ff408729f8205c475d7d5 Author: Eric Wong Date: Fri Dec 24 09:20:40 2010 +0000 doc: use wrongdoc for documentation wrongdoc factors out a bunch of common code from this project into its own and removes JavaScript from RDoc to boot. commit 64bbc95d2192fb621b763c1c4d1ae32940c1a5ac Author: Eric Wong Date: Wed Dec 22 12:12:18 2010 -0800 fix errors in RDoc Noticed-by: Iñaki Baz Castillo commit f093312ad1ed336363f352991b6b99d96f7aed1d Author: Eric Wong Date: Thu Nov 18 17:16:53 2010 -0800 kgio 2.0.0 - major internal API changes (no code changes from 2.0.0pre1) This release should make Kgio easier and more consistent to use across a variety of libraries/applications. The global Kgio.wait_*able(=) accessor methods are gone in favor of having default kgio_wait_readable and kgio_wait_writable methods added to all Kgio-using classes. Sub-classes may (and are encouraged to) redefine these if needed. Eric Wong (7): expand Kgio::*#kgio_read! documentation prefer symbolic names for waiting read/writability EOFError message matches Ruby's README: Gemcutter => RubyGems.org update documentation with mailing list info add default kgio_wait_*able methods switch entirely to kgio_wait_*able methods commit edfa7e60de5556b6abc9febe6a21e12dadbafd0b Author: Eric Wong Date: Thu Nov 18 15:42:27 2010 -0800 Rakefile: list prerelease tags as well Since we do prerelease nowadays before real ones. commit d78a2075bdb0a30bf0064d2857011c330cc0d09e Author: Eric Wong Date: Thu Nov 18 15:38:12 2010 -0800 move website to bogomips.org This project is useful enough for others and to stand alone without needing to be associated with Unicorn. commit 28070c522aff233eadb7e167f8d4e8122cd0bb47 Author: Eric Wong Date: Thu Nov 18 15:15:40 2010 -0800 kgio 2.0.0pre1 - major internal API changes This release should make Kgio easier and more consistent to use across a variety of libraries/applications. The global Kgio.wait_*able(=) accessor methods are gone in favor of having default kgio_wait_readable and kgio_wait_writable methods added to all Kgio-using classes. Sub-classes may (and are encouraged to) redefine these if needed. Eric Wong (7): expand Kgio::*#kgio_read! documentation prefer symbolic names for waiting read/writability EOFError message matches Ruby's README: Gemcutter => RubyGems.org update documentation with mailing list info add default kgio_wait_*able methods switch entirely to kgio_wait_*able methods commit c69955e64648ab6a3471a54f7885a320428682f9 Author: Eric Wong Date: Thu Nov 18 14:37:05 2010 -0800 switch entirely to kgio_wait_*able methods This removes the global Kgio.wait_*able accesors and requires each class to define (or fall back to) the Kgio::DefaultWaiters methods. commit f1b497e601ed2acb54f75dc989d0a5ec7afebca0 Author: Eric Wong Date: Thu Nov 18 13:38:32 2010 -0800 add default kgio_wait_*able methods It makes it easier for people to use certain overrides without killing other methods. This is the first step in fixing problems people were having with dalli 0.11.1+ while running Unicorn. commit 827ad6b4fba768a5cac8fb4e83fbbf61cf7a3194 Author: Eric Wong Date: Mon Nov 15 10:33:55 2010 -0800 update documentation with mailing list info We're a real project, apparently, so it can have its own mailing list. commit fd88eae588c1e715dcaf3a1a000391cc13481e02 Author: Eric Wong Date: Mon Nov 15 10:22:49 2010 -0800 README: Gemcutter => RubyGems.org That's the new name for it and it's official commit 8615a3f9554df0fd7f7f088cd49cf1e3be49de9f Author: Eric Wong Date: Fri Nov 12 20:25:50 2010 -0800 EOFError message matches Ruby's This makes messages appear less different than Ruby when using kgio_read! Requested-by: Mike Perham commit 2772ed8bfe108b66b7493bc5cb0c40ddeb1ca57d Author: Eric Wong Date: Fri Nov 5 09:01:08 2010 +0800 prefer symbolic names for waiting read/writability There's no point in using constants that point to symbols instead of just the symbols themselves. commit bf3b507791403811bece9dff915ca10757bca519 Author: Eric Wong Date: Thu Oct 28 21:02:31 2010 +0000 expand Kgio::*#kgio_read! documentation If the author can forget why it was written, so can the rest of the world. commit f4d08a07a02393cca5ddd1277acc4f95c83307ff Author: Eric Wong Date: Fri Oct 8 14:55:16 2010 -0700 kgio 1.3.1 - fix zero-length reads kgio_read and kgio_tryread will now return an empty string when a length of zero is specified instead of nil (which would signal an EOF). This emulates the behavior of IO#read, IO#readpartial, IO#sysread, IO#read_nonblock in core Ruby for consistency. commit d225ede82d820d045bd7cfb826f444cf6601577c Author: Eric Wong Date: Fri Oct 8 02:53:38 2010 -0700 return empty string on length=0 This matches behavior of all the core Ruby methods. commit e4599227f0da0f652cbcb52838e631d7384dcd0d Author: Eric Wong Date: Thu Oct 7 20:02:40 2010 -0700 kgio 1.3.0 - bug and usability fixes * make Kgio::WaitWritable and Kgio::WaitReadable symbols * trywrite: fix stupid off-by-one error causing corrupt writes on retries commit f5fc35221d37141b0f72278c7b969211410e94c0 Author: Eric Wong Date: Thu Oct 7 20:00:09 2010 -0700 tests: don't trust what I think I know about Ruby case/when and === didn't actually work as I expected them to. commit 2152188f41bf2a5067e84a4404b48b2282a9dd55 Author: Eric Wong Date: Thu Oct 7 19:56:57 2010 -0700 trywrite: fix stupid off-by-one error causing corrupt writes Oops! commit c448ad898ecb7f354a32a320294da4727fc9af52 Author: Eric Wong Date: Thu Oct 7 19:55:49 2010 -0700 make WaitWritable and WaitReadable symbols This makes them easier to compare with === when used in case/when statements in Ruby commit 49f0b98c69f1f0bf637953d0bfc96b764f00ab9b Author: Eric Wong Date: Thu Oct 7 07:15:49 2010 +0000 kgio 1.2.1 - doc and *BSD workarounds This fixes our accept4() wrapper which did not work as expected on some *BSD-based systems due to fcntl(fd, F_GETFL) returning false information. Linux 2.6+ users are unnaffected, including those without accept4(). Also some RDoc fixes. commit 03344bb763f5269afe7fafd56a47270719c7ef9e Author: Eric Wong Date: Thu Oct 7 07:14:07 2010 +0000 doc: fix RDoc generation Oops, completely broken by the splitting of the code. commit 637317eb479525dca543eda7a8977410bc43b832 Author: Eric Wong Date: Wed Oct 6 14:08:35 2010 -0700 accept4: workaround (P)OS X bug w/O_NONBLOCK Apparently fcntl(fd, F_GETFL) can return falsely return the O_NONBLOCK flag without actually having it set in the kernel. This is totally broken on the part of the OS. commit ca76c75f8a24d0cd6828fe16ca3790a277b35f8d Author: Eric Wong Date: Wed Oct 6 14:06:27 2010 -0700 build: pick on on modified extension files We build more than one file nowadays. commit 65f96b7750616bc210397c16eea40961e578a788 Author: Eric Wong Date: Wed Oct 6 11:51:04 2010 -0700 doc: fix typo in Kgio.accept_cloexec= doc oops... commit 414dd17f1009c571e2d7657721271756e3d4dd8e Author: Eric Wong Date: Tue Oct 5 16:09:40 2010 -0700 kgio 1.2.0 - cleanups and minor improvements The C extension is now split into several files for ease-of-maintenance. Slightly more common, client-triggerable exceptions (EOFError, Errno::EPIPE, Errno::ECONNRESET) are now less expensive as they are generated without backtraces. commit e085bb9600b190692beb5efc85656ebf127ae08c Author: Eric Wong Date: Tue Oct 5 15:45:16 2010 -0700 generate empty backtraces for EPIPE and ECONNRESET Malicious clients may disconnect during big writes to cause EPIPE and ECONNRESET exceptions. Generating backtraces can be expensive with Ruby, so mitigate the DoS vector by lowering the cost of generating an exception. commit b168cc894037620cab82fa82f3ab37a3aab81570 Author: Eric Wong Date: Tue Oct 5 15:26:57 2010 -0700 add kgio_read! methods which may raise EOFError Except EOFError is gently raised to not include a huge backtrace. Large backtraces can be a performance problem on busy servers that malicious clients may exploit to deny service. commit 870ada92db7071c7982913e508ac35b97d6e8761 Author: Eric Wong Date: Tue Oct 5 11:45:02 2010 -0700 GNUmakefile: use portable tar invocation We've been spoiled by GNU tar. commit 2a6115a89d5c95428bd6c3e0bc10e5a3a4c3c3be Author: Eric Wong Date: Wed Sep 29 18:25:58 2010 -0700 refactor and split into separate files Making the code easier to read and navigate. This also frees us from having to use the stupid A4_ prefix for accept4(2) flags since it conflicts with the socket(2) ones. commit 8fe89997453d6c530c3f5e08bc9c1da40a621248 Author: Eric Wong Date: Wed Sep 29 17:13:21 2010 -0700 Make kgio_trywrite more aggressive with retrying Partial writes can be retried until completely denied with EAGAIN. Often times, it is beneficial to retry immediately after a partial write because the kernel may allocate more buffers or the reader can drain the buffers. This helps the caller avoid crossing the Ruby <-> C boundary more than necessary. commit 39c851e595970a2349a8a39878afd94a3324e102 Author: Eric Wong Date: Tue Sep 28 18:16:53 2010 -0700 kgio 1.1.0 - flexible accept methods * an alternate class now be returned by accept/tryaccept by setting "Kgio.accept_class =" commit 911f6ab306aff1e24c9c570eeae33923fa1b99d9 Author: Eric Wong Date: Tue Sep 28 18:04:51 2010 -0700 alternate classes may be returned by accept/tryaccept These can be useful for avoiding wrapper objects and also allows users to more easily try different things without stepping on others' toe^H^H^Hclasses. commit 526b4bd48a20a34ef5959fdc4aa580d5f9199652 Author: Eric Wong Date: Mon Sep 27 19:59:34 2010 -0700 kgio 1.0.1 - compatibility fixes * add compatibility for ancient Rubies (1.8.6) * linux: fix accept4() support for newer Linux commit 20cbc0355104470fb433dd13e87a5d5c7e888ab1 Author: Eric Wong Date: Tue Sep 28 02:56:41 2010 +0000 linux: fix accept4() support for newer Linux Oops :x Tested on Debian sid. commit 24f1d168eb0937f0586c45b266bcd208431f0107 Author: Eric Wong Date: Mon Sep 27 18:06:34 2010 -0700 add compatibility for ancient Rubies This is tested on Ruby 1.8.6-p114, but may work for 1.8.5, too. Ugh, people ought to upgrade. commit e4d204c86e9420023ba3e4d8dbeb6b3fea8d6cf7 Author: Eric Wong Date: Tue Sep 28 00:27:44 2010 +0000 kgio 1.0.0 - initial release Documentation and release infrastructure updates and such... commit 8984b9556a3493570fbb4f747fce712d58f2cdd8 Author: Eric Wong Date: Tue Sep 28 00:07:43 2010 +0000 doc: TODO update commit 2c64a1fc07d3b9a80d112e3b0e2baa7ec29c2f47 Author: Eric Wong Date: Tue Sep 28 00:03:39 2010 +0000 read/write: account for buffer changes during wait It's possible for applications to modify the buffer during reads and writes, so make a best effort to account for those. commit f2ea9918655e8ee0576bee2950d16485031fc361 Author: Eric Wong Date: Mon Sep 27 23:59:59 2010 +0000 tests: fix broken monster trywrite test Oops, use random data so it's easier to detect this. commit 7abc0eb3dd804c2e65660b7dd9c828df0e03b80a Author: Eric Wong Date: Mon Sep 27 15:09:44 2010 -0700 test_tcp*read_write: use blocking kgio_accept in setup Some OSes (FreeBSD 7.0) do not seem to setup connections as quickly. commit 95d2eae6a4da34c504427af6ae0ab4c8c70c0ce5 Author: Eric Wong Date: Mon Sep 27 15:09:43 2010 -0700 set blocking flag before blocking IO#read Some older Rubies may not behave correctly otherwise commit 0806cac89f9d0e169b6c1e4da68c1ad66daa23ae Author: Eric Wong Date: Mon Sep 27 23:16:53 2010 +0000 tess: ensure buffer is cleared on failures No need to leak data. commit 50b86bf23063f3e6c3777b39c9464f73ccfd6ef5 Author: Eric Wong Date: Mon Sep 27 22:55:52 2010 +0000 more documentation Somebody's gotta do it... commit 5123d66fe0b2dad67539a20fe5b91f5b9afd814a Author: Eric Wong Date: Mon Sep 27 16:56:13 2010 +0000 avoid initiating syscalls before rb_io_wait_* Some Ruby implementations (Rubinius) may call lseek and clobber the intended errno when looking up the open file, causing rb_io_wait_* functions to fail. commit 6c818b0b6f76ef733679bcea1024142b4ef3ce00 Author: Eric Wong Date: Mon Sep 27 01:13:30 2010 +0000 add kgio_tryaccept, kgio_accept _really_ blocks We'll stick with the "try" prefix if we're going to be non-blocking. kgio_accept will favor a blocking accept() call where it's possible to release the GVL, allowing it to avoid thundering herd problems. Otherwise it'll use thread-safe blocking under Ruby 1.8. commit f81cb3c05a0eb46ec61ceb295b51ead16e6a0da4 Author: Eric Wong Date: Mon Sep 27 00:57:14 2010 +0000 use SOCK_NONBLOCK for socket(2) if possible This saves us a relatively expensive fcntl() system call. commit 87cf3ce6185b9138032a5af53cecae98f8c93564 Author: Eric Wong Date: Mon Sep 27 00:24:50 2010 +0000 connect: no do not leak descriptors on failure We cannot raise exceptions and expect GC to clean up after us until we've created an actual IO object. commit 6fbde1518578dd1b828efcecaf2caf893bddc110 Author: Eric Wong Date: Mon Sep 27 00:11:43 2010 +0000 "start" singleton methods for non-blocking connect These initiate (but do not wait for) non-blocking connects. commit fdfecc6d815bab8dfc1d8ad6758a66d44ab51e31 Author: Eric Wong Date: Sun Sep 26 07:51:12 2010 +0000 introduce kgio_try* methods Avoid altering behavior based on globals that Kgio.wait_{read,writ}able stored in, since that's too confusing. The non-try variants are closer to the normal IO read/write methods, except they can be more easily plugged into alternate reactors and event frameworks. commit d8ee79e1e5c6e6908009213324db25cf41c583ce Author: Eric Wong Date: Sat Sep 25 17:55:07 2010 +0000 kgio_read returns nil on EOF Just like IO#read commit af03e4471de3d3b91eec16e26e93a84d4a717116 Author: Eric Wong Date: Sat Sep 25 17:47:13 2010 +0000 split out reusable bits into separate headers No point in cluttering up the meat of our code. commit db53263856d864ba6273e6cac73011f699509d71 Author: Eric Wong Date: Sat Sep 25 01:36:13 2010 -0700 only use MSG_DONTWAIT under Linux MSG_DONTWAIT is less consistently implemented/supported on other platforms on stream sockets, so fallback to fcntl() + read()/write() for stream sockets. This also fixes our previously broken support of non-MSG_DONTWAIT systems. commit a82dc40c2a509c4ab692da34b572693f243fbfae Author: Eric Wong Date: Sat Sep 25 01:36:12 2010 -0700 write/send may fail with ECONNRESET Tested on FreeBSD 7.0 commit 0c60192621303f5e4ebd46d43a058de48126bc8a Author: Eric Wong Date: Sat Sep 25 01:36:11 2010 -0700 fix missing netinet/in.h include This is needed for FreeBSD 7.0, at least. commit 0beb82437f4ab0b8422e225080b234361092315e Author: Eric Wong Date: Sat Sep 25 08:15:13 2010 +0000 beef up the test suite We need to test server <-> client interaction more thoroughly since some systems don't implement everything right. commit 460e6b025896dee64b39d194d4c1a536129654de Author: Eric Wong Date: Thu Sep 23 22:56:44 2010 +0000 initial commit + release everything shou^Wmight be working... kgio-2.8.1/TODO0000644000004100000410000000010612214142252013163 0ustar www-datawww-data* SSL/TLS support with SNI ({done}[http://bogomips.org/kgio-monkey/]) kgio-2.8.1/pkg.mk0000644000004100000410000001215612214142252013615 0ustar www-datawww-dataRUBY = ruby RAKE = rake RSYNC = rsync WRONGDOC = wrongdoc GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @./GIT-VERSION-GEN -include GIT-VERSION-FILE -include local.mk DLEXT := $(shell $(RUBY) -rrbconfig -e 'puts RbConfig::CONFIG["DLEXT"]') RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION') RUBY_ENGINE := $(shell $(RUBY) -e 'puts((RUBY_ENGINE rescue "ruby"))') lib := lib ifeq ($(shell test -f script/isolate_for_tests && echo t),t) isolate_libs := tmp/isolate/$(RUBY_ENGINE)-$(RUBY_VERSION)/isolate.mk $(isolate_libs): script/isolate_for_tests @$(RUBY) script/isolate_for_tests -include $(isolate_libs) lib := $(lib):$(ISOLATE_LIBS) endif ext := $(firstword $(wildcard ext/*)) ifneq ($(ext),) ext_pfx := tmp/ext/$(RUBY_ENGINE)-$(RUBY_VERSION) ext_h := $(wildcard $(ext)/*/*.h $(ext)/*.h) ext_src := $(wildcard $(ext)/*.c $(ext_h)) ext_pfx_src := $(addprefix $(ext_pfx)/,$(ext_src)) ext_d := $(ext_pfx)/$(ext)/.d $(ext)/extconf.rb: $(wildcard $(ext)/*.h) @>> $@ $(ext_d): @mkdir -p $(@D) @> $@ $(ext_pfx)/$(ext)/%: $(ext)/% $(ext_d) install -m 644 $< $@ $(ext_pfx)/$(ext)/Makefile: $(ext)/extconf.rb $(ext_d) $(ext_h) $(RM) -f $(@D)/*.o cd $(@D) && $(RUBY) $(CURDIR)/$(ext)/extconf.rb ext_sfx := _ext.$(DLEXT) ext_dl := $(ext_pfx)/$(ext)/$(notdir $(ext)_ext.$(DLEXT)) $(ext_dl): $(ext_src) $(ext_pfx_src) $(ext_pfx)/$(ext)/Makefile @echo $^ == $@ $(MAKE) -C $(@D) lib := $(lib):$(ext_pfx)/$(ext) build: $(ext_dl) else build: endif pkg_extra += GIT-VERSION-FILE NEWS ChangeLog LATEST ChangeLog: GIT-VERSION-FILE .wrongdoc.yml $(WRONGDOC) prepare NEWS LATEST: ChangeLog manifest: $(RM) .manifest $(MAKE) .manifest .manifest: $(pkg_extra) (git ls-files && for i in $@ $(pkg_extra); do echo $$i; done) | \ LC_ALL=C sort > $@+ cmp $@+ $@ || mv $@+ $@ $(RM) $@+ doc:: .document .wrongdoc.yml $(pkg_extra) -find lib -type f -name '*.rbc' -exec rm -f '{}' ';' -find ext -type f -name '*.rbc' -exec rm -f '{}' ';' $(RM) -r doc $(WRONGDOC) all install -m644 COPYING doc/COPYING install -m644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/ ifneq ($(VERSION),) pkggem := pkg/$(rfpackage)-$(VERSION).gem pkgtgz := pkg/$(rfpackage)-$(VERSION).tgz release_notes := release_notes-$(VERSION) release_changes := release_changes-$(VERSION) release-notes: $(release_notes) release-changes: $(release_changes) $(release_changes): $(WRONGDOC) release_changes > $@+ $(VISUAL) $@+ && test -s $@+ && mv $@+ $@ $(release_notes): $(WRONGDOC) release_notes > $@+ $(VISUAL) $@+ && test -s $@+ && mv $@+ $@ # ensures we're actually on the tagged $(VERSION), only used for release verify: test x"$(shell umask)" = x0022 git rev-parse --verify refs/tags/v$(VERSION)^{} git diff-index --quiet HEAD^0 test $$(git rev-parse --verify HEAD^0) = \ $$(git rev-parse --verify refs/tags/v$(VERSION)^{}) fix-perms: -git ls-tree -r HEAD | awk '/^100644 / {print $$NF}' | xargs chmod 644 -git ls-tree -r HEAD | awk '/^100755 / {print $$NF}' | xargs chmod 755 gem: $(pkggem) install-gem: $(pkggem) gem install $(CURDIR)/$< $(pkggem): manifest fix-perms gem build $(rfpackage).gemspec mkdir -p pkg mv $(@F) $@ $(pkgtgz): distdir = $(basename $@) $(pkgtgz): HEAD = v$(VERSION) $(pkgtgz): manifest fix-perms @test -n "$(distdir)" $(RM) -r $(distdir) mkdir -p $(distdir) tar cf - $$(cat .manifest) | (cd $(distdir) && tar xf -) cd pkg && tar cf - $(basename $(@F)) | gzip -9 > $(@F)+ mv $@+ $@ package: $(pkgtgz) $(pkggem) test-release:: verify package $(release_notes) $(release_changes) # make tgz release on RubyForge @echo rubyforge add_release -f \ -n $(release_notes) -a $(release_changes) \ $(rfproject) $(rfpackage) $(VERSION) $(pkgtgz) @echo gem push $(pkggem) @echo rubyforge add_file \ $(rfproject) $(rfpackage) $(VERSION) $(pkggem) release:: verify package $(release_notes) $(release_changes) # make tgz release on RubyForge rubyforge add_release -f -n $(release_notes) -a $(release_changes) \ $(rfproject) $(rfpackage) $(VERSION) $(pkgtgz) # push gem to RubyGems.org gem push $(pkggem) # in case of gem downloads from RubyForge releases page rubyforge add_file \ $(rfproject) $(rfpackage) $(VERSION) $(pkggem) else gem install-gem: GIT-VERSION-FILE $(MAKE) $@ VERSION=$(GIT_VERSION) endif all:: test test_units := $(wildcard test/test_*.rb) test: test-unit test-unit: $(test_units) $(test_units): build $(RUBY) -I $(lib) $@ $(RUBY_TEST_OPTS) # this requires GNU coreutils variants ifneq ($(RSYNC_DEST),) publish_doc: -git set-file-times $(MAKE) doc find doc/images -type f | \ TZ=UTC xargs touch -d '1970-01-01 00:00:06' doc/rdoc.css $(MAKE) doc_gz $(RSYNC) -av doc/ $(RSYNC_DEST)/ git ls-files | xargs touch endif # Create gzip variants of the same timestamp as the original so nginx # "gzip_static on" can serve the gzipped versions directly. doc_gz: docs = $(shell find doc -type f ! -regex '^.*\.\(gif\|jpg\|png\|gz\)$$') doc_gz: for i in $(docs); do \ gzip --rsyncable -9 < $$i > $$i.gz; touch -r $$i $$i.gz; done check-warnings: @(for i in $$(git ls-files '*.rb'| grep -v '^setup\.rb$$'); \ do $(RUBY) -d -W2 -c $$i; done) | grep -v '^Syntax OK$$' || : .PHONY: all .FORCE-GIT-VERSION-FILE doc test $(test_units) manifest .PHONY: check-warnings kgio-2.8.1/GNUmakefile0000644000004100000410000000033712214142252014553 0ustar www-datawww-dataall:: RSYNC_DEST := bogomips.org:/srv/bogomips/kgio rfproject := rainbows rfpackage := kgio include pkg.mk ifneq ($(VERSION),) release:: $(RAKE) raa_update VERSION=$(VERSION) $(RAKE) publish_news VERSION=$(VERSION) endif kgio-2.8.1/LATEST0000644000004100000410000000174312214142252013422 0ustar www-datawww-data=== kgio 2.8.1 - minor improvements and test fixes / 2013-09-11 00:22 UTC Improved error reporting for kgio_accept/kgio_tryaccept. Minor size reduction throughout. There are also several test case fixes for race conditions. Thanks to Hleb Valoshka and the Debian project for all the help with this release! Eric Wong (7): check syscall returns against < 0 instead of == -1 accept: more informative exception on unknown family test_tryopen: skip EACCES test when euid == 0 test/lib_read_write: account for larger-than-normal pipes test_poll: avoid potentially thread-unsafe test test_poll: preserve original trap(:USR1) handler test_poll: be less dependent on signal handler ordering Hleb Valoshka (4): Change prefix of temporary sockets to prevent races Don't dump 20M in case of failure Create own directory for every unix socket in unit tests Close tempfile and unlink it immediately. kgio-2.8.1/.document0000644000004100000410000000032312214142252014313 0ustar www-datawww-dataLICENSE README TODO NEWS LATEST ChangeLog ISSUES HACKING lib ext/kgio/accept.c ext/kgio/autopush.c ext/kgio/connect.c ext/kgio/kgio_ext.c ext/kgio/poll.c ext/kgio/read_write.c ext/kgio/wait.c ext/kgio/tryopen.c kgio-2.8.1/metadata.yml0000644000004100000410000000761012214142252015005 0ustar www-datawww-data--- !ruby/object:Gem::Specification name: kgio version: !ruby/object:Gem::Version hash: 45 prerelease: segments: - 2 - 8 - 1 version: 2.8.1 platform: ruby authors: - kgio hackers autorequire: bindir: bin cert_chain: [] date: 2013-09-11 00:00:00 Z dependencies: [] description: |- kgio provides non-blocking I/O methods for Ruby without raising exceptions on EAGAIN and EINPROGRESS. It is intended for use with the Unicorn and Rainbows! Rack servers, but may be used by other applications (that run on Unix-like platforms). email: kgio@librelist.org executables: [] extensions: - ext/kgio/extconf.rb extra_rdoc_files: - LICENSE - README - TODO - NEWS - LATEST - ChangeLog - ISSUES - HACKING - lib/kgio.rb - ext/kgio/accept.c - ext/kgio/autopush.c - ext/kgio/connect.c - ext/kgio/kgio_ext.c - ext/kgio/poll.c - ext/kgio/read_write.c - ext/kgio/wait.c - ext/kgio/tryopen.c files: - .document - .gitignore - .manifest - .wrongdoc.yml - COPYING - ChangeLog - GIT-VERSION-FILE - GIT-VERSION-GEN - GNUmakefile - HACKING - ISSUES - LATEST - LICENSE - NEWS - README - Rakefile - TODO - ext/kgio/accept.c - ext/kgio/ancient_ruby.h - ext/kgio/autopush.c - ext/kgio/blocking_io_region.h - ext/kgio/broken_system_compat.h - ext/kgio/connect.c - ext/kgio/extconf.rb - ext/kgio/kgio.h - ext/kgio/kgio_ext.c - ext/kgio/missing_accept4.h - ext/kgio/my_fileno.h - ext/kgio/nonblock.h - ext/kgio/poll.c - ext/kgio/read_write.c - ext/kgio/set_file_path.h - ext/kgio/sock_for_fd.h - ext/kgio/tryopen.c - ext/kgio/wait.c - kgio.gemspec - lib/kgio.rb - pkg.mk - setup.rb - test/lib_read_write.rb - test/lib_server_accept.rb - test/test_accept_class.rb - test/test_accept_flags.rb - test/test_autopush.rb - test/test_connect_fd_leak.rb - test/test_cross_thread_close.rb - test/test_default_wait.rb - test/test_kgio_addr.rb - test/test_no_dns_on_tcp_connect.rb - test/test_peek.rb - test/test_pipe_popen.rb - test/test_pipe_read_write.rb - test/test_poll.rb - test/test_singleton_read_write.rb - test/test_socket.rb - test/test_socketpair_read_write.rb - test/test_tcp6_client_read_server_write.rb - test/test_tcp_client_read_server_write.rb - test/test_tcp_connect.rb - test/test_tcp_server.rb - test/test_tcp_server_read_client_write.rb - test/test_tfo.rb - test/test_tryopen.rb - test/test_unix_client_read_server_write.rb - test/test_unix_connect.rb - test/test_unix_server.rb - test/test_unix_server_read_client_write.rb homepage: http://bogomips.org/kgio/ licenses: [] post_install_message: rdoc_options: - -t - kgio - kinder, gentler I/O for Ruby - -W - http://bogomips.org/kgio.git/tree/%s require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" required_rubygems_version: !ruby/object:Gem::Requirement none: false requirements: - - ">=" - !ruby/object:Gem::Version hash: 3 segments: - 0 version: "0" requirements: [] rubyforge_project: rainbows rubygems_version: 1.8.24 signing_key: specification_version: 3 summary: kinder, gentler I/O for Ruby test_files: - test/test_poll.rb - test/test_peek.rb - test/test_socket.rb - test/test_default_wait.rb - test/test_no_dns_on_tcp_connect.rb - test/test_unix_connect.rb - test/test_pipe_read_write.rb - test/test_unix_server.rb - test/test_accept_flags.rb - test/test_socketpair_read_write.rb - test/test_tfo.rb - test/test_tcp_server.rb - test/test_unix_server_read_client_write.rb - test/test_cross_thread_close.rb - test/test_tcp_connect.rb - test/test_autopush.rb - test/test_connect_fd_leak.rb - test/test_singleton_read_write.rb - test/test_kgio_addr.rb - test/test_tryopen.rb - test/test_tcp6_client_read_server_write.rb - test/test_tcp_server_read_client_write.rb - test/test_unix_client_read_server_write.rb - test/test_tcp_client_read_server_write.rb - test/test_pipe_popen.rb - test/test_accept_class.rb kgio-2.8.1/.manifest0000644000004100000410000000255612214142252014315 0ustar www-datawww-data.document .gitignore .manifest .wrongdoc.yml COPYING ChangeLog GIT-VERSION-FILE GIT-VERSION-GEN GNUmakefile HACKING ISSUES LATEST LICENSE NEWS README Rakefile TODO ext/kgio/accept.c ext/kgio/ancient_ruby.h ext/kgio/autopush.c ext/kgio/blocking_io_region.h ext/kgio/broken_system_compat.h ext/kgio/connect.c ext/kgio/extconf.rb ext/kgio/kgio.h ext/kgio/kgio_ext.c ext/kgio/missing_accept4.h ext/kgio/my_fileno.h ext/kgio/nonblock.h ext/kgio/poll.c ext/kgio/read_write.c ext/kgio/set_file_path.h ext/kgio/sock_for_fd.h ext/kgio/tryopen.c ext/kgio/wait.c kgio.gemspec lib/kgio.rb pkg.mk setup.rb test/lib_read_write.rb test/lib_server_accept.rb test/test_accept_class.rb test/test_accept_flags.rb test/test_autopush.rb test/test_connect_fd_leak.rb test/test_cross_thread_close.rb test/test_default_wait.rb test/test_kgio_addr.rb test/test_no_dns_on_tcp_connect.rb test/test_peek.rb test/test_pipe_popen.rb test/test_pipe_read_write.rb test/test_poll.rb test/test_singleton_read_write.rb test/test_socket.rb test/test_socketpair_read_write.rb test/test_tcp6_client_read_server_write.rb test/test_tcp_client_read_server_write.rb test/test_tcp_connect.rb test/test_tcp_server.rb test/test_tcp_server_read_client_write.rb test/test_tfo.rb test/test_tryopen.rb test/test_unix_client_read_server_write.rb test/test_unix_connect.rb test/test_unix_server.rb test/test_unix_server_read_client_write.rb kgio-2.8.1/setup.rb0000644000004100000410000010652612214142252014175 0ustar www-datawww-data# -*- encoding: binary -*- # # setup.rb # # Copyright (c) 2000-2005 Minero Aoki # # This program is free software. # You can distribute/modify this program under the terms of # the GNU LGPL, Lesser General Public License version 2.1. # unless Enumerable.method_defined?(:map) # Ruby 1.4.6 module Enumerable alias map collect end end unless File.respond_to?(:read) # Ruby 1.6 def File.read(fname) open(fname) {|f| return f.read } end end unless Errno.const_defined?(:ENOTEMPTY) # Windows? module Errno class ENOTEMPTY # We do not raise this exception, implementation is not needed. end end end def File.binread(fname) open(fname, 'rb') {|f| return f.read } end # for corrupted Windows' stat(2) def File.dir?(path) File.directory?((path[-1,1] == '/') ? path : path + '/') end class ConfigTable include Enumerable def initialize(rbconfig) @rbconfig = rbconfig @items = [] @table = {} # options @install_prefix = nil @config_opt = nil @verbose = true @no_harm = false end attr_accessor :install_prefix attr_accessor :config_opt attr_writer :verbose def verbose? @verbose end attr_writer :no_harm def no_harm? @no_harm end def [](key) lookup(key).resolve(self) end def []=(key, val) lookup(key).set val end def names @items.map {|i| i.name } end def each(&block) @items.each(&block) end def key?(name) @table.key?(name) end def lookup(name) @table[name] or setup_rb_error "no such config item: #{name}" end def add(item) @items.push item @table[item.name] = item end def remove(name) item = lookup(name) @items.delete_if {|i| i.name == name } @table.delete_if {|name, i| i.name == name } item end def load_script(path, inst = nil) if File.file?(path) MetaConfigEnvironment.new(self, inst).instance_eval File.read(path), path end end def savefile '.config' end def load_savefile begin File.foreach(savefile()) do |line| k, v = *line.split(/=/, 2) self[k] = v.strip end rescue Errno::ENOENT setup_rb_error $!.message + "\n#{File.basename($0)} config first" end end def save @items.each {|i| i.value } File.open(savefile(), 'w') {|f| @items.each do |i| f.printf "%s=%s\n", i.name, i.value if i.value? and i.value end } end def load_standard_entries standard_entries(@rbconfig).each do |ent| add ent end end def standard_entries(rbconfig) c = rbconfig rubypath = File.join(c['bindir'], c['ruby_install_name'] + c['EXEEXT']) major = c['MAJOR'].to_i minor = c['MINOR'].to_i teeny = c['TEENY'].to_i version = "#{major}.#{minor}" # ruby ver. >= 1.4.4? newpath_p = ((major >= 2) or ((major == 1) and ((minor >= 5) or ((minor == 4) and (teeny >= 4))))) if c['rubylibdir'] # V > 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = c['rubylibdir'] librubyverarch = c['archdir'] siteruby = c['sitedir'] siterubyver = c['sitelibdir'] siterubyverarch = c['sitearchdir'] elsif newpath_p # 1.4.4 <= V <= 1.6.3 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = c['sitedir'] siterubyver = "$siteruby/#{version}" siterubyverarch = "$siterubyver/#{c['arch']}" else # V < 1.4.4 libruby = "#{c['prefix']}/lib/ruby" librubyver = "#{c['prefix']}/lib/ruby/#{version}" librubyverarch = "#{c['prefix']}/lib/ruby/#{version}/#{c['arch']}" siteruby = "#{c['prefix']}/lib/ruby/#{version}/site_ruby" siterubyver = siteruby siterubyverarch = "$siterubyver/#{c['arch']}" end parameterize = lambda {|path| path.sub(/\A#{Regexp.quote(c['prefix'])}/, '$prefix') } if arg = c['configure_args'].split.detect {|arg| /--with-make-prog=/ =~ arg } makeprog = arg.sub(/'/, '').split(/=/, 2)[1] else makeprog = 'make' end [ ExecItem.new('installdirs', 'std/site/home', 'std: install under libruby; site: install under site_ruby; home: install under $HOME')\ {|val, table| case val when 'std' table['rbdir'] = '$librubyver' table['sodir'] = '$librubyverarch' when 'site' table['rbdir'] = '$siterubyver' table['sodir'] = '$siterubyverarch' when 'home' setup_rb_error '$HOME was not set' unless ENV['HOME'] table['prefix'] = ENV['HOME'] table['rbdir'] = '$libdir/ruby' table['sodir'] = '$libdir/ruby' end }, PathItem.new('prefix', 'path', c['prefix'], 'path prefix of target environment'), PathItem.new('bindir', 'path', parameterize.call(c['bindir']), 'the directory for commands'), PathItem.new('libdir', 'path', parameterize.call(c['libdir']), 'the directory for libraries'), PathItem.new('datadir', 'path', parameterize.call(c['datadir']), 'the directory for shared data'), PathItem.new('mandir', 'path', parameterize.call(c['mandir']), 'the directory for man pages'), PathItem.new('sysconfdir', 'path', parameterize.call(c['sysconfdir']), 'the directory for system configuration files'), PathItem.new('localstatedir', 'path', parameterize.call(c['localstatedir']), 'the directory for local state data'), PathItem.new('libruby', 'path', libruby, 'the directory for ruby libraries'), PathItem.new('librubyver', 'path', librubyver, 'the directory for standard ruby libraries'), PathItem.new('librubyverarch', 'path', librubyverarch, 'the directory for standard ruby extensions'), PathItem.new('siteruby', 'path', siteruby, 'the directory for version-independent aux ruby libraries'), PathItem.new('siterubyver', 'path', siterubyver, 'the directory for aux ruby libraries'), PathItem.new('siterubyverarch', 'path', siterubyverarch, 'the directory for aux ruby binaries'), PathItem.new('rbdir', 'path', '$siterubyver', 'the directory for ruby scripts'), PathItem.new('sodir', 'path', '$siterubyverarch', 'the directory for ruby extentions'), PathItem.new('rubypath', 'path', rubypath, 'the path to set to #! line'), ProgramItem.new('rubyprog', 'name', rubypath, 'the ruby program using for installation'), ProgramItem.new('makeprog', 'name', makeprog, 'the make program to compile ruby extentions'), SelectItem.new('shebang', 'all/ruby/never', 'ruby', 'shebang line (#!) editing mode'), BoolItem.new('without-ext', 'yes/no', 'no', 'does not compile/install ruby extentions') ] end private :standard_entries def load_multipackage_entries multipackage_entries().each do |ent| add ent end end def multipackage_entries [ PackageSelectionItem.new('with', 'name,name...', '', 'ALL', 'package names that you want to install'), PackageSelectionItem.new('without', 'name,name...', '', 'NONE', 'package names that you do not want to install') ] end private :multipackage_entries ALIASES = { 'std-ruby' => 'librubyver', 'stdruby' => 'librubyver', 'rubylibdir' => 'librubyver', 'archdir' => 'librubyverarch', 'site-ruby-common' => 'siteruby', # For backward compatibility 'site-ruby' => 'siterubyver', # For backward compatibility 'bin-dir' => 'bindir', 'bin-dir' => 'bindir', 'rb-dir' => 'rbdir', 'so-dir' => 'sodir', 'data-dir' => 'datadir', 'ruby-path' => 'rubypath', 'ruby-prog' => 'rubyprog', 'ruby' => 'rubyprog', 'make-prog' => 'makeprog', 'make' => 'makeprog' } def fixup ALIASES.each do |ali, name| @table[ali] = @table[name] end @items.freeze @table.freeze @options_re = /\A--(#{@table.keys.join('|')})(?:=(.*))?\z/ end def parse_opt(opt) m = @options_re.match(opt) or setup_rb_error "config: unknown option #{opt}" m.to_a[1,2] end def dllext @rbconfig['DLEXT'] end def value_config?(name) lookup(name).value? end class Item def initialize(name, template, default, desc) @name = name.freeze @template = template @value = default @default = default @description = desc end attr_reader :name attr_reader :description attr_accessor :default alias help_default default def help_opt "--#{@name}=#{@template}" end def value? true end def value @value end def resolve(table) @value.gsub(%r<\$([^/]+)>) { table[$1] } end def set(val) @value = check(val) end private def check(val) setup_rb_error "config: --#{name} requires argument" unless val val end end class BoolItem < Item def config_type 'bool' end def help_opt "--#{@name}" end private def check(val) return 'yes' unless val case val when /\Ay(es)?\z/i, /\At(rue)?\z/i then 'yes' when /\An(o)?\z/i, /\Af(alse)\z/i then 'no' else setup_rb_error "config: --#{@name} accepts only yes/no for argument" end end end class PathItem < Item def config_type 'path' end private def check(path) setup_rb_error "config: --#{@name} requires argument" unless path path[0,1] == '$' ? path : File.expand_path(path) end end class ProgramItem < Item def config_type 'program' end end class SelectItem < Item def initialize(name, selection, default, desc) super @ok = selection.split('/') end def config_type 'select' end private def check(val) unless @ok.include?(val.strip) setup_rb_error "config: use --#{@name}=#{@template} (#{val})" end val.strip end end class ExecItem < Item def initialize(name, selection, desc, &block) super name, selection, nil, desc @ok = selection.split('/') @action = block end def config_type 'exec' end def value? false end def resolve(table) setup_rb_error "$#{name()} wrongly used as option value" end undef set def evaluate(val, table) v = val.strip.downcase unless @ok.include?(v) setup_rb_error "invalid option --#{@name}=#{val} (use #{@template})" end @action.call v, table end end class PackageSelectionItem < Item def initialize(name, template, default, help_default, desc) super name, template, default, desc @help_default = help_default end attr_reader :help_default def config_type 'package' end private def check(val) unless File.dir?("packages/#{val}") setup_rb_error "config: no such package: #{val}" end val end end class MetaConfigEnvironment def initialize(config, installer) @config = config @installer = installer end def config_names @config.names end def config?(name) @config.key?(name) end def bool_config?(name) @config.lookup(name).config_type == 'bool' end def path_config?(name) @config.lookup(name).config_type == 'path' end def value_config?(name) @config.lookup(name).config_type != 'exec' end def add_config(item) @config.add item end def add_bool_config(name, default, desc) @config.add BoolItem.new(name, 'yes/no', default ? 'yes' : 'no', desc) end def add_path_config(name, default, desc) @config.add PathItem.new(name, 'path', default, desc) end def set_config_default(name, default) @config.lookup(name).default = default end def remove_config(name) @config.remove(name) end # For only multipackage def packages raise '[setup.rb fatal] multi-package metaconfig API packages() called for single-package; contact application package vendor' unless @installer @installer.packages end # For only multipackage def declare_packages(list) raise '[setup.rb fatal] multi-package metaconfig API declare_packages() called for single-package; contact application package vendor' unless @installer @installer.packages = list end end end # class ConfigTable # This module requires: #verbose?, #no_harm? module FileOperations def mkdir_p(dirname, prefix = nil) dirname = prefix + File.expand_path(dirname) if prefix $stderr.puts "mkdir -p #{dirname}" if verbose? return if no_harm? # Does not check '/', it's too abnormal. dirs = File.expand_path(dirname).split(%r<(?=/)>) if /\A[a-z]:\z/i =~ dirs[0] disk = dirs.shift dirs[0] = disk + dirs[0] end dirs.each_index do |idx| path = dirs[0..idx].join('') Dir.mkdir path unless File.dir?(path) end end def rm_f(path) $stderr.puts "rm -f #{path}" if verbose? return if no_harm? force_remove_file path end def rm_rf(path) $stderr.puts "rm -rf #{path}" if verbose? return if no_harm? remove_tree path end def remove_tree(path) if File.symlink?(path) remove_file path elsif File.dir?(path) remove_tree0 path else force_remove_file path end end def remove_tree0(path) Dir.foreach(path) do |ent| next if ent == '.' next if ent == '..' entpath = "#{path}/#{ent}" if File.symlink?(entpath) remove_file entpath elsif File.dir?(entpath) remove_tree0 entpath else force_remove_file entpath end end begin Dir.rmdir path rescue Errno::ENOTEMPTY # directory may not be empty end end def move_file(src, dest) force_remove_file dest begin File.rename src, dest rescue File.open(dest, 'wb') {|f| f.write File.binread(src) } File.chmod File.stat(src).mode, dest File.unlink src end end def force_remove_file(path) begin remove_file path rescue end end def remove_file(path) File.chmod 0777, path File.unlink path end def install(from, dest, mode, prefix = nil) $stderr.puts "install #{from} #{dest}" if verbose? return if no_harm? realdest = prefix ? prefix + File.expand_path(dest) : dest realdest = File.join(realdest, File.basename(from)) if File.dir?(realdest) str = File.binread(from) if diff?(str, realdest) verbose_off { rm_f realdest if File.exist?(realdest) } File.open(realdest, 'wb') {|f| f.write str } File.chmod mode, realdest File.open("#{objdir_root()}/InstalledFiles", 'a') {|f| if prefix f.puts realdest.sub(prefix, '') else f.puts realdest end } end end def diff?(new_content, path) return true unless File.exist?(path) new_content != File.binread(path) end def command(*args) $stderr.puts args.join(' ') if verbose? system(*args) or raise RuntimeError, "system(#{args.map{|a| a.inspect }.join(' ')}) failed" end def ruby(*args) command config('rubyprog'), *args end def make(task = nil) command(*[config('makeprog'), task].compact) end def extdir?(dir) File.exist?("#{dir}/MANIFEST") or File.exist?("#{dir}/extconf.rb") end def files_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.file?("#{dir}/#{ent}") } } end DIR_REJECT = %w( . .. CVS SCCS RCS CVS.adm .svn ) def directories_of(dir) Dir.open(dir) {|d| return d.select {|ent| File.dir?("#{dir}/#{ent}") } - DIR_REJECT } end end # This module requires: #srcdir_root, #objdir_root, #relpath module HookScriptAPI def get_config(key) @config[key] end alias config get_config # obsolete: use metaconfig to change configuration def set_config(key, val) @config[key] = val end # # srcdir/objdir (works only in the package directory) # def curr_srcdir "#{srcdir_root()}/#{relpath()}" end def curr_objdir "#{objdir_root()}/#{relpath()}" end def srcfile(path) "#{curr_srcdir()}/#{path}" end def srcexist?(path) File.exist?(srcfile(path)) end def srcdirectory?(path) File.dir?(srcfile(path)) end def srcfile?(path) File.file?(srcfile(path)) end def srcentries(path = '.') Dir.open("#{curr_srcdir()}/#{path}") {|d| return d.to_a - %w(. ..) } end def srcfiles(path = '.') srcentries(path).select {|fname| File.file?(File.join(curr_srcdir(), path, fname)) } end def srcdirectories(path = '.') srcentries(path).select {|fname| File.dir?(File.join(curr_srcdir(), path, fname)) } end end class ToplevelInstaller Version = '3.4.1' Copyright = 'Copyright (c) 2000-2005 Minero Aoki' TASKS = [ [ 'all', 'do config, setup, then install' ], [ 'config', 'saves your configurations' ], [ 'show', 'shows current configuration' ], [ 'setup', 'compiles ruby extentions and others' ], [ 'install', 'installs files' ], [ 'test', 'run all tests in test/' ], [ 'clean', "does `make clean' for each extention" ], [ 'distclean',"does `make distclean' for each extention" ] ] def ToplevelInstaller.invoke config = ConfigTable.new(load_rbconfig()) config.load_standard_entries config.load_multipackage_entries if multipackage? config.fixup klass = (multipackage?() ? ToplevelInstallerMulti : ToplevelInstaller) klass.new(File.dirname($0), config).invoke end def ToplevelInstaller.multipackage? File.dir?(File.dirname($0) + '/packages') end def ToplevelInstaller.load_rbconfig if arg = ARGV.detect {|arg| /\A--rbconfig=/ =~ arg } ARGV.delete(arg) load File.expand_path(arg.split(/=/, 2)[1]) $".push 'rbconfig.rb' else require 'rbconfig' end ::Config::CONFIG end def initialize(ardir_root, config) @ardir = File.expand_path(ardir_root) @config = config # cache @valid_task_re = nil end def config(key) @config[key] end def inspect "#<#{self.class} #{__id__()}>" end def invoke run_metaconfigs case task = parsearg_global() when nil, 'all' parsearg_config init_installers exec_config exec_setup exec_install else case task when 'config', 'test' ; when 'clean', 'distclean' @config.load_savefile if File.exist?(@config.savefile) else @config.load_savefile end __send__ "parsearg_#{task}" init_installers __send__ "exec_#{task}" end end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig" end def init_installers @installer = Installer.new(@config, @ardir, File.expand_path('.')) end # # Hook Script API bases # def srcdir_root @ardir end def objdir_root '.' end def relpath '.' end # # Option Parsing # def parsearg_global while arg = ARGV.shift case arg when /\A\w+\z/ setup_rb_error "invalid task: #{arg}" unless valid_task?(arg) return arg when '-q', '--quiet' @config.verbose = false when '--verbose' @config.verbose = true when '--help' print_usage $stdout exit 0 when '--version' puts "#{File.basename($0)} version #{Version}" exit 0 when '--copyright' puts Copyright exit 0 else setup_rb_error "unknown global option '#{arg}'" end end nil end def valid_task?(t) valid_task_re() =~ t end def valid_task_re @valid_task_re ||= /\A(?:#{TASKS.map {|task,desc| task }.join('|')})\z/ end def parsearg_no_options unless ARGV.empty? task = caller(0).first.slice(%r<`parsearg_(\w+)'>, 1) setup_rb_error "#{task}: unknown options: #{ARGV.join(' ')}" end end alias parsearg_show parsearg_no_options alias parsearg_setup parsearg_no_options alias parsearg_test parsearg_no_options alias parsearg_clean parsearg_no_options alias parsearg_distclean parsearg_no_options def parsearg_config evalopt = [] set = [] @config.config_opt = [] while i = ARGV.shift if /\A--?\z/ =~ i @config.config_opt = ARGV.dup break end name, value = *@config.parse_opt(i) if @config.value_config?(name) @config[name] = value else evalopt.push [name, value] end set.push name end evalopt.each do |name, value| @config.lookup(name).evaluate value, @config end # Check if configuration is valid set.each do |n| @config[n] if @config.value_config?(n) end end def parsearg_install @config.no_harm = false @config.install_prefix = '' while a = ARGV.shift case a when '--no-harm' @config.no_harm = true when /\A--prefix=/ path = a.split(/=/, 2)[1] path = File.expand_path(path) unless path[0,1] == '/' @config.install_prefix = path else setup_rb_error "install: unknown option #{a}" end end end def print_usage(out) out.puts 'Typical Installation Procedure:' out.puts " $ ruby #{File.basename $0} config" out.puts " $ ruby #{File.basename $0} setup" out.puts " # ruby #{File.basename $0} install (may require root privilege)" out.puts out.puts 'Detailed Usage:' out.puts " ruby #{File.basename $0} " out.puts " ruby #{File.basename $0} [] []" fmt = " %-24s %s\n" out.puts out.puts 'Global options:' out.printf fmt, '-q,--quiet', 'suppress message outputs' out.printf fmt, ' --verbose', 'output messages verbosely' out.printf fmt, ' --help', 'print this message' out.printf fmt, ' --version', 'print version and quit' out.printf fmt, ' --copyright', 'print copyright and quit' out.puts out.puts 'Tasks:' TASKS.each do |name, desc| out.printf fmt, name, desc end fmt = " %-24s %s [%s]\n" out.puts out.puts 'Options for CONFIG or ALL:' @config.each do |item| out.printf fmt, item.help_opt, item.description, item.help_default end out.printf fmt, '--rbconfig=path', 'rbconfig.rb to load',"running ruby's" out.puts out.puts 'Options for INSTALL:' out.printf fmt, '--no-harm', 'only display what to do if given', 'off' out.printf fmt, '--prefix=path', 'install path prefix', '' out.puts end # # Task Handlers # def exec_config @installer.exec_config @config.save # must be final end def exec_setup @installer.exec_setup end def exec_install @installer.exec_install end def exec_test @installer.exec_test end def exec_show @config.each do |i| printf "%-20s %s\n", i.name, i.value if i.value? end end def exec_clean @installer.exec_clean end def exec_distclean @installer.exec_distclean end end # class ToplevelInstaller class ToplevelInstallerMulti < ToplevelInstaller include FileOperations def initialize(ardir_root, config) super @packages = directories_of("#{@ardir}/packages") raise 'no package exists' if @packages.empty? @root_installer = Installer.new(@config, @ardir, File.expand_path('.')) end def run_metaconfigs @config.load_script "#{@ardir}/metaconfig", self @packages.each do |name| @config.load_script "#{@ardir}/packages/#{name}/metaconfig" end end attr_reader :packages def packages=(list) raise 'package list is empty' if list.empty? list.each do |name| raise "directory packages/#{name} does not exist"\ unless File.dir?("#{@ardir}/packages/#{name}") end @packages = list end def init_installers @installers = {} @packages.each do |pack| @installers[pack] = Installer.new(@config, "#{@ardir}/packages/#{pack}", "packages/#{pack}") end with = extract_selection(config('with')) without = extract_selection(config('without')) @selected = @installers.keys.select {|name| (with.empty? or with.include?(name)) \ and not without.include?(name) } end def extract_selection(list) a = list.split(/,/) a.each do |name| setup_rb_error "no such package: #{name}" unless @installers.key?(name) end a end def print_usage(f) super f.puts 'Inluded packages:' f.puts ' ' + @packages.sort.join(' ') f.puts end # # Task Handlers # def exec_config run_hook 'pre-config' each_selected_installers {|inst| inst.exec_config } run_hook 'post-config' @config.save # must be final end def exec_setup run_hook 'pre-setup' each_selected_installers {|inst| inst.exec_setup } run_hook 'post-setup' end def exec_install run_hook 'pre-install' each_selected_installers {|inst| inst.exec_install } run_hook 'post-install' end def exec_test run_hook 'pre-test' each_selected_installers {|inst| inst.exec_test } run_hook 'post-test' end def exec_clean rm_f @config.savefile run_hook 'pre-clean' each_selected_installers {|inst| inst.exec_clean } run_hook 'post-clean' end def exec_distclean rm_f @config.savefile run_hook 'pre-distclean' each_selected_installers {|inst| inst.exec_distclean } run_hook 'post-distclean' end # # lib # def each_selected_installers Dir.mkdir 'packages' unless File.dir?('packages') @selected.each do |pack| $stderr.puts "Processing the package `#{pack}' ..." if verbose? Dir.mkdir "packages/#{pack}" unless File.dir?("packages/#{pack}") Dir.chdir "packages/#{pack}" yield @installers[pack] Dir.chdir '../..' end end def run_hook(id) @root_installer.run_hook id end # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end end # class ToplevelInstallerMulti class Installer FILETYPES = %w( bin lib ext data conf man ) include FileOperations include HookScriptAPI def initialize(config, srcroot, objroot) @config = config @srcdir = File.expand_path(srcroot) @objdir = File.expand_path(objroot) @currdir = '.' end def inspect "#<#{self.class} #{File.basename(@srcdir)}>" end def noop(rel) end # # Hook Script API base methods # def srcdir_root @srcdir end def objdir_root @objdir end def relpath @currdir end # # Config Access # # module FileOperations requires this def verbose? @config.verbose? end # module FileOperations requires this def no_harm? @config.no_harm? end def verbose_off begin save, @config.verbose = @config.verbose?, false yield ensure @config.verbose = save end end # # TASK config # def exec_config exec_task_traverse 'config' end alias config_dir_bin noop alias config_dir_lib noop def config_dir_ext(rel) extconf if extdir?(curr_srcdir()) end alias config_dir_data noop alias config_dir_conf noop alias config_dir_man noop def extconf ruby "#{curr_srcdir()}/extconf.rb", *@config.config_opt end # # TASK setup # def exec_setup exec_task_traverse 'setup' end def setup_dir_bin(rel) files_of(curr_srcdir()).each do |fname| update_shebang_line "#{curr_srcdir()}/#{fname}" end end alias setup_dir_lib noop def setup_dir_ext(rel) make if extdir?(curr_srcdir()) end alias setup_dir_data noop alias setup_dir_conf noop alias setup_dir_man noop def update_shebang_line(path) return if no_harm? return if config('shebang') == 'never' old = Shebang.load(path) if old $stderr.puts "warning: #{path}: Shebang line includes too many args. It is not portable and your program may not work." if old.args.size > 1 new = new_shebang(old) return if new.to_s == old.to_s else return unless config('shebang') == 'all' new = Shebang.new(config('rubypath')) end $stderr.puts "updating shebang: #{File.basename(path)}" if verbose? open_atomic_writer(path) {|output| File.open(path, 'rb') {|f| f.gets if old # discard output.puts new.to_s output.print f.read } } end def new_shebang(old) if /\Aruby/ =~ File.basename(old.cmd) Shebang.new(config('rubypath'), old.args) elsif File.basename(old.cmd) == 'env' and old.args.first == 'ruby' Shebang.new(config('rubypath'), old.args[1..-1]) else return old unless config('shebang') == 'all' Shebang.new(config('rubypath')) end end def open_atomic_writer(path, &block) tmpfile = File.basename(path) + '.tmp' begin File.open(tmpfile, 'wb', &block) File.rename tmpfile, File.basename(path) ensure File.unlink tmpfile if File.exist?(tmpfile) end end class Shebang def Shebang.load(path) line = nil File.open(path) {|f| line = f.gets } return nil unless /\A#!/ =~ line parse(line) end def Shebang.parse(line) cmd, *args = *line.strip.sub(/\A\#!/, '').split(' ') new(cmd, args) end def initialize(cmd, args = []) @cmd = cmd @args = args end attr_reader :cmd attr_reader :args def to_s "#! #{@cmd}" + (@args.empty? ? '' : " #{@args.join(' ')}") end end # # TASK install # def exec_install rm_f 'InstalledFiles' exec_task_traverse 'install' end def install_dir_bin(rel) install_files targetfiles(), "#{config('bindir')}/#{rel}", 0755 end def install_dir_lib(rel) install_files libfiles(), "#{config('rbdir')}/#{rel}", 0644 end def install_dir_ext(rel) return unless extdir?(curr_srcdir()) install_files rubyextentions('.'), "#{config('sodir')}/#{File.dirname(rel)}", 0555 end def install_dir_data(rel) install_files targetfiles(), "#{config('datadir')}/#{rel}", 0644 end def install_dir_conf(rel) # FIXME: should not remove current config files # (rename previous file to .old/.org) install_files targetfiles(), "#{config('sysconfdir')}/#{rel}", 0644 end def install_dir_man(rel) install_files targetfiles(), "#{config('mandir')}/#{rel}", 0644 end def install_files(list, dest, mode) mkdir_p dest, @config.install_prefix list.each do |fname| install fname, dest, mode, @config.install_prefix end end def libfiles glob_reject(%w(*.y *.output), targetfiles()) end def rubyextentions(dir) ents = glob_select("*.#{@config.dllext}", targetfiles()) if ents.empty? setup_rb_error "no ruby extention exists: 'ruby #{$0} setup' first" end ents end def targetfiles mapdir(existfiles() - hookfiles()) end def mapdir(ents) ents.map {|ent| if File.exist?(ent) then ent # objdir else "#{curr_srcdir()}/#{ent}" # srcdir end } end # picked up many entries from cvs-1.11.1/src/ignore.c JUNK_FILES = %w( core RCSLOG tags TAGS .make.state .nse_depinfo #* .#* cvslog.* ,* .del-* *.olb *~ *.old *.bak *.BAK *.orig *.rej _$* *$ *.org *.in .* ) def existfiles glob_reject(JUNK_FILES, (files_of(curr_srcdir()) | files_of('.'))) end def hookfiles %w( pre-%s post-%s pre-%s.rb post-%s.rb ).map {|fmt| %w( config setup install clean ).map {|t| sprintf(fmt, t) } }.flatten end def glob_select(pat, ents) re = globs2re([pat]) ents.select {|ent| re =~ ent } end def glob_reject(pats, ents) re = globs2re(pats) ents.reject {|ent| re =~ ent } end GLOB2REGEX = { '.' => '\.', '$' => '\$', '#' => '\#', '*' => '.*' } def globs2re(pats) /\A(?:#{ pats.map {|pat| pat.gsub(/[\.\$\#\*]/) {|ch| GLOB2REGEX[ch] } }.join('|') })\z/ end # # TASK test # TESTDIR = 'test' def exec_test unless File.directory?('test') $stderr.puts 'no test in this package' if verbose? return end $stderr.puts 'Running tests...' if verbose? begin require 'test/unit' rescue LoadError setup_rb_error 'test/unit cannot loaded. You need Ruby 1.8 or later to invoke this task.' end runner = Test::Unit::AutoRunner.new(true) runner.to_run << TESTDIR runner.run end # # TASK clean # def exec_clean exec_task_traverse 'clean' rm_f @config.savefile rm_f 'InstalledFiles' end alias clean_dir_bin noop alias clean_dir_lib noop alias clean_dir_data noop alias clean_dir_conf noop alias clean_dir_man noop def clean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'clean' if File.file?('Makefile') end # # TASK distclean # def exec_distclean exec_task_traverse 'distclean' rm_f @config.savefile rm_f 'InstalledFiles' end alias distclean_dir_bin noop alias distclean_dir_lib noop def distclean_dir_ext(rel) return unless extdir?(curr_srcdir()) make 'distclean' if File.file?('Makefile') end alias distclean_dir_data noop alias distclean_dir_conf noop alias distclean_dir_man noop # # Traversing # def exec_task_traverse(task) run_hook "pre-#{task}" FILETYPES.each do |type| if type == 'ext' and config('without-ext') == 'yes' $stderr.puts 'skipping ext/* by user option' if verbose? next end traverse task, type, "#{task}_dir_#{type}" end run_hook "post-#{task}" end def traverse(task, rel, mid) dive_into(rel) { run_hook "pre-#{task}" __send__ mid, rel.sub(%r[\A.*?(?:/|\z)], '') directories_of(curr_srcdir()).each do |d| traverse task, "#{rel}/#{d}", mid end run_hook "post-#{task}" } end def dive_into(rel) return unless File.dir?("#{@srcdir}/#{rel}") dir = File.basename(rel) Dir.mkdir dir unless File.dir?(dir) prevdir = Dir.pwd Dir.chdir dir $stderr.puts '---> ' + rel if verbose? @currdir = rel yield Dir.chdir prevdir $stderr.puts '<--- ' + rel if verbose? @currdir = File.dirname(rel) end def run_hook(id) path = [ "#{curr_srcdir()}/#{id}", "#{curr_srcdir()}/#{id}.rb" ].detect {|cand| File.file?(cand) } return unless path begin instance_eval File.read(path), path, 1 rescue raise if $DEBUG setup_rb_error "hook #{path} failed:\n" + $!.message end end end # class Installer class SetupError < StandardError; end def setup_rb_error(msg) raise SetupError, msg end if $0 == __FILE__ begin ToplevelInstaller.invoke rescue SetupError raise if $DEBUG $stderr.puts $!.message $stderr.puts "Try 'ruby #{$0} --help' for detailed usage." exit 1 end end kgio-2.8.1/COPYING0000644000004100000410000001672512214142252013544 0ustar www-datawww-data GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. kgio-2.8.1/GIT-VERSION-FILE0000644000004100000410000000002412214142252014700 0ustar www-datawww-dataGIT_VERSION = 2.8.1 kgio-2.8.1/GIT-VERSION-GEN0000755000004100000410000000133412214142252014602 0ustar www-datawww-data#!/bin/sh GVF=GIT-VERSION-FILE DEF_VER=v2.8.1 LF=' ' # First see if there is a version file (included in release tarballs), # then try git-describe, then default. if test -f version then VN=$(cat version) || VN="$DEF_VER" elif test -d .git -o -f .git && VN=$(git describe --abbrev=4 HEAD 2>/dev/null) && case "$VN" in *$LF*) (exit 1) ;; v[0-9]*) git update-index -q --refresh test -z "$(git diff-index --name-only HEAD --)" || VN="$VN-dirty" ;; esac then VN=$(echo "$VN" | sed -e 's/-/./g'); else VN="$DEF_VER" fi VN=$(expr "$VN" : v*'\(.*\)') if test -r $GVF then VC=$(sed -e 's/^GIT_VERSION = //' <$GVF) else VC=unset fi test "$VN" = "$VC" || { echo >&2 "GIT_VERSION = $VN" echo "GIT_VERSION = $VN" >$GVF } kgio-2.8.1/.gitignore0000644000004100000410000000026412214142252014470 0ustar www-datawww-data/local.mk *.o *.log *.so *.rbc /.config /InstalledFiles /doc /local.mk /test/install-* Makefile log/ pkg/ /NEWS /ChangeLog /.manifest /GIT-VERSION-FILE /man tags TAGS /LATEST /tmp kgio-2.8.1/lib/0000755000004100000410000000000012214142252013244 5ustar www-datawww-datakgio-2.8.1/lib/kgio.rb0000644000004100000410000000204112214142252014517 0ustar www-datawww-data# -*- encoding: binary -*- require 'socket' # See the {README}[link:index.html] module Kgio # The IPv4 address of UNIX domain sockets, useful for creating # Rack (and CGI) servers that also serve HTTP traffic over # UNIX domain sockets. LOCALHOST = '127.0.0.1' # Kgio::PipeMethods#kgio_tryread and Kgio::SocketMethods#kgio_tryread will # return :wait_readable when waiting for a read is required. WaitReadable = :wait_readable # PipeMethods#kgio_trywrite and SocketMethods#kgio_trywrite will return # :wait_writable when waiting for a read is required. WaitWritable = :wait_writable end require 'kgio_ext' # use Kgio::Pipe.popen and Kgio::Pipe.new instead of IO.popen # and IO.pipe to get PipeMethods#kgio_read and PipeMethod#kgio_write # methods. class Kgio::Pipe < IO include Kgio::PipeMethods class << self # call-seq: # # rd, wr = Kgio::Pipe.new # # This creates a new pipe(7) with Kgio::Pipe objects that respond # to PipeMethods#kgio_read and PipeMethod#kgio_write alias new pipe end end kgio-2.8.1/NEWS0000644000004100000410000002372112214142252013202 0ustar www-datawww-data=== kgio 2.8.1 - minor improvements and test fixes / 2013-09-11 00:22 UTC Improved error reporting for kgio_accept/kgio_tryaccept. Minor size reduction throughout. There are also several test case fixes for race conditions. Thanks to Hleb Valoshka and the Debian project for all the help with this release! Eric Wong (7): check syscall returns against < 0 instead of == -1 accept: more informative exception on unknown family test_tryopen: skip EACCES test when euid == 0 test/lib_read_write: account for larger-than-normal pipes test_poll: avoid potentially thread-unsafe test test_poll: preserve original trap(:USR1) handler test_poll: be less dependent on signal handler ordering Hleb Valoshka (4): Change prefix of temporary sockets to prevent races Don't dump 20M in case of failure Create own directory for every unix socket in unit tests Close tempfile and unlink it immediately. === kgio 2.8.0 - TCP Fast Open, writev/trywritev / 2013-01-18 10:52 UTC TCP Fast Open in Linux 3.7 and later is now supported in the client via Kgio::Socket#kgio_fastopen. This release also adds the kgio_writev and kgio_trywritev methods, thanks to funny-falcon === kgio 2.7.4 - small fixes and cleanups / 2012-03-24 01:15 UTC Fix build for platforms lacking both TCP_CORK _and_ TCP_NOPUSH There are many test case fixes and cleanups, too. === kgio 2.7.3 - compatibility fixes / 2012-03-15 07:11 UTC Fixed build and autopush support under Debian GNU/kFreeBSD. Test case fixes for timing-sensitive tests. === kgio 2.7.2 - for older, and older Rubies / 2012-01-08 03:42 UTC Fix a missing #include for Ruby 1.8.5 users. No need to upgrade to this (nor 2.7.1) if you're on a modern version of Ruby. === kgio 2.7.1 - compatibility with older Rubies / 2012-01-08 01:59 UTC This release fixes some compatibility issues with people stuck on older versions of Ruby/RubyGems. * define RARRAY_PTR/RARRAY_LEN macros for Ruby 1.8.6 * test/test_autopush: skip strace tests if not available * gemspec: disable development dependencies for old systems === kgio 2.7.0 - minor updates / 2011-12-13 06:16 UTC When running under Ruby trunk/2.0.0dev, all IO objects created by kgio will be close-on-exec by default to match the (future) 2.0.0 behavior. accept()ed sockets in kgio have always been close-on-exec by default.. Singleton Kgio.accept_* methods are deprecated as the kgio_accept/kgio_tryaccept methods all take an additional flags argument. There are various, test, documentation, and error message improvements. === kgio 2.6.0 - minor feature update and cleanups / 2011-07-15 02:01 UTC We now export SOCK_NONBLOCK, SOCK_CLOEXEC constants in the Kgio namespace to make kgio_tryaccept/kgio_accept easier-to-use. There are also some minor internal cleanups. === kgio 2.5.0 - a minor feature update / 2011-06-20 19:30 UTC * Kgio::File.tryopen method added. It is like File.open but won't raise exceptions. The Kgio::File class includes Kgio::PipeMethods, so FIFOs opened through this get the kgio_* API. * The kgio_wait_*able methods in Kgio::DefaultWaiters now accept an optional timeout argument. === kgio 2.4.2 - OpenSolaris build fix / 2011-06-14 18:41 UTC * adds -lnsl and -lsocket checks for OpenSolaris === kgio 2.4.1 - Kgio.poll avoids EINTR, really / 2011-05-21 02:54 UTC This release fixes a race condition that could allow Errno::EINTR to be raised even though the 2.4.0 release was supposed to stop that. Nobody uses Kgio.poll, really, so this shouldn't be an issue for real code, yet. === kgio 2.4.0 - portability fixes and more / 2011-05-05 22:58 UTC == All supported platforms (*nix + MRI 1.8+, Rubinius) * OpenBSD (and possibly other *BSD) fixes, thanks to Jeremy Evans. * kgio_accept and kgio_tryaccept now take an optional second argument for flags (like the accept4() flags argument). == Ruby 1.9-only things * Kgio.poll no longer raises Errno::EINTR to match IO.select. == Ruby 1.9 trunk things * close() on an active FD in a different thread is better handled/detected. * copy-on-write for strings is properly triggered === kgio 2.3.3 - minor fixes / 2011-03-15 12:09 UTC We no longer over-allocate memory for Kgio.poll (1.9.x-only). Under Ruby 1.9.3dev, we also use rb_thread_io_blocking_region to properly deal with cross-thread IO#close. === kgio 2.3.2 - OpenBSD build fix / 2011-02-15 16:56 UTC Thanks to Jeremy Evans, this release fixes the build under OpenBSD. === kgio 2.3.1 - compatibility fix / 2011-02-14 00:51 UTC * connect.c: disable AI_NUMERICSERV It's not needed since we already verify the service is a numeric port. AI_NUMERICSERV is not available in older glibc (<2.3.4) and probably other old systems. === kgio 2.3.0 - MSG_PEEK and poll(2) support / 2011-02-09 10:26 UTC recv() with MSG_PEEK for sockets is added with the try* interface. SocketMethods#kgio_trypeek and SocketMethods#kgio_peek or Kgio.trypeek for non-Kgio-enabled sockets. For Ruby 1.9 only: poll(2) is exposed via the Kgio.poll singleton method and should provide an alternative for IO.select users. Both of these new features should work well on modern Unix-like operating systems. === kgio 2.2.0 - kinder, gentler I/O for the Internets / 2011-02-04 03:07 UTC * sockets accept()ed by a TCP_NOPUSH/TCP_CORK listener automatically flush on kgio_*read calls if there is pending data. "Kgio.autopush = false" disables this globally, and Kgio::Socket also get "kgio_autopush=" to enable/disable on a per-object individual basis. * ECONNRESET exceptions get empty backtraces for kgio_*read. There's nothing a programmer can do about these, so there's no point in going through the expensive backtrace generation process. * Kgio.try* singleton methods added for working with non-Kgio enhanced objects. No more needing to use Object#extend and blowing away your method cache to make existing I/O objects kinder and gentler. * IPv6 support should be complete, systems without a native getaddrinfo(3) are now unsupported (and will remain so unless somebody complains). There should be no other backwards-incompatible changes other than requiring getaddrinfo(3) and friends for IPv6 support. === kgio 2.1.1 - one small Rubinius fix / 2010-12-26 02:08 UTC We now avoid errno side-effects in kgio_wait_*able methods. This affects Rubinius, but may affect other Ruby platforms (particularly those that use stdio) as well. === kgio 2.1.0 - accept improvements and fixes / 2010-12-26 01:07 UTC kgio_accept and kgio_tryaccept now take an optional argument to override the default Kgio::Socket class that is returned. These methods also fall back to using regular accept() if kgio was built on a system with accept4() and later run on a system without accept4(). === kgio 2.0.0 - major internal API changes / 2010-11-19 01:18 UTC (no code changes from 2.0.0pre1) This release should make Kgio easier and more consistent to use across a variety of libraries/applications. The global Kgio.wait_*able(=) accessor methods are gone in favor of having default kgio_wait_readable and kgio_wait_writable methods added to all Kgio-using classes. Sub-classes may (and are encouraged to) redefine these if needed. Eric Wong (7): expand Kgio::*#kgio_read! documentation prefer symbolic names for waiting read/writability EOFError message matches Ruby's README: Gemcutter => RubyGems.org update documentation with mailing list info add default kgio_wait_*able methods switch entirely to kgio_wait_*able methods === kgio 2.0.0pre1 - major internal API changes / 2010-11-18 23:16 UTC This release should make Kgio easier and more consistent to use across a variety of libraries/applications. The global Kgio.wait_*able(=) accessor methods are gone in favor of having default kgio_wait_readable and kgio_wait_writable methods added to all Kgio-using classes. Sub-classes may (and are encouraged to) redefine these if needed. Eric Wong (7): expand Kgio::*#kgio_read! documentation prefer symbolic names for waiting read/writability EOFError message matches Ruby's README: Gemcutter => RubyGems.org update documentation with mailing list info add default kgio_wait_*able methods switch entirely to kgio_wait_*able methods === kgio 1.3.1 - fix zero-length reads / 2010-10-08 22:20 UTC kgio_read and kgio_tryread will now return an empty string when a length of zero is specified instead of nil (which would signal an EOF). This emulates the behavior of IO#read, IO#readpartial, IO#sysread, IO#read_nonblock in core Ruby for consistency. === kgio 1.3.0 - bug and usability fixes / 2010-10-08 03:03 UTC * make Kgio::WaitWritable and Kgio::WaitReadable symbols * trywrite: fix stupid off-by-one error causing corrupt writes on retries === kgio 1.2.1 - doc and *BSD workarounds / 2010-10-07 07:20 UTC This fixes our accept4() wrapper which did not work as expected on some *BSD-based systems due to fcntl(fd, F_GETFL) returning false information. Linux 2.6+ users are unnaffected, including those without accept4(). Also some RDoc fixes. === kgio 1.2.0 - cleanups and minor improvements / 2010-10-05 23:14 UTC The C extension is now split into several files for ease-of-maintenance. Slightly more common, client-triggerable exceptions (EOFError, Errno::EPIPE, Errno::ECONNRESET) are now less expensive as they are generated without backtraces. === kgio 1.1.0 - flexible accept methods / 2010-09-29 01:17 UTC * alternate classes may now be returned by accept/tryaccept by setting Kgio.accept_class= === kgio 1.0.1 - compatibility fixes / 2010-09-28 03:00 UTC * add compatibility for ancient Rubies (1.8.6) * linux: fix accept4() support for newer Linux === kgio 1.0.0 - initial release / 2010-09-28 00:29 UTC A kinder, gentler I/O library for Ruby