Section 9.17. Core Foundation IPC


9.17. Core Foundation IPC

Core Foundation is an important Mac OS X framework that provides fundamental data types and several essential services, including a variety of IPC mechanisms.

9.17.1. Notifications

The Core Foundation (CF) framework provides the CFNotificationCenter data type, which, along with its associated functions, can be used for sending and receiving intraprocess and interprocess notifications. A CF notification is a message consisting of the following three elements:

  • A notification name (a CFStringRef), which must be non-NULL.

  • An object identifier, which can either be NULL or point to a value that identifies the object that posted the notification. For distributed (interprocess) notifications, the identifier must be a string (a CFStringRef).

  • A dictionary, which can either be NULL or contain arbitrary information that further describes the notification. For distributed notifications, the dictionary can contain only property list objects.

The CF notification API supports the following three types of notification centers (a process can have at most one of each type): local center, distributed center, and Darwin notify center. The local center is process-local, and the other two are for distributed notifications. The distributed center provides access to distnoted (see Section 9.16.1), whereas the Darwin notify center provides access to notifyd (see Section 9.16.2). A reference to any of these centers is obtained by calling the appropriate CFNotificationCenterGet* function, which returns a CFNotificationCenterRef data type.

// distributed notification center (/usr/sbin/notifyd) CFNotificationCenterRef CFNotificationCenterGetDarwinNotifyCenter(void); // distributed notification center (/usr/sbin/distnoted) CFNotificationCenterRef CFNotificationCenterGetDistributedCenter(void); // process-local notification center CFNotificationCenterRef CFNotificationCenterGetLocalCenter(void);


Once you have a reference to a notification center, you can add an observer, remove an observer, or post notifications. The same set of functions is used to perform these operations regardless of the notification center type. Figure 962 shows a program that posts notifications to the distributed center.

Figure 962. A program for posting Core Foundation distributed notifications

// CFNotificationPoster.c #include <CoreFoundation/CoreFoundation.h> #define PROGNAME "cfposter" int main(int argc, char **argv) {     CFStringRef             name, object;     CFNotificationCenterRef distributedCenter;     CFStringEncoding        encoding = kCFStringEncodingASCII;     if (argc != 3) {         fprintf(stderr, "usage: %s <name string> <value string>\n", PROGNAME);         exit(1);     }     name = CFStringCreateWithCString(kCFAllocatorDefault, argv[1], encoding);     object = CFStringCreateWithCString(kCFAllocatorDefault, argv[2], encoding);     distributedCenter = CFNotificationCenterGetDistributedCenter();     CFNotificationCenterPostNotification(         distributedCenter, // the notification center to use         name,              // name of the notification to post         object,            // optional object identifier         NULL,              // optional dictionary of "user" information         false);            // deliver immediately (if true) or respect the                            // suspension behaviors of observers (if false)     CFRelease(name);     CFRelease(object);     exit(0); }

Figure 963 shows a program that registers an observer of all distributed notifications, after which it runs in a loop, printing information about each received notification. When it receives a notification named cancel, it removes the observer and terminates the loop. Note that the observer program uses the concept of a run loop, which is discussed in Section 9.17.2.

Figure 963. A program for observing Core Foundation distributed notifications

// CFNotificationObserver.c #include <CoreFoundation/CoreFoundation.h> void genericCallback(CFNotificationCenterRef  center,                 void                    *observer,                 CFStringRef              name,                 const void              *object,                 CFDictionaryRef          userInfo) {     if (!CFStringCompare(name, CFSTR("cancel"), kCFCompareCaseInsensitive)) {         CFNotificationCenterRemoveObserver(center, observer, NULL, NULL);         CFRunLoopStop(CFRunLoopGetCurrent());     }     printf("Received notification ==>\n");     CFShow(center), CFShow(name), CFShow(object), CFShow(userInfo); } int main(void) {     CFNotificationCenterRef distributedCenter;     CFStringRef             observer = CFSTR("A CF Observer");     distributedCenter = CFNotificationCenterGetDistributedCenter();     CFNotificationCenterAddObserver(         distributedCenter, // the notification center to use         observer,          // an arbitrary observer-identifier         genericCallback,   // callback to call when a notification is posted         NULL,              // optional notification name to filter notifications         NULL,              // optional object identifier to filter notifications         CFNotificationSuspensionBehaviorDrop); // suspension behavior     CFRunLoopRun();     // not reached     exit(0); }

