| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235 |
- .\" Copyright (c) 2008-2012 Apple Inc. All rights reserved.
- .Dd May 1, 2009
- .Dt dispatch_async 3
- .Os Darwin
- .Sh NAME
- .Nm dispatch_async ,
- .Nm dispatch_sync
- .Nd schedule blocks for execution
- .Sh SYNOPSIS
- .Fd #include <dispatch/dispatch.h>
- .Ft void
- .Fo dispatch_async
- .Fa "dispatch_queue_t queue" "void (^block)(void)"
- .Fc
- .Ft void
- .Fo dispatch_sync
- .Fa "dispatch_queue_t queue" "void (^block)(void)"
- .Fc
- .Ft void
- .Fo dispatch_async_f
- .Fa "dispatch_queue_t queue" "void *context" "void (*function)(void *)"
- .Fc
- .Ft void
- .Fo dispatch_sync_f
- .Fa "dispatch_queue_t queue" "void *context" "void (*function)(void *)"
- .Fc
- .Sh DESCRIPTION
- The
- .Fn dispatch_async
- and
- .Fn dispatch_sync
- functions schedule blocks for concurrent execution within the
- .Xr dispatch 3
- framework. Blocks are submitted to a queue which dictates the policy for their
- execution. See
- .Xr dispatch_queue_create 3
- for more information about creating dispatch queues.
- .Pp
- These functions support efficient temporal synchronization, background
- concurrency and data-level concurrency. These same functions can also be used
- for efficient notification of the completion of asynchronous blocks (a.k.a.
- callbacks).
- .Sh TEMPORAL SYNCHRONIZATION
- Synchronization is often required when multiple threads of execution access
- shared data concurrently. The simplest form of synchronization is
- mutual-exclusion (a lock), whereby different subsystems execute concurrently
- until a shared critical section is entered. In the
- .Xr pthread 3
- family of procedures, temporal synchronization is accomplished like so:
- .Bd -literal -offset indent
- int r = pthread_mutex_lock(&my_lock);
- assert(r == 0);
- // critical section
- r = pthread_mutex_unlock(&my_lock);
- assert(r == 0);
- .Ed
- .Pp
- The
- .Fn dispatch_sync
- function may be used with a serial queue to accomplish the same style of
- synchronization. For example:
- .Bd -literal -offset indent
- dispatch_sync(my_queue, ^{
- // critical section
- });
- .Ed
- .Pp
- In addition to providing a more concise expression of synchronization, this
- approach is less error prone as the critical section cannot be accidentally
- left without restoring the queue to a reentrant state.
- .Pp
- The
- .Fn dispatch_async
- function may be used to implement deferred critical sections when the result
- of the block is not needed locally. Deferred critical sections have the same
- synchronization properties as the above code, but are non-blocking and
- therefore more efficient to perform. For example:
- .Bd -literal
- dispatch_async(my_queue, ^{
- // critical section
- });
- .Ed
- .Sh BACKGROUND CONCURRENCY
- .The
- .Fn dispatch_async
- function may be used to execute trivial background tasks on a global concurrent
- queue. For example:
- .Bd -literal
- dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{
- // background operation
- });
- .Ed
- .Pp
- This approach is an efficient replacement for
- .Xr pthread_create 3 .
- .Sh COMPLETION CALLBACKS
- Completion callbacks can be accomplished via nested calls to the
- .Fn dispatch_async
- function. It is important to remember to retain the destination queue before the
- first call to
- .Fn dispatch_async ,
- and to release that queue at the end of the completion callback to ensure the
- destination queue is not deallocated while the completion callback is pending.
- For example:
- .Bd -literal
- void
- async_read(object_t obj,
- void *where, size_t bytes,
- dispatch_queue_t destination_queue,
- void (^reply_block)(ssize_t r, int err))
- {
- // There are better ways of doing async I/O.
- // This is just an example of nested blocks.
- dispatch_retain(destination_queue);
- dispatch_async(obj->queue, ^{
- ssize_t r = read(obj->fd, where, bytes);
- int err = errno;
- dispatch_async(destination_queue, ^{
- reply_block(r, err);
- });
- dispatch_release(destination_queue);
- });
- }
- .Ed
- .Sh RECURSIVE LOCKS
- While
- .Fn dispatch_sync
- can replace a lock, it cannot replace a recursive lock. Unlike locks, queues
- support both asynchronous and synchronous operations, and those operations are
- ordered by definition. A recursive call to
- .Fn dispatch_sync
- causes a simple deadlock as the currently executing block waits for the next
- block to complete, but the next block will not start until the currently
- running block completes.
- .Pp
- As the dispatch framework was designed, we studied recursive locks. We found
- that the vast majority of recursive locks are deployed retroactively when
- ill-defined lock hierarchies are discovered. As a consequence, the adoption of
- recursive locks often mutates obvious bugs into obscure ones. This study also
- revealed an insight: if reentrancy is unavoidable, then reader/writer locks are
- preferable to recursive locks. Disciplined use of reader/writer locks enable
- reentrancy only when reentrancy is safe (the "read" side of the lock).
- .Pp
- Nevertheless, if it is absolutely necessary, what follows is an imperfect way of
- implementing recursive locks using the dispatch framework:
- .Bd -literal
- void
- sloppy_lock(object_t object, void (^block)(void))
- {
- if (object->owner == pthread_self()) {
- return block();
- }
- dispatch_sync(object->queue, ^{
- object->owner = pthread_self();
- block();
- object->owner = NULL;
- });
- }
- .Ed
- .Pp
- The above example does not solve the case where queue A runs on thread X which
- calls
- .Fn dispatch_sync
- against queue B which runs on thread Y which recursively calls
- .Fn dispatch_sync
- against queue A, which deadlocks both examples. This is bug-for-bug compatible
- with nontrivial pthread usage. In fact, nontrivial reentrancy is impossible to
- support in recursive locks once the ultimate level of reentrancy is deployed
- (IPC or RPC).
- .Sh IMPLIED REFERENCES
- Synchronous functions within the dispatch framework hold an implied reference
- on the target queue. In other words, the synchronous function borrows the
- reference of the calling function (this is valid because the calling function
- is blocked waiting for the result of the synchronous function, and therefore
- cannot modify the reference count of the target queue until after the
- synchronous function has returned).
- For example:
- .Bd -literal
- queue = dispatch_queue_create("com.example.queue", NULL);
- assert(queue);
- dispatch_sync(queue, ^{
- do_something();
- //dispatch_release(queue); // NOT SAFE -- dispatch_sync() is still using 'queue'
- });
- dispatch_release(queue); // SAFELY balanced outside of the block provided to dispatch_sync()
- .Ed
- .Pp
- This is in contrast to asynchronous functions which must retain both the block
- and target queue for the duration of the asynchronous operation (as the calling
- function may immediately release its interest in these objects).
- .Sh FUNDAMENTALS
- Conceptually,
- .Fn dispatch_sync
- is a convenient wrapper around
- .Fn dispatch_async
- with the addition of a semaphore to wait for completion of the block, and a
- wrapper around the block to signal its completion. See
- .Xr dispatch_semaphore_create 3
- for more information about dispatch semaphores. The actual implementation of the
- .Fn dispatch_sync
- function may be optimized and differ from the above description.
- .Pp
- The
- .Fn dispatch_async
- function is a wrapper around
- .Fn dispatch_async_f .
- The application-defined
- .Fa context
- parameter is passed to the
- .Fa function
- when it is invoked on the target
- .Fa queue .
- .Pp
- The
- .Fn dispatch_sync
- function is a wrapper around
- .Fn dispatch_sync_f .
- The application-defined
- .Fa context
- parameter is passed to the
- .Fa function
- when it is invoked on the target
- .Fa queue .
- .Pp
- .Sh SEE ALSO
- .Xr dispatch 3 ,
- .Xr dispatch_apply 3 ,
- .Xr dispatch_once 3 ,
- .Xr dispatch_queue_create 3 ,
- .Xr dispatch_semaphore_create 3
|