Important: The information in this document is obsolete and should not be used for new development.
Using List Management Functions
The use of most of the utility functions introduced in this chapter and described in "Utilities Reference" is fairly straightforward. As mentioned in the previous section, using these utilities can often result in better performance. However, the group of functions used to manipulate linked lists merits additional comment. This section focuses on the use of these functions by way of a sample program, "ListMania," which is designed to illustrate the use of the Open Transport linked-list routines in a simple producer-consumer environment. The code includes two key routines,ProduceWidgets
andConsumeWidgets
.The ListMania program uses Open Transport LIFO list routines throughout. These routines are atomic with respect to interrupts and threads, and they are faster than the standard Mac OS equivalent functions (
Enqueue
andDequeue
). All code included in this sample program is running at system task time; however, all the list management in the critical portions of the code is perfectly safe to run at interrupt time. This program also demonstrates another advantage of using Open Transport list management functions: they make it easy for you to keep elements on multiple lists simultaneously. For example, in the ListMania program any given widget is always on thegAllWidgetList
(linked through thefAllWidgets
field) and on either thegPendingWidgetList
or thegFreeWidgetList
(linked through the fNext field).Before looking at the program itself, we'll briefly discuss the data structures used by the program. The objects being produced and consumed are widgets, as defined by the
Widget
data type:
struct Widget { OTLink fNext; OTLink fAllWidgets; UInt32 fSequenceNumber; OTTimeStamp fCreationTime; OTTimeStamp fLastProducedTime; }; typedef struct Widget Widget, *WidgetPtr;The first two fields are link fields: ThefNext
field is used to link all the elements on either the pending or free widget list; thefAllWidgets
field is used to link all the widgets in one long list, regardless of their status. ThefSequenceNumber
field is a unique monotonically increasing sequence number for each widget that is created. ThefCreationTime
field specifies the time when a widget is created, and thefLastProducedTime
field specifies the time when a widget was last produced. The program also uses three LIFO lists:gAllWidgetList
(which contains all widgets),gPendingWidgetList
, andgFreeWidgetList
.Listing 9-1 shows the the global variable declarations for the ListMania program.
Listing 9-1 ListMania: global declarations
struct Widget { OTLink fNext; OTLink fAllWidgets; UInt32 fSequenceNumber; OTTimeStamp fCreationTime; OTTimeStamp fLastProducedTime; }; typedef struct Widget Widget, *WidgetPtr; static OTLIFO gAllWidgetList; static OTLIFO gPendingWidgetList; static OTLIFO gFreeWidgetList; static UInt32 gLastWidgetSequenceNumber;The functionInitWidgetLists
, shown in Listing 9-2 initializes all of the widget lists to empty.Listing 9-2 The InitWidgetLists function
static void InitWidgetLists(void) /* Initializes all of the widget lists to empty.*/ { gAllWidgetList.fHead = nil; gPendingWidgetList.fHead = nil; gFreeWidgetList.fHead = nil; gLastWidgetSequenceNumber = 0; }The function shown in Listing 9-3creates a widget.Listing 9-3 The CreateWidget function
static WidgetPtr CreateWidget(void) { WidgetPtr result; /* Allocate the memory for the widget. */ result = OTAllocMem(sizeof(Widget)); OTAssert("CreateWidget: Could not create widget", result != nil); OTMemzero(result, sizeof(Widget)); result->fSequenceNumber = OTAtomicAdd32(1, (long *) &gLastWidgetSequenceNumber); OTGetTimeStamp(&result->fCreationTime); /* Add the widget to the list of all the widgets in the system.*/ OTLIFOEnqueue(&gAllWidgetList, (OTLink *) &result->fAllWidgets); return (result); }TheCreateWidget
function allocates memory for a widget and then fills out the information fields for the widget structure:fSequenceNumber
andfCreationTime
. Note the use of the utility functionOTAtomicAdd32
to increment the variablegLastWidgetSequenceNumber
atomically. This guarantees that the sequence number is unique, even it this routine is re-entered. After creating the widget, the function adds it to the list of all the widgets in the system,gAllWidgetList
, using the Open Transport list routineOTLIFOEnqueue
, and then it returns a pointer to the newly created widget.The
ProduceWidgets
function (shown in Listing 9-4) either callsCreateWidgets
if there are no free widgets or obtains a free widget from the free widget list and then adds the widget to the pending widget list.Listing 9-4 The ProduceWidgets function
static void ProduceWidgets(UInt32 howMany) { UInt32 i; OTLink *freeLink; WidgetPtr thisWidget; for (i = 0; i < howMany; i++) { freeLink = OTLIFODequeue(&gFreeWidgetList); if ( freeLink != nil ) { thisWidget = OTGetLinkObject(freeLink, Widget, fNext); } else { thisWidget = CreateWidget(); } OTGetTimeStamp(&thisWidget->fLastProducedTime); OTLIFOEnqueue(&gPendingWidgetList, (OTLink *) &thisWidget->fNext); } }Thefor
loop used in the functionProduceWidgets
takes a free element from the front of the free widget list using theOTLIFODequeue
function . If the function returns nil, there is no free element and a new widget needs to be created by calling theCreateWidget
function . If the free widget list does contain a free element, theOTGetLinkObject
macro is used to derive the widget fromfreeLink
. After this, the widget is no longer on the free widget list and we can now produce the widget by calling the utility functionOTGetTimeStamp
to set thefLastProducedTime
field. Once the widget is produced, it is added to the list of pending widgets using theOTLIFOEnqueue
function.The function
ConsumeWidgets
, shown in Listing 9-5 first calls theOTLIFOStealList
function to remove all of the widgets on the pending list and then calls theOTReverseList
function so that the widgets can be consumed in the same order they were produced. While there are still widgets left on the pending list, the function then calls thePrintWidget
function, shown in Listing 9-6, and then adds the most recently consumed widget to the free list by calling theOTLIFOEnque
function.Listing 9-5 The ConsumeWidgets function
static void ConsumeWidgets(void) { OTLink *listToConsume; WidgetPtr thisWidget; /* Remove widgets from pending list; put them in list to consume */ listToConsume = OTLIFOStealList(&gPendingWidgetList); listToConsume = OTReverseList(listToConsume); while ( listToConsume != nil ) { /* Given the link element, derive the actual widget object.*/ thisWidget = OTGetLinkObject(listToConsume, Widget, fNext); /* Consume the widget by printing to stdout */ PrintWidget(thisWidget); printf("\n"); /* Get next list element... */ listToConsume = listToConsume->fNext; /* add the most recently consumed widget to free list */ OTLIFOEnqueue(&gFreeWidgetList, (OTLink *) &thisWidget->fNext); } }It's important to note the order of the two operations used to consume (print) the widget and to add the most recently consumed widget to the free list. This is because the fieldthisWidget->fNext
occupies the same memory location as the fieldlistToConsume->fNext
, so we can't changethisWidget->fNext
by enqueuing it until we have extracted the linkage information from it.Listing 9-6 The PrintWidget function
static void PrintWidget(WidgetPtr thisWidget) { printf(" %03d, Created @ %08x%08x, Produced @ %08x%08x", thisWidget->fSequenceNumber, thisWidget->fCreationTime.hi, thisWidget->fCreationTime.lo, thisWidget->fLastProducedTime.hi, thisWidget->fLastProducedTime.lo ); }TheDumpAllWidgetLists
function, shown in Listing 9-7 dumps all of the widgets on all of the lists. Because the widgets are linked in different ways on the three lists, theDumpAllWidgetLists
function must call an additional function,DumpWidgetList
, to dump the widgets that are linked using thefNext
field--that is, the widgets in the pending and free lists.Listing 9-7 The DumpAllWidgetLists
static void DumpAllWidgetLists(void) { OTLink *link; WidgetPtr thisWidget; printf("gPendingWidgetList\n"); DumpWidgetList(&gPendingWidgetList); printf("gFreeWidgetList\n"); DumpWidgetList(&gFreeWidgetList); printf("gAllWidgetList\n"); link = gAllWidgetList.fHead; while ( link != nil ) { thisWidget = OTGetLinkObject(link, Widget, fAllWidgets); PrintWidget(thisWidget); printf("\n"); link = link->fNext; } }TheDumpWidgetList
function is shown in Listing 9-8.Listing 9-8 The DumpWidgetList function
static void DumpWidgetList(OTLIFO *list) /* Dump a widget list that is linked using the fNext field. */ /* This is appropriate for the pending and free lists of widgets. */ { OTLink *link; WidgetPtr thisWidget; link = list->fHead; while ( link != nil ) { thisWidget = OTGetLinkObject(link, Widget, fNext); PrintWidget(thisWidget); printf("\n"); link = link->fNext; } }