Let us now test the poster and observer programs from Figures 962 and 963, respectively.

$ gcc -Wall -o cfposter CFNotificationPoster.c -framework CoreFoundation $ gcc -Wall -o cfobserver CFNotificationObserver.c -framework CoreFoundation $ ./cfobserver         # another shell prompt         $ ./cfposter system mach Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> system mach (null)         $ ./cfposter cancel junk Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> cancel junk (null) $


As we noted in Section 9.16.1, an observer of all distributed notifications will receive notifications from other posters. If you let the cfobserver program run for some time, you will see a variety of notifications being sent by different parts of the operating system.

... Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> com.apple.carbon.core.DirectoryNotification /.vol/234881027/244950 (null) ... Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> com.apple.screensaver.willstop (null) (null) ... Received notification ==> <CFNotificationCenter 0x300980 [0xa0728150]> com.apple.Cookies.Synced 448 (null) ...


The object identifier 448 in the notification named com.apple. Cookies.Synced is the process ID of the Safari application. The iTunes application is a poster of interesting notifications that contain dictionaries with detailed song informationsuch as details of a new song as it starts to play.


9.17.2. The Run Loop

A run loop is an event loop that monitors sources of input to a task, and when an input source becomes ready for processing (i.e., the source has some activity), the run loop dispatches control to all entities that have registered interest in the sources. Examples of such input sources include user-input devices, network connections, timer events, and asynchronous callbacks.

A CFRunLoop is an opaque Core Foundation object that provides the run-loop abstraction. Carbon and Cocoa both use CFRunLoop as a building block to implement higher-level event loops. For example, the NSRunLoop class in Cocoa is implemented atop CFRunLoop.


The following points are noteworthy about run loops.

  • An event-driven application enters its main run loop after initializing.

  • Each thread has exactly one run loop, which is automatically created by Core Foundation. A thread's run loop cannot be created or destroyed programmatically.

  • An input source object is placed into (registered in) a run loop by calling the appropriate CFRunLoopAdd* function for that input source. Then, the run loop is typically run. If there are no events, the run loop blocks. When an event occurs because of an input source, the run loop wakes up and calls any callback functions that may be registered for that source.

  • Run-loop event sources are grouped into sets called modes, where a mode restricts which event sources the run loop will monitor. A run loop can run in several modes. In each mode, a run loop monitors a particular set of objects. Examples of modes include NSModalPanelRunLoopMode (used when waiting for input from a modal panel) and NSEventTrackingRunLoopMode (used in event-tracking loops). The default modekCFRunLoopDefaultModeis used for monitoring objects while the thread is idle.

Figure 964 shows the types of objects that can be placed into a run loop, the functions used for creating the objects, and the functions used for adding them to a run loop.

Figure 964. Creating and adding run-loop input sources


9.17.2.1. Run-Loop Observers

A run-loop observer (CFRunLoopObserver) is a run-loop input source that generates events at one or more specified locations, or activity stages, within the run loop. The stages of interest are specified as the bitwise OR of individual stage identifiers when the observer is created. For example, the kCFRunLoopEntry stage represents the entrance of the run loopit will be hit each time the run loop starts running as a result of either CFRunLoopRun() or CFRunLoopRunInMode().

Note that the term observer is somewhat confusing in that so far we have used the term to refer to entities that receive notifications; here, an observer generates notifications, although it is observing the run loop's activities in doing so.


9.17.2.2. Run-Loop Sources

