首页 > 代码库 > Creating a Timer

Creating a Timer

Creating a Timer

  Even if you specify a leeway value of 0, you should never expect a timer to fire at the exact nanosecond you requested. The system does its best to accommodate your needs but cannot guarantee exact firing times.

  When a computer goes to sleep, all timer dispatch sources are suspended. When the computer wakes up, those timer dispatch sources are automatically woken up as well. Depending on the configuration of the timer, pauses of this nature may affect when your timer fires next. If you set up your timer dispatch source using the dispatch_time function or the DISPATCH_TIME_NOW constant, the timer dispatch source uses the default system clock to determine when to fire. However, the default clock does not advance while the computer is asleep. By contrast, when you set up your timer dispatch source using the dispatch_walltime function, the timer dispatch source tracks its firing time to the wall clock time. This latter option is typically appropriate for timers whose firing interval is relatively large because it prevents there from being too much drift between event times.

  1 NextPrevious
  2 Dispatch Sources
  3 
  4 Whenever you interact with the underlying system, you must be prepared for that task to take a nontrivial amount of time. Calling down to the kernel or other system layers involves a change in context that is reasonably expensive compared to calls that occur within your own process. As a result, many system libraries provide asynchronous interfaces to allow your code to submit a request to the system and continue to do other work while that request is processed. Grand Central Dispatch builds on this general behavior by allowing you to submit your request and have the results reported back to your code using blocks and dispatch queues.
  5 
  6 About Dispatch Sources
  7 
  8 A dispatch source is a fundamental data type that coordinates the processing of specific low-level system events. Grand Central Dispatch supports the following types of dispatch sources:
  9 
 10 Timer dispatch sources generate periodic notifications.
 11 Signal dispatch sources notify you when a UNIX signal arrives.
 12 Descriptor sources notify you of various file- and socket-based operations, such as:
 13 When data is available for reading
 14 When it is possible to write data
 15 When files are deleted, moved, or renamed in the file system
 16 When file meta information changes
 17 Process dispatch sources notify you of process-related events, such as:
 18 When a process exits
 19 When a process issues a fork or exec type of call
 20 When a signal is delivered to the process
 21 Mach port dispatch sources notify you of Mach-related events.
 22 Custom dispatch sources are ones you define and trigger yourself.
 23 Dispatch sources replace the asynchronous callback functions that are typically used to process system-related events. When you configure a dispatch source, you specify the events you want to monitor and the dispatch queue and code to use to process those events. You can specify your code using block objects or functions. When an event of interest arrives, the dispatch source submits your block or function to the specified dispatch queue for execution.
 24 
 25 Unlike tasks that you submit to a queue manually, dispatch sources provide a continuous source of events for your application. A dispatch source remains attached to its dispatch queue until you cancel it explicitly. While attached, it submits its associated task code to the dispatch queue whenever the corresponding event occurs. Some events, such as timer events, occur at regular intervals but most occur only sporadically as specific conditions arise. For this reason, dispatch sources retain their associated dispatch queue to prevent it from being released prematurely while events may still be pending.
 26 
 27 To prevent events from becoming backlogged in a dispatch queue, dispatch sources implement an event coalescing scheme. If a new event arrives before the event handler for a previous event has been dequeued and executed, the dispatch source coalesces the data from the new event data with data from the old event. Depending on the type of event, coalescing may replace the old event or update the information it holds. For example, a signal-based dispatch source provides information about only the most recent signal but also reports how many total signals have been delivered since the last invocation of the event handler.
 28 
 29 Creating Dispatch Sources
 30 
 31 Creating a dispatch source involves creating both the source of the events and the dispatch source itself. The source of the events is whatever native data structures are required to process the events. For example, for a descriptor-based dispatch source you would need to open the descriptor and for a process-based source you would need to obtain the process ID of the target program. When you have your event source, you can then create the corresponding dispatch source as follows:
 32 
 33 Create the dispatch source using the dispatch_source_create function.
 34 Configure the dispatch source:
 35 Assign an event handler to the dispatch source; see “Writing and Installing an Event Handler.”
 36 For timer sources, set the timer information using the dispatch_source_set_timer function; see “Creating a Timer.”
 37 Optionally assign a cancellation handler to the dispatch source; see “Installing a Cancellation Handler.”
 38 Call the dispatch_resume function to start processing events; see “Suspending and Resuming Dispatch Sources.”
 39 Because dispatch sources require some additional configuration before they can be used, the dispatch_source_create function returns dispatch sources in a suspended state. While suspended, a dispatch source receives events but does not process them. This gives you time to install an event handler and perform any additional configuration needed to process the actual events.
 40 
 41 The following sections show you how to configure various aspects of a dispatch source. For detailed examples showing you how to configure specific types of dispatch sources, see “Dispatch Source Examples.” For additional information about the functions you use to create and configure dispatch sources, see Grand Central Dispatch (GCD) Reference.
 42 
 43 Writing and Installing an Event Handler
 44 To handle the events generated by a dispatch source, you must define an event handler to process those events. An event handler is a function or block object that you install on your dispatch source using the dispatch_source_set_event_handler or dispatch_source_set_event_handler_f function. When an event arrives, the dispatch source submits your event handler to the designated dispatch queue for processing.
 45 
 46 The body of your event handler is responsible for processing any events that arrive. If your event handler is already queued and waiting to process an event when a new event arrives, the dispatch source coalesces the two events. An event handler generally sees information only for the most recent event, but depending on the type of the dispatch source it may also be able to get information about other events that occurred and were coalesced. If one or more new events arrive after the event handler has begun executing, the dispatch source holds onto those events until the current event handler has finished executing. At that point, it submits the event handler to the queue again with the new events.
 47 
 48 Function-based event handlers take a single context pointer, containing the dispatch source object, and return no value. Block-based event handlers take no parameters and have no return value.
 49 
 50 // Block-based event handler
 51 void (^dispatch_block_t)(void)
 52  
 53 // Function-based event handler
 54 void (*dispatch_function_t)(void *)
 55 Inside your event handler, you can get information about the given event from the dispatch source itself. Although function-based event handlers are passed a pointer to the dispatch source as a parameter, block-based event handlers must capture that pointer themselves. You can do this for your blocks by referencing the variable containing the dispatch source normally. For example, the following code snippet captures the source variable, which is declared outside the scope of the block.
 56 
 57 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ,
 58                                  myDescriptor, 0, myQueue);
 59 dispatch_source_set_event_handler(source, ^{
 60    // Get some data from the source variable, which is captured
 61    // from the parent context.
 62    size_t estimated = dispatch_source_get_data(source);
 63  
 64    // Continue reading the descriptor...
 65 });
 66 dispatch_resume(source);
 67 Capturing variables inside of a block is commonly done to allow for greater flexibility and dynamism. Of course, captured variables are read-only within the block by default. Although the blocks feature provides support for modifying captured variables under specific circumstances, you should not attempt to do so in the event handlers associated with a dispatch source. Dispatch sources always execute their event handlers asynchronously, so the defining scope of any variables you captured is likely gone by the time your event handler executes. For more information about how to capture and use variables inside of blocks, see Blocks Programming Topics.
 68 
 69 Table 4-1 lists the functions you can call from your event handler code to obtain information about an event.
 70 
 71 Table 4-1  Getting data from a dispatch source
 72 Function
 73 Description
 74 dispatch_source_get_handle
 75 This function returns the underlying system data type that the dispatch source manages.
 76 For a descriptor dispatch source, this function returns an int type containing the descriptor associated with the dispatch source.
 77 For a signal dispatch source, this function returns an int type containing the signal number for the most recent event.
 78 For a process dispatch source, this function returns a pid_t data structure for the process being monitored.
 79 For a Mach port dispatch source, this function returns a mach_port_t data structure.
 80 For other dispatch sources, the value returned by this function is undefined.
 81 dispatch_source_get_data
 82 This function returns any pending data associated with the event.
 83 For a descriptor dispatch source that reads data from a file, this function returns the number of bytes available for reading.
 84 For a descriptor dispatch source that writes data to a file, this function returns a positive integer if space is available for writing.
 85 For a descriptor dispatch source that monitors file system activity, this function returns a constant indicating the type of event that occurred. For a list of constants, see the dispatch_source_vnode_flags_t enumerated type.
 86 For a process dispatch source, this function returns a constant indicating the type of event that occurred. For a list of constants, see the dispatch_source_proc_flags_t enumerated type.
 87 For a Mach port dispatch source, this function returns a constant indicating the type of event that occurred. For a list of constants, see the dispatch_source_machport_flags_t enumerated type.
 88 For a custom dispatch source, this function returns the new data value created from the existing data and the new data passed to the dispatch_source_merge_data function.
 89 dispatch_source_get_mask
 90 This function returns the event flags that were used to create the dispatch source.
 91 For a process dispatch source, this function returns a mask of the events that the dispatch source receives. For a list of constants, see the dispatch_source_proc_flags_t enumerated type.
 92 For a Mach port dispatch source with send rights, this function returns a mask of the desired events. For a list of constants, see the dispatch_source_mach_send_flags_t enumerated type.
 93 For a custom OR dispatch source, this function returns the mask used to merge the data values.
 94 For some examples of how to write and install event handlers for specific types of dispatch sources, see “Dispatch Source Examples.”
 95 
 96 Installing a Cancellation Handler
 97 Cancellation handlers are used to clean up a dispatch source before it is released. For most types of dispatch sources, cancellation handlers are optional and only necessary if you have some custom behaviors tied to the dispatch source that also need to be updated. For dispatch sources that use a descriptor or Mach port, however, you must provide a cancellation handler to close the descriptor or release the Mach port. Failure to do so can lead to subtle bugs in your code resulting from those structures being reused unintentionally by your code or other parts of the system.
 98 
 99 You can install a cancellation handler at any time but usually you would do so when creating the dispatch source. You install a cancellation handler using the dispatch_source_set_cancel_handler or dispatch_source_set_cancel_handler_f function, depending on whether you want to use a block object or a function in your implementation. The following example shows a simple cancellation handler that closes a descriptor that was opened for a dispatch source. The fd variable is a captured variable containing the descriptor.
