io-poll 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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
+