A run-loop source (CFRunLoopSource) abstracts an underlying source of events, such as a Mach port (CFMachPort), a message port (CFMessagePort), or a network socket (CFSocket). The arrival of a message on one of these communication end points is an asynchronous event that represents input to a run loop. Core Foundation also allows custom input sources to be created.

Given an underlying primitive type supported by Core Foundation as an input source, a CFRunLoopSource object must be created before the source can be added to a run loop. For example, given a CFSocket, the CFSocketCreateRunLoopSource() function returns a reference to a CFRunLoopSource, which can then be added to a run loop using CFRunLoopAddSource().

Let us look at some properties of the input sources provided by Core Foundation.

CFMachPort

A CFMachPort is a wrapper around a native Mach port, but it allows the port to be used only for receiving messagesCore Foundation does not provide a function for sending messages. However, CFMachPortGetPort() retrieves the underlying native Mach port, which can then be used with the Mach APIs to send messages. Conversely, CFMachPortCreateWithPort() creates a CFMachPort from an existing native Mach port. If an existing port is not being used, CFMachPortCreate() can be used to create both a CFMachPort and the underlying native port. Both creation functions accept as an argument a callback function, which is called when a message arrives on the port. The callback is passed a pointer to the raw messagespecifically, the mach_msg_header_t structure. CFMachPortSetInvalidationCallBack() can be used to set another callback function that would be invoked when the port is invalidated.

CFMessagePort

A CFMessagePort is a wrapper around two native Mach portsunlike a CFMachPort, a CFMessagePort supports bidirectional communication. Like a CFMachPort, it can be used only for local (non-networked) intraprocess or interprocess communication since Mac OS X does not provide network-transparent Mach IPC.

In a typical use of CFMessagePort, a process creates a local port through CFMessagePortCreateLocal(), specifying a string name with which to register the port. The name can also be set later or changed by using CFMessagePortSetName(). Thereafter, another process can call CFMessagePortCreateRemote() with the same string name to create a CFMessagePort that is connected to the remote (to this process) port.

Let us look at an example of using a CFMessagePort. Figure 965 shows a server that creates a CFMessagePort and advertises its name. The server then creates a run-loop source from the port and adds it to the main run loop. On receiving a message, the only service the server provides is printing the contents of the messageno reply is sent.

Figure 965. A CFMessagePort server

// CFMessagePortServer.c #include <CoreFoundation/CoreFoundation.h> #define LOCAL_NAME "com.osxbook.CFMessagePort.server" CFDataRef localPortCallBack(CFMessagePortRef local, SInt32 msgid, CFDataRef data,                   void *info) {     printf("message received\n");     CFShow(data);     return NULL; } int main(void) {     CFMessagePortRef   localPort;     CFRunLoopSourceRef runLoopSource;     localPort = CFMessagePortCreateLocal(                     kCFAllocatorDefault, // allocator                     CFSTR(LOCAL_NAME),   // name for registering the port                     localPortCallBack,   // call this when message received                     NULL,                // contextual information                     NULL);               // free "info" field of context?     if (localPort == NULL) {         fprintf(stderr, "*** CFMessagePortCreateLocal\n");         exit(1);     }     runLoopSource = CFMessagePortCreateRunLoopSource(                         kCFAllocatorDefault, // allocator                         localPort, // create run-loop source for this port                         0);        // priority index     CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource,                        kCFRunLoopCommonModes);     CFRunLoopRun();     CFRelease(runLoopSource);     CFRelease(localPort);     exit(0); }

Figure 966 shows the source for a client of the CFMessagePort server. The client uses the remote port's name (shared between the client and the server) to create a connection and sends a few bytes of data to the server.

Figure 966. A CFMessagePort client