100 
101 dispatch_source_set_cancel_handler(mySource, ^{
102    close(fd); // Close a file descriptor opened earlier.
103 });
104 To see a complete code example for a dispatch source that uses a cancellation handler, see “Reading Data from a Descriptor.”
105 
106 Changing the Target Queue
107 Although you specify the queue on which to run your event and cancellation handlers when you create a dispatch source, you can change that queue at any time using the dispatch_set_target_queue function. You might do this to change the priority at which the dispatch source’s events are processed.
108 
109 Changing a dispatch source’s queue is an asynchronous operation and the dispatch source does its best to make the change as quickly as possible. If an event handler is already queued and waiting to be processed, it executes on the previous queue. However, other events arriving around the time you make the change could be processed on either queue.
110 
111 Associating Custom Data with a Dispatch Source
112 Like many other data types in Grand Central Dispatch, you can use the dispatch_set_context function to associate custom data with a dispatch source. You can use the context pointer to store any data your event handler needs to process events. If you do store any custom data in the context pointer, you should also install a cancellation handler (as described in “Installing a Cancellation Handler”) to release that data when the dispatch source is no longer needed.
113 
114 If you implement your event handler using blocks, you can also capture local variables and use them within your block-based code. Although this might alleviate the need to store data in the context pointer of the dispatch source, you should always use this feature judiciously. Because dispatch sources may be long-lived in your application, you should be careful when capturing variables containing pointers. If the data pointed to by a pointer could be deallocated at any time, you should either copy the data or retain it to prevent that from happening. In either case, you would then need to provide a cancellation handler to release the data later.
115 
116 Memory Management for Dispatch Sources
117 Like other dispatch objects, dispatch sources are reference counted data types. A dispatch source has an initial reference count of 1 and can be retained and released using the dispatch_retain and dispatch_release functions. When the reference count of a queue reaches zero, the system automatically deallocates the dispatch source data structures.
118 
119 Because of the way they are used, the ownership of dispatch sources can be managed either internally or externally to the dispatch source itself. With external ownership, another object or piece of code takes ownership of the dispatch source and is responsible for releasing it when it is no longer needed. With internal ownership, the dispatch source owns itself and is responsible for releasing itself at the appropriate time. Although external ownership is very common, you might use internal ownership in cases where you want to create an autonomous dispatch source and let it manage some behavior of your code without any further interactions. For example, if a dispatch source is designed to respond to a single global event, you might have it handle that event and then exit immediately.
120 
121 Dispatch Source Examples
122 
123 The following sections show you how to create and configure some of the more commonly used dispatch sources. For more information about configuring specific types of dispatch sources, see Grand Central Dispatch (GCD) Reference.
124 
125 Creating a Timer
126 Timer dispatch sources generate events at regular, time-based intervals. You can use timers to initiate specific tasks that need to be performed regularly. For example, games and other graphics-intensive applications might use timers to initiate screen or animation updates. You could also set up a timer and use the resulting events to check for new information on a frequently updated server.
127 
128 All timer dispatch sources are interval timers—that is, once created, they deliver regular events at the interval you specify. When you create a timer dispatch source, one of the values you must specify is a leeway value to give the system some idea of the desired accuracy for timer events. Leeway values give the system some flexibility in how it manages power and wakes up cores. For example, the system might use the leeway value to advance or delay the fire time and align it better with other system events. You should therefore specify a leeway value whenever possible for your own timers.
129 
130 Note: Even if you specify a leeway value of 0, you should never expect a timer to fire at the exact nanosecond you requested. The system does its best to accommodate your needs but cannot guarantee exact firing times.
131 When a computer goes to sleep, all timer dispatch sources are suspended. When the computer wakes up, those timer dispatch sources are automatically woken up as well. Depending on the configuration of the timer, pauses of this nature may affect when your timer fires next. If you set up your timer dispatch source using the dispatch_time function or the DISPATCH_TIME_NOW constant, the timer dispatch source uses the default system clock to determine when to fire. However, the default clock does not advance while the computer is asleep. By contrast, when you set up your timer dispatch source using the dispatch_walltime function, the timer dispatch source tracks its firing time to the wall clock time. This latter option is typically appropriate for timers whose firing interval is relatively large because it prevents there from being too much drift between event times.
132 
133 Listing 4-1 shows an example of a timer that fires once every 30 seconds and has a leeway value of 1 second. Because the timer interval is relatively large, the dispatch source is created using the dispatch_walltime function. The first firing of the timer occurs immediately and subsequent events arrive every 30 seconds. The MyPeriodicTask and MyStoreTimer symbols represent custom functions that you would write to implement the timer behavior and to store the timer somewhere in your application’s data structures.
134 
135 Listing 4-1  Creating a timer dispatch source
136 dispatch_source_t CreateDispatchTimer(uint64_t interval,
137               uint64_t leeway,
138               dispatch_queue_t queue,
139               dispatch_block_t block)
140 {
141    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,
142                                                      0, 0, queue);
143    if (timer)
144    {
145       dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
146       dispatch_source_set_event_handler(timer, block);
147       dispatch_resume(timer);
148    }
149    return timer;
150 }