dispatch_source_create(3)

πŸ“¦ version 0~svn197, in ubuntu jammy

✨ dispatch_source_create(3) β€” dispatch event sources ✨

okay so bestie, this is dispatch_source_create(3) from the Library Functions Manual, and it's literally the blueprint for dispatch event sources. No cap. Let's get into it. πŸ’–

🌸 NAME 🌸

dispatch_source_create β€” dispatch event sources, but make it a whole vibe. Periodt. πŸ’…

πŸŽ€ SYNOPSIS πŸŽ€

       #include <dispatch/dispatch.h>

       dispatch_source_t
       dispatch_source_create(dispatch_source_type_t type, uintptr_t handle,
           unsigned long mask, dispatch_queue_t queue);

       void
       dispatch_source_set_event_handler(dispatch_source_t source,
           void (^block)(void));

       void
       dispatch_source_set_event_handler_f(dispatch_source_t source,
           void (*function)(void *));

       void
       dispatch_source_set_cancel_handler(dispatch_source_t source,
           void (^block)(void));

       void
       dispatch_source_set_cancel_handler_f(dispatch_source_t source,
           void (*function)(void *));

       void
       dispatch_source_cancel(dispatch_source_t source);

       void
       dispatch_source_testcancel(dispatch_source_t source);

       uintptr_t
       dispatch_source_get_handle(dispatch_source_t source);

       unsigned long
       dispatch_source_get_mask(dispatch_source_t source);

       unsigned long
       dispatch_source_get_data(dispatch_source_t source);

       void
       dispatch_source_merge_data(dispatch_source_t source,
           unsigned long data);

       void
       dispatch_source_set_timer(dispatch_source_t source,
           dispatch_time_t start, uint64_t interval, uint64_t leeway);

πŸ’… DESCRIPTION πŸ’…

okay so like, dispatch event sources are these iconic little things you can use to monitor a whole bunch of system objects and events. We're talking file descriptors, mach ports, processes, virtual filesystem nodes, signal delivery, and timers. It's giving very versatile. πŸ’β€β™€οΈ

When a state change happens, the dispatch source will submit its event handler block to its target queue. Slay. ✨

dispatch_source_create() creates a new dispatch source object that may be retained and released with calls to dispatch_retain() and dispatch_release() respectively. Newly created sources are created in a suspended state. After the source has been configured by setting an event handler, cancellation handler, context, etc., the source must be activated by a call to dispatch_resume() before any events will be delivered. So like, don't forget to resume, bestie. πŸ™ˆ

Dispatch sources may be one of the following types:

  • DISPATCH_SOURCE_TYPE_DATA_ADD πŸ’–
  • DISPATCH_SOURCE_TYPE_DATA_OR ✨
  • DISPATCH_SOURCE_TYPE_MACH_SEND πŸš€
  • DISPATCH_SOURCE_TYPE_MACH_RECV πŸ“₯
  • DISPATCH_SOURCE_TYPE_PROC 🧠
  • DISPATCH_SOURCE_TYPE_READ πŸ“–
  • DISPATCH_SOURCE_TYPE_SIGNAL πŸ“‘
  • DISPATCH_SOURCE_TYPE_TIMER ⏰
  • DISPATCH_SOURCE_TYPE_VNODE πŸ—‚οΈ
  • DISPATCH_SOURCE_TYPE_WRITE ✍️

The handle and mask arguments to dispatch_source_create() and the return values of the dispatch_source_get_handle(), dispatch_source_get_mask(), and dispatch_source_get_data() functions should be interpreted according to the type of the dispatch source. So like, context matters, bestie. πŸ’…

The dispatch_source_get_handle() function returns the underlying handle to the dispatch source (i.e. file descriptor, mach port, process identifer, etc.). The result of this function may be cast directly to the underlying type. No cap. πŸ”₯

The dispatch_source_get_mask() function returns the set of flags that were specified at source creation time via the mask argument. It's giving very consistent. πŸ‘‘

The dispatch_source_get_data() function returns the currently pending data for the dispatch source. This function should only be called from within the source's event handler. The result of calling this function from any other context is undefined. So like, don't do it. Seriously. 🚫

