methodmissing-rb_aio 0.1.0

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 ADDED
@@ -0,0 +1,6 @@
1
+ POSIX Realtime IO extension for Ruby
2
+ (c) 2009 Lourens Naudé (methodmissing), James Tucker (raggi) and Aman Gupta (tmm1)
3
+
4
+ http://github.com/methodmissing/rb_aio
5
+
6
+ Work in progress.See http://www.opengroup.org/onlinepubs/009695399/basedefs/aio.h.html
data/Rakefile ADDED
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env rake
2
+ require 'rake/testtask'
3
+ require 'rake/clean'
4
+ $:.unshift(File.expand_path('lib'))
5
+ AIO_ROOT = 'ext/aio'
6
+
7
+ desc 'Default: test'
8
+ task :default => :test
9
+
10
+ desc 'Run AIO tests.'
11
+ Rake::TestTask.new(:test) do |t|
12
+ t.libs = [AIO_ROOT]
13
+ t.pattern = 'test/test_*.rb'
14
+ t.verbose = true
15
+ end
16
+ task :test => :build
17
+
18
+ namespace :build do
19
+ file "#{AIO_ROOT}/aio.c"
20
+ file "#{AIO_ROOT}/extconf.rb"
21
+ file "#{AIO_ROOT}/Makefile" => %W(#{AIO_ROOT}/aio.c #{AIO_ROOT}/extconf.rb) do
22
+ Dir.chdir(AIO_ROOT) do
23
+ ruby 'extconf.rb'
24
+ end
25
+ end
26
+
27
+ desc "generate makefile"
28
+ task :makefile => %W(#{AIO_ROOT}/Makefile #{AIO_ROOT}/aio.c)
29
+
30
+ dlext = Config::CONFIG['DLEXT']
31
+ file "#{AIO_ROOT}/aio.#{dlext}" => %W(#{AIO_ROOT}/Makefile #{AIO_ROOT}/aio.c) do
32
+ Dir.chdir(AIO_ROOT) do
33
+ sh 'make' # TODO - is there a config for which make somewhere?
34
+ end
35
+ end
36
+
37
+ desc "compile aio extension"
38
+ task :compile => "#{AIO_ROOT}/aio.#{dlext}"
39
+
40
+ task :clean do
41
+ Dir.chdir(AIO_ROOT) do
42
+ sh 'make clean'
43
+ end if File.exists?("#{AIO_ROOT}/Makefile")
44
+ end
45
+
46
+ CLEAN.include("#{AIO_ROOT}/Makefile")
47
+ CLEAN.include("#{AIO_ROOT}/aio.#{dlext}")
48
+ end
49
+
50
+ task :clean => %w(build:clean)
51
+
52
+ desc "compile"
53
+ task :build => %w(build:compile)
54
+
55
+ task :install do |t|
56
+ Dir.chdir(AIO_ROOT) do
57
+ sh 'sudo make install'
58
+ end
59
+ end
60
+
61
+ desc "clean build install"
62
+ task :setup => %w(clean build install)
63
+
64
+ desc "run benchmarks"
65
+ task :bench do |t|
66
+ ruby "bench/read.rb"
67
+ ruby "bench/write.rb"
68
+ end
69
+ task :bench => :build
data/aio.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "rb_aio"
3
+ s.version = "0.1.0"
4
+ s.date = "2009-09-01"
5
+ s.summary = "POSIX Realtime IO extension for Ruby"
6
+ s.email = "lourens@methodmissing.com"
7
+ s.homepage = "http://github.com/methodmissing/rb_aio"
8
+ s.description = "POSIX Realtime IO extension for Ruby MRI (1.8.{6,7} and 1.9.2)"
9
+ s.has_rdoc = true
10
+ s.authors = ["Lourens Naudé (methodmissing)","James Tucker (raggi)","Aman Gupta (tmm1)"]
11
+ s.platform = Gem::Platform::RUBY
12
+ s.files = %w[
13
+ README
14
+ Rakefile
15
+ bench/read.rb
16
+ bench/write.rb
17
+ ext/aio/extconf.rb
18
+ ext/aio/aio.c
19
+ aio.gemspec
20
+ ] + Dir.glob('test/*')
21
+ s.rdoc_options = ["--main", "README"]
22
+ s.extra_rdoc_files = ["README"]
23
+ s.extensions << "ext/aio/extconf.rb"
24
+ end
data/bench/read.rb ADDED
@@ -0,0 +1,28 @@
1
+ $:.unshift "."
2
+ require File.dirname(__FILE__) + '/../ext/aio/aio'
3
+ require "benchmark"
4
+
5
+ CALLBACKS = (1..8).to_a.map{|f| AIO::CB.new( File.dirname(__FILE__) + "/../test/fixtures/#{f}.txt" ) }
6
+ NON_BLOCKING = [AIO::NOWAIT].concat( CALLBACKS )
7
+ PATHS = CALLBACKS.map{|cb| cb.path }
8
+
9
+ aio_results, io_results = [], []
10
+
11
+ begin
12
+ puts "* Bench reads ..."
13
+ Benchmark.bmbm do |results|
14
+ results.report("AIO.lio_listio(AIO::WAIT)") { aio_results << AIO.lio_listio( *CALLBACKS ) }
15
+ results.report("IO.read") { io_results << PATHS.map{|p| IO.read(p) } }
16
+ end
17
+
18
+ Benchmark.bmbm do |results|
19
+ results.report("AIO.lio_listio(AIO::NOWAIT)") { aio_results << AIO.lio_listio( *NON_BLOCKING ) }
20
+ results.report("IO.read") { io_results << PATHS.map{|p| IO.read(p) } }
21
+ end
22
+ ensure
23
+ CALLBACKS.each{|cb| cb.close }
24
+ puts "* AIO results ..."
25
+ aio_results.each{|r| p r }
26
+ puts "* IO results ..."
27
+ io_results.each{|r| p r }
28
+ end
data/bench/write.rb ADDED
@@ -0,0 +1,32 @@
1
+ $:.unshift "."
2
+ require File.dirname(__FILE__) + '/../ext/aio/aio'
3
+ require "benchmark"
4
+ require "fileutils"
5
+
6
+ PAYLOAD = 'a' * 4096
7
+ CALLBACKS = (1..8).to_a.map{|f| c = AIO::CB.new; c.open(File.dirname(__FILE__) + "/../test/scratch/#{f}.aio.txt", 'w+' ); c.buf = PAYLOAD; c.lio_opcode = AIO::WRITE; c }
8
+ NON_BLOCKING = [AIO::NOWAIT].concat( CALLBACKS )
9
+ IOS = CALLBACKS.map{|cb| File.new(cb.path.gsub(/\.aio\.txt/,'.io.txt'), 'w+') }
10
+
11
+ aio_results, io_results = [], []
12
+
13
+ begin
14
+ puts "* Bench writes ..."
15
+ Benchmark.bmbm do |results|
16
+ results.report("AIO.lio_listio(AIO::WAIT)") { aio_results << AIO.lio_listio( *CALLBACKS ) }
17
+ results.report("IO.write") { io_results << IOS.map{|io| io.write(PAYLOAD) } }
18
+ end
19
+
20
+ Benchmark.bmbm do |results|
21
+ results.report("AIO.lio_listio(AIO::NOWAIT)") { aio_results << AIO.lio_listio( *NON_BLOCKING ) }
22
+ results.report("IO.write") { io_results << IOS.map{|io| io.write(PAYLOAD) } }
23
+ end
24
+ ensure
25
+ CALLBACKS.each{|cb| cb.close }
26
+ IOS.each{|io| io.close }
27
+ FileUtils.rm Dir.glob(File.dirname(__FILE__) + "/../test/scratch/*.txt")
28
+ puts "* AIO results ..."
29
+ aio_results.each{|r| p r }
30
+ puts "* IO results ..."
31
+ io_results.each{|r| p r }
32
+ end
data/ext/aio/aio.c ADDED
@@ -0,0 +1,851 @@
1
+ #include "ruby.h"
2
+ #include <unistd.h>
3
+ #include <stdio.h>
4
+ #include <sys/types.h>
5
+ #include <sys/stat.h>
6
+ #include <errno.h>
7
+ #include <signal.h>
8
+ #include <fcntl.h>
9
+ #ifdef _POSIX_ASYNCHRONOUS_IO
10
+ #include <aio.h>
11
+ #endif
12
+
13
+ #ifndef RSTRING_PTR
14
+ #define RSTRING_PTR(obj) RSTRING(obj)->ptr
15
+ #endif
16
+
17
+ #ifndef RSTRING_LEN
18
+ #define RSTRING_LEN(obj) RSTRING(obj)->len
19
+ #endif
20
+
21
+ #ifndef RARRAY_PTR
22
+ #define RARRAY_PTR(obj) RARRAY(obj)->ptr
23
+ #endif
24
+
25
+ #ifndef RARRAY_LEN
26
+ #define RARRAY_LEN(obj) RARRAY(obj)->len
27
+ #endif
28
+
29
+ #ifdef RUBY19
30
+ #include "ruby/io.h"
31
+ #define TRAP_BEG
32
+ #define TRAP_END
33
+ #else
34
+ #include "rubysig.h"
35
+ #include "rubyio.h"
36
+ #endif
37
+
38
+ /* Max I/O operations across all supported platforms */
39
+ #ifdef AIO_LISTIO_MAX
40
+ #define AIO_MAX_LIST AIO_LISTIO_MAX
41
+ #else
42
+ #define AIO_MAX_LIST 16
43
+ #endif
44
+
45
+ static VALUE mAio, eAio;
46
+
47
+ VALUE rb_cCB;
48
+
49
+ typedef struct aiocb aiocb_t;
50
+
51
+ typedef struct{
52
+ aiocb_t cb;
53
+ VALUE io;
54
+ VALUE rcb;
55
+ } rb_aiocb_t;
56
+
57
+ static ID s_to_str, s_to_s, s_buf;
58
+
59
+ static VALUE c_aio_sync, c_aio_queue, c_aio_inprogress, c_aio_alldone;
60
+ static VALUE c_aio_canceled, c_aio_notcanceled, c_aio_wait, c_aio_nowait;
61
+ static VALUE c_aio_nop, c_aio_read, c_aio_write;
62
+
63
+ static void rb_aio_error( const char * msg ){
64
+ rb_raise( eAio, msg );
65
+ }
66
+
67
+ #define GetCBStruct(obj) (Check_Type(obj, T_DATA), (rb_aiocb_t*)DATA_PTR(obj))
68
+
69
+ static void
70
+ mark_control_block(rb_aiocb_t *cb)
71
+ {
72
+ rb_gc_mark(cb->io);
73
+ rb_gc_mark(cb->rcb);
74
+ }
75
+
76
+ static void
77
+ free_control_block(rb_aiocb_t* cb)
78
+ {
79
+ xfree(cb);
80
+ }
81
+
82
+ static VALUE
83
+ control_block_nbytes_set(VALUE cb, VALUE bytes)
84
+ {
85
+ rb_aiocb_t *cbs = GetCBStruct(cb);
86
+ Check_Type(bytes, T_FIXNUM);
87
+ cbs->cb.aio_nbytes = FIX2INT(bytes);
88
+ if (cbs->cb.aio_buf != NULL) free((char *)cbs->cb.aio_buf);
89
+ cbs->cb.aio_buf = malloc(cbs->cb.aio_nbytes + 1);
90
+ if (!cbs->cb.aio_buf) rb_aio_error( "not able to allocate a read buffer" );
91
+ return bytes;
92
+ }
93
+
94
+ static VALUE
95
+ control_block_open(int argc, VALUE *argv, VALUE cb)
96
+ {
97
+ const char *fmode;
98
+ #ifdef RUBY19
99
+ rb_io_t *fptr;
100
+ #else
101
+ OpenFile *fptr;
102
+ #endif
103
+ VALUE file, mode;
104
+ rb_aiocb_t *cbs = GetCBStruct(cb);
105
+ rb_scan_args(argc, argv, "02", &file, &mode);
106
+ fmode = NIL_P(mode) ? "r" : RSTRING_PTR(mode);
107
+ struct stat stats;
108
+
109
+ Check_Type(file, T_STRING);
110
+
111
+ cbs->io = rb_file_open(RSTRING_PTR(file), fmode);
112
+ GetOpenFile(cbs->io, fptr);
113
+ rb_io_check_readable(fptr);
114
+
115
+ if ( cbs->cb.aio_fildes == 0 && cbs->cb.aio_nbytes == 0){
116
+ #ifdef RUBY19
117
+ cbs->cb.aio_fildes = fptr->fd;
118
+ #else
119
+ cbs->cb.aio_fildes = fileno(fptr->f);
120
+ #endif
121
+ fstat(cbs->cb.aio_fildes, &stats);
122
+ control_block_nbytes_set(cb, INT2FIX(stats.st_size));
123
+ }
124
+ return cb;
125
+ }
126
+
127
+ static void
128
+ control_block_reset0(rb_aiocb_t *cb)
129
+ {
130
+ bzero(cb, sizeof(rb_aiocb_t));
131
+ bzero(&cb->cb, sizeof(aiocb_t));
132
+ /* cleanup with rb_io_close(cb->io) */
133
+ cb->io = Qnil;
134
+ cb->rcb = Qnil;
135
+ cb->cb.aio_fildes = 0;
136
+ cb->cb.aio_buf = NULL;
137
+ cb->cb.aio_nbytes = 0;
138
+ cb->cb.aio_offset = 0;
139
+ cb->cb.aio_reqprio = 0;
140
+ cb->cb.aio_lio_opcode = LIO_READ;
141
+ /* Disable signals for the time being */
142
+ cb->cb.aio_sigevent.sigev_notify = SIGEV_NONE;
143
+ cb->cb.aio_sigevent.sigev_signo = 0;
144
+ cb->cb.aio_sigevent.sigev_value.sival_int = 0;
145
+ }
146
+
147
+ static VALUE
148
+ control_block_reset(VALUE cb)
149
+ {
150
+ rb_aiocb_t *cbs = GetCBStruct(cb);
151
+ control_block_reset0(cbs);
152
+ return cb;
153
+ }
154
+
155
+ static VALUE control_block_alloc _((VALUE));
156
+ static VALUE
157
+ control_block_alloc(VALUE klass)
158
+ {
159
+ VALUE obj;
160
+ rb_aiocb_t *cb;
161
+ obj = Data_Make_Struct(klass, rb_aiocb_t, mark_control_block, free_control_block, cb);
162
+ control_block_reset0(cb);
163
+ return obj;
164
+ }
165
+
166
+ static VALUE
167
+ control_block_initialize(int argc, VALUE *argv, VALUE cb)
168
+ {
169
+ VALUE file, mode;
170
+ VALUE args[2];
171
+ rb_scan_args(argc, argv, "02", &file, &mode);
172
+ if (RTEST(file)){
173
+ args[0] = file;
174
+ args[1] = mode;
175
+ control_block_open(1, (VALUE *)args, cb);
176
+ }
177
+ if (rb_block_given_p()) rb_obj_instance_eval( 0, 0, cb );
178
+ return cb;
179
+ }
180
+
181
+ static VALUE
182
+ control_block_path(VALUE cb)
183
+ {
184
+ rb_aiocb_t *cbs = GetCBStruct(cb);
185
+ #ifdef RUBY19
186
+ rb_io_t *fptr;
187
+ #else
188
+ OpenFile *fptr;
189
+ #endif
190
+ if NIL_P(cbs->io) return rb_str_new2("");
191
+ GetOpenFile(cbs->io, fptr);
192
+ rb_io_check_readable(fptr);
193
+ #ifdef RUBY19
194
+ return rb_file_s_expand_path( 1, &fptr->pathv );
195
+ #else
196
+ VALUE path = rb_str_new2(fptr->path);
197
+ return rb_file_s_expand_path( 1, &path );
198
+ #endif
199
+ }
200
+
201
+ static VALUE
202
+ control_block_callback_get(VALUE cb)
203
+ {
204
+ rb_aiocb_t *cbs = GetCBStruct(cb);
205
+ return cbs->rcb;
206
+ }
207
+
208
+ static VALUE
209
+ control_block_callback_set(VALUE cb, VALUE rcb)
210
+ {
211
+ rb_aiocb_t *cbs = GetCBStruct(cb);
212
+ if (RBASIC(rcb)->klass != rb_cProc) rb_aio_error("Block required for callback!");
213
+ cbs->rcb = rcb;
214
+ return cbs->rcb;
215
+ }
216
+
217
+ static VALUE
218
+ control_block_fildes_get(VALUE cb)
219
+ {
220
+ rb_aiocb_t *cbs = GetCBStruct(cb);
221
+ return INT2FIX(cbs->cb.aio_fildes);
222
+ }
223
+
224
+ static VALUE
225
+ control_block_fildes_set(VALUE cb, VALUE fd)
226
+ {
227
+ rb_aiocb_t *cbs = GetCBStruct(cb);
228
+ Check_Type(fd, T_FIXNUM);
229
+ cbs->cb.aio_fildes = FIX2INT(fd);
230
+ return fd;
231
+ }
232
+
233
+ static VALUE
234
+ control_block_buf_get(VALUE cb)
235
+ {
236
+ rb_aiocb_t *cbs = GetCBStruct(cb);
237
+ return cbs->cb.aio_buf == NULL ? rb_str_new2("") : rb_str_new((char *)cbs->cb.aio_buf, cbs->cb.aio_nbytes);
238
+ }
239
+
240
+ static VALUE
241
+ control_block_buf_set(VALUE cb, VALUE buf)
242
+ {
243
+ rb_aiocb_t *cbs = GetCBStruct(cb);
244
+ Check_Type(buf, T_STRING);
245
+ if (cbs->cb.aio_buf != NULL){
246
+ free((char *)cbs->cb.aio_buf);
247
+ cbs->cb.aio_buf = NULL;
248
+ }
249
+ cbs->cb.aio_buf = RSTRING_PTR(buf);
250
+ cbs->cb.aio_nbytes = RSTRING_LEN(buf);
251
+ return buf;
252
+ }
253
+
254
+ static VALUE
255
+ control_block_nbytes_get(VALUE cb)
256
+ {
257
+ rb_aiocb_t *cbs = GetCBStruct(cb);
258
+ return INT2FIX(cbs->cb.aio_nbytes);
259
+ }
260
+
261
+ static VALUE
262
+ control_block_offset_get(VALUE cb)
263
+ {
264
+ rb_aiocb_t *cbs = GetCBStruct(cb);
265
+ return INT2FIX(cbs->cb.aio_offset);
266
+ }
267
+
268
+ static VALUE
269
+ control_block_offset_set(VALUE cb, VALUE offset)
270
+ {
271
+ rb_aiocb_t *cbs = GetCBStruct(cb);
272
+ Check_Type(offset, T_FIXNUM);
273
+ cbs->cb.aio_offset = FIX2INT(offset);
274
+ return offset;
275
+ }
276
+
277
+ static VALUE
278
+ control_block_reqprio_get(VALUE cb)
279
+ {
280
+ rb_aiocb_t *cbs = GetCBStruct(cb);
281
+ return INT2FIX(cbs->cb.aio_reqprio);
282
+ }
283
+
284
+ static VALUE
285
+ control_block_reqprio_set(VALUE cb, VALUE reqprio)
286
+ {
287
+ rb_aiocb_t *cbs = GetCBStruct(cb);
288
+ Check_Type(reqprio, T_FIXNUM);
289
+ cbs->cb.aio_reqprio = FIX2INT(reqprio);
290
+ return reqprio;
291
+ }
292
+
293
+ static VALUE
294
+ control_block_lio_opcode_get(VALUE cb)
295
+ {
296
+ rb_aiocb_t *cbs = GetCBStruct(cb);
297
+ return INT2FIX(cbs->cb.aio_lio_opcode);
298
+ }
299
+
300
+ static VALUE
301
+ control_block_lio_opcode_set(VALUE cb, VALUE opcode)
302
+ {
303
+ rb_aiocb_t *cbs = GetCBStruct(cb);
304
+ Check_Type(opcode, T_FIXNUM);
305
+ if (opcode != c_aio_read && opcode != c_aio_write) rb_aio_error( "Only AIO::READ and AIO::WRITE modes supported" );
306
+ cbs->cb.aio_lio_opcode = NUM2INT(opcode);
307
+ return opcode;
308
+ }
309
+
310
+ static VALUE
311
+ control_block_validate(VALUE cb)
312
+ {
313
+ rb_aiocb_t *cbs = GetCBStruct(cb);
314
+ if (cbs->cb.aio_fildes <= 0) rb_aio_error( "Invalid file descriptor" );
315
+ if (cbs->cb.aio_nbytes <= 0) rb_aio_error( "Invalid buffer length" );
316
+ if (cbs->cb.aio_offset < 0) rb_aio_error( "Invalid file offset" );
317
+ if (cbs->cb.aio_reqprio < 0) rb_aio_error( "Invalid request priority" );
318
+ if (cbs->cb.aio_lio_opcode != LIO_READ && cbs->cb.aio_lio_opcode != LIO_WRITE) rb_aio_error( "Only AIO::READ and AIO::WRITE modes supported" );
319
+ if (!cbs->cb.aio_buf) rb_aio_error( "No buffer allocated" );
320
+ return cb;
321
+ }
322
+
323
+ static VALUE
324
+ control_block_open_p(VALUE cb)
325
+ {
326
+ rb_aiocb_t *cbs = GetCBStruct(cb);
327
+ return NIL_P(cbs->io) ? Qfalse : Qtrue;
328
+ }
329
+
330
+ static VALUE
331
+ control_block_closed_p(VALUE cb)
332
+ {
333
+ rb_aiocb_t *cbs = GetCBStruct(cb);
334
+ return NIL_P(cbs->io) ? Qtrue : Qfalse;
335
+ }
336
+
337
+ static VALUE
338
+ control_block_close(VALUE cb)
339
+ {
340
+ rb_aiocb_t *cbs = GetCBStruct(cb);
341
+ if NIL_P(cbs->io) return Qfalse;
342
+ rb_io_close(cbs->io);
343
+ cbs->io = Qnil;
344
+ cbs->rcb = Qnil;
345
+ return Qtrue;
346
+ }
347
+
348
+ /*
349
+ * Error handling for aio_write
350
+ */
351
+ static void
352
+ rb_aio_write_error()
353
+ {
354
+ switch(errno){
355
+ case EAGAIN:
356
+ rb_aio_error( "[EAGAIN] The request cannot be queued due to exceeding resource (queue) limitations." );
357
+ case EBADF:
358
+ rb_aio_error( "[EBADF] File descriptor is not valid for writing." );
359
+ case ENOSYS:
360
+ rb_aio_error( "[ENOSYS] aio_read not supported by this implementation." );
361
+ case EINVAL:
362
+ rb_aio_error( "[EINVAL] Read offset is invalid" );
363
+ case EOVERFLOW:
364
+ rb_aio_error( "[EOVERFLOW] Control block offset exceeded." );
365
+ case ECANCELED:
366
+ rb_aio_error( "[ECANCELED] The requested I/O was canceled by an explicit aio_cancel() request." );
367
+ case EFBIG:
368
+ rb_aio_error( "[EFBIG] Wrong offset." );
369
+ }
370
+ }
371
+
372
+ /*
373
+ * Error handling for aio_read
374
+ */
375
+ static void
376
+ rb_aio_read_error()
377
+ {
378
+ switch(errno){
379
+ case EAGAIN:
380
+ rb_aio_error( "[EAGAIN] The request cannot be queued due to exceeding resource (queue) limitations." );
381
+ case EBADF:
382
+ rb_aio_error( "[EBADF] File descriptor is not valid for reading." );
383
+ case ENOSYS:
384
+ rb_aio_error( "[ENOSYS] aio_read not supported by this implementation." );
385
+ case EINVAL:
386
+ rb_aio_error( "[EINVAL] Read offset is invalid" );
387
+ case EOVERFLOW:
388
+ rb_aio_error( "[EOVERFLOW] Control block offset exceeded." );
389
+ }
390
+ }
391
+
392
+ /*
393
+ * Initiates a *blocking* write
394
+ */
395
+ static VALUE
396
+ rb_aio_write( aiocb_t *cb )
397
+ {
398
+ int ret;
399
+
400
+ TRAP_BEG;
401
+ ret = aio_write( cb );
402
+ TRAP_END;
403
+ if (ret != 0) rb_aio_write_error();
404
+ while ( aio_error( cb ) == EINPROGRESS );
405
+ if ((ret = aio_return( cb )) > 0) {
406
+ return INT2NUM(cb->aio_nbytes);
407
+ }else{
408
+ return INT2NUM(errno);
409
+ }
410
+ }
411
+
412
+ /*
413
+ * Initiates a *blocking* read
414
+ */
415
+ static VALUE
416
+ rb_aio_read( aiocb_t *cb )
417
+ {
418
+ int ret;
419
+
420
+ TRAP_BEG;
421
+ ret = aio_read( cb );
422
+ TRAP_END;
423
+ if (ret != 0) rb_aio_read_error();
424
+ while ( aio_error( cb ) == EINPROGRESS );
425
+ if ((ret = aio_return( cb )) > 0) {
426
+ return rb_str_new( (char *)cb->aio_buf, cb->aio_nbytes );
427
+ }else{
428
+ return INT2NUM(errno);
429
+ }
430
+ }
431
+
432
+ /*
433
+ * Error handling for lio_listio
434
+ */
435
+ static void
436
+ rb_aio_listio_error()
437
+ {
438
+ switch(errno){
439
+ case EAGAIN:
440
+ rb_aio_error( "[EAGAIN] Resources necessary to queue all the requests are not available at the moment." );
441
+ case EIO:
442
+ rb_aio_error( "[EIO] One or more requests failed" );
443
+ case ENOSYS:
444
+ rb_aio_error( "[ENOSYS] lio_listio not supported by this implementation." );
445
+ case EINVAL:
446
+ rb_aio_error( "[EINVAL] Maximum number of allowed simultaneous requests exceeded." );
447
+ case EINTR:
448
+ rb_aio_error( "[EINTR] A signal was delivered while waiting for all I/O requests to complete during a LIO_WAIT operation." );
449
+ }
450
+ }
451
+
452
+ static void
453
+ rb_aio_lio_listio0( int mode, VALUE *cbs, aiocb_t **list, int ops )
454
+ {
455
+ int op;
456
+ bzero( (char *)list, sizeof(list) );
457
+ for (op=0; op < ops; op++) {
458
+ rb_aiocb_t *cb = GetCBStruct(RARRAY_PTR(cbs)[op]);
459
+ if (rb_block_given_p()){
460
+ cb->rcb = rb_block_proc();
461
+ }
462
+ list[op] = &cb->cb;
463
+ }
464
+ }
465
+
466
+ /*
467
+ * Initiates lio_listio
468
+ */
469
+ static int
470
+ rb_aio_lio_listio( int mode, VALUE *cbs, aiocb_t **list )
471
+ {
472
+ int ret;
473
+ int ops = RARRAY_LEN(cbs);
474
+ rb_aio_lio_listio0(mode, cbs, list, ops);
475
+ TRAP_BEG;
476
+ ret = lio_listio( mode, list, ops, NULL );
477
+ TRAP_END;
478
+ if (ret != 0) rb_aio_listio_error();
479
+ return ops;
480
+ }
481
+
482
+ /*
483
+ * Blocking lio_listio
484
+ */
485
+ static VALUE
486
+ rb_aio_lio_listio_blocking( VALUE *cbs )
487
+ {
488
+ aiocb_t *list[AIO_MAX_LIST];
489
+ int op;
490
+ int ops = rb_aio_lio_listio( LIO_WAIT, cbs, list );
491
+ VALUE results = rb_ary_new2( ops );
492
+ for (op=0; op < ops; op++) {
493
+ if (list[op]->aio_lio_opcode == LIO_READ){
494
+ rb_ary_push( results, rb_str_new( (char *)list[op]->aio_buf, list[op]->aio_nbytes ) );
495
+ }else{
496
+ rb_ary_push( results, INT2FIX(list[op]->aio_nbytes) );
497
+ }
498
+ }
499
+ return results;
500
+ }
501
+
502
+ /*
503
+ * No-op lio_listio
504
+ */
505
+ static VALUE
506
+ rb_aio_lio_listio_noop( VALUE *cbs )
507
+ {
508
+ aiocb_t *list[AIO_MAX_LIST];
509
+ rb_aio_lio_listio( LIO_NOP, cbs, list );
510
+ return Qnil;
511
+ }
512
+
513
+ /*
514
+ * Non-blocking lio_listio
515
+ */
516
+ static VALUE
517
+ rb_aio_lio_listio_non_blocking( VALUE *cbs )
518
+ {
519
+ aiocb_t *list[AIO_MAX_LIST];
520
+ rb_aio_lio_listio( LIO_NOWAIT, cbs, list );
521
+ return Qnil;
522
+ }
523
+
524
+ /*
525
+ * Helper to ensure files opened via AIO.lio_listio is closed.
526
+ */
527
+ static void
528
+ rb_io_closes( VALUE cbs ){
529
+ int io;
530
+ for (io=0; io < RARRAY_LEN(cbs); io++) {
531
+ control_block_close( RARRAY_PTR(cbs)[io] );
532
+ }
533
+ }
534
+
535
+ /*
536
+ * call-seq:
537
+ * AIO.write(cb) -> fixnum
538
+ *
539
+ * Asynchronously writes to a file.This is an initial *blocking* implementation until
540
+ * cross platform notification is supported.
541
+ */
542
+ static VALUE
543
+ rb_aio_s_write( VALUE aio, VALUE cb )
544
+ {
545
+ rb_aiocb_t *cbs = GetCBStruct(cb);
546
+ #ifdef RUBY19
547
+ rb_io_t *fptr;
548
+ #else
549
+ OpenFile *fptr;
550
+ #endif
551
+ GetOpenFile(cbs->io, fptr);
552
+ rb_io_check_writable(fptr);
553
+ if (rb_block_given_p()){
554
+ cbs->rcb = rb_block_proc();
555
+ }
556
+
557
+ return rb_ensure( rb_aio_write, (VALUE)&cbs->cb, control_block_close, cb );
558
+ }
559
+
560
+ /*
561
+ * call-seq:
562
+ * AIO.read(cb) -> string
563
+ *
564
+ * Asynchronously reads a file.This is an initial *blocking* implementation until
565
+ * cross platform notification is supported.
566
+ */
567
+ static VALUE
568
+ rb_aio_s_read( VALUE aio, VALUE cb )
569
+ {
570
+ rb_aiocb_t *cbs = GetCBStruct(cb);
571
+ if (rb_block_given_p()){
572
+ cbs->rcb = rb_block_proc();
573
+ }
574
+ return rb_ensure( rb_aio_read, (VALUE)&cbs->cb, control_block_close, cb );
575
+ }
576
+
577
+ /*
578
+ * call-seq:
579
+ * AIO.lio_listio(cb1, cb2, ...) -> array
580
+ *
581
+ * Schedules a batch of read requests for execution by the kernel in order
582
+ * to reduce system calls.Blocks until all the requests complete and returns
583
+ * an array equal in length to the given files, with the read buffers as string
584
+ * elements.The number of operations is currently limited to 16 due to cross
585
+ * platform limitations.
586
+ *
587
+ * open_nocancel("first.txt\0", 0x0, 0x1B6) = 3 0
588
+ * fstat(0x3, 0xBFFFEE04, 0x1B6) = 0 0
589
+ * open_nocancel("second.txt\0", 0x0, 0x1B6) = 4 0
590
+ * fstat(0x4, 0xBFFFEE04, 0x1B6) = 0 0
591
+ * open_nocancel("third.txt\0", 0x0, 0x1B6) = 5 0
592
+ * fstat(0x5, 0xBFFFEE04, 0x1B6) = 0 0
593
+ * fstat64(0x1, 0xBFFFE234, 0x1B6) = 0 0
594
+ * ioctl(0x1, 0x4004667A, 0xBFFFE29C) = 0 0
595
+ * lio_listio(0x2, 0xBFFFEE64, 0x3) = 0 0
596
+ * close_nocancel(0x4) = 0 0
597
+ * close_nocancel(0x3) = 0 0
598
+ */
599
+ static VALUE
600
+ rb_aio_s_lio_listio( VALUE aio, VALUE cbs )
601
+ {
602
+ VALUE mode_arg, mode;
603
+ mode_arg = RARRAY_PTR(cbs)[0];
604
+ mode = (mode_arg == c_aio_wait || mode_arg == c_aio_nowait || mode_arg == c_aio_nop) ? rb_ary_shift(cbs) : c_aio_wait;
605
+ int ops = RARRAY_LEN(cbs);
606
+ if (ops > AIO_MAX_LIST) return c_aio_queue;
607
+ switch(NUM2INT(mode)){
608
+ case LIO_WAIT:
609
+ return rb_ensure( rb_aio_lio_listio_blocking, (VALUE)cbs, rb_io_closes, (VALUE)cbs );
610
+ case LIO_NOWAIT:
611
+ return rb_ensure( rb_aio_lio_listio_non_blocking, (VALUE)cbs, rb_io_closes, (VALUE)cbs );
612
+ case LIO_NOP:
613
+ return rb_ensure( rb_aio_lio_listio_noop, (VALUE)cbs, rb_io_closes, (VALUE)cbs );
614
+ }
615
+ rb_aio_error("Only modes AIO::WAIT, AIO::NOWAIT and AIO::NOP supported");
616
+ }
617
+
618
+ /*
619
+ * Error handling for aio_cancel
620
+ */
621
+ static void
622
+ rb_aio_cancel_error()
623
+ {
624
+ switch(errno){
625
+ case EBADF:
626
+ rb_aio_error( "[EBADF] Invalid file descriptor." );
627
+ case ENOSYS:
628
+ rb_aio_error( "[ENOSYS] aio_cancel is not supported by this implementation." );
629
+ }
630
+ }
631
+
632
+ static VALUE
633
+ rb_aio_cancel( int fd, void *cb )
634
+ {
635
+ int ret;
636
+ TRAP_BEG;
637
+ ret = aio_cancel( fd, cb );
638
+ TRAP_END;
639
+ if (ret != 0) rb_aio_cancel_error();
640
+ switch(ret){
641
+ case AIO_CANCELED:
642
+ return c_aio_canceled;
643
+ case AIO_NOTCANCELED:
644
+ return c_aio_notcanceled;
645
+ case AIO_ALLDONE:
646
+ return c_aio_alldone;
647
+ }
648
+ return Qnil;
649
+ }
650
+
651
+ static VALUE
652
+ rb_aio_s_cancel(int argc, VALUE *argv, VALUE aio)
653
+ {
654
+ VALUE fd, cb;
655
+ rb_scan_args(argc, argv, "02", &fd, &cb);
656
+ if (NIL_P(fd) || !FIXNUM_P(fd)) rb_aio_error("No file descriptor given");
657
+ if (NIL_P(cb)) return rb_aio_cancel( NUM2INT(fd), NULL );
658
+ rb_aiocb_t *cbs = GetCBStruct(cb);
659
+ if (rb_block_given_p()){
660
+ cbs->rcb = rb_block_proc();
661
+ }
662
+ return rb_aio_cancel( NUM2INT(fd), &cbs->cb );
663
+ }
664
+
665
+ /*
666
+ * Error handling for aio_return
667
+ */
668
+ static void
669
+ rb_aio_return_error()
670
+ {
671
+ switch(errno){
672
+ case ENOMEM:
673
+ rb_aio_error( "[ENOMEM] There were no Kernel control blocks available to service this request." );
674
+ case EINVAL:
675
+ rb_aio_error( "[EINVAL] The control block does not refer to an asynchronous operation whose return status has not yet been retrieved." );
676
+ case ENOSYS:
677
+ rb_aio_error( "[ENOSYS] aio_return not supported by this implementation." );
678
+ }
679
+ }
680
+
681
+ static VALUE
682
+ rb_aio_return( aiocb_t *cb )
683
+ {
684
+ int ret;
685
+ TRAP_BEG;
686
+ ret = aio_return( cb );
687
+ TRAP_END;
688
+ if (ret != 0) rb_aio_return_error();
689
+ return INT2FIX(ret);
690
+ }
691
+
692
+ static VALUE
693
+ rb_aio_s_return( VALUE aio, VALUE cb )
694
+ {
695
+ rb_aiocb_t *cbs = GetCBStruct(cb);
696
+ if (rb_block_given_p()){
697
+ cbs->rcb = rb_block_proc();
698
+ }
699
+ return rb_aio_return( &cbs->cb );
700
+ }
701
+
702
+ /*
703
+ * Error handling for aio_error
704
+ */
705
+ static void
706
+ rb_aio_err_error()
707
+ {
708
+ switch(errno){
709
+ case EINVAL:
710
+ rb_aio_error( "[EINVAL] The control block does not refer to an asynchronous operation whose return status has not yet been retrieved." );
711
+ case ENOSYS:
712
+ rb_aio_error( "[ENOSYS] aio_error not supported by this implementation." );
713
+ }
714
+ }
715
+
716
+ static VALUE
717
+ rb_aio_err( aiocb_t *cb )
718
+ {
719
+ int ret;
720
+ TRAP_BEG;
721
+ ret = aio_error( cb );
722
+ TRAP_END;
723
+ if (ret != 0) rb_aio_err_error();
724
+ return INT2FIX(ret);
725
+ }
726
+
727
+ static VALUE
728
+ rb_aio_s_error( VALUE aio, VALUE cb )
729
+ {
730
+ rb_aiocb_t *cbs = GetCBStruct(cb);
731
+ if (rb_block_given_p()){
732
+ cbs->rcb = rb_block_proc();
733
+ }
734
+ return rb_aio_err( &cbs->cb );
735
+ }
736
+
737
+ /*
738
+ * Error handling for aio_error
739
+ */
740
+ static void
741
+ rb_aio_sync_error()
742
+ {
743
+ switch(errno){
744
+ case EINVAL:
745
+ rb_aio_error( "[EINVAL] A value of op other than AIO::DSYNC or AIO::SYNC was specified." );
746
+ case EAGAIN:
747
+ rb_aio_error( "[EAGAIN] The requested asynchronous operation was not queued due to temporary resource limitations." );
748
+ case ENOSYS:
749
+ rb_aio_error( "[ENOSYS] aio_error not supported by this implementation." );
750
+ case EBADF:
751
+ rb_aio_error( "[EBADF] Invalid file descriptor." );
752
+ }
753
+ }
754
+
755
+ static VALUE
756
+ rb_aio_sync( int op, aiocb_t *cb )
757
+ {
758
+ int ret;
759
+ TRAP_BEG;
760
+ ret = aio_fsync( op, cb );
761
+ TRAP_END;
762
+ if (ret != 0) rb_aio_sync_error();
763
+ return INT2FIX(ret);
764
+ }
765
+
766
+ static VALUE
767
+ rb_aio_s_sync( VALUE aio, VALUE op, VALUE cb )
768
+ {
769
+ rb_aiocb_t *cbs = GetCBStruct(cb);
770
+ if (rb_block_given_p()){
771
+ cbs->rcb = rb_block_proc();
772
+ }
773
+ Check_Type( op, T_FIXNUM );
774
+ /* XXX handle AIO::DSYNC gracefully as well */
775
+ if (op != c_aio_sync) rb_aio_error("Operation AIO::SYNC expected");
776
+ return rb_aio_sync( NUM2INT(op), &cbs->cb );
777
+ }
778
+
779
+ void Init_aio()
780
+ {
781
+ s_buf = rb_intern("buf");
782
+ s_to_str = rb_intern("to_str");
783
+ s_to_s = rb_intern("to_s");
784
+
785
+ mAio = rb_define_module("AIO");
786
+
787
+ rb_cCB = rb_define_class_under( mAio, "CB", rb_cObject);
788
+ rb_define_alloc_func(rb_cCB, control_block_alloc);
789
+ rb_define_method(rb_cCB, "initialize", control_block_initialize, -1);
790
+ rb_define_method(rb_cCB, "fildes", control_block_fildes_get, 0);
791
+ rb_define_method(rb_cCB, "fildes=", control_block_fildes_set, 1);
792
+ rb_define_method(rb_cCB, "buf", control_block_buf_get, 0);
793
+ rb_define_method(rb_cCB, "buf=", control_block_buf_set, 1);
794
+ rb_define_method(rb_cCB, "nbytes", control_block_nbytes_get, 0);
795
+ rb_define_method(rb_cCB, "nbytes=", control_block_nbytes_set, 1);
796
+ rb_define_method(rb_cCB, "offset", control_block_offset_get, 0);
797
+ rb_define_method(rb_cCB, "offset=", control_block_offset_set, 1);
798
+ rb_define_method(rb_cCB, "reqprio", control_block_reqprio_get, 0);
799
+ rb_define_method(rb_cCB, "reqprio=", control_block_reqprio_set, 1);
800
+ rb_define_method(rb_cCB, "lio_opcode", control_block_lio_opcode_get, 0);
801
+ rb_define_method(rb_cCB, "lio_opcode=", control_block_lio_opcode_set, 1);
802
+ rb_define_method(rb_cCB, "callback", control_block_callback_get, 0);
803
+ rb_define_method(rb_cCB, "callback=", control_block_callback_set, 1);
804
+ rb_define_method(rb_cCB, "validate", control_block_validate, 0);
805
+ rb_define_method(rb_cCB, "reset", control_block_reset, 0);
806
+ rb_define_method(rb_cCB, "open", control_block_open, -1);
807
+ rb_define_method(rb_cCB, "open?", control_block_open_p, 0);
808
+ rb_define_method(rb_cCB, "close", control_block_close, 0);
809
+ rb_define_method(rb_cCB, "closed?", control_block_closed_p, 0);
810
+ rb_define_method(rb_cCB, "path", control_block_path, 0);
811
+
812
+ rb_alias( rb_cCB, s_to_str, s_buf );
813
+ rb_alias( rb_cCB, s_to_s, s_buf );
814
+
815
+ rb_define_const(mAio, "SYNC", INT2NUM(O_SYNC));
816
+ /*
817
+ XXX O_DSYNC not supported by Darwin
818
+ rb_define_const(mAio, "DSYNC", INT2NUM(O_DSYNC));*/
819
+ rb_define_const(mAio, "QUEUE", INT2NUM(100));
820
+ rb_define_const(mAio, "INPROGRESS", INT2NUM(EINPROGRESS));
821
+ rb_define_const(mAio, "ALLDONE", INT2NUM(AIO_ALLDONE));
822
+ rb_define_const(mAio, "CANCELED", INT2NUM(AIO_CANCELED));
823
+ rb_define_const(mAio, "NOTCANCELED", INT2NUM(AIO_NOTCANCELED));
824
+ rb_define_const(mAio, "WAIT", INT2NUM(LIO_WAIT));
825
+ rb_define_const(mAio, "NOWAIT", INT2NUM(LIO_NOWAIT));
826
+ rb_define_const(mAio, "NOP", INT2NUM(LIO_NOP));
827
+ rb_define_const(mAio, "READ", INT2NUM(LIO_READ));
828
+ rb_define_const(mAio, "WRITE", INT2NUM(LIO_WRITE));
829
+
830
+ c_aio_sync = INT2NUM(O_SYNC);
831
+ c_aio_queue = INT2NUM(100);
832
+ c_aio_inprogress = INT2NUM(EINPROGRESS);
833
+ c_aio_alldone = INT2NUM(AIO_ALLDONE);
834
+ c_aio_canceled = INT2NUM(AIO_CANCELED);
835
+ c_aio_notcanceled = INT2NUM(AIO_NOTCANCELED);
836
+ c_aio_wait = INT2NUM(LIO_WAIT);
837
+ c_aio_nowait = INT2NUM(LIO_NOWAIT);
838
+ c_aio_nop = INT2NUM(LIO_NOP);
839
+ c_aio_read = INT2NUM(LIO_READ);
840
+ c_aio_write = INT2NUM(LIO_WRITE);
841
+
842
+ eAio = rb_define_class_under(mAio, "Error", rb_eStandardError);
843
+
844
+ rb_define_module_function( mAio, "lio_listio", rb_aio_s_lio_listio, -2 );
845
+ rb_define_module_function( mAio, "read", rb_aio_s_read, 1 );
846
+ rb_define_module_function( mAio, "write", rb_aio_s_write, 1 );
847
+ rb_define_module_function( mAio, "cancel", rb_aio_s_cancel, -1 );
848
+ rb_define_module_function( mAio, "return", rb_aio_s_return, 1 );
849
+ rb_define_module_function( mAio, "error", rb_aio_s_error, 1 );
850
+ rb_define_module_function( mAio, "sync", rb_aio_s_sync, 2 );
851
+ }
@@ -0,0 +1,15 @@
1
+ require 'mkmf'
2
+
3
+ def add_define(name)
4
+ $defs.push("-D#{name}")
5
+ end
6
+
7
+ dir_config('aio')
8
+
9
+ if RUBY_PLATFORM =~ /linux/i
10
+ raise 'cannot find AIO' unless have_library('rt', 'aio_read', 'aio.h')
11
+ end
12
+ add_define 'RUBY19' if have_func('rb_thread_blocking_region') and have_macro('RUBY_UBF_IO', 'ruby.h')
13
+ add_define 'RUBY18' if have_var('rb_trap_immediate', ['ruby.h', 'rubysig.h'])
14
+
15
+ create_makefile('aio')
metadata ADDED
@@ -0,0 +1,62 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: methodmissing-rb_aio
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - "Lourens Naud\xC3\xA9 (methodmissing)"
8
+ - James Tucker (raggi)
9
+ - Aman Gupta (tmm1)
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2009-09-01 00:00:00 -07:00
15
+ default_executable:
16
+ dependencies: []
17
+
18
+ description: POSIX Realtime IO extension for Ruby MRI (1.8.{6,7} and 1.9.2)
19
+ email: lourens@methodmissing.com
20
+ executables: []
21
+
22
+ extensions:
23
+ - ext/aio/extconf.rb
24
+ extra_rdoc_files:
25
+ - README
26
+ files:
27
+ - README
28
+ - Rakefile
29
+ - bench/read.rb
30
+ - bench/write.rb
31
+ - ext/aio/extconf.rb
32
+ - ext/aio/aio.c
33
+ - aio.gemspec
34
+ has_rdoc: true
35
+ homepage: http://github.com/methodmissing/rb_aio
36
+ post_install_message:
37
+ rdoc_options:
38
+ - --main
39
+ - README
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ version:
48
+ required_rubygems_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ requirements: []
55
+
56
+ rubyforge_project:
57
+ rubygems_version: 1.2.0
58
+ signing_key:
59
+ specification_version: 2
60
+ summary: POSIX Realtime IO extension for Ruby
61
+ test_files: []
62
+