// CFMessagePortClient.c #include <CoreFoundation/CoreFoundation.h> #define REMOTE_NAME "com.osxbook.CFMessagePort.server" int main(void) {     SInt32           status;     CFMessagePortRef remotePort;     CFDataRef        sendData;     const UInt8      bytes[] = { 1, 2, 3, 4 };     sendData = CFDataCreate(kCFAllocatorDefault, bytes,                             sizeof(bytes)/sizeof(UInt8));     if (sendData == NULL) {         fprintf(stderr, "*** CFDataCreate\n");         exit(1);     }     remotePort = CFMessagePortCreateRemote(kCFAllocatorDefault,                                            CFSTR(REMOTE_NAME));     if (remotePort == NULL) {         CFRelease(sendData);         fprintf(stderr, "*** CFMessagePortCreateRemote\n");         exit(1);     }     status = CFMessagePortSendRequest(                  remotePort,     // message port to which data should be sent                  (SInt32)0x1234, // msgid, an arbitrary integer value                  sendData,       // data                  5.0,            // send timeout                  5.0,            // receive timeout                  NULL,           // reply mode (no reply expected or desired)                  NULL);          // reply data     if (status != kCFMessagePortSuccess)         fprintf(stderr, "*** CFMessagePortSendRequest: error %ld.\n", status);     else         printf("message sent\n");     CFRelease(sendData);     CFRelease(remotePort);     exit(0); }

Let us now test the CFMessagePort server and client programs.

$ gcc -Wall -o client CFMessagePortClient.c -framework CoreFoundation $ gcc -Wall -o server CFMessagePortServer.c -framework CoreFoundation $ ./server         # another shell prompt         $ ./client         message sent message received <CFData 0x300990 [0xa0728150]>{length = 4, capacity = 4, bytes = 0x01020304}


CFSocket

A CFSocket is conceptually similar to a CFMessagePort with the key difference being that BSD sockets are used as the underlying communication channel. A CFSocket can be created in several ways: from scratch, from an existing native socket, or even from a native socket that is already connected.

A CFSocket supports callbacks for several types of socket activity, for example, when there is data to read (kCFSocketReadCallBack), when the socket is writable (kCFSocketWriteCallBack), when an explicitly backgrounded connection attempt finishes (kCFSocketConnectCallBack), and so on.

Figure 967 shows a client program that uses a CFSocket to connect to a well-known time server and retrieves the current time.

Figure 967. A CFSocket client

// CFSocketTimeClient.c #include <CoreFoundation/CoreFoundation.h> #include <netdb.h> #define REMOTE_HOST "time.nist.gov" void dataCallBack(CFSocketRef s, CFSocketCallBackType callBackType,              CFDataRef address, const void *data, void *info) {     if (data) {         CFShow((CFDataRef)data);         printf("%s", CFDataGetBytePtr((CFDataRef)data));     } } int main(int argc, char **argv) {     CFSocketRef         timeSocket;     CFSocketSignature   timeSignature;     struct sockaddr_in  remote_addr;     struct hostent     *host;     CFDataRef           address;     CFOptionFlags       callBackTypes;     CFRunLoopSourceRef  source;     CFRunLoopRef        loop;     struct servent     *service;     if (!(host = gethostbyname(REMOTE_HOST))) {         perror("gethostbyname");         exit(1);     }     if (!(service = getservbyname("daytime", "tcp"))) {         perror("getservbyname");         exit(1);     }     remote_addr.sin_family = AF_INET;     remote_addr.sin_port = htons(service->s_port);     bcopy(host->h_addr, &(remote_addr.sin_addr.s_addr), host->h_length);     // a CFSocketSignature structure fully specifies a CFSocket's     // communication protocol and connection address     timeSignature.protocolFamily = PF_INET;     timeSignature.socketType     = SOCK_STREAM;     timeSignature.protocol       = IPPROTO_TCP;     address = CFDataCreate(kCFAllocatorDefault, (UInt8 *)&remote_addr,                            sizeof(remote_addr));     timeSignature.address = address;     // this is a variant of the read callback (kCFSocketReadCallBack): it     // reads incoming data in the background and gives it to us packaged     // as a CFData by invoking our callback     callBackTypes = kCFSocketDataCallBack;     timeSocket = CFSocketCreateConnectedToSocketSignature(                      kCFAllocatorDefault, // allocator to use                      &timeSignature,      // address and protocol                      callBackTypes,       // activity type we are interested in                      dataCallBack,        // call this function                      NULL,                // context                      10.0);               // timeout (in seconds)     source = CFSocketCreateRunLoopSource(kCFAllocatorDefault, timeSocket, 0);     loop = CFRunLoopGetCurrent();     CFRunLoopAddSource(loop, source, kCFRunLoopDefaultMode);     CFRunLoopRun();     CFRelease(source);     CFRelease(timeSocket);     CFRelease(address);     exit(0); } $ gcc -Wall -o timeclient CFSocketTimeClient.c -framework CoreFoundation $ ./timeclient <CFData 0x500fb0 [0xa0728150]>{length = 51, capacity = 51, bytes = 0x0a35333633322030352d30392d313920 ... 49535429202a200a} 53632 05-09-19 04:21:43 50 0 0 510.7 UTC(NIST) * <CFData 0x500b40 [0xa0728150]>{length = 0, capacity = 16, bytes = 0x}