The dispatch_source_merge_data() function is intended for use with the DISPATCH_SOURCE_TYPE_DATA_ADD and DISPATCH_SOURCE_TYPE_DATA_OR source types. The result of using this function with any other source type is undefined. Calling this function will atomically add or logical OR the data into the source's data, and trigger the delivery of the source's event handler. It's giving very atomic. ✨

πŸ¦‹ SOURCE EVENT HANDLERS πŸ¦‹

In order to receive events from the dispatch source, an event handler should be specified via dispatch_source_set_event_handler(). The event handler block is submitted to the source's target queue when the state of the underlying system handle changes, or when an event occurs. So like, that's the whole point. πŸ’β€β™€οΈ

Dispatch sources may be suspended or resumed independently of their target queues using dispatch_suspend() and dispatch_resume() on the dispatch source directly. The data describing events which occur while a source is suspended are coalesced and delivered once the source is resumed. So like, they don't get lost, they just get saved up. Queen behavior. πŸ‘‘

The handler block need not be reentrant safe, as it is not resubmitted to the target queue until any prior invocation for that dispatch source has completed. When the handler is set, the dispatch source will perform a Block_copy() on the handler block. So like, it copies the block, periodt. πŸ’…

πŸ’€ CANCELLATION πŸ’€

The dispatch_source_cancel() function asynchronously cancels the dispatch source, preventing any further invocation of its event handler block. Cancellation does not interrupt a currently executing handler block (non-preemptive). So like, if it's already running, it's gonna finish. No cap. πŸ’―

The dispatch_source_testcancel() function may be used to determine whether the specified source has been canceled. A non-zero value will be returned if the source is canceled. So like, truthy means canceled. πŸ’…

When a dispatch source is canceled its optional cancellation handler will be submitted to its target queue. The cancellation handler may be specified via dispatch_source_set_cancel_handler(). This cancellation handler is invoked only once, and only as a direct consequence of calling dispatch_source_cancel(). So like, one and done. ✨

Important: a cancellation handler is required for file descriptor and mach port based sources in order to safely close the descriptor or destroy the port. Closing the descriptor or port before the cancellation handler has run may result in a race condition: if a new descriptor is allocated with the same value as the recently closed descriptor while the source's event handler is still running, the event handler may read/write data to the wrong descriptor. So like, this is serious. Don't skip it. 🚨


✨ DISPATCH SOURCE TYPES ✨

Okay so this next bit is basically the menu of supported dispatch event types, plus how to read their parameters and the data they hand back. πŸ’…


DISPATCH_SOURCE_TYPE_DATA_ADD, DISPATCH_SOURCE_TYPE_DATA_OR 🫢

These ones let your app manually trigger the source's event handler by calling dispatch_source_merge_data(). The data gets merged with the source's pending data using an atomic add or logic OR (depends on which type you picked), and then the event handler block gets submitted to the source's target queue. The data is application defined. These sources have no handle or mask, so use zero. Periodt.

DISPATCH_SOURCE_TYPE_MACH_SEND πŸš€

These monitor a mach port with a send right for state changes. The handle is the mach port (mach_port_t) to monitor, and the mask may be:

  • DISPATCH_MACH_SEND_DEAD β€” the port's corresponding receive right has been destroyed πŸ’€

The data returned by dispatch_source_get_data() tells you which of the events in the mask were observed.

DISPATCH_SOURCE_TYPE_MACH_RECV πŸ“¬

These monitor a mach port with a receive right for state changes. The handle is the mach port (mach_port_t) to monitor, and the mask is unused and should be zero. The event handler block gets submitted to the target queue when a message on the mach port is waiting to be received.

DISPATCH_SOURCE_TYPE_PROC πŸ‘€

These monitor processes for state changes. The handle is the process identifier (pid_t) of the process to monitor, and the mask may be one or more of these:

  • DISPATCH_PROC_EXIT β€” the process has exited and is available to wait(2).
  • DISPATCH_PROC_FORK β€” the process has created one or more child processes.
  • DISPATCH_PROC_EXEC β€” the process has become another executable image via a call to execve(2) or posix_spawn(2).
  • DISPATCH_PROC_REAP β€” the process status has been collected by its parent process via wait(2).
  • DISPATCH_PROC_SIGNAL β€” a signal was delivered to the process.

The data returned by dispatch_source_get_data() tells you which of the events in the mask were observed.

DISPATCH_SOURCE_TYPE_READ πŸ“–

