Exemplo n.º 1
0
/*
 *  call-seq:
 *     Dispatch::Queue.concurrent(priority=:default)    => Dispatch::Queue
 *
 *  Returns one of the global concurrent priority queues.
 * 
 *  A dispatch queue is a FIFO queue that accepts tasks in the form of a block. 
 *  Blocks submitted to dispatch queues are executed on a pool of threads fully 
 *  managed by the system. Dispatched tasks execute one at a time in FIFO order.
 *  GCD takes take of using multiple cores effectively and better accommodate 
 *  the needs of all running applications, matching them to the 
 *  available system resources in a balanced fashion.
 *   
 *  Use concurrent queues to execute large numbers of tasks concurrently.
 *  GCD automatically creates three concurrent dispatch queues that are global 
 *  to your application and are differentiated only by their priority level. 
 *  
 *  The three priority levels are: +:low+, +:default+, 
 *  +:high+, corresponding to the DISPATCH_QUEUE_PRIORITY_HIGH, 
 *  DISPATCH_QUEUE_PRIORITY_DEFAULT, and DISPATCH_QUEUE_PRIORITY_LOW 
 *  (detailed in the dispatch_queue_create(3)[http://developer.apple.com/mac/library/DOCUMENTATION/Darwin/Reference/ManPages/man3/dispatch_queue_create.3.html]
 *  man page). The GCD thread dispatcher
 *  will perform actions submitted to the high priority queue before any actions 
 *  submitted to the default or low queues, and will only perform actions on the 
 *  low queues if there are no actions queued on the high or default queues.
 *  When installed on Mac OS 10.7 or later, the +:background+ priority level is 
 *  available. Actions submitted to this queue will execute on a thread set to 
 *  background state (via setpriority(2)), which throttles disk I/O and sets the 
 *  thread's scheduling priority to the lowest value possible.
 * 
 *  On Mac OS 10.7 and later, passing a string to +concurrent+ creates a new 
 *  concurrent queue with the specified string as its label. Private concurrent queues 
 *  created this way are identical to private FIFO queues created with +new+, except 
 *  for the fact that they execute their blocks in parallel.
 *
 *     gcdq = Dispatch::Queue.concurrent(:high)
 *     5.times { gcdq.async { print 'foo' } }
 *     gcdq_2 = Dispatch::Queue.concurrent(:low)
 *     gcdq_2.sync { print 'bar' }  # will always print 'foofoofoofoofoobar'.
 *
 */
