barlume 0.0.1.rc.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ barlume - a dim light over asynchronous I/O land
2
+ ================================================
data/barlume.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ Kernel.load 'lib/barlume/version.rb'
2
+
3
+ Gem::Specification.new {|s|
4
+ s.name = 'barlume'
5
+ s.version = Barlume.version
6
+ s.author = 'meh.'
7
+ s.email = 'meh@paranoici.org'
8
+ s.homepage = 'http://github.com/meh/barlume'
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = 'A dim light over asynchronous I/O land.'
11
+
12
+ s.files = `git ls-files`.split("\n")
13
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.require_paths = ['lib']
16
+
17
+ s.add_dependency 'ffi'
18
+ }
data/examples/test.rb ADDED
@@ -0,0 +1,27 @@
1
+ #! /usr/bin/env ruby
2
+ require 'barlume'
3
+ require 'socket'
4
+
5
+ lantern = Barlume::Lanterna.best
6
+ server = TCPServer.new 43215
7
+ clients = []
8
+
9
+ puts "Using #{lantern.name}..."
10
+
11
+ lantern << server
12
+
13
+ loop do
14
+ lantern.readable.each {|lucciola|
15
+ if lucciola == server
16
+ server.accept.tap {|client|
17
+ clients.push(lantern.add(client))
18
+ }
19
+ else
20
+ puts lucciola.readline rescue nil
21
+
22
+ if lucciola.closed?
23
+ clients.delete(lantern.remove(lucciola))
24
+ end
25
+ end
26
+ }
27
+ end
data/lib/barlume.rb ADDED
@@ -0,0 +1,20 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'barlume/lanterna'
@@ -0,0 +1,134 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'barlume/lucciola'
21
+
22
+ module Barlume
23
+
24
+ class Lanterna
25
+ class << self
26
+ %w[select poll epoll kqueue].each {|name|
27
+ define_method "#{name}?" do
28
+ const_get(name.capitalize).supported?
29
+ end
30
+
31
+ define_method name do
32
+ const_get(name.capitalize).new
33
+ end
34
+ }
35
+
36
+ def best
37
+ return kqueue if kqueue?
38
+
39
+ return epoll if epoll?
40
+
41
+ return poll if poll?
42
+
43
+ return select
44
+ end
45
+
46
+ def new (*)
47
+ raise 'unsupported platform' unless supported?
48
+
49
+ super
50
+ end
51
+ end
52
+
53
+ Available = Struct.new(:readable, :writable, :error)
54
+
55
+ class Breaker
56
+ def initialize
57
+ @pipes = IO.pipe
58
+ end
59
+
60
+ def break
61
+ @pipes.last.write_nonblock 'x'
62
+ end
63
+
64
+ def flush
65
+ @pipes.first.read_nonblock 2048 rescue nil
66
+ end
67
+
68
+ def to_io
69
+ @pipes.first
70
+ end
71
+
72
+ def to_i
73
+ to_io.to_i
74
+ end
75
+ end
76
+
77
+ attr_reader :descriptors
78
+
79
+ def initialize
80
+ @breaker = Breaker.new
81
+ @descriptors = []
82
+ @report_errors = false
83
+ end
84
+
85
+ def name
86
+ self.class.name[/(?:::)?([^:]*)$/, 1].downcase.to_sym
87
+ end
88
+
89
+ def report_errors?
90
+ @report_errors
91
+ end
92
+
93
+ def report_errors!
94
+ @report_errors = true
95
+ end
96
+
97
+ def dont_report_errors!
98
+ @report_errors = false
99
+ end
100
+
101
+ def break
102
+ @breaker.break
103
+ end
104
+
105
+ def add (what)
106
+ Lucciola.wrap(what).tap {|l|
107
+ return false if @descriptors.member?(l)
108
+
109
+ @descriptors.push(l)
110
+ }
111
+ end
112
+
113
+ def push (*args)
114
+ args.each { |a| add a }
115
+
116
+ self
117
+ end
118
+
119
+ alias << push
120
+
121
+ def remove (what)
122
+ @descriptors.delete(Lucciola.wrap(what))
123
+ end
124
+
125
+ alias delete remove
126
+ end
127
+
128
+ end
129
+
130
+ require 'barlume/lanterna/utils'
131
+ require 'barlume/lanterna/select'
132
+ require 'barlume/lanterna/poll'
133
+ require 'barlume/lanterna/epoll'
134
+ require 'barlume/lanterna/kqueue'
@@ -0,0 +1,207 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'ffi'
21
+
22
+ module Barlume; class Lanterna
23
+
24
+ class Epoll < Lanterna; begin
25
+ module C
26
+ extend FFI::Library
27
+
28
+ ffi_lib FFI::Library::LIBC
29
+
30
+ class EpollData < FFI::Union
31
+ layout \
32
+ :ptr, :pointer,
33
+ :fd, :int,
34
+ :u32, :uint32,
35
+ :u64, :uint64
36
+ end
37
+
38
+ class EpollEvent < FFI::Struct
39
+ pack 1
40
+
41
+ layout \
42
+ :events, :uint32,
43
+ :data, EpollData
44
+ end
45
+
46
+ Control = FFI::Enum.new([:add, 1, :del, :mod])
47
+
48
+ attach_function :epoll_create, [:int], :int
49
+ attach_function :epoll_create1, [:int], :int
50
+ attach_function :epoll_ctl, [:int, Control, :int, :pointer], :int
51
+ attach_function :epoll_wait, [:int, :pointer, :int, :int], :int
52
+
53
+ MAX = 4294967295
54
+
55
+ EPOLLIN = 0x001
56
+ EPOLLPRI = 0x002
57
+ EPOLLOUT = 0x004
58
+
59
+ EPOLLERR = 0x008
60
+ EPOLLHUP = 0x010
61
+ EPOLLNVAL = 0x020
62
+
63
+ EPOLLRDNORM = 0x040
64
+ EPOLLRDBAND = 0x080
65
+ EPOLLWRNORM = 0x100
66
+ EPOLLWRBAND = 0x200
67
+
68
+ EPOLLMSG = 0x0400
69
+ EPOLLREMOVE = 0x1000
70
+ EPOLLRDHUP = 0x2000
71
+
72
+ EPOLLONESHOT = 1 << 30
73
+ EPOLLET = 1 << 31
74
+ end
75
+
76
+ def self.supported?
77
+ true
78
+ end
79
+
80
+ def self.new (*)
81
+ super.tap {|c|
82
+ ObjectSpace.define_finalizer c, finalizer(c.instance_variable_get :@fd)
83
+ }
84
+ end
85
+
86
+ def self.finalizer (fd)
87
+ proc {
88
+ IO.for_fd(fd).close
89
+ }
90
+ end
91
+
92
+ def initialize
93
+ super
94
+
95
+ FFI.raise_if((@fd = C.epoll_create1(0)) < 0)
96
+
97
+ p = C::EpollEvent.new
98
+ p[:events] = C::EPOLLIN
99
+ p[:data][:u32] = C::MAX
100
+
101
+ FFI.raise_if(C.epoll_ctl(@fd, :add, @breaker.to_i, p) < 0)
102
+
103
+ self.size = 4096
104
+ end
105
+
106
+ def size
107
+ @events.size / C::EpollEvent.size
108
+ end
109
+
110
+ def size= (n)
111
+ @events = FFI::MemoryPointer.new C::EpollEvent.size, n
112
+ end
113
+
114
+ def add (what)
115
+ super.tap {|l|
116
+ FFI.raise_if(C.epoll_ctl(@fd, :add, l.to_i, C::EpollEvent.new) < 0)
117
+
118
+ @last = nil
119
+ }
120
+ end
121
+
122
+ def remove (what)
123
+ super.tap {|l|
124
+ begin
125
+ FFI.raise_if(C.epoll_ctl(@fd, :del, l.to_i, nil) < 0)
126
+ rescue Errno::ENOENT; end
127
+
128
+ @last = nil
129
+ }
130
+ end
131
+
132
+ def available (timeout = nil)
133
+ set :both; epoll timeout
134
+
135
+ Available.new(to(:read), to(:write), to(:error))
136
+ end
137
+
138
+ def readable (timeout = nil)
139
+ set :read; epoll timeout
140
+
141
+ if report_errors?
142
+ [to(:read), to(:error)]
143
+ else
144
+ to :read
145
+ end
146
+ end
147
+
148
+ def writable (timeout = nil)
149
+ set :write; epoll timeout
150
+
151
+ if report_errors?
152
+ [to(:write), to(:error)]
153
+ else
154
+ to :write
155
+ end
156
+ end
157
+
158
+ def set (what)
159
+ return if @last == what
160
+
161
+ p = C::EpollEvent.new
162
+ p[:events] = case what
163
+ when :both then C::EPOLLIN | C::EPOLLOUT
164
+ when :read then C::EPOLLIN
165
+ when :write then C::EPOLLOUT
166
+ end
167
+
168
+ descriptors.each_with_index {|descriptor, index|
169
+ p[:data][:u32] = index
170
+
171
+ FFI.raise_if(C.epoll_ctl(@fd, :mod, descriptor.to_i, p) < 0)
172
+ }
173
+
174
+ @last = what
175
+ end
176
+
177
+ def to (what)
178
+ result = []
179
+ events = case what
180
+ when :read then C::EPOLLIN
181
+ when :write then C::EPOLLOUT
182
+ when :error then C::EPOLLERR | C::EPOLLHUP
183
+ end
184
+
185
+ 0.upto(@length - 1) {|n|
186
+ p = C::EpollEvent.new(@events + (n * C::EpollEvent.size))
187
+
188
+ if p[:data][:u32] != C::MAX && (p[:events] & events).nonzero?
189
+ result << descriptors[p[:data][:u32]]
190
+ end
191
+ }
192
+
193
+ result
194
+ end
195
+
196
+ def epoll (timeout = nil)
197
+ FFI.raise_if((@length = C.epoll_wait(@fd, @events, size, timeout ? timeout * 1000 : -1)) < 0)
198
+
199
+ @breaker.flush
200
+ end
201
+ rescue Exception
202
+ def self.supported?
203
+ false
204
+ end
205
+ end; end
206
+
207
+ end; end
@@ -0,0 +1,249 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ module Barlume; class Lanterna
21
+
22
+ class Kqueue < Lanterna; begin
23
+ module C
24
+ extend FFI::Library
25
+
26
+ ffi_lib FFI::Library::LIBC
27
+
28
+ class Kevent < FFI::Struct
29
+ layout \
30
+ :ident, :intptr_t,
31
+ :filter, :short,
32
+ :flags, :ushort,
33
+ :fflags, :uint,
34
+ :data, :long,
35
+ :udata, :pointer
36
+ end
37
+
38
+ class TimeSpec < FFI::Struct
39
+ layout \
40
+ :tv_sec, :time_t,
41
+ :tv_nsec, :int
42
+ end
43
+
44
+ attach_function :kqueue, [], :int
45
+ attach_function :kevent, [:int, :pointer, :int, :pointer, :int, :pointer], :int
46
+
47
+ MAX = 4294967295
48
+
49
+ EVFILT_READ = -1
50
+ EVFILT_WRITE = -2
51
+ EVFILT_AIO = -3
52
+ EVFILT_VNODE = -4
53
+ EVFILT_PROC = -5
54
+ EVFILT_SIGNAL = -6
55
+ EVFILT_TIMER = -7
56
+ EVFILT_NETDEV = -8
57
+ EVFILT_FS = -9
58
+ EVFILT_LIO = -10
59
+ EVFILT_USER = -11
60
+ EVFILT_SYSCOUNT = 11
61
+
62
+ EV_ADD = 0x0001
63
+ EV_DELETE = 0x0002
64
+ EV_ENABLE = 0x0004
65
+ EV_DISABLE = 0x0008
66
+ EV_ONESHOT = 0x0010
67
+ EV_CLEAR = 0x0020
68
+ EV_RECEIPT = 0x0040
69
+ EV_DISPATCH = 0x0080
70
+ EV_SYSFLAGS = 0xF000
71
+ EV_FLAG1 = 0x2000
72
+ EV_EOF = 0x8000
73
+ EV_ERROR = 0x4000
74
+
75
+ def self.EV_SET (event, a, b, c, d, e, f)
76
+ event[:ident] = a
77
+ event[:filter] = b
78
+ event[:flags] = c
79
+ event[:fflags] = d
80
+ event[:data] = e
81
+ event[:udata] = f
82
+
83
+ event
84
+ end
85
+ end
86
+
87
+ def self.supported?
88
+ true
89
+ end
90
+
91
+ def self.new (*)
92
+ super.tap {|c|
93
+ ObjectSpace.define_finalizer c, finalizer(c.instance_variable_get :@fd)
94
+ }
95
+ end
96
+
97
+ def self.finalizer (fd)
98
+ proc {
99
+ IO.for_fd(fd).close
100
+ }
101
+ end
102
+
103
+ def initialize
104
+ super
105
+
106
+ FFI.raise_if((@fd = C.kqueue) < 0)
107
+
108
+ if C.kevent(@fd, C::EV_SET(C::Kevent.new, @breaker.to_i, C::EVFILT_READ, C::EV_ADD | C::EV_ENABLE, 0, 0, FFI::Pointer.new(C::MAX)), 1, nil, 0, nil) < 0
109
+ FFI.raise
110
+ end
111
+
112
+ self.size = 4096
113
+ end
114
+
115
+ def size
116
+ @events.size / C::Kevent.size
117
+ end
118
+
119
+ def size= (n)
120
+ @events = FFI::MemoryPointer.new C::Kevent.size, n
121
+ end
122
+
123
+ def add (*)
124
+ super.tap {
125
+ @last = nil
126
+ }
127
+ end
128
+
129
+ def remove (what)
130
+ super.tap {|l|
131
+ begin
132
+ FFI.raise_if(C.kevent(@fd, C::EV_SET(C::Kevent.new, what.to_i, C::EVFILT_READ, C::EV_DELETE | C::EV_DISABLE, 0, 0, 0), 1, nil, 0, nil) < 0)
133
+ rescue Errno::ENOENT; end
134
+
135
+ begin
136
+ FFI.raise_if(C.kevent(@fd, C::EV_SET(C::Kevent.new, what.to_i, C::EVFILT_WRITE, C::EV_DELETE | C::EV_DISABLE, 0, 0, 0), 1, nil, 0, nil) < 0)
137
+ rescue Errno::ENOENT; end
138
+
139
+ @last = nil
140
+ }
141
+ end
142
+
143
+ def available (timeout = nil)
144
+ set :both; kevent timeout
145
+
146
+ Available.new(to(:read), to(:write), to(:error))
147
+ end
148
+
149
+ def readable (timeout = nil)
150
+ set :read; kevent timeout
151
+
152
+ if report_errors?
153
+ [to(:read), to(:error)]
154
+ else
155
+ to :read
156
+ end
157
+ end
158
+
159
+ def writable (timeout = nil)
160
+ set :write; kevent timeout
161
+
162
+ if report_errors?
163
+ [to(:write), to(:error)]
164
+ else
165
+ to :write
166
+ end
167
+ end
168
+
169
+ def set (what)
170
+ return if @last == what
171
+
172
+ ev = C::Kevent.new
173
+
174
+ if what == :read
175
+ descriptors.each_with_index {|descriptor, index|
176
+ index = FFI::Pointer.new(index)
177
+
178
+ FFI.raise_if(C.kevent(@fd, C::EV_SET(ev, descriptor.to_i, C::EVFILT_READ, C::EV_ADD | C::EV_ENABLE, 0, 0, index), 1, nil, 0, nil) < 0)
179
+ FFI.raise_if(C.kevent(@fd, C::EV_SET(ev, descriptor.to_i, C::EVFILT_WRITE, C::EV_ADD | C::EV_DISABLE, 0, 0, index), 1, nil, 0, nil) < 0)
180
+ }
181
+ elsif what == :write
182
+ descriptors.each_with_index {|descriptor, index|
183
+ index = FFI::Pointer.new(index)
184
+
185
+ FFI.raise_if(C.kevent(@fd, C::EV_SET(ev, descriptor.to_i, C::EVFILT_WRITE, C::EV_ADD | C::EV_ENABLE, 0, 0, index), 1, nil, 0, nil) < 0)
186
+ FFI.raise_if(C.kevent(@fd, C::EV_SET(ev, descriptor.to_i, C::EVFILT_READ, C::EV_ADD | C::EV_DISABLE, 0, 0, index), 1, nil, 0, nil) < 0)
187
+ }
188
+ else
189
+ descriptors.each_with_index {|descriptor, index|
190
+ index = FFI::Pointer.new(index)
191
+
192
+ FFI.raise_if(C.kevent(@fd, C::EV_SET(ev, descriptor.to_i, C::EVFILT_WRITE, C::EV_ADD | C::EV_ENABLE, 0, 0, index), 1, nil, 0, nil) < 0)
193
+ FFI.raise_if(C.kevent(@fd, C::EV_SET(ev, descriptor.to_i, C::EVFILT_READ, C::EV_ADD | C::EV_ENABLE, 0, 0, index), 1, nil, 0, nil) < 0)
194
+ }
195
+ end
196
+
197
+ @last = what
198
+ end
199
+
200
+ def to (what)
201
+ result = []
202
+
203
+ if what == :error
204
+ 0.upto(@length - 1) {|n|
205
+ p = C::Kevent.new(@events + (n * C::Kevent.size))
206
+ index = p[:udata].address
207
+
208
+ if p != index && (p[:flags] & C::EV_ERROR).nonzero?
209
+ result << descriptors[index]
210
+ end
211
+ }
212
+ else
213
+ filter = case what
214
+ when :read then C::EVFILT_READ
215
+ when :write then C::EVFILT_WRITE
216
+ end
217
+
218
+ 0.upto(@length - 1) {|n|
219
+ p = C::Kevent.new(@events + (n * C::Kevent.size))
220
+ index = p[:udata].address
221
+
222
+ if index != C::MAX && p[:filter] == filter
223
+ result << descriptors[index]
224
+ end
225
+ }
226
+ end
227
+
228
+ result
229
+ end
230
+
231
+ def kevent (timeout = nil)
232
+ if timeout
233
+ timeout = C::TimeSpec.new.tap {|t|
234
+ t[:tv_sec] = timeout.to_i
235
+ t[:tv_nsec] = (timeout - timeout.to_i) * 1000
236
+ }
237
+ end
238
+
239
+ FFI.raise_if((@length = C.kevent(@fd, nil, 0, @events, size, timeout)) < 0)
240
+
241
+ @breaker.flush
242
+ end
243
+ rescue Exception
244
+ def self.supported?
245
+ false
246
+ end
247
+ end; end
248
+
249
+ end; end
@@ -0,0 +1,177 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'ffi'
21
+
22
+ module Barlume; class Lanterna
23
+
24
+ class Poll < Lanterna; begin
25
+ module C
26
+ extend FFI::Library
27
+
28
+ ffi_lib FFI::Library::LIBC
29
+
30
+ class PollFD < FFI::Struct
31
+ layout \
32
+ :fd, :int,
33
+ :events, :short,
34
+ :revents, :short
35
+ end
36
+
37
+ attach_function :poll, [:pointer, :ulong, :int], :int
38
+
39
+ attach_function :malloc, [:size_t], :pointer
40
+ attach_function :realloc, [:pointer, :size_t], :pointer
41
+ attach_function :free, [:pointer], :void
42
+
43
+ POLLIN = 0x001
44
+ POLLPRI = 0x002
45
+ POLLOUT = 0x004
46
+
47
+ POLLERR = 0x008
48
+ POLLHUP = 0x010
49
+ POLLNVAL = 0x020
50
+
51
+ POLLRDNORM = 0x040
52
+ POLLRDBAND = 0x080
53
+ POLLWRNORM = 0x100
54
+ POLLWRBAND = 0x200
55
+
56
+ POLLMSG = 0x0400
57
+ POLLREMOVE = 0x1000
58
+ POLLRDHUP = 0x2000
59
+ end
60
+
61
+ def self.supported?
62
+ true
63
+ end
64
+
65
+ def initialize
66
+ super
67
+
68
+ @set = FFI::AutoPointer.new(C.malloc(C::PollFD.size), C.method(:free))
69
+
70
+ pfd = C::PollFD.new(@set + 0)
71
+ pfd[:fd] = @breaker.to_i
72
+ pfd[:events] = C::POLLIN
73
+ end
74
+
75
+ def add (what)
76
+ super.tap {|l|
77
+ next unless l
78
+
79
+ @set.autorelease = false
80
+ @set = FFI::AutoPointer.new(C.realloc(@set, (descriptors.length + 1) * C::PollFD.size), C.method(:free))
81
+
82
+ pfd = C::PollFD.new(@set + descriptors.length * C::PollFD.size)
83
+ pfd[:fd] = l.to_i
84
+
85
+ @last = nil
86
+ }
87
+ end
88
+
89
+ def remove (what)
90
+ Lucciola.wrap(what).tap {|l|
91
+ index = descriptors.index(l)
92
+ offset = (index + 1) * C::PollFD.size
93
+ pointer = @set + offset
94
+
95
+ pointer.write_bytes((pointer + C::PollFD.size).read_bytes((descriptors.length - index) * C::PollFD.size))
96
+
97
+ @set.autorelease = false
98
+ @set = FFI::AutoPointer.new(C.realloc(@set, (descriptors.length) * C::PollFD.size), C.method(:free))
99
+
100
+ super(l)
101
+ }
102
+ end
103
+
104
+ def available (timeout = nil)
105
+ set :both; poll timeout
106
+
107
+ Available.new(to(:read), to(:write), to(:error))
108
+ end
109
+
110
+ def readable (timeout = nil)
111
+ set :read; poll timeout
112
+
113
+ if report_errors?
114
+ [to(:read), to(:error)]
115
+ else
116
+ to :read
117
+ end
118
+ end
119
+
120
+ def writable (timeout = nil)
121
+ set :write; poll timeout
122
+
123
+ if report_errors?
124
+ [to(:write), to(:error)]
125
+ else
126
+ to :write
127
+ end
128
+ end
129
+
130
+ def set (what)
131
+ return if @last == what
132
+
133
+ events = case what
134
+ when :both then C::POLLIN | C::POLLOUT
135
+ when :read then C::POLLIN
136
+ when :write then C::POLLOUT
137
+ end
138
+
139
+ 1.upto(descriptors.length) {|n|
140
+ pfd = C::PollFD.new(@set + (n * C::PollFD.size))
141
+ pfd[:events] = events
142
+ }
143
+
144
+ @last = what
145
+ end
146
+
147
+ def to (what)
148
+ result = []
149
+ events = case what
150
+ when :read then C::POLLIN
151
+ when :write then C::POLLOUT
152
+ when :error then C::POLLERR | C::POLLHUP
153
+ end
154
+
155
+ 1.upto(descriptors.length) {|n|
156
+ pfd = C::PollFD.new(@set + (n * C::PollFD.size))
157
+
158
+ if (pfd[:revents] & events).nonzero?
159
+ result << descriptors[n - 1]
160
+ end
161
+ }
162
+
163
+ result
164
+ end
165
+
166
+ def poll (timeout = nil)
167
+ FFI.raise_if(C.poll(@set, descriptors.length + 1, timeout ? timeout * 1000 : -1) < 0)
168
+
169
+ @breaker.flush
170
+ end
171
+ rescue Exception
172
+ def self.supported?
173
+ false
174
+ end
175
+ end; end
176
+
177
+ end; end
@@ -0,0 +1,83 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ module Barlume; class Lanterna
21
+
22
+ class Select < Lanterna
23
+ def self.supported?
24
+ true
25
+ end
26
+
27
+ def add (*)
28
+ super.tap {
29
+ @descriptors_with_breaker = nil
30
+ }
31
+ end
32
+
33
+ def remove (*)
34
+ super.tap {
35
+ @descriptors_with_breaker = nil
36
+ }
37
+ end
38
+
39
+ def available (timeout = nil)
40
+ readable, writable, error = IO.select(descriptors_with_breaker, descriptors, descriptors, timeout)
41
+
42
+ if readable && readable.delete(@breaker.to_io)
43
+ @breaker.flush
44
+ end
45
+
46
+ Available.new(readable || [], writable || [], error || [])
47
+ end
48
+
49
+ def readable (timeout = nil)
50
+ readable, writable, error = IO.select(descriptors_with_breaker, nil, descriptors, timeout)
51
+
52
+ if readable && readable.delete(@breaker.to_io)
53
+ @breaker.flush
54
+ end
55
+
56
+ if report_errors?
57
+ [readable || [], error || []]
58
+ else
59
+ readable || []
60
+ end
61
+ end
62
+
63
+ def writable (timeout = nil)
64
+ readable, writable, error = IO.select([@breaker], descriptors, descriptors, timeout)
65
+
66
+ if readable && readable.delete(@breaker.to_io)
67
+ @breaker.flush
68
+ end
69
+
70
+ if report_errors?
71
+ [writable || [], error || []]
72
+ else
73
+ writable || []
74
+ end
75
+ end
76
+
77
+ private
78
+ def descriptors_with_breaker
79
+ @descriptors_with_breaker ||= [@breaker.to_io] + descriptors
80
+ end
81
+ end
82
+
83
+ end; end
@@ -0,0 +1,48 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'ffi'
21
+
22
+ module FFI
23
+ def self.raise
24
+ value = FFI.errno
25
+
26
+ Kernel.raise Errno.constants[FFI.errno].new
27
+ rescue Exception => e
28
+ e.backtrace.shift(3)
29
+
30
+ Kernel.raise e
31
+ end
32
+
33
+ def self.raise_unless (what)
34
+ what ? what : FFI.raise
35
+ rescue Exception => e
36
+ e.backtrace.shift(1)
37
+
38
+ Kernel.raise e
39
+ end
40
+
41
+ def self.raise_if (what)
42
+ what ? FFI.raise : what
43
+ rescue Exception => e
44
+ e.backtrace.shift(1)
45
+
46
+ Kernel.raise e
47
+ end
48
+ end
@@ -0,0 +1,108 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ require 'fcntl'
21
+
22
+ module Barlume
23
+
24
+ class Lucciola
25
+ def self.wrap (what)
26
+ return what if what.is_a? self
27
+
28
+ new(what)
29
+ end
30
+
31
+ def initialize (what)
32
+ @io = what.is_a?(::Integer) ? ::IO.for_fd(what) : what
33
+
34
+ unless @io.respond_to? :to_i
35
+ raise ArgumentError, 'the object must respond to to_i'
36
+ end
37
+
38
+ @fd = @io.to_i
39
+ end
40
+
41
+ def respond_to_missing? (*args)
42
+ @io.respond_to? *args
43
+ end
44
+
45
+ def method_missing (id, *args, &block)
46
+ if @io.respond_to? id
47
+ begin
48
+ return @io.__send__ id, *args, &block
49
+ rescue EOFError
50
+ @closed = true
51
+
52
+ raise
53
+ end
54
+ end
55
+
56
+ super
57
+ end
58
+
59
+ def == (other)
60
+ to_i == other.to_i
61
+ end
62
+
63
+ alias eql? ==
64
+
65
+ def equal? (other)
66
+ to_io == (other.respond_to?(:to_io) ? other.to_io : other)
67
+ end
68
+
69
+ def hash
70
+ to_i.hash
71
+ end
72
+
73
+ def closed?
74
+ @closed or @io.respond_to?(:closed?) ? @io.closed? : false
75
+ end
76
+
77
+ def nonblocking?
78
+ (@io.fcntl(Fcntl::F_GETFL, 0) & Fcntl::O_NONBLOCK).nonzero?
79
+ end
80
+
81
+ alias asynchronous? nonblocking?
82
+
83
+ def blocking?
84
+ !nonblocking?
85
+ end
86
+
87
+ def blocking!
88
+ @io.fcntl(Fcntl::F_SETFL, @io.fcntl(Fcntl::F_GETFL, 0) | Fcntl::O_NONBLOCK)
89
+ end
90
+
91
+ def nonblocking!
92
+ @io.fcntl(Fcntl::F_SETFL, @io.fcntl(Fcntl::F_GETFL, 0) & ~Fcntl::O_NONBLOCK)
93
+ end
94
+
95
+ def to_io
96
+ @io
97
+ end
98
+
99
+ def to_i
100
+ @fd
101
+ end
102
+
103
+ def inspect
104
+ "#<#{self.class.name}: #{to_io.inspect}>"
105
+ end
106
+ end
107
+
108
+ end
@@ -0,0 +1,24 @@
1
+ #--
2
+ # Copyleft meh. [http://meh.paranoid.pk | meh@paranoici.org]
3
+ #
4
+ # This file is part of barlume.
5
+ #
6
+ # barlume is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU Affero General Public License as published
8
+ # by the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # barlume is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU Affero General Public License
17
+ # along with barlume. If not, see <http://www.gnu.org/licenses/>.
18
+ #++
19
+
20
+ module Barlume
21
+ def self.version
22
+ '0.0.1.rc.1'
23
+ end
24
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: barlume
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.rc.1
5
+ prerelease: 6
6
+ platform: ruby
7
+ authors:
8
+ - meh.
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description:
31
+ email: meh@paranoici.org
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - README.md
37
+ - barlume.gemspec
38
+ - examples/test.rb
39
+ - lib/barlume.rb
40
+ - lib/barlume/lanterna.rb
41
+ - lib/barlume/lanterna/epoll.rb
42
+ - lib/barlume/lanterna/kqueue.rb
43
+ - lib/barlume/lanterna/poll.rb
44
+ - lib/barlume/lanterna/select.rb
45
+ - lib/barlume/lanterna/utils.rb
46
+ - lib/barlume/lucciola.rb
47
+ - lib/barlume/version.rb
48
+ homepage: http://github.com/meh/barlume
49
+ licenses: []
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>'
64
+ - !ruby/object:Gem::Version
65
+ version: 1.3.1
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 1.8.24
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: A dim light over asynchronous I/O land.
72
+ test_files: []