io-poll 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.md +13 -0
- data/README.md +23 -0
- data/lib/io/poll.rb +163 -0
- metadata +83 -0
data/LICENSE.md
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright 2011 Square Inc.
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
you may not use this file except in compliance with the License.
|
5
|
+
You may obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
See the License for the specific language governing permissions and
|
13
|
+
limitations under the License.
|
data/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
io-poll
|
2
|
+
==========
|
3
|
+
|
4
|
+
FFI bindings for poll(2) and select(2) emulation
|
5
|
+
Ruby 1.8's IO.select() smashes the stack when given >1024 fds, and Ruby doesn't implement IO.poll().
|
6
|
+
|
7
|
+
|
8
|
+
Usage
|
9
|
+
-----
|
10
|
+
|
11
|
+
require 'io/poll'
|
12
|
+
read_fds = [STDIN]
|
13
|
+
write_fds = [STDOUT]
|
14
|
+
err_fds = []
|
15
|
+
poll_period = 60
|
16
|
+
read_fds, write_fds, err_fds = IO.select_using_poll(read_fds, write_fds, err_fds, poll_period)
|
17
|
+
|
18
|
+
|
19
|
+
BUGS/TODO
|
20
|
+
---------
|
21
|
+
|
22
|
+
* This is a hack.
|
23
|
+
|
data/lib/io/poll.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
class IO
|
4
|
+
module Poll
|
5
|
+
arch, os = RUBY_PLATFORM.split('-')
|
6
|
+
if os =~ /^darwin/
|
7
|
+
# /usr/include/sys/poll.h
|
8
|
+
# Requestable events. If poll(2) finds any of these set, they are
|
9
|
+
# copied to revents on return.
|
10
|
+
POLLIN = 0x0001 #/* any readable data available */
|
11
|
+
POLLPRI = 0x0002 #/* OOB/Urgent readable data */
|
12
|
+
POLLOUT = 0x0004 #/* file descriptor is writeable */
|
13
|
+
POLLRDNORM = 0x0040 #/* non-OOB/URG data available */
|
14
|
+
POLLWRNORM = POLLOUT #/* no write type differentiation */
|
15
|
+
POLLRDBAND = 0x0080 #/* OOB/Urgent readable data */
|
16
|
+
POLLWRBAND = 0x0100 #/* OOB/Urgent data can be written */
|
17
|
+
# FreeBSD extensions: polling on a regular file might return one
|
18
|
+
# of these events (currently only supported on local filesystems).
|
19
|
+
POLLEXTEND = 0x0200 #/* file may have been extended */
|
20
|
+
POLLATTRIB = 0x0400 #/* file attributes may have changed */
|
21
|
+
POLLNLINK = 0x0800 #/* (un)link/rename may have happened */
|
22
|
+
POLLWRITE = 0x1000 #/* file's contents may have changed */
|
23
|
+
# These events are set if they occur regardless of whether they were
|
24
|
+
# requested
|
25
|
+
POLLERR = 0x0008 #/* some poll error occurred */
|
26
|
+
POLLHUP = 0x0010 #/* file descriptor was "hung up" */
|
27
|
+
POLLNVAL = 0x0020 #/* requested events "invalid" */
|
28
|
+
|
29
|
+
POLLSTANDARD = (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND|POLLWRBAND|POLLERR|POLLHUP|POLLNVAL)
|
30
|
+
|
31
|
+
elsif os == "linux"
|
32
|
+
# /usr/include/bits/poll.h
|
33
|
+
#/* Event types that can be polled for. These bits may be set in `events'
|
34
|
+
# to indicate the interesting event types; they will appear in `revents'
|
35
|
+
# to indicate the status of the file descriptor. */
|
36
|
+
POLLIN = 0x001 #/* There is data to read. */
|
37
|
+
POLLPRI = 0x002 #/* There is urgent data to read. */
|
38
|
+
POLLOUT = 0x004 #/* Writing now will not block. */
|
39
|
+
|
40
|
+
#/* These values are defined in XPG4.2. */
|
41
|
+
POLLRDNORM = 0x040 #/* Normal data may be read. */
|
42
|
+
POLLRDBAND = 0x080 #/* Priority data may be read. */
|
43
|
+
POLLWRNORM = 0x100 #/* Writing now will not block. */
|
44
|
+
POLLWRBAND = 0x200 #/* Priority data may be written. */
|
45
|
+
|
46
|
+
#/* These are extensions for Linux. */
|
47
|
+
POLLMSG = 0x400
|
48
|
+
POLLREMOVE = 0x1000
|
49
|
+
POLLRDHUP = 0x2000
|
50
|
+
|
51
|
+
#/* Event types always implicitly polled for. These bits need not be set in
|
52
|
+
# `events', but they will appear in `revents' to indicate the status of
|
53
|
+
# the file descriptor. */
|
54
|
+
POLLERR = 0x008 # /* Error condition. */
|
55
|
+
POLLHUP = 0x010 # /* Hung up. */
|
56
|
+
POLLNVAL = 0x020 # /* Invalid polling request. */
|
57
|
+
|
58
|
+
# not part of poll.h on linux, but looks handy
|
59
|
+
POLLSTANDARD = (POLLIN|POLLPRI|POLLOUT|POLLRDNORM|POLLRDBAND|POLLWRBAND|POLLERR|POLLHUP|POLLNVAL)
|
60
|
+
|
61
|
+
else
|
62
|
+
raise "unknown platform: #{RUBY_PLATFORM}"
|
63
|
+
end
|
64
|
+
end # module IO::Poll
|
65
|
+
end # class IO
|
66
|
+
|
67
|
+
# poll() constants
|
68
|
+
|
69
|
+
|
70
|
+
|
71
|
+
# s_ | Integer | signed short, native endian
|
72
|
+
# i, i_ | Integer | signed int, native endian
|
73
|
+
# syscall SYS_poll, [STDIN.fileno, ]
|
74
|
+
|
75
|
+
require 'rubygems'
|
76
|
+
require 'ffi'
|
77
|
+
|
78
|
+
class IO
|
79
|
+
extend FFI::Library
|
80
|
+
ffi_lib FFI::Library::LIBC
|
81
|
+
|
82
|
+
class PollFdStruct < FFI::Struct
|
83
|
+
layout :fd, :int,
|
84
|
+
:events, :short,
|
85
|
+
:revents, :short
|
86
|
+
end
|
87
|
+
|
88
|
+
attach_function 'poll', [:pointer, :int, :int], :int
|
89
|
+
|
90
|
+
def IO.select_using_poll(read, write, error, timeout)
|
91
|
+
read = [] if read.nil?
|
92
|
+
write = [] if write.nil?
|
93
|
+
error = [] if error.nil?
|
94
|
+
all = read.map { |f| { :io => f, :events => IO::Poll::POLLIN } } +
|
95
|
+
write.map { |f| { :io => f, :events => IO::Poll::POLLOUT } } +
|
96
|
+
error.map { |f| { :io => f, :events => IO::Poll::POLLERR } }
|
97
|
+
pollfds = FFI::MemoryPointer.new(IO::PollFdStruct, all.length)
|
98
|
+
all.each_with_index do |poll, i|
|
99
|
+
struct = IO::PollFdStruct.new(pollfds[i])
|
100
|
+
struct[:fd] = poll[:io].fileno
|
101
|
+
struct[:events] = poll[:events]
|
102
|
+
struct[:revents] = 0
|
103
|
+
end
|
104
|
+
ret = IO.poll(pollfds, all.length, timeout)
|
105
|
+
if ret < 0
|
106
|
+
# error. IO.select() returns nil in this case
|
107
|
+
return nil
|
108
|
+
elsif 0 == ret
|
109
|
+
# timed out, exit fast
|
110
|
+
return [[],[],[]]
|
111
|
+
else
|
112
|
+
# returned some interesting descriptors
|
113
|
+
ret_read, ret_write, ret_error = [], [], []
|
114
|
+
all.each_with_index do |poll, i|
|
115
|
+
# select() will signal unrequested flags (eg POLLNVAL) which must be passed
|
116
|
+
# back to the correct read/write/error fdset. So, test what we were requesting,
|
117
|
+
# and return the fd if *any* notification occured, even if it wasn't what we asked for
|
118
|
+
struct = IO::PollFdStruct.new(pollfds[i])
|
119
|
+
next if 0 == struct[:revents]
|
120
|
+
ret_read << poll[:io] if poll[:events] == IO::Poll::POLLIN and not struct[:revents].zero?
|
121
|
+
ret_write << poll[:io] if poll[:events] == IO::Poll::POLLOUT and not struct[:revents].zero?
|
122
|
+
ret_error << poll[:io] if poll[:events] == IO::Poll::POLLERR and not struct[:revents].zero?
|
123
|
+
end
|
124
|
+
return [ret_read, ret_write, ret_error]
|
125
|
+
end # if/else ret
|
126
|
+
end # select_using_poll()
|
127
|
+
|
128
|
+
end # class IO
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
if __FILE__ == $0
|
133
|
+
# allocate memory the size of of 3 PollFdStructs
|
134
|
+
pollfd_len = 3
|
135
|
+
pollfds = FFI::MemoryPointer.new(IO::PollFdStruct, pollfd_len)
|
136
|
+
|
137
|
+
# populate it with stdin/out/err, poll all events. revents is 0 going in.
|
138
|
+
pollfd_len.times do |i|
|
139
|
+
struct = IO::PollFdStruct.new(pollfds[i])
|
140
|
+
struct[:fd] = i
|
141
|
+
struct[:events] = IO::Poll::POLLSTANDARD
|
142
|
+
struct[:revents] = 0
|
143
|
+
end
|
144
|
+
|
145
|
+
# call poll
|
146
|
+
# The resulting pollfds structure will have :revents populated with the poll results
|
147
|
+
# The pollfds structure can be re-used
|
148
|
+
ret = IO.poll(pollfds, pollfd_len, 4);
|
149
|
+
|
150
|
+
STDERR.puts "ret: #{ret.inspect}"
|
151
|
+
|
152
|
+
# Implement IO.select() using poll(). Easy to use, terrible performance.
|
153
|
+
using_poll = IO.select_using_poll([STDIN], [STDOUT], [], 5);
|
154
|
+
using_kern_select = IO.select([STDIN], [STDOUT], [], 5);
|
155
|
+
|
156
|
+
STDERR.puts "IO.select_using_poll([#{STDIN.inspect}], [#{STDOUT.inspect}], [], 5):"
|
157
|
+
STDERR.puts using_poll.inspect
|
158
|
+
STDERR.puts "and inval is: #{IO::Poll::POLLNVAL}"
|
159
|
+
STDERR.puts "XXXX"
|
160
|
+
# compare to IO.select
|
161
|
+
STDERR.puts "IO.select([STDIN], [STDOUT], [], 5);"
|
162
|
+
STDERR.puts using_kern_select.inspect
|
163
|
+
end
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: io-poll
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 4
|
10
|
+
version: 0.0.4
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Evan Miller
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2013-02-14 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: ffi
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
32
|
+
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
description: Ruby 1.8's IO.select() smashes the stack when given >1024 fds, and Ruby doesn't implement IO.poll().
|
35
|
+
email:
|
36
|
+
- github@squareup.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE.md
|
43
|
+
files:
|
44
|
+
- lib/io/poll.rb
|
45
|
+
- README.md
|
46
|
+
- LICENSE.md
|
47
|
+
homepage: http://github.com/square/prodeng
|
48
|
+
licenses: []
|
49
|
+
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options:
|
52
|
+
- --charset=UTF-8
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 0
|
63
|
+
version: "0"
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
hash: 23
|
70
|
+
segments:
|
71
|
+
- 1
|
72
|
+
- 3
|
73
|
+
- 6
|
74
|
+
version: 1.3.6
|
75
|
+
requirements: []
|
76
|
+
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 1.8.24
|
79
|
+
signing_key:
|
80
|
+
specification_version: 3
|
81
|
+
summary: FFI bindings for poll(2) and select(2) emulation
|
82
|
+
test_files: []
|
83
|
+
|