CFRunLoopTimer

A CFRunLoopTimer is a special case of a run-loop source that can be set to fire at some time in the future, either periodically or one time only. In the latter case, the timer is automatically invalidated. A CFRunLoopTimer is created using CFRunLoopTimerCreate(), which takes a callback function as an argument. The timer can then be added to a run loop.

A run loop must be running to be able to process a timer. A timer can only be added to one run loop at a time, although it can be in multiple modes in that run loop.


Figure 968 shows a program that creates a periodic timer, adds it to the main run loop, and sets the run loop running for a given time. While the run loop is running, the timer gets processed and the associated callback is invoked.

Figure 968. Using a CFRunLoopTimer

// CFRunLoopTimerDemo.c #include <CoreFoundation/CoreFoundation.h> #include <unistd.h> void timerCallBack(CFRunLoopTimerRef timer, void *info); void timerCallBack(CFRunLoopTimerRef timer, void *info) {     CFShow(timer); } int main(int argc, char **argv) {     CFRunLoopTimerRef runLoopTimer = CFRunLoopTimerCreate(         kCFAllocatorDefault,              // allocator         CFAbsoluteTimeGetCurrent() + 2.0, // fire date (now + 2 seconds)         1.0,           // fire interval (0 or -ve means a one-shot timer)         0,             // flags (ignored)         0,             // order (ignored)         timerCallBack, // called when the timer fires         NULL);         // context     CFRunLoopAddTimer(CFRunLoopGetCurrent(),  // the run loop to use                       runLoopTimer,           // the run-loop timer to add                       kCFRunLoopDefaultMode); // add timer to this mode     CFRunLoopRunInMode(kCFRunLoopDefaultMode, // run it in this mode                        4.0,    // run it for this long                        false); // exit after processing one source?     printf("Run Loop stopped\n");     // sleep for a bit to show that the timer is not processed any more     sleep(4);     CFRunLoopTimerInvalidate(runLoopTimer);     CFRelease(runLoopTimer);     exit(0); } $ gcc -Wall -o timerdemo CFRunLoopTimerDemo.c -framework CoreFoundation $ ./timerdemo <CFRunLoopTimer ...>{locked = No, valid = Yes, interval = 1, next fire date = 148797186, order = 0, callout = 0x28ec, context = <CFRunLoopTimer context 0x0>} <CFRunLoopTimer ...>{locked = No, valid = Yes, interval = 1, next fire date = 148797187, order = 0, callout = 0x28ec, context = <CFRunLoopTimer context 0x0>} <CFRunLoopTimer ...>{locked = No, valid = Yes, interval = 1, next fire date = 148797188, order = 0, callout = 0x28ec, context = <CFRunLoopTimer context 0x0>} Run Loop stopped $




Mac OS X Internals. A Systems Approach
Mac OS X Internals: A Systems Approach
ISBN: 0321278542
EAN: 2147483647
Year: 2006
Pages: 161
Authors: Amit Singh

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net