static VALUE
rb_queue_get_concurrent(VALUE klass, SEL sel, int argc, VALUE *argv)
{
    VALUE priority;
    rb_scan_args(argc, argv, "01", &priority);
    if (!NIL_P(priority)) {
	
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
	if (TYPE(priority) == T_STRING) {
	    return rb_queue_from_dispatch(
		dispatch_queue_create(RSTRING_PTR(priority), DISPATCH_QUEUE_CONCURRENT), 1);
	} else if (TYPE(priority) != T_SYMBOL) {
		rb_raise(rb_eTypeError, "must pass a symbol or string to `concurrent`");
	}
#endif
	
	ID id = rb_to_id(priority);
	if (id == high_priority_id) {
	    return qHighPriority;
	}
	else if (id == low_priority_id) {
	    return qLowPriority;
	}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= 1070
	else if (id == background_priority_id) {
	    return qBackgroundPriority;
	}
#endif
	else if (id != default_priority_id) {
	    rb_raise(rb_eArgError,
		    "invalid priority `%s' (expected either :low, :default or :high)",
		    rb_id2name(id));
        }
    }
    return qDefaultPriority;
}
Exemplo n.º 2
0
static VALUE
rb_queue_get_current(VALUE klass, SEL sel)
{
    // TODO: check this to see if we need to retain it
    return rb_queue_from_dispatch(dispatch_get_current_queue(), false);
}
Exemplo n.º 3
0
void
Init_Dispatch(void)
{
    high_priority_id = rb_intern("high");
    low_priority_id = rb_intern("low");
    default_priority_id = rb_intern("default");

/*
 *  Grand Central Dispatch (GCD) is a novel approach to multicore computing
 *  first release in Mac OS X version 10.6 Snow Leopard, and available as
 *  open source via the libdispatch project. GCD shifts the
 *  responsibility for managing threads and their execution from
 *  applications onto the operating system. This allows programmers to easily
 *  refactor their programs into small chunks of independent work, which GCD
 *  then schedules onto per-process thread pools.  Because GCD knows the load
 *  across the entire system, it ensures the resulting programs perform
 *  optimally on a wide range of hardware.
 *
 *  The Dispatch module allows Ruby blocks to be scheduled for asynchronous and
 *  concurrent execution, either explicitly or in response to
 *  various kinds of events. It provides a convenient high-level interface
 *  to the underlying C API via objects for the four primary abstractions.
 *
 *  Dispatch::Queue is the basic units of organization of blocks.
 *  Several queues are created by default, and applications may create
 *  additional queues for their own use.
 *
 *  Dispatch::Group allows applications to track the progress of blocks
 *  submitted to queues and take action when the blocks complete.
 * 
 *  Dispatch::Source monitors and coalesces low-level system events so that they
 *  can be responded to asychronously via simple event handlers.
 *
 *  Dispatch::Semaphore synchronizes threads via a combination of
 *  waiting and signalling.
 *
 *  For more information, see the dispatch(3)[http://developer.apple.com/mac/library/DOCUMENTATION/Darwin/Reference/ManPages/man3/dispatch.3.html] man page.  
 */
    mDispatch = rb_define_module("Dispatch");

    cObject = rb_define_class_under(mDispatch, "Object", rb_cObject);
    rb_objc_define_method(cObject, "resume!", rb_dispatch_resume, 0);
    rb_objc_define_method(cObject, "suspend!", rb_dispatch_suspend, 0);
    rb_objc_define_method(cObject, "suspended?", rb_dispatch_suspended_p, 0);

    // This API allows Ruby code to pass the internal dispatch_queue_t object
    // to C/Objective-C APIs.
    class_replaceMethod((Class)cObject, sel_registerName("dispatch_object"),
	    (IMP)dispatch_object_imp, "^v@:");

/*
 * A Dispatch::Queue is the fundamental mechanism for scheduling blocks for
 * execution, either synchronously or asychronously.
 *
 * All blocks submitted to dispatch queues begin executing in the order
 * they were received. The system-defined concurrent queues can execute
 * multiple blocks in parallel, depending on the number of idle threads
 * in the thread pool. Serial queues (the main and user-created queues)
 * wait for the prior block to complete before dequeuing and executing
 * the next block.
 *
 * Queues are not bound to any specific thread of execution and blocks
 * submitted to independent queues may execute concurrently.
 */ 
 
    cQueue = rb_define_class_under(mDispatch, "Queue", cObject);    
    rb_objc_define_method(*(VALUE *)cQueue, "alloc", rb_queue_alloc, 0);
    rb_objc_define_method(*(VALUE *)cQueue, "concurrent",
	    rb_queue_get_concurrent, -1);
    rb_objc_define_method(*(VALUE *)cQueue, "current", rb_queue_get_current, 0);
    rb_objc_define_method(*(VALUE *)cQueue, "main", rb_queue_get_main, 0);
    rb_objc_define_method(cQueue, "initialize", rb_queue_init, 1);
    rb_objc_define_method(cQueue, "initialize", rb_raise_init, 0);
    rb_objc_define_method(cQueue, "apply", rb_queue_apply, 1);
    rb_objc_define_method(cQueue, "async", rb_queue_dispatch_async, -1);
    rb_objc_define_method(cQueue, "sync", rb_queue_dispatch_sync, 0);
    rb_objc_define_method(cQueue, "after", rb_queue_dispatch_after, 1);
    rb_objc_define_method(cQueue, "label", rb_queue_label, 0); // deprecated
    rb_objc_define_method(cQueue, "to_s", rb_queue_label, 0);
    
    rb_queue_finalize_super = rb_objc_install_method2((Class)cQueue,
	    "finalize", (IMP)rb_queue_finalize);

    qHighPriority = rb_queue_from_dispatch(dispatch_get_global_queue(
		DISPATCH_QUEUE_PRIORITY_HIGH, 0), true);
    qDefaultPriority = rb_queue_from_dispatch(dispatch_get_global_queue(
		DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), true);
    qLowPriority = rb_queue_from_dispatch(dispatch_get_global_queue(
		DISPATCH_QUEUE_PRIORITY_LOW, 0), true);
    
    qMain = rb_queue_from_dispatch(dispatch_get_main_queue(), true);
    rb_objc_define_method(rb_singleton_class(qMain), "run", rb_main_queue_run,
	    0);
    
/*
 * Dispatch::Group is used to aggregate multiple blocks 
 * that have been submitted asynchronously to different queues.
 * This lets you ensure they have all completed before beginning
 * or submitting additional work.
 */ 
    cGroup = rb_define_class_under(mDispatch, "Group", cObject);
    rb_objc_define_method(*(VALUE *)cGroup, "alloc", rb_group_alloc, 0);
    rb_objc_define_method(cGroup, "initialize", rb_group_init, 0);
    rb_objc_define_method(cGroup, "notify", rb_group_notify, 1);
    rb_objc_define_method(cGroup, "on_completion", rb_group_notify, 1); // deprecated
    rb_objc_define_method(cGroup, "wait", rb_group_wait, -1);
    
    rb_group_finalize_super = rb_objc_install_method2((Class)cGroup,
	    "finalize", (IMP)rb_group_finalize);

/*
 *  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, 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 GCD's C API, Dispatch::Source objects start off resumed
 *  (since the event handler -et al- have already been set).
 *   
 *     src.suspended? #=? false
 *     src.merge(0)
 *     gcdq.sync { } #=> Fired!
 */
    cSource = rb_define_class_under(mDispatch, "Source", cObject);
    rb_define_const(cSource, "DATA_ADD", INT2NUM(SOURCE_TYPE_DATA_ADD));
    rb_define_const(cSource, "DATA_OR", INT2NUM(SOURCE_TYPE_DATA_OR));
    rb_define_const(cSource, "PROC", INT2NUM(SOURCE_TYPE_PROC));
    rb_define_const(cSource, "READ", INT2NUM(SOURCE_TYPE_READ));
    rb_define_const(cSource, "SIGNAL", INT2NUM(SOURCE_TYPE_SIGNAL));
    rb_define_const(cSource, "VNODE", INT2NUM(SOURCE_TYPE_VNODE));
    rb_define_const(cSource, "WRITE", INT2NUM(SOURCE_TYPE_WRITE));
    
    rb_define_const(cSource, "PROC_EXIT", INT2NUM(DISPATCH_PROC_EXIT));
    rb_define_const(cSource, "PROC_FORK", INT2NUM(DISPATCH_PROC_FORK));
    rb_define_const(cSource, "PROC_EXEC", INT2NUM(DISPATCH_PROC_EXEC));
    rb_define_const(cSource, "PROC_SIGNAL", INT2NUM(DISPATCH_PROC_SIGNAL));

    rb_define_const(cSource, "VNODE_DELETE", INT2NUM(DISPATCH_VNODE_DELETE));
    rb_define_const(cSource, "VNODE_WRITE", INT2NUM(DISPATCH_VNODE_WRITE));
    rb_define_const(cSource, "VNODE_EXTEND", INT2NUM(DISPATCH_VNODE_EXTEND));
    rb_define_const(cSource, "VNODE_ATTRIB", INT2NUM(DISPATCH_VNODE_ATTRIB));
    rb_define_const(cSource, "VNODE_LINK", INT2NUM(DISPATCH_VNODE_LINK));
    rb_define_const(cSource, "VNODE_RENAME", INT2NUM(DISPATCH_VNODE_RENAME));
    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, "initialize", rb_raise_init, 0);
    rb_objc_define_method(cSource, "cancelled?", rb_source_cancelled_p, 0);
    rb_objc_define_method(cSource, "cancel!", rb_source_cancel, 0);
    rb_objc_define_method(cSource, "handle", rb_source_get_handle, 0);
    rb_objc_define_method(cSource, "mask", rb_source_get_mask, 0);
    rb_objc_define_method(cSource, "data", rb_source_get_data, 0);
    rb_objc_define_method(cSource, "<<", rb_source_merge, 1);

    rb_source_finalize_super = rb_objc_install_method2((Class)cSource,
	    "finalize", (IMP)rb_source_finalize);

