barlume 0.0.1.rc.1

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/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: []