[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, &param);
 }
 
+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