kgio-2.11.2/ 0000755 0000041 0000041 00000000000 13241032461 012552 5 ustar www-data www-data kgio-2.11.2/kgio.gemspec 0000644 0000041 0000041 00000001454 13241032461 015054 0 ustar www-data www-data ENV["VERSION"] or abort "VERSION= must be specified"
manifest = File.readlines('.manifest').map! { |x| x.chomp! }
require 'olddoc'
extend Olddoc::Gemspec
name, summary, title = readme_metadata
Gem::Specification.new do |s|
s.name = %q{kgio}
s.version = ENV["VERSION"].dup
s.homepage = Olddoc.config['rdoc_url']
s.authors = ["#{name} hackers"]
s.description = readme_description
s.email = %q{kgio-public@bogomips.org}
s.extra_rdoc_files = extra_rdoc_files(manifest)
s.files = manifest
s.summary = summary
s.test_files = Dir['test/test_*.rb']
s.extensions = %w(ext/kgio/extconf.rb)
s.add_development_dependency('olddoc', '~> 1.0')
s.add_development_dependency('test-unit', '~> 3.0')
# s.add_development_dependency('strace_me', '~> 1.0') # Linux only
s.licenses = %w(LGPL-2.1+)
end
kgio-2.11.2/ISSUES 0000644 0000041 0000041 00000002250 13241032461 013507 0 ustar www-data www-data = Issues
The kgio {mailing list}[mailto:kgio-public@bogomips.org] is the best
place to report bugs, submit patches and/or obtain support after you
have searched the mailing list archives at https://bogomips.org/kgio-public/
and {documentation}[https://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: mailto:kgio-public+subscribe@bogomips.org
* post: mailto:kgio-public@bogomips.org
* private: mailto:kgio@bogomips.org
== Mailing List Archives
* https://bogomips.org/kgio-public/
* nntp://news.public-inbox.org/inbox.comp.lang.ruby.kgio
kgio-2.11.2/.olddoc.yml 0000644 0000041 0000041 00000000602 13241032461 014615 0 ustar www-data www-data ---
cgit_url: https://bogomips.org/kgio.git
git_url: git://bogomips.org/kgio.git
ml_url: https://bogomips.org/kgio-public/
rdoc_url: https://bogomips.org/kgio/
public_email: kgio-public@bogomips.org
private_email: kgio@bogomips.org
noindex:
- LATEST
- TODO
nntp_url:
- nntp://news.public-inbox.org/inbox.comp.lang.ruby.kgio
- nntp://news.gmane.org/gmane.comp.lang.ruby.kgio.general
kgio-2.11.2/LATEST 0000644 0000041 0000041 00000000532 13241032461 013471 0 ustar www-data www-data === kgio 2.11.2 - fix Ruby 2.5 compatibility for accept_class / 2018-01-30 21:11 UTC
This release fixes a bug affecting the Rainbows! web server
which uses Kgio.accept_class:
https://bogomips.org/rainbows-public/CANp6QoJXRbPLqmOPpG7XmZBc+oYqLWCcq1ipvOyNEQPXDEwx9Q@mail.gmail.com/
* accept: avoid passing unnecessary arg to rb_funcall
kgio-2.11.2/GNUmakefile 0000644 0000041 0000041 00000000125 13241032461 014622 0 ustar www-data www-data all::
RSYNC_DEST := bogomips.org:/srv/bogomips/kgio
rfpackage := kgio
include pkg.mk
kgio-2.11.2/README 0000644 0000041 0000041 00000004063 13241032461 013435 0 ustar www-data www-data = kgio - kinder, gentler I/O for Ruby
This is a legacy project, do not use it for new projects. Ruby
2.3 and later should make this obsolete. kgio provides
non-blocking I/O methods for Ruby without raising exceptions on
EAGAIN and EINPROGRESS.
== 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 were fixed in Ruby 1.9.3 and later to 1.9.1 performance levels,
which were still bad)
* 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://man7.org/linux/man-pages/man2/accept4.2.html]
on newer 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.
You may 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:
* https://bogomips.org/kgio.git
* 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-public@bogomips.org]
For the latest on kgio releases, you may check our NEWS page (and
subscribe to our Atom feed).
kgio-2.11.2/NEWS 0000644 0000041 0000041 00000036257 13241032461 013266 0 ustar www-data www-data === kgio 2.11.2 - fix Ruby 2.5 compatibility for accept_class / 2018-01-30 21:11 UTC
This release fixes a bug affecting the Rainbows! web server
which uses Kgio.accept_class:
https://bogomips.org/rainbows-public/CANp6QoJXRbPLqmOPpG7XmZBc+oYqLWCcq1ipvOyNEQPXDEwx9Q@mail.gmail.com/
* accept: avoid passing unnecessary arg to rb_funcall
=== kgio 2.11.1 - fix Ruby 2.5.0dev compatibility / 2017-12-15 19:40 UTC
This release fixes compatibility with Ruby 2.5.0 preview and
release candidates (and thus should be ready for 2.5.0 final)
on some platforms. At least clang 5.0.0 on OpenBSD is affected.
Thanks to Jeremy Evans for the fix.
There's also minor documentation updates since 2.11.0
=== kgio 2.11.0 - reverting 2.10.0 breakage / 2016-12-16 22:56 UTC
Mainly this release reverts back to kgio 2.9.3 behavior by
supporting "autopush" functionality in the C extension.
Removing this feature in 2.10.0 was a monumental mistake(*) on
my part which caused a performance regression for ONE known user
of kgio on the deprecated (but supported) Rainbows! server.
This release does not affect known users outside of Rainbows!,
but we may have unknown users, too.
The attempt at a pure Ruby version of autopush had flaws and
regressions in real-world testing, so that was abandoned.
So yeah, kgio will be supported indefinitely even in the face
of internal changes to MRI, but I strongly discourage its use
for new projects.
Users who only support Ruby 2.3 (and later) are encouraged to
use `exception: false` with all *_nonblock methods.
4 changes since 2.10.0:
resurrect Kgio.autopush support in pure Ruby [REVERTED]
reinstate the original (and dangerous) autopush in C
TODO: update with Ruby 2.3 status
test: increase test data sizes to fill socket buffers
(*) - as was making this a separate project, at all
=== kgio 2.10.0 - slimming down... / 2015-09-06 08:12 UTC
The largely-unused autopush functionality is now a no-op.
Existing applications will probably not notice, and are
better off using MSG_MORE anyways if they're on Linux.
Our poll(2) wrapper now uses rb_hash_clear if available,
reducing global method cache dependence.
shortlog of changes since 2.9.3:
README: remove reference to Rainbows!
writev: update comment on preserving errno
poll: avoid rb_funcall for rb_hash_clear
minor doc updates
remove autopush support and make it a no-op
HACKING: update URL for Message-ID lookup
=== kgio 2.9.3 - minor cleanups and fixes / 2015-01-12 08:46 UTC
Most notably there's a new RB_GC_GUARD to fix an occasional segfault at
load time for Ruby 1.8 users thanks to Petr Novodvorskiy for reporting
the issue.
There's been a new mailing list for a few months now at:
kgio-public@bogomips.org
No subscription is required, but if you wish to subscribe
(existing librelist subscribers must resubscribe)
kgio-public+subscribe@bogomips.org
There's also a bunch of code cleanups and packaging
cleanups. Our website is faster now thanks to olddoc:
http://bogomips.org/kgio/
shortlog:
test/lib_read_write: fix trywritev blocking test
new mailing list info: kgio-public@bogomips.org
relax license to allow LGPLv2.1 or later
gemspec: modernize for more recent rubygems
doc: switch to olddoc
remove references to rubyforge and ancient Rubies
TODO: updates for the future of kgio
tryopen: add RB_GC_GUARD for Ruby 1.8
LICENSE: prefer URL to FSF address
cleanup packaging harder
fixup -Wshorten-64-to-32 warnings
cleanup: avoid shadowing rb_str_subseq
cleanup: avoid shadowing rb_ary_subseq
pkg.mk: synchronize with my other projects
gemspec: remove invalid rdoc_options
gemspec: use SPDX license abbreviation
=== kgio 2.9.2 - avoid deprecated/removed function / 2014-02-15 09:27 UTC
This release is for compatibility with future releases of mainline ruby,
as rb_thread_blocking_region is removed in r44955 of ruby trunk
This also avoids deprecation warnings fo rb_thread_blocking_region
2.0 and 2.1.
=== kgio 2.9.1 - various Ruby 1.8.7 fixes / 2014-02-05 17:50 UTC
Thanks to Christopher Rigor for this release.
Eric Wong (2):
only define and test kgio_syssend on 1.9+
various 1.8.7 fixes
=== kgio 2.9.0 - cleanups, bug fixes, minor improvements / 2014-02-04 03:09 UTC
This adds a new kgio_syssend method for Ruby 1.9+ which behaves
like BasicSocket#send, but will not raise exceptions on EAGAIN.
Eric Wong (12):
test_poll: remove race prone test_poll_EINTR_changed test
tryopen: remove RARRAY_PTR usage in init
read_write: remove the rest of RARRAY_PTR usage
my_writev: stylistic fixes
Rakefile: kill raa_update task
avoid downsizing casts
connect: constify RSTRING-related things
set RSTRING_MODIFIED where appropriate for Rubinius
split read_write.c into {read,write,writev}.c
add kgio_syssend method to wrap send(2)
write: correct check for various blocking regions
tryopen: additional debug information for bad Errno values
Hleb Valoshka (1):
Don't use deprecated api
=== 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
kgio-2.11.2/lib/ 0000755 0000041 0000041 00000000000 13241032461 013320 5 ustar www-data www-data kgio-2.11.2/lib/kgio.rb 0000644 0000041 0000041 00000002041 13241032461 014573 0 ustar www-data www-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.11.2/.manifest 0000644 0000041 0000041 00000002664 13241032461 014371 0 ustar www-data www-data .document
.gitignore
.manifest
.olddoc.yml
COPYING
GIT-VERSION-FILE
GIT-VERSION-GEN
GNUmakefile
HACKING
ISSUES
LATEST
LICENSE
NEWS
README
TODO
archive/.gitignore
archive/slrnpull.conf
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.c
ext/kgio/set_file_path.h
ext/kgio/sock_for_fd.h
ext/kgio/tryopen.c
ext/kgio/wait.c
ext/kgio/write.c
ext/kgio/writev.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_syssend.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.11.2/test/ 0000755 0000041 0000041 00000000000 13241032461 013531 5 ustar www-data www-data kgio-2.11.2/test/test_default_wait.rb 0000644 0000041 0000041 00000001757 13241032461 017577 0 ustar www-data www-data require '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.11.2/test/test_syssend.rb 0000644 0000041 0000041 00000002130 13241032461 016601 0 ustar www-data www-data require 'test/unit'
require 'kgio'
class TestKgioSyssend < Test::Unit::TestCase
def setup
@host = '127.0.0.1' || ENV["TEST_HOST"]
end
def test_syssend
srv = Kgio::TCPServer.new(@host, 0)
port = srv.addr[1]
client = TCPSocket.new(@host, port)
acc = srv.kgio_accept
th = Thread.new { client.readpartial(4) }
sleep(0.05)
assert_nil acc.kgio_syssend("HI", Socket::MSG_DONTWAIT | Socket::MSG_MORE)
assert_nil acc.kgio_syssend("HI", Socket::MSG_DONTWAIT)
assert_equal "HIHI", th.value
buf = "*" * 123
res = []
case rv = acc.kgio_syssend(buf, Socket::MSG_DONTWAIT)
when nil
when String
res << rv
when Symbol
res << rv
break
end while true
assert_equal :wait_writable, res.last
if res.size > 1
assert_kind_of String, res[-2]
else
warn "res too small"
end
# blocking
th = Thread.new { loop { acc.kgio_syssend("ZZZZ", 0) } }
assert_nil th.join(0.1)
ensure
[ srv, acc, client ].each { |io| io.close if io }
end
end if RUBY_PLATFORM =~ /linux/ && Socket.const_defined?(:MSG_MORE)
kgio-2.11.2/test/test_tfo.rb 0000644 0000041 0000041 00000003455 13241032461 015714 0 ustar www-data www-data require '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.11.2/test/test_socketpair_read_write.rb 0000644 0000041 0000041 00000000257 13241032461 021472 0 ustar www-data www-data require './test/lib_read_write.rb'
class TestKgioUNIXSocketPair < Test::Unit::TestCase
def setup
@rd, @wr = Kgio::UNIXSocket.pair
end
include LibReadWriteTest
end
kgio-2.11.2/test/test_tryopen.rb 0000644 0000041 0000041 00000004305 13241032461 016617 0 ustar www-data www-data require '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.11.2/test/test_poll.rb 0000644 0000041 0000041 00000005227 13241032461 016071 0 ustar www-data www-data require '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_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.11.2/test/test_socket.rb 0000644 0000041 0000041 00000000576 13241032461 016415 0 ustar www-data www-data require '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.11.2/test/lib_server_accept.rb 0000644 0000041 0000041 00000004331 13241032461 017532 0 ustar www-data www-data require '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.11.2/test/test_pipe_read_write.rb 0000644 0000041 0000041 00000000236 13241032461 020260 0 ustar www-data www-data require './test/lib_read_write.rb'
class TestKgioPipe < Test::Unit::TestCase
def setup
@rd, @wr = Kgio::Pipe.new
end
include LibReadWriteTest
end
kgio-2.11.2/test/test_connect_fd_leak.rb 0000644 0000041 0000041 00000000550 13241032461 020213 0 ustar www-data www-data require '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.11.2/test/test_tcp_server_read_client_write.rb 0000644 0000041 0000041 00000000501 13241032461 023030 0 ustar www-data www-data require './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.11.2/test/test_tcp_connect.rb 0000644 0000041 0000041 00000004421 13241032461 017415 0 ustar www-data www-data require '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.11.2/test/test_tcp6_client_read_server_write.rb 0000644 0000041 0000041 00000001167 13241032461 023127 0 ustar www-data www-data require './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.11.2/test/test_unix_client_read_server_write.rb 0000644 0000041 0000041 00000000633 13241032461 023233 0 ustar www-data www-data require './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.11.2/test/test_unix_server_read_client_write.rb 0000644 0000041 0000041 00000000633 13241032461 023233 0 ustar www-data www-data require './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.11.2/test/test_singleton_read_write.rb 0000644 0000041 0000041 00000001027 13241032461 021324 0 ustar www-data www-data require '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.11.2/test/test_unix_server.rb 0000644 0000041 0000041 00000000634 13241032461 017471 0 ustar www-data www-data require '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.11.2/test/test_accept_class.rb 0000644 0000041 0000041 00000003302 13241032461 017537 0 ustar www-data www-data require '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.11.2/test/test_pipe_popen.rb 0000644 0000041 0000041 00000000505 13241032461 017253 0 ustar www-data www-data require '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.11.2/test/test_accept_flags.rb 0000644 0000041 0000041 00000002773 13241032461 017541 0 ustar www-data www-data require '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.11.2/test/test_tcp_server.rb 0000644 0000041 0000041 00000000457 13241032461 017277 0 ustar www-data www-data require './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.11.2/test/test_peek.rb 0000644 0000041 0000041 00000001505 13241032461 016042 0 ustar www-data www-data require '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.11.2/test/test_cross_thread_close.rb 0000644 0000041 0000041 00000001131 13241032461 020756 0 ustar www-data www-data require '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.11.2/test/lib_read_write.rb 0000644 0000041 0000041 00000027045 13241032461 017041 0 ustar www-data www-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") do |fp|
nr = 31
buf = fp.read(nr)
# get roughly a 20MB block of random data
(buf * (20 * 1024 * 1024 / nr)) + (buf * rand(123))
end
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,
"penultimate is #{penultimate.inspect}")
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 = RANDOM_BLOB
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 = [ RANDOM_BLOB, RANDOM_BLOB ]
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.11.2/test/test_unix_connect.rb 0000644 0000041 0000041 00000004017 13241032461 017613 0 ustar www-data www-data require '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.11.2/test/test_tcp_client_read_server_write.rb 0000644 0000041 0000041 00000000501 13241032461 023030 0 ustar www-data www-data require './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.11.2/test/test_kgio_addr.rb 0000644 0000041 0000041 00000001024 13241032461 017035 0 ustar www-data www-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.11.2/test/test_no_dns_on_tcp_connect.rb 0000644 0000041 0000041 00000000502 13241032461 021445 0 ustar www-data www-data require '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.11.2/test/test_autopush.rb 0000644 0000041 0000041 00000010701 13241032461 016764 0 ustar www-data www-data require '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.11.2/HACKING 0000644 0000041 0000041 00000004415 13241032461 013545 0 ustar www-data www-data = kgio Hacker's Guide
=== Documentation
We use the latest version of {olddoc}[https://80x24.org/olddoc/] 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
https://bogomips.org/kgio-public/$MESSAGE_ID/ if possible
Message-ID remains searchable even if the archive becomes unavailable.
=== Code Compatibility
We target mainline Ruby 1.9.3 and later.
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 support non-Free platforms under any circumstances.
Our C code follows Linux kernel coding 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-public@bogomips.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:
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 for historical reasons. 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.11.2/archive/ 0000755 0000041 0000041 00000000000 13241032461 014173 5 ustar www-data www-data kgio-2.11.2/archive/slrnpull.conf 0000644 0000041 0000041 00000000271 13241032461 016715 0 ustar www-data www-data # group_name max expire headers_only
gmane.comp.lang.ruby.kgio.general 1000000000 1000000000 0
# usage: slrnpull -d $PWD -h news.gmane.org --no-post
kgio-2.11.2/archive/.gitignore 0000644 0000041 0000041 00000000026 13241032461 016161 0 ustar www-data www-data /data
/news
/requests
kgio-2.11.2/.gitignore 0000644 0000041 0000041 00000000270 13241032461 014541 0 ustar www-data www-data /local.mk
*.o
*.log
*.so
*.rbc
/.config
/InstalledFiles
/doc
/local.mk
/test/install-*
Makefile
log/
pkg/
/NEWS
/.manifest
/GIT-VERSION-FILE
/man
tags
TAGS
/LATEST
/tmp
/NEWS.atom.xml
kgio-2.11.2/pkg.mk 0000644 0000041 0000041 00000007727 13241032461 013701 0 ustar www-data www-data RUBY = ruby
RAKE = rake
RSYNC = rsync
OLDDOC = olddoc
RDOC = rdoc
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
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 $(EXTCONF_ARGS)
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 LATEST
NEWS: GIT-VERSION-FILE .olddoc.yml
$(OLDDOC) prepare
LATEST: NEWS
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 .olddoc.yml $(pkg_extra) $(PLACEHOLDERS)
-find lib -type f -name '*.rbc' -exec rm -f '{}' ';'
-find ext -type f -name '*.rbc' -exec rm -f '{}' ';'
$(RM) -r doc
$(RDOC) -f oldweb
$(OLDDOC) merge
install -m644 COPYING doc/COPYING
install -m644 NEWS doc/NEWS
install -m644 NEWS.atom.xml doc/NEWS.atom.xml
install -m644 $(shell LC_ALL=C grep '^[A-Z]' .document) doc/
ifneq ($(VERSION),)
pkggem := pkg/$(rfpackage)-$(VERSION).gem
pkgtgz := pkg/$(rfpackage)-$(VERSION).tgz
# 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)
release:: verify package
# push gem to RubyGems.org
gem push $(pkggem)
else
gem install-gem: GIT-VERSION-FILE
$(MAKE) $@ VERSION=$(GIT_VERSION)
endif
all:: check
test_units := $(wildcard test/test_*.rb)
test: check
check: 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
$(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 '^.*\.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$$' || :
ifneq ($(PLACEHOLDERS),)
$(PLACEHOLDERS):
echo olddoc_placeholder > $@
endif
.PHONY: all .FORCE-GIT-VERSION-FILE doc check test $(test_units) manifest
.PHONY: check-warnings
kgio-2.11.2/GIT-VERSION-FILE 0000644 0000041 0000041 00000000025 13241032461 014755 0 ustar www-data www-data GIT_VERSION = 2.11.2
kgio-2.11.2/LICENSE 0000644 0000041 0000041 00000001366 13241032461 013565 0 ustar www-data www-data kgio 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}[https://www.gnu.org/licenses/lgpl-2.1.txt] or
(at your option) any later version.
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, see: https://www.gnu.org/licenses/lgpl-2.1.txt
kgio-2.11.2/setup.rb 0000644 0000041 0000041 00000106526 13241032461 014251 0 ustar www-data www-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.11.2/TODO 0000644 0000041 0000041 00000000213 13241032461 013236 0 ustar www-data www-data * obsolete kgio by improving *_nonblock methods in Ruby itself
(Done for *_nonblock in Ruby 2.3.x, not sure for poll and writev support)
kgio-2.11.2/ext/ 0000755 0000041 0000041 00000000000 13241032461 013352 5 ustar www-data www-data kgio-2.11.2/ext/kgio/ 0000755 0000041 0000041 00000000000 13241032461 014303 5 ustar www-data www-data kgio-2.11.2/ext/kgio/kgio.h 0000644 0000041 0000041 00000006051 13241032461 015407 0 ustar www-data www-data #ifndef KGIO_H
#define KGIO_H
#include
#ifdef HAVE_RUBY_IO_H
# include
#else
# include
#endif
#ifdef HAVE_RUBY_THREAD_H
# include
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "ancient_ruby.h"
void init_kgio_wait(void);
void init_kgio_read(void);
void init_kgio_write(void);
void init_kgio_writev(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_CALL_WITHOUT_GVL) && defined(HAVE_RUBY_THREAD_H)
# define KGIO_WITHOUT_GVL(fn,data1,ubf,data2) \
rb_thread_call_without_gvl((fn),(data1),(ubf),(data2))
#elif defined(HAVE_RB_THREAD_BLOCKING_REGION)
typedef VALUE(*kgio_blocking_fn_t)(void*);
# define KGIO_WITHOUT_GVL(fn,data1,ubf,data2) \
rb_thread_blocking_region((kgio_blocking_fn_t)(fn),(data1),(ubf),(data2))
#endif /* HAVE_RB_THREAD_CALL_WITHOUT_GVL || HAVE_RB_THREAD_BLOCKING_REGION */
#if defined(KGIO_WITHOUT_GVL) && 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;
NORETURN(void kgio_raise_empty_bt(VALUE, const char *));
NORETURN(void kgio_wr_sys_fail(const char *));
NORETURN(void kgio_rd_sys_fail(const char *));
/*
* 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__)
# define USE_MSG_DONTWAIT
# endif
#ifdef USE_MSG_DONTWAIT
/* we don't need these variants, we call kgio_autopush_send/recv directly */
static inline void kgio_autopush_write(VALUE io) { }
#else
static inline void kgio_autopush_write(VALUE io) { kgio_autopush_send(io); }
#endif
/* prefer rb_str_subseq because we don't use negative offsets */
#ifndef HAVE_RB_STR_SUBSEQ
#define MY_STR_SUBSEQ(str,beg,len) rb_str_substr((str),(beg),(len))
#else
#define MY_STR_SUBSEQ(str,beg,len) rb_str_subseq((str),(beg),(len))
#endif
#endif /* KGIO_H */
kgio-2.11.2/ext/kgio/my_fileno.h 0000644 0000041 0000041 00000001263 13241032461 016437 0 ustar www-data www-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.11.2/ext/kgio/ancient_ruby.h 0000644 0000041 0000041 00000001161 13241032461 017135 0 ustar www-data www-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_LEN
# define RARRAY_LEN(s) (RARRAY(s)->len)
#endif /* !defined(RARRAY_LEN) */
#endif /* MISSING_ANCIENT_RUBY_H */
kgio-2.11.2/ext/kgio/writev.c 0000644 0000041 0000041 00000020266 13241032461 015775 0 ustar www-data www-data /*
* we're currently too lazy to use rb_ensure to free an allocation, so we
* the abuse rb_str_* API for a temporary buffer
*/
#define RSTRING_MODIFIED 1
#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
#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 MY_ARY_SUBSEQ(ary,idx,len) my_ary_subseq((ary),(idx),(len))
#else
#define MY_ARY_SUBSEQ(ary,idx,len) rb_ary_subseq((ary),(idx),(len))
#endif
static VALUE sym_wait_writable;
#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 int iov_max = 1024; /* this could be overriden in init */
struct wrv_args {
VALUE io;
VALUE buf;
VALUE vec_buf; /* FIXME: this requires RSTRING_MODIFY for rbx */
struct iovec *vec;
int iov_cnt;
size_t batch_len;
int something_written;
int fd;
};
static ssize_t custom_writev(int fd, const struct iovec *vec, int iov_cnt, size_t total_len)
{
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);
/* free() may alter errno */
i = errno;
free(buf);
errno = i;
return result;
}
static void prepare_writev(struct wrv_args *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 = MY_ARY_SUBSEQ(ary, 0, RARRAY_LEN(ary));
else
a->buf = rb_Array(ary);
a->vec_buf = rb_str_new(0, 0);
a->vec = NULL;
}
#ifndef RARRAY_LENINT
static inline int rarray_int(VALUE val)
{
long num = RARRAY_LEN(val);
if ((long)(int)num != num)
rb_raise(rb_eRangeError, "%ld cannot to be an int", num);
return (int)num;
}
#define RARRAY_LENINT(n) rarray_int(n)
#endif
static void fill_iovec(struct wrv_args *a)
{
int i;
struct iovec *curvec;
a->iov_cnt = RARRAY_LENINT(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++) {
VALUE str = rb_ary_entry(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 wrv_args *a, ssize_t n)
{
long i;
long ary_len = RARRAY_LEN(a->buf);
if (n == (ssize_t)a->batch_len) {
i = a->iov_cnt;
n = 0;
} else {
for (i = 0; n && i < ary_len; i++) {
VALUE entry = rb_ary_entry(a->buf, i);
n -= (ssize_t)RSTRING_LEN(entry);
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 = MY_ARY_SUBSEQ(a->buf, i, ary_len - i);
/* setup+replace partially written buffer */
if (n < 0) {
VALUE str = rb_ary_entry(a->buf, 0);
long str_len = RSTRING_LEN(str);
str = MY_STR_SUBSEQ(str, str_len + n, -n);
rb_ary_store(a->buf, 0, str);
}
return RARRAY_LEN(a->buf);
}
static long
writev_check(struct wrv_args *a, ssize_t 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;
}
kgio_wr_sys_fail(msg);
}
return 0;
}
static VALUE my_writev(VALUE io, VALUE ary, int io_wait)
{
struct wrv_args a;
ssize_t n;
prepare_writev(&a, io, ary);
set_nonblocking(a.fd);
do {
fill_iovec(&a);
if (a.iov_cnt == 0)
n = 0;
else if (a.iov_cnt == 1)
n = write(a.fd, a.vec[0].iov_base, a.vec[0].iov_len);
/* for big strings use library function */
else if (USE_WRITEV &&
((long)(a.batch_len/WRITEV_IMPL_THRESHOLD) > a.iov_cnt))
n = writev(a.fd, a.vec, a.iov_cnt);
else
n = 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);
}
/*
* 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_writev(void)
{
#ifdef IOV_MAX
int sys_iov_max = IOV_MAX;
#else
int sys_iov_max = (int)sysconf(_SC_IOV_MAX);
#endif
VALUE mPipeMethods, mSocketMethods;
VALUE mKgio = rb_define_module("Kgio");
if (sys_iov_max < iov_max)
iov_max = sys_iov_max;
sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
rb_define_singleton_method(mKgio, "trywritev", s_trywritev, 2);
mPipeMethods = rb_define_module_under(mKgio, "PipeMethods");
rb_define_method(mPipeMethods, "kgio_writev", kgio_writev, 1);
rb_define_method(mPipeMethods, "kgio_trywritev", kgio_trywritev, 1);
mSocketMethods = rb_define_module_under(mKgio, "SocketMethods");
rb_define_method(mSocketMethods, "kgio_writev", kgio_writev, 1);
rb_define_method(mSocketMethods, "kgio_trywritev", kgio_trywritev, 1);
}
kgio-2.11.2/ext/kgio/accept.c 0000644 0000041 0000041 00000034753 13241032461 015722 0 ustar www-data www-data /* ref: rubinius b2811f260de16d1e972462e27852470364608de5 */
#define RSTRING_MODIFIED 1
#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(KGIO_WITHOUT_GVL)
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);
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 KGIO_WITHOUT_GVL
# 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 /* ! KGIO_WITHOUT_GVL */
# 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 /* ! KGIO_WITHOUT_GVL */
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.11.2/ext/kgio/extconf.rb 0000644 0000041 0000041 00000004004 13241032461 016274 0 ustar www-data www-data require '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_header('ruby/thread.h')
have_func('rb_thread_call_without_gvl', %w{ruby/thread.h})
have_func('rb_thread_blocking_region')
have_func('rb_thread_io_blocking_region')
have_func('rb_str_set_len')
have_func("rb_hash_clear", "ruby.h") # Ruby 2.0+
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.11.2/ext/kgio/blocking_io_region.h 0000644 0000041 0000041 00000001033 13241032461 020273 0 ustar www-data www-data #ifdef KGIO_WITHOUT_GVL
# if defined(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);
# elif defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
# define rb_thread_io_blocking_region(fn,data,fd) \
rb_thread_call_without_gvl((fn),(data),RUBY_UBF_IO,0)
# elif defined(HAVE_RB_THREAD_BLOCKING_REGION)
# define rb_thread_io_blocking_region(fn,data,fd) \
rb_thread_blocking_region((fn),(data),RUBY_UBF_IO,0)
# endif
#endif
kgio-2.11.2/ext/kgio/write.c 0000644 0000041 0000041 00000014446 13241032461 015612 0 ustar www-data www-data /* we do not modify RSTRING pointers here */
#include "kgio.h"
#include "my_fileno.h"
#include "nonblock.h"
static VALUE sym_wait_writable;
struct wr_args {
VALUE io;
VALUE buf;
const char *ptr;
long len;
int fd;
int flags;
};
static void prepare_write(struct wr_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 wr_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 = MY_STR_SUBSEQ(a->buf, written, a->len);
} else {
a->buf = sym_wait_writable;
}
return 0;
}
kgio_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 wr_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);
}
#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 wr_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 */
#if defined(KGIO_WITHOUT_GVL)
# include "blocking_io_region.h"
#ifdef MSG_DONTWAIT /* Linux only */
# define MY_MSG_DONTWAIT (MSG_DONTWAIT)
#else
# define MY_MSG_DONTWAIT (0)
#endif
static VALUE nogvl_send(void *ptr)
{
struct wr_args *a = ptr;
return (VALUE)send(a->fd, a->ptr, a->len, a->flags);
}
/*
* call-seq:
*
* io.kgio_syssend(str, flags) -> 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.
*
* This method is only available on Ruby 1.9.3 or later.
*/
static VALUE kgio_syssend(VALUE io, VALUE str, VALUE flags)
{
struct wr_args a;
long n;
a.flags = NUM2INT(flags);
prepare_write(&a, io, str);
if (a.flags & MY_MSG_DONTWAIT) {
do {
n = (long)send(a.fd, a.ptr, a.len, a.flags);
} while (write_check(&a, n, "send", 0) != 0);
} else {
do {
n = (long)rb_thread_io_blocking_region(
nogvl_send, &a, a.fd);
} while (write_check(&a, n, "send", 0) != 0);
}
return a.buf;
}
#endif /* HAVE_RB_THREAD_IO_BLOCKING_REGION */
/*
* 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);
}
void init_kgio_write(void)
{
VALUE mPipeMethods, mSocketMethods;
VALUE mKgio = rb_define_module("Kgio");
sym_wait_writable = ID2SYM(rb_intern("wait_writable"));
rb_define_singleton_method(mKgio, "trywrite", s_trywrite, 2);
/*
* 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_write", kgio_write, 1);
rb_define_method(mPipeMethods, "kgio_trywrite", kgio_trywrite, 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_write", kgio_send, 1);
rb_define_method(mSocketMethods, "kgio_trywrite", kgio_trysend, 1);
#if defined(KGIO_WITHOUT_GVL)
rb_define_method(mSocketMethods, "kgio_syssend", kgio_syssend, 2);
#endif
}
kgio-2.11.2/ext/kgio/tryopen.c 0000644 0000041 0000041 00000011621 13241032461 016150 0 ustar www-data www-data /* We do not modify RSTRING in this file, so RSTRING_MODIFIED is not needed */
#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"
#include "kgio.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 void * nogvl_open(void *ptr)
{
struct open_args *o = ptr;
long fd = (long)rb_cloexec_open(o->pathname, o->flags, o->mode);
return (void *)fd;
}
#ifndef KGIO_WITHOUT_GVL
# define RUBY_UBF_IO ((void *)(-1))
# include "rubysig.h"
typedef void my_unblock_function_t(void *);
typedef void *my_blocking_function_t(void *);
static void * my_thread_blocking_region(
my_blocking_function_t *fn, void *data1,
my_unblock_function_t *ubf, void *data2)
{
void *rv;
TRAP_BEG; /* for FIFO */
rv = fn(data1);
TRAP_END;
return rv;
}
#define KGIO_WITHOUT_GVL(fn,data1,ubf,data2) \
my_thread_blocking_region((fn),(data1),(ubf),(data2))
#endif /* ! KGIO_WITHOUT_GVL */
/*
* 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)
{
long 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 = (long)KGIO_WITHOUT_GVL(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, LONG2FIX(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;
long i, 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);
len = RARRAY_LEN(tmp);
for (i = 0; i < len; i++) {
VALUE error;
VALUE err = rb_ary_entry(tmp, i);
ID const_id;
switch (TYPE(err)) {
case T_SYMBOL: const_id = SYM2ID(err); break;
case T_STRING: const_id = rb_intern(RSTRING_PTR(err)); break;
default: {
VALUE i = rb_inspect(err);
const char *s = RSTRING_PTR(i);
rb_bug("constant not a symbol or string: %s", s);
RB_GC_GUARD(i);
}
}
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));
}
}
RB_GC_GUARD(tmp);
}
kgio-2.11.2/ext/kgio/wait.c 0000644 0000041 0000041 00000007133 13241032461 015417 0 ustar www-data www-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);
}
VALUE kgio_call_wait_readable(VALUE io)
{
return rb_funcall(io, id_wait_rd, 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.11.2/ext/kgio/missing_accept4.h 0000644 0000041 0000041 00000003027 13241032461 017532 0 ustar www-data www-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.11.2/ext/kgio/set_file_path.h 0000644 0000041 0000041 00000001307 13241032461 017263 0 ustar www-data www-data /* We do not modify RSTRING in this file, so RSTRING_MODIFIED is not needed */
#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.11.2/ext/kgio/autopush.c 0000644 0000041 0000041 00000014575 13241032461 016333 0 ustar www-data www-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).
*
* Please do not use this (or kgio at all) in new code. Under Linux,
* use MSG_MORE, instead, as it requires fewer syscalls. Users of
* other systems are encouraged to add MSG_MORE support to their
* favorite OS.
*/
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.11.2/ext/kgio/read.c 0000644 0000041 0000041 00000020333 13241032461 015363 0 ustar www-data www-data /* ref: rubinius b2811f260de16d1e972462e27852470364608de5 */
#define RSTRING_MODIFIED 1
#include "kgio.h"
#include "my_fileno.h"
#include "nonblock.h"
static VALUE sym_wait_readable;
#ifdef USE_MSG_DONTWAIT
static const int peek_flags = MSG_DONTWAIT|MSG_PEEK;
/* we don't need these variants, we call kgio_autopush_recv directly */
static inline void kgio_autopush_read(VALUE io) { }
#else
static const int peek_flags = MSG_PEEK;
static inline void kgio_autopush_read(VALUE io) { kgio_autopush_recv(io); }
#endif
struct rd_args {
VALUE io;
VALUE buf;
char *ptr;
long len;
int fd;
};
NORETURN(static void my_eof_error(void));
static void my_eof_error(void)
{
kgio_raise_empty_bt(rb_eEOFError, "end of file reached");
}
static void prepare_read(struct rd_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 rd_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;
}
}
kgio_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 rd_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 rd_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 rd_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]);
}
/*
* 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]);
}
void init_kgio_read(void)
{
VALUE mPipeMethods, mSocketMethods;
VALUE mKgio = rb_define_module("Kgio");
sym_wait_readable = ID2SYM(rb_intern("wait_readable"));
rb_define_singleton_method(mKgio, "tryread", s_tryread, -1);
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_tryread", kgio_tryread, -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_tryread", kgio_tryrecv, -1);
rb_define_method(mSocketMethods, "kgio_trypeek", kgio_trypeek, -1);
rb_define_method(mSocketMethods, "kgio_peek", kgio_peek, -1);
}
kgio-2.11.2/ext/kgio/nonblock.h 0000644 0000041 0000041 00000000657 13241032461 016271 0 ustar www-data www-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.11.2/ext/kgio/sock_for_fd.h 0000644 0000041 0000041 00000003324 13241032461 016734 0 ustar www-data www-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.11.2/ext/kgio/broken_system_compat.h 0000644 0000041 0000041 00000002675 13241032461 020715 0 ustar www-data www-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.11.2/ext/kgio/connect.c 0000644 0000041 0000041 00000025623 13241032461 016110 0 ustar www-data www-data /* We do not modify RSTRING in this file, so RSTRING_MODIFIED is not needed */
#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,
const 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 const struct sockaddr *sockaddr_from(socklen_t *addrlen, VALUE addr)
{
if (TYPE(addr) == T_STRING) {
*addrlen = (socklen_t)RSTRING_LEN(addr);
return (const struct sockaddr *)(RSTRING_PTR(addr));
}
rb_raise(rb_eTypeError, "invalid address");
return NULL;
}
#if defined(MSG_FASTOPEN) && defined(KGIO_WITHOUT_GVL)
struct tfo_args {
int fd;
const void *buf;
size_t buflen;
const 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 MY_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;
const struct sockaddr *sockaddr = sockaddr_from(&addrlen, addr);
switch (((const 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(KGIO_WITHOUT_GVL)
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.11.2/ext/kgio/kgio_ext.c 0000644 0000041 0000041 00000004750 13241032461 016266 0 ustar www-data www-data #include "kgio.h"
#include
#include
/* true if TCP Fast Open is usable */
unsigned kgio_tfo;
static VALUE eErrno_EPIPE, eErrno_ECONNRESET;
static ID id_set_backtrace;
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 kgio_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);
}
void kgio_wr_sys_fail(const char *msg)
{
switch (errno) {
case EPIPE:
errno = 0;
kgio_raise_empty_bt(eErrno_EPIPE, msg);
case ECONNRESET:
errno = 0;
kgio_raise_empty_bt(eErrno_ECONNRESET, msg);
}
rb_sys_fail(msg);
}
void kgio_rd_sys_fail(const char *msg)
{
if (errno == ECONNRESET) {
errno = 0;
kgio_raise_empty_bt(eErrno_ECONNRESET, msg);
}
rb_sys_fail(msg);
}
void Init_kgio_ext(void)
{
VALUE mKgio = rb_define_module("Kgio");
VALUE mPipeMethods = rb_define_module_under(mKgio, "PipeMethods");
VALUE mSocketMethods = rb_define_module_under(mKgio, "SocketMethods");
VALUE mWaiters = rb_define_module_under(mKgio, "DefaultWaiters");
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"));
/*
* 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);
rb_include_module(mPipeMethods, mWaiters);
rb_include_module(mSocketMethods, mWaiters);
tfo_maybe();
init_kgio_wait();
init_kgio_read();
init_kgio_write();
init_kgio_writev();
init_kgio_connect();
init_kgio_accept();
init_kgio_autopush();
init_kgio_poll();
init_kgio_tryopen();
}
kgio-2.11.2/ext/kgio/poll.c 0000644 0000041 0000041 00000013174 13241032461 015423 0 ustar www-data www-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;
#ifdef HAVE_RB_HASH_CLEAR /* Ruby >= 2.0 */
# define my_hash_clear(h) (void)rb_hash_clear(h)
#else /* !HAVE_RB_HASH_CLEAR - Ruby <= 1.9.3 */
static ID id_clear;
static void my_hash_clear(VALUE h)
{
rb_funcall(h, id_clear, 0);
}
#endif /* HAVE_RB_HASH_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 void * nogvl_poll(void *ptr)
{
struct poll_args *a = ptr;
long n;
if (a->timeout > 0)
clock_gettime(hopefully_CLOCK_MONOTONIC, &a->start);
n = poll(a->fds, a->nfds, a->timeout);
return (void *)n;
}
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)
my_hash_clear(a->ios);
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;
long nr;
Check_Type(a->ios, T_HASH);
retry:
hash2pollfds(a);
nr = (long)KGIO_WITHOUT_GVL(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((int)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"));
#ifndef HAVE_RB_HASH_CLEAR
id_clear = rb_intern("clear");
#endif
#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.11.2/GIT-VERSION-GEN 0000755 0000041 0000041 00000001335 13241032461 014657 0 ustar www-data www-data #!/bin/sh
GVF=GIT-VERSION-FILE
DEF_VER=v2.11.2
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.11.2/.document 0000644 0000041 0000041 00000000311 13241032461 014364 0 ustar www-data www-data LICENSE
README
TODO
NEWS
LATEST
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.11.2/COPYING 0000644 0000041 0000041 00000016725 13241032461 013620 0 ustar www-data www-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.