[macruby-changes] [3275] MacRuby/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Thu Jan 14 15:32:17 PST 2010
Revision: 3275
http://trac.macosforge.org/projects/ruby/changeset/3275
Author: ernest.prabhakar at gmail.com
Date: 2010-01-14 15:32:13 -0800 (Thu, 14 Jan 2010)
Log Message:
-----------
Initial mege of Timer and FileSource into Dispatch::Source
Modified Paths:
--------------
MacRuby/trunk/gcd.c
MacRuby/trunk/spec/macruby/core/gcd/source_spec.rb
Modified: MacRuby/trunk/gcd.c
===================================================================
--- MacRuby/trunk/gcd.c 2010-01-14 21:08:49 UTC (rev 3274)
+++ MacRuby/trunk/gcd.c 2010-01-14 23:32:13 UTC (rev 3275)
@@ -49,7 +49,7 @@
*
*/
-static SEL selClose, selFileNo;
+static SEL selClose, selCloseRead, selCloseWrite, selFileNo;
typedef struct {
struct RBasic basic;
@@ -76,9 +76,24 @@
#define RGroup(val) ((rb_group_t*)val)
+typedef enum SOURCE_TYPE_ENUM
+{
+ SOURCE_TYPE_DATA_ADD,
+ SOURCE_TYPE_DATA_OR,
+ SOURCE_TYPE_MACH_SEND,
+ SOURCE_TYPE_MACH_RECV,
+ SOURCE_TYPE_PROC,
+ SOURCE_TYPE_READ,
+ SOURCE_TYPE_SIGNAL,
+ SOURCE_TYPE_TIMER,
+ SOURCE_TYPE_VNODE,
+ SOURCE_TYPE_WRITE
+} source_enum_t;
+
typedef struct {
struct RBasic basic;
int suspension_count;
+ source_enum_t source_enum;
dispatch_source_t source;
rb_vm_block_t *event_handler;
VALUE rb_context;
@@ -108,9 +123,8 @@
static VALUE cGroup;
static VALUE cSource;
-static VALUE cFileSource;
-static VALUE cTimer;
static VALUE cSemaphore;
+static VALUE cIO;
static inline void
Check_Queue(VALUE object)
@@ -380,7 +394,7 @@
* p @i #=> 42
*
* If a group is specified, the dispatch will be associated with that group via
- * dispatch_group_async(3)[http://developer.apple.com/mac/library/DOCUMENTATION/Darwin/Reference/ManPages/man3/dispatch_group_async.3.html]
+ * dispatch_group_async(3)[http://developer.apple.com/mac/library/DOCUMENTATION/Darwin/Reference/ManPages/man3/dispatch_group_async.3.html]:
*
* gcdq = Dispatch::Queue.new('doc')
* gcdg = Dispatch::Group.new
@@ -688,24 +702,9 @@
}
}
-enum SOURCE_TYPE_ENUM
-{
- SOURCE_TYPE_DATA_ADD,
- SOURCE_TYPE_DATA_OR,
- SOURCE_TYPE_MACH_SEND,
- SOURCE_TYPE_MACH_RECV,
- SOURCE_TYPE_PROC,
- SOURCE_TYPE_READ,
- SOURCE_TYPE_SIGNAL,
- SOURCE_TYPE_TIMER,
- SOURCE_TYPE_VNODE,
- SOURCE_TYPE_WRITE
-};
-
static inline dispatch_source_type_t
-rb_num2source_type(VALUE num)
+rb_source_enum2type(source_enum_t value)
{
- enum SOURCE_TYPE_ENUM value = NUM2LONG(num);
switch (value)
{
case SOURCE_TYPE_DATA_ADD: return DISPATCH_SOURCE_TYPE_DATA_ADD;
@@ -725,9 +724,9 @@
}
static inline BOOL
-rb_is_file_source_type(VALUE num)
+rb_source_is_file(rb_source_t *src)
{
- enum SOURCE_TYPE_ENUM value = NUM2LONG(num);
+ source_enum_t value = src->source_enum;
if (value == SOURCE_TYPE_READ || value == SOURCE_TYPE_VNODE
|| value == SOURCE_TYPE_WRITE) {
return true;
@@ -754,13 +753,67 @@
rb_vm_block_eval(the_block, 1, ¶m);
}
+static void
+rb_source_close_handler(void* sourceptr)
+{
+ assert(sourceptr != NULL);
+ rb_source_t *src = RSource(sourceptr);
+ VALUE io = src->rb_context;
+ printf("Closing IO object %s\n", object_getClassName((id)io));
+ switch(src->source_enum)
+ {
+ case SOURCE_TYPE_READ:
+ rb_vm_call(io, selCloseRead, 0, NULL, false);
+ break;
+ case SOURCE_TYPE_WRITE:
+ rb_vm_call(io, selCloseWrite, 0, NULL, false);
+ break;
+ case SOURCE_TYPE_VNODE:
+ rb_vm_call(io, selClose, 0, NULL, false);
+ break;
+ default: rb_raise(rb_eArgError, "Unknown source type enum `%d'",
+ src->source_enum);
+ }
+}
+
+
+/*
+ * call-seq:
+ * Dispatch::Source.new(type, handle, mask, queue) {|src| block}
+ * => Dispatch::Source
+ *
+ * Returns a Source used to monitor a variety of system objects and events,
+ * using dispatch_source_create(3)[http://developer.apple.com/Mac/library/documentation/Darwin/Reference/ManPages/man3/dispatch_source_create.3.html]
+ *
+ * If an IO object (e.g., File) is passed as the handle, it will automatically
+ * create a cancel handler that closes that file (see +cancel!+ for details).
+ * The type must be one of:
+ * - Dispatch::Source::READ (calls +handle.close_read+)
+ * - Dispatch::Source::WRITE (calls +handle.close_write+)
+ * - Dispatch::Source::VNODE (calls +handle.close+)
+ * This is the only way to set the cancel_handler, since in MacRuby
+ * sources start off resumed. This is safer than closing the file
+ * yourself, as the cancel handler is guaranteed to only run once,
+ * and only after all pending events are processed.
+ * If you do *not* want the file closed on cancel, simply use
+ * +file.to_i+ to instead pass a descriptor as the handle.
+ */
+
static VALUE
-rb_source_setup(VALUE self, SEL sel,
+rb_source_init(VALUE self, SEL sel,
VALUE type, VALUE handle, VALUE mask, VALUE queue)
{
Check_Queue(queue);
rb_source_t *src = RSource(self);
- dispatch_source_type_t c_type = rb_num2source_type(type);
+ src->source_enum = (source_enum_t) NUM2LONG(type);
+ BOOL handle_is_file = rb_source_is_file(src) &&
+ rb_obj_is_kind_of(handle, cIO);
+ if (handle_is_file) {
+ printf("Extracting handler from IO object\n");
+ GC_WB(&src->rb_context, handle);
+ handle = rb_vm_call(handle, selFileNo, 0, NULL, false);
+ }
+ dispatch_source_type_t c_type = rb_source_enum2type(src->source_enum);
assert(c_type != NULL);
uintptr_t c_handle = NUM2UINT(handle);
unsigned long c_mask = NUM2LONG(mask);
@@ -770,42 +823,61 @@
rb_vm_block_t *block = get_prepared_block();
GC_WB(&src->event_handler, block);
- GC_RETAIN(self);
+ GC_RETAIN(self); // apparently needed to ensure consistent counting
dispatch_set_context(src->source, (void *)self);
dispatch_source_set_event_handler_f(src->source, rb_source_event_handler);
+
+ if (handle_is_file) {
+ printf("Initializing cancel handler\n");
+ dispatch_source_set_cancel_handler_f(src->source, rb_source_close_handler);
+ }
+ rb_dispatch_resume(self, 0);
return self;
}
/*
* call-seq:
- * Dispatch::Source.new(type, handle, mask, queue) {|src| block}
- * => Dispatch::Source
+ * Dispatch::Source.timer(delay, interval, leeway, queue)
+ * => Dispatch::Source
*
- * Returns a Source used to monitor a variety of system objects and events
- * including file descriptors, processes, virtual filesystem nodes, signal
- * delivery and timers.
- * When a state change occurs, the dispatch source will submit its event
- * handler block to its target queue, with the source as a parameter.
+ * Returns a Source that will submit the event handler block to
+ * the target queue after delay, repeated at interval, within leeway, via
+ * a call to dispatch_source_set_timer(3)[http://developer.apple.com/mac/library/DOCUMENTATION/Darwin/Reference/ManPages/man3/dispatch_source_set_timer.3.html].
+ * A best effort attempt is made to submit the event handler block to the
+ * target queue at the specified time; however, actual invocation may occur at
+ * a later time even if the leeway is zero.
*
* gcdq = Dispatch::Queue.new('doc')
- * src = Dispatch::Source.new(Dispatch::Source::DATA_ADD, 0, 0, gcdq) do |s|
- * puts "Fired with #{s.data}"
+ * timer = Dispatch::Source.timer(0, 5, 0.1, gcdq) do |s|
+ * puts s.data
* end
*
- * Unlike with the C API, Dispatch::Source objects start off resumed
- * (since the event handler has already been set).
- *
- * src.suspended? #=? false
- * src.merge(0)
- * gcdq.sync { } #=> Fired!
- *
*/
static VALUE
-rb_source_init(VALUE self, SEL sel,
- VALUE type, VALUE handle, VALUE mask, VALUE queue)
+rb_source_timer(VALUE self, SEL sel,
+ VALUE delay, VALUE interval, VALUE leeway, VALUE queue)
{
- rb_source_setup(self, sel, type, handle, mask, queue);
+ dispatch_time_t start_time;
+ rb_source_t *src = RSource(self);
+ VALUE argv[4] = {INT2FIX(SOURCE_TYPE_TIMER),
+ INT2FIX(0), INT2FIX(0), queue};
+ Check_Queue(queue);
+
+ rb_class_new_instance(4, argv, cSource);
+
+ if (NIL_P(leeway)) {
+ leeway = INT2FIX(0);
+ }
+ if (NIL_P(delay)) {
+ start_time = DISPATCH_TIME_NOW;
+ }
+ else {
+ start_time = rb_num2timeout(delay);
+ }
+
+ dispatch_source_set_timer(src->source, start_time,
+ rb_num2nsec(interval), rb_num2nsec(leeway));
rb_dispatch_resume(self, 0);
return self;
}
@@ -903,9 +975,10 @@
* invocation of its event handler block. Cancellation does not interrupt a
* currently executing handler block (non-preemptive).
*
- * When a dispatch source is canceled its cancellation handler will be submitted
- * to its target queue. In MacRuby, this is only done for Dispatch::FileSource,
- * which automatically sets a cancellation handler to close the File object.
+ * When a dispatch source is canceled its cancellation handler will be
+ * submitted to its target queue. This is only used by Dispatch::Source;
+ * when initialized a File (or IO) object, it will automatically set a
+ * cancellation handler that closes it.
*/
static VALUE
@@ -947,108 +1020,7 @@
}
}
-static void
-rb_source_close_handler(void* sourceptr)
-{
- assert(sourceptr != NULL);
- rb_source_t *src = RSource(sourceptr);
- VALUE io = src->rb_context;
- rb_vm_call(io, selClose, 0, NULL, false);
- GC_RELEASE(io);
-}
-
-/*
- * call-seq:
- * Dispatch::FileSource.new(type, io, mask, queue)
- * {|src| block} => Dispatch::Source
- *
- * Like Dispatch::Source.new, except:
- * - it takes an IO (File) object instead of an integer handle
- * - it automatically creates a cancel handler that closes that object
- *
- * gcdq = Dispatch::Queue.new('doc')
- * file = File.open("/etc/passwd", r)
- * src = Dispatch::FileSource.new(Dispatch::Source::READ,
- * file, 0, gcdq) { }
- * src.cancel!
- * @q.sync { }
- * @file.closed? # => true
- *
- * The type must be one of:
- * - Dispatch::Source::READ
- * - Dispatch::Source::WRITE
- * - Dispatch::Source::VNODE
- *
- * This is the only way to set the cancel_handler, since in MacRuby
- * sources start off resumed. This is preferred to closing the file
- * yourself, as the cancel handler is guaranteed to only run once.
- *
- * NOTE: If you do NOT want the file closed on cancel, use Dispatch::Source.
- *
- */
-
static VALUE
-rb_source_file_init(VALUE self, SEL sel,
- VALUE type, VALUE io, VALUE mask, VALUE queue)
-{
- if (rb_is_file_source_type(type) == false) {
- rb_raise(rb_eArgError, "%ld not a file source type", NUM2LONG(type));
- }
-
- VALUE handle = rb_vm_call(io, selFileNo, 0, NULL, false);
- rb_source_setup(self, sel, type, handle, mask, queue);
- rb_source_t *src = RSource(self);
- GC_RETAIN(io);
- src->rb_context = io;
- dispatch_source_set_cancel_handler_f(src->source, rb_source_close_handler);
- rb_dispatch_resume(self, 0);
- return self;
-}
-
-/*
- * call-seq:
- * Dispatch::Timer.new(delay, interval, leeway, queue) => Dispatch::Timer
- *
- * Returns a Source that will submit the event handler block to
- * the target queue after delay, repeated at interval, within leeway, via
- * a call to dispatch_source_set_timer(3)[http://developer.apple.com/mac/library/DOCUMENTATION/Darwin/Reference/ManPages/man3/dispatch_source_set_timer.3.html].
- * A best effort attempt is made to submit the event handler block to the
- * target queue at the specified time; however, actual invocation may occur at
- * a later time even if the leeway is zero.
- *
- * gcdq = Dispatch::Queue.new('doc')
- * timer = Dispatch::Timer.new(Time.now, 5, 0.1, gcdq)
- *
- */
-
-static VALUE
-rb_source_timer_init(VALUE self, SEL sel,
- VALUE delay, VALUE interval, VALUE leeway, VALUE queue)
-{
- dispatch_time_t start_time;
- rb_source_t *src = RSource(self);
- Check_Queue(queue);
-
- rb_source_setup(self, sel, INT2FIX(SOURCE_TYPE_TIMER),
- INT2FIX(0), INT2FIX(0), queue);
-
- if (NIL_P(leeway)) {
- leeway = INT2FIX(0);
- }
- if (NIL_P(delay)) {
- start_time = DISPATCH_TIME_NOW;
- }
- else {
- start_time = rb_num2timeout(delay);
- }
-
- dispatch_source_set_timer(src->source, start_time,
- rb_num2nsec(interval), rb_num2nsec(leeway));
- rb_dispatch_resume(self, 0);
- return self;
-}
-
-static VALUE
rb_semaphore_alloc(VALUE klass, SEL sel)
{
NEWOBJ(s, rb_semaphore_t);
@@ -1282,8 +1254,21 @@
/*
* Dispatch::Source monitors a variety of system objects and events including
* file descriptors, processes, virtual filesystem nodes, signals and timers.
- * When a state change occurs, the dispatch source will submit its event handler
- * block to its target queue.
+ *
+ * When a state change occurs, the dispatch source will submit its event
+ * handler block to its target queue, with the source as a parameter.
+ *
+ * gcdq = Dispatch::Queue.new('doc')
+ * src = Dispatch::Source.new(Dispatch::Source::DATA_ADD, 0, 0, gcdq) do |s|
+ * puts "Fired with #{s.data}"
+ * end
+ *
+ * Unlike with the C API, Dispatch::Source objects start off resumed
+ * (since the event handler has already been set).
+ *
+ * src.suspended? #=? false
+ * src.merge(0)
+ * gcdq.sync { } #=> Fired!
*/
cSource = rb_define_class_under(mDispatch, "Source", rb_cObject);
rb_define_const(cSource, "DATA_ADD", INT2NUM(SOURCE_TYPE_DATA_ADD));
@@ -1308,6 +1293,7 @@
rb_define_const(cSource, "VNODE_REVOKE", INT2NUM(DISPATCH_VNODE_REVOKE));
rb_objc_define_method(*(VALUE *)cSource, "alloc", rb_source_alloc, 0);
+ rb_objc_define_method(*(VALUE *)cSource, "timer", rb_source_timer, 4);
rb_objc_define_method(cSource, "initialize", rb_source_init, 4);
rb_objc_define_method(cSource, "cancelled?", rb_source_cancelled_p, 0);
rb_objc_define_method(cSource, "cancel!", rb_source_cancel, 0);
@@ -1321,11 +1307,6 @@
rb_source_finalize_super = rb_objc_install_method2((Class)cSource,
"finalize", (IMP)rb_source_finalize);
- cFileSource = rb_define_class_under(mDispatch, "FileSource", cSource);
- rb_objc_define_method(cFileSource, "initialize", rb_source_file_init, 4);
-
- cTimer = rb_define_class_under(mDispatch, "Timer", cSource);
- rb_objc_define_method(cTimer, "initialize", rb_source_timer_init, 4);
cSemaphore = rb_define_class_under(mDispatch, "Semaphore", rb_cObject);
rb_objc_define_method(*(VALUE *)cSemaphore, "alloc", rb_semaphore_alloc, 0);
rb_objc_define_method(cSemaphore, "initialize", rb_semaphore_init, 1);
@@ -1343,10 +1324,20 @@
rb_define_const(mDispatch, "TIME_NOW", ULL2NUM(DISPATCH_TIME_NOW));
rb_define_const(mDispatch, "TIME_FOREVER", ULL2NUM(DISPATCH_TIME_FOREVER));
+/* Constants for future reference */
+ cIO = (VALUE) objc_lookUpClass("IO");
+ assert(cIO != 0);
selClose = sel_registerName("close");
+ assert(selClose != NULL);
+ selCloseRead = sel_registerName("close_read");
+ assert(selCloseRead != NULL);
+ selCloseWrite = sel_registerName("close_write");
+ assert(selCloseWrite != NULL);
selFileNo = sel_registerName("fileno");
+ assert(selFileNo != NULL);
}
+
#else
void
Modified: MacRuby/trunk/spec/macruby/core/gcd/source_spec.rb
===================================================================
--- MacRuby/trunk/spec/macruby/core/gcd/source_spec.rb 2010-01-14 21:08:49 UTC (rev 3274)
+++ MacRuby/trunk/spec/macruby/core/gcd/source_spec.rb 2010-01-14 23:32:13 UTC (rev 3275)
@@ -233,10 +233,15 @@
@file = File.open(@filename, "r")
end
- it "returns an instance of Dispatch::Source" do
+ it "returns an instance of Dispatch::Source given descriptor" do
@src = Dispatch::Source.new(@type, @file.to_i, 0, @q) { }
@src.should be_kind_of(Dispatch::Source)
end
+
+ it "returns an instance of Dispatch::Source given IO" do
+ @src = Dispatch::Source.new(@type, @file, 0, @q) { }
+ @src.should be_kind_of(Dispatch::Source)
+ end
it "fires with data on estimated # of readable bytes" do
@result = ""
@@ -252,15 +257,23 @@
@result.should == @msg
end
- it "does not close file when cancelled" do
+ it "does not close file when cancelled given descriptor" do
@src = Dispatch::Source.new(@type, @file.to_i, 0, @q) { }
@src.cancel!
@q.sync { }
@file.closed?.should == false
end
+
+ it "does close file when cancelled given IO" do
+ @src = Dispatch::Source.new(@type, @file, 0, @q) { }
+ @file.closed?.should == false
+ src.cancel!
+ @q.sync { }
+ @file.closed?.should == true
+ end
- it "raises TypeError if non-Number passed as handle" do
- lambda { Dispatch::Source.new(@type, @file, 0, @q) { } }.should raise_error(TypeError)
+ it "raises TypeError if neither number or IO passed as handle" do
+ lambda { Dispatch::Source.new(@type, "", 0, @q) { } }.should raise_error(TypeError)
end
end
@@ -292,6 +305,15 @@
File.read(@filename).should == @msg
@src.cancel!
end
+
+ it "does close file when cancelled given IO" do
+ @src = Dispatch::Source.new(@type, @file, 0, @q) { }
+ @file.closed?.should == false
+ src.cancel!
+ @q.sync { }
+ @file.closed?.should == true
+ end
+
end
describe :VNODE do
@@ -321,87 +343,65 @@
@flag.should == @mask
end
end
+
+ it "does close file when cancelled given IO" do
+ @src = Dispatch::Source.new(@type, @file, 0, @q) { }
+ @file.closed?.should == false
+ src.cancel!
+ @q.sync { }
+ @file.closed?.should == true
+ end
end
- end
- end
-
- describe "Dispatch::FileSource" do
- before :each do
- @q = Dispatch::Queue.new("org.macruby.gcd_spec.filesource-#{Time.now}")
- @filename = "/dev/null"
- @type = Dispatch::Source::READ
- end
+ end # file
- it "returns an instance of Dispatch::Source" do
- @file = File.open(@filename, "r")
- src = Dispatch::FileSource.new(@type, @file, 0, @q) { }
- src.should be_kind_of(Dispatch::Source)
- src.should be_kind_of(Dispatch::FileSource)
- src.cancel!
- end
- it "raises NoMethodError if non-IO passed as handle" do
- @file = File.open(@filename, "r")
- lambda { Dispatch::FileSource.new(@type, @file.to_i, 0, @q) { } }.should raise_error(NoMethodError)
- end
-
- it "closes file when cancelled" do
- @file = File.open(@filename, "r")
- src = Dispatch::FileSource.new(@type, @file, 0, @q) { }
- @file.closed?.should == false
- src.cancel!
- @q.sync { }
- @file.closed?.should == true
- end
+ describe :Timer do
+ before :each do
+ @interval = 0.02
+ @src = nil
+ end
- after :each do
- @q.sync { }
- end
- end
-
- describe "Dispatch::Timer" do
- before :each do
- @q = Dispatch::Queue.new('org.macruby.gcd_spec.sources')
- @interval = 0.02
- @src = nil
- end
+ after :each do
+ @src.cancel!
+ @q.sync { }
+ end
- after :each do
- @src.cancel!
- @q.sync { }
- end
-
- it "returns an instance of Dispatch::Source" do
- @src = Dispatch::Timer.new(0, @interval, 0, @q) { }
- @src.should be_kind_of(Dispatch::Source)
- @src.should be_kind_of(Dispatch::Timer)
- end
-
- it "should not be suspended" do
- @src = Dispatch::Timer.new(0, @interval, 0, @q) { }
- @src.suspended?.should == false
- end
+ it "returns an instance of Dispatch::Source" do
+ @src = Dispatch::Source.timer(0, @interval, 0, @q) { }
+ @src.should be_kind_of(Dispatch::Source)
+ @src.should be_kind_of(Dispatch::Timer)
+ end
- it "fires after the delay" do
- delay = 2*@interval
- @latest = start = Time.now
- @src = Dispatch::Timer.new(delay, @interval, 0, @q) { @latest = Time.now }
- @latest.should == start
- sleep delay
- @q.sync { }
- @latest.should > start
- end
+ it "should not be suspended" do
+ @src = Dispatch::Source.timer(0, @interval, 0, @q) { }
+ @src.suspended?.should == false
+ end
- it "fires every interval thereafter" do
- repeats = 3
- @count = -1 # ignore zeroeth event to simplify interval counting
- t0 = Time.now
- @src = Dispatch::Timer.new(0, @interval, 0, @q) { |s| @count += s.data }
- sleep repeats*@interval
- @q.sync { }
- t1 = Time.now
- @count.should == repeats
- @count.should == ((t1-t0).to_f / @interval).to_i
+ it "fires after the delay" do
+ delay = 2*@interval
+ @latest = start = Time.now
+ @src = Dispatch::Source.timer(delay, @interval, 0, @q) do
+ @latest = Time.now
+ end
+ @latest.should == start
+ sleep delay
+ @q.sync { }
+ @latest.should > start
+ end
+
+ it "fires every interval thereafter" do
+ repeats = 3
+ @count = -1 # ignore zeroeth event to simplify interval counting
+ t0 = Time.now
+ @src = Dispatch::Source.timer(0, @interval, 0, @q) do |s|
+ @count += s.data
+ end
+ sleep repeats*@interval
+ @q.sync { }
+ t1 = Time.now
+ @count.should == repeats
+ @count.should == ((t1-t0).to_f / @interval).to_i
+ end
end
end
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/macruby-changes/attachments/20100114/ffc8cb05/attachment-0001.html>
More information about the macruby-changes
mailing list