/* * 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; }
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); }
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); }