/*
 * Dispatch::Semaphore provides an efficient mechanism to synchronizes threads
 * via a combination of waiting and signalling.
 * This is especially useful for controlling access to limited resources.
 */
    cSemaphore = rb_define_class_under(mDispatch, "Semaphore", cObject);
    rb_objc_define_method(*(VALUE *)cSemaphore, "alloc", rb_semaphore_alloc, 0);
    rb_objc_define_method(cSemaphore, "initialize", rb_semaphore_init, 1);
    rb_objc_define_method(cSemaphore, "initialize", rb_raise_init, 0);
    rb_objc_define_method(cSemaphore, "wait", rb_semaphore_wait, -1);
    rb_objc_define_method(cSemaphore, "signal", rb_semaphore_signal, 0);

    rb_semaphore_finalize_super = rb_objc_install_method2((Class)cSemaphore,
	    "finalize", (IMP)rb_semaphore_finalize);

/*
 * Constants for use with
 * dispatch_time(3)[http://developer.apple.com/Mac/library/documentation/Darwin/Reference/ManPages/man3/dispatch_time.3.html]
 */

    rb_define_const(mDispatch, "TIME_NOW", ULL2NUM(DISPATCH_TIME_NOW));
    rb_define_const(mDispatch, "TIME_FOREVER", ULL2NUM(DISPATCH_TIME_FOREVER));
    
/* Constants for future reference */
    selClose = sel_registerName("close");
    assert(selClose != NULL);
}