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.
Files changed (4) hide show
  1. data/LICENSE.md +13 -0
  2. data/README.md +23 -0
  3. data/lib/io/poll.rb +163 -0
  4. 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
+