These monitor file descriptors for pending data. The handle is the file descriptor (int) to monitor, and the mask is unused and should be zero.

The data returned by dispatch_source_get_data() is an estimated number of bytes available to be read from the descriptor. Treat this estimate as a suggested minimum read buffer size. There are no guarantees that a complete read of this size will be performed. 😭

If you're using this source type, you are strongly encouraged to perform non-blocking I/O and handle any truncated reads or error conditions that may occur. See fcntl(2) for more info on setting the O_NONBLOCK flag on a file descriptor.

DISPATCH_SOURCE_TYPE_SIGNAL 🚨

These monitor signals delivered to the current process. The handle is the signal number to monitor (int), and the mask is unused and should be zero.

The data returned by dispatch_source_get_data() is the number of signals received since the last invocation of the event handler block.

Unlike signal handlers specified via sigaction(), the execution of the event handler block does not interrupt the current thread of execution. So the handler block is not limited to the signal safe interfaces defined in sigaction(2). Plus, multiple observers of a given signal are supported, which lets applications and libraries cooperate safely. 🫢

However β€” and this is important β€” a dispatch source does not install a signal handler or otherwise alter the behavior of signal delivery. So applications must ignore or at least catch any signal that terminates a process by default. For example, near the top of main():

signal(SIGTERM, SIG_IGN);

DISPATCH_SOURCE_TYPE_TIMER ⏰

These periodically submit the event handler block to the target queue on an interval specified by dispatch_source_set_timer(). The handle and mask arguments are unused and should be zero.

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. πŸ™ˆ

The data returned by dispatch_source_get_data() is the number of times the timer has fired since the last invocation of the event handler block.

The function dispatch_source_set_timer() takes as an argument the start time of the timer (initial fire time) represented as a dispatch_time_t. The timer dispatch source will use the same clock as the function used to create this value. (See dispatch_time(3) for more information.) The interval, in nanoseconds, specifies the period at which the timer should repeat. All timers will repeat indefinitely until dispatch_source_cancel() is called. The leeway, in nanoseconds, is a hint to the system that it may defer the timer in order to align with other system activity for improved system performance or reduced power consumption. (For example, an application might perform a periodic task every 5 minutes with a leeway of up to 30 seconds.) Note that some latency is to be expected for all timers even when a value of zero is used. πŸ’…

Note: Under the C language, untyped numbers default to the int type. This can lead to truncation bugs when arithmetic operations with other numbers are expected to generate a uint64_t sized result. When in doubt, use ull as a suffix. For example:
3ull * NSEC_PER_SEC

DISPATCH_SOURCE_TYPE_VNODE 🌳

These monitor the virtual filesystem nodes for state changes. The handle is a file descriptor (int) referencing the node to monitor, and the mask may be one or more of these:

  • DISPATCH_VNODE_DELETE β€” the referenced node was removed from the filesystem namespace via unlink(2).
  • DISPATCH_VNODE_WRITE β€” a write to the referenced file occurred.
  • DISPATCH_VNODE_EXTEND β€” the referenced file was extended.
  • DISPATCH_VNODE_ATTRIB β€” the metadata attributes of the referenced node have changed.
  • DISPATCH_VNODE_LINK β€” the link count on the referenced node has changed.
  • DISPATCH_VNODE_RENAME β€” the referenced node was renamed.
  • DISPATCH_VNODE_REVOKE β€” access to the referenced node was revoked via revoke(2) or the underlying fileystem was unmounted.

The data returned by dispatch_source_get_data() tells you which of the events in the mask were observed.

DISPATCH_SOURCE_TYPE_WRITE ✍️

These monitor file descriptors for available write buffer space. The handle is the file descriptor (int) to monitor, and the mask is unused and should be zero.

If you're using this source type, you are strongly encouraged to perform non-blocking I/O and handle any truncated reads or error conditions that may occur. See fcntl(2) for more info on setting the O_NONBLOCK flag on a file descriptor.


πŸ‘‘ SEE ALSO πŸ‘‘

dispatch(3), dispatch_object(3), dispatch_queue_create(3)


Okay bestie, that's the whole vibe. This page came from the Darwin crew, dated May 1, 2009, for dispatch_source_create(3) β€” mwah πŸ’‹βœ¨

πŸ“¦ where this page comes from