@DATABASE Libraries Manual
@NODE MAIN "Amiga RKM Libraries: 23 Exec Lists and Queues"
@INDEX Lib_Index/MAIN
@TOC Libraries_Manual/Exec
The Amiga system software operates in a dynamic environment of data
structures.  An early design goal of Exec was to keep the system flexible
and open-ended by eliminating artificial boundaries on the number of
system structures used.  Rather than using static system tables, Exec uses
dynamically created structures that are added and removed as needed.
These structures can be put in an unordered list, or in an ordered list
known as a queue.  A list can be empty, but never full.  This concept is
central to the design of Exec.  Understanding lists and queues is
important to understanding not only Exec itself, but also the mechanism
behind the Amiga's message and port based interprocess communication.

Exec uses lists to maintain its internal database of system structures.
Tasks, interrupts, libraries, devices, messages, I/O requests, and all
other Exec data structures are supported and serviced through the
consistent application of Exec's list mechanism.  Lists have a common data
structure, and a common set of functions is used for manipulating them.
Because all of these structures are treated in a similar manner, only a
small number of list handling functions need be supported by Exec.

@{" List Structure " link 23-1}  @{" List Functions " link 23-2}  @{" Function Reference " link 23-3}

@ENDNODE

@NODE 23-1 "23 Exec Lists and Queues / List Structure"
A list is composed of a header and a doubly-linked chain of elements
called nodes.  The header contains memory pointers to the first and last
nodes of the linked chain.  The address of the header is used as the
handle to the entire list.  To manipulate a list, you must provide the
address of its header.
                                    _______________
                                 _\|               |
                                / /|   First Node  |
                               /  /|_______________|
                              /  /       |  /|\
            _______________  /  /        |   |
           |               |/  /    ____\|/__|_____
           |   Head Node   |/_/    |               |
           |_______________|\      |  Second Node  |
           |               |/_     |_______________|
           |   Tail Node   |\ \          |  /|\
           |_______________|\  \         |   |
                             \  \   ____\|/__|_____
                              \  \ |               |
                               \  \|   Third Node  |
                                \_\|_______________|
                                  /

             Figure 23-1: Simplified Overview of an Exec List


Nodes may be scattered anywhere in memory.  Each node contains two
pointers; a successor and a predecessor.  As illustrated above, a list
header contains two placeholder nodes that contain no data.  In an empty
list, the head and tail nodes point to each other.

@{" Node Structure Definition " link 23-1-1}  @{" List Header Structure Definition " link 23-1-3}
@{" Node Initialization " link 23-1-2}        @{" Header Initialization " link 23-1-4}

@ENDNODE

@NODE 23-1-1 "23 / List Structure / Node Structure Definition"
A Node structure is divided into three parts: linkage, information, and
content.  The linkage part contains memory pointers to the node's
successor and predecessor nodes.  The information part contains the node
type, the priority, and a name pointer.  The content part stores the
actual data structure of interest.  For nodes that require linkage only, a
small @{"MinNode" link ADCD_v1.2:Inc&AD2.1/includes/exec/nodes.h/MAIN 31} structure is used.

    struct MinNode
    {
        struct MinNode *mln_Succ;
        struct MinNode *mln_Pred;
    };

ln_Succ
    points to the next node in the list (successor).

ln_Pred
    points to the previous node in the list (predecessor).

When a type, priority, or name is required, a full-featured @{"Node" link ADCD_v1.2:Inc&AD2.1/includes/exec/nodes.h/MAIN 23} structure
is used.

    struct Node
    {
        struct Node *ln_Succ;
        struct Node *ln_Pred;
        UBYTE        ln_Type;
        BYTE         ln_Pri;
        char        *ln_Name;
    };

ln_Type
    defines the type of the node (see <exec/@{"nodes.h" link ADCD_v1.2:Inc&AD2.1/includes/exec/nodes.h/MAIN 43}> for a list).

ln_Pri
    specifies the priority of the node  (+127 (highest) to -128
    (lowest)).

ln_Name
    points to a printable name for the node (a NULL-terminated
    string).

The Node and MinNode structures are often incorporated into larger
structures, so groups of the larger structures can easily be linked
together.   For example, the Exec Interrupt structure is defined as
follows:

    struct Interrupt
    {
        struct Node is_Node;
        APTR        is_Data;
        VOID        (*is_Code)();
    };

Here the is_Data and is_Code fields represent the useful content of the
node.  Because the Interrupt structure begins with a Node structure, it
may be passed to any of the Exec @{"List" link 23-1-3 23} manipulation functions.

@ENDNODE

@NODE 23-1-2 "23 / List Structure / Node Initialization"
Before linking a node into a list, certain fields may need initialization.
Initialization consists of setting the @{"ln_Type" link 23-1-1 31}, @{"ln_Pri" link 23-1-1 34}, and @{"ln_Name" link 23-1-1 38} fields
to their appropriate values (A @{"MinNode" link 23-7} structure does not have these
fields).  The successor and predecessor fields do not require
initialization.

The @{"ln_Type" link 23-1-1 31} field contains the data type of the node.  This indicates to
Exec (and other subsystems) the type, and hence the structure, of the
content portion of the node (the extra data after the @{"Node" link 23-1-1 19} structure).
The standard system types are defined in the <exec/@{"nodes.h" link ADCD_v1.2:Inc&AD2.1/includes/exec/nodes.h/MAIN 43}> include file.
Some examples of standard system types are NT_TASK, NT_INTERRUPT,
NT_DEVICE, and NT_MSGPORT.

The @{"ln_Pri" link 23-1-1 34} field uses a signed numerical value ranging from +127 to -128
to indicate the priority of the node.  Higher-priority nodes have greater
values; for example, 127 is the highest priority, zero is nominal
priority, and -128 is the lowest priority.  Some Exec lists are kept
sorted by priority order.  In such lists, the highest-priority node is at
the head of the list, and the lowest-priority node is at the tail of the
list.  Most Exec node types do not use a priority.  In such cases,
initialize the priority field to zero.

The @{"ln_Name" link 23-1-1 38} field is a pointer to a NULL-terminated string of characters.
Node names are used to find and identify list-bound objects (like public
message ports and libraries), and to bind symbolic names to actual nodes.
Names are also useful for debugging purposes, so it is a good idea to
provide every node with a name.  Take care to provide a valid name
pointer; Exec does not copy name strings.

This fragment initializes a @{"Node" link 23-1-1 19} called myInt, an instance of the
@{"Interrupt" link 23-1-1 47} data structure introduced above.

    struct Interrupt interrupt;

    interrupt.is_Node.ln_Type = NT_INTERRUPT;
    interrupt.is_Node.ln_Pri  = -10;
    interrupt.is_Node.ln_Name = "sample.interrupt";

@ENDNODE

@NODE 23-1-3 "23 / List Structure / List Header Structure Definition"
As mentioned earlier, a list header maintains memory pointers to the first
and last nodes of the linked chain of nodes.  It also serves as a handle
for referencing the entire list.  The minimum list header ("mlh_") and the
full-featured list header ("lh_") are generally interchangeable.

The structure MinList defines a minimum list header.

    struct MinList
    {
        struct MinNode *mlh_Head;
        struct MinNode *mlh_Tail;
        struct MinNode *mlh_TailPred;
    };

mlh_Head
    points to the first node in the list.

mlh_Tail
    is always NULL.

mlh_TailPred
    points to the last node in the list.

In a few limited cases a full-featured List structure will be required:

    struct List
    {
        struct Node *lh_Head;
        struct Node *lh_Tail;
        struct Node *lh_TailPred;
        UBYTE        lh_Type;
        UBYTE        lh_Pad;
    };

lh_Type
    defines the type of nodes within the list (see <exec/@{"nodes.h" link ADCD_v1.2:Inc&AD2.1/includes/exec/nodes.h/MAIN 43}>).

lh_pad
    is a structure alignment byte.

One subtlety here must be explained further.  The list header is
constructed in an efficient, but confusing manner.  Think of the header as
a structure containing the head and tail nodes for the list.  The head and
tail nodes are placeholders, and never carry data.  The head and tail
portions of the header actually overlap in memory. lh_Head and lh_Tail
form the head node; lh_Tail and lh_TailPred form the tail node.  This
makes it easy to find the start or end of the list, and eliminates any
special cases for insertion or removal.

           ___________                     ___________
          |           |                   |           |
          |  ln_Succ  |                   |  lh_Head  |
          |___________|___________        |___________|
          |           |           |       |           |
          | ln_Pred=0 | ln_Succ=0 |       | lh_Tail=0 |
          |___________|___________| ____\ |___________|
                      |           |     / |           |
                      |  ln_Pred  |       |lh_TailPred|
                      |___________|       |___________|


               Figure 23-2: List Header Overlap


The lh_Head and lh_Tail fields of the list header act like the @{"ln_Succ" link 23-1-1 13} and
lh_Pred fields of a node.  The lh_Tail field is set permanently to NULL,
indicating that the head node is indeed the first on the list -- that is,
it has no predecessors.  See the figure above.

Likewise, the lh_Tail and lh_TailPred fields of the list header act like
the @{"ln_Succ" link 23-1-1 13} and lh_Pred fields of a node. Here the NULL lh_Tail indicates
that the tail node is indeed the last on the list -- that is, it has no
successors.  See the figure above.

@ENDNODE

@NODE 23-1-4 "23 / List Structure / Header Initialization"
List headers must be properly initialized before use.  It is not adequate
to initialize the entire header to zero.  The head and tail entries must
have specific values. The header must be initialized as follows:

    1. Set the @{"lh_Head" link 23-1-3 14} field to the address of @{"lh_Tail" link 23-1-3 17}.

    2. Clear the @{"lh_Tail" link 23-1-3 17} field.

    3. Set the @{"lh_TailPred" link 23-1-3 20} field to the address of @{"lh_Head" link 23-1-3 14}.

    4. Set @{"lh_Type" link 23-1-3 34} to the same data type as the nodes to be kept the list.
       (Unless you are using a @{"MinList" link 23-1-3 5}).


         Figure 23-3: Initializing a List Header Structure

                                             _________________
                                            |   ___________   |
                                            |  |           |__|
                                            |  |  lh_Head  |/_
                                            |  |___________|\ |
                                            |_\|           |  |
                                              /| lh_Tail=0 |  |
                                               |___________|  |
                                               |           |  |
                                               |lh_TailPred|__|
                                               |_ _ _ _ _ _|
                                               |     |     |
    /* C example - equivalent to NewList() */  |     |     |
    struct List list;                          |_ _ _|_ _ _|

    list.lh_Head          = (struct Node *) &list.lh_Tail;
    list.lh_Tail          = 0;
    list.lh_TailPred      = (struct Node*) &list.lh_Head;
    /* Now set lh_Type, if needed */

    ;Assembly example - equivalent to NEWLIST
          MOVE.L  A0,LH_HEAD(A0)  ;A0 points to the list header
          ADDQ.L  #4,LH_HEAD(A0)  ;Bump LH_HEAD(A0) to address of LH_TAIL
          CLR.L   LH_TAIL(A0)
          MOVE.L  A0,LH_TAILPRED(A0)
    ;Now set LH_TYPE, if needed.


The sequence of assembly instructions in the figure above is equivalent to
the macro NEWLIST, contained in the include file <exec/@{"lists.i" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 44}>.  Since
the @{"MinList" link 23-1-3 5} structure is the same as the @{"List" link 23-1-3 23} structure except for the
type and pad fields, this sequence of assembly language code will work for
both structures.  The sequence performs its function without destroying
the pointer to the list header in A0 (which is why ADDQ.L is used).  This
function may also be accessed from C as a call to @{"NewList" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/NewList()}(header), where
header is the address of a list header.

@ENDNODE

@NODE 23-2 "23 Exec Lists and Queues / List Functions"
Exec provides a number of symmetric functions for handling lists.  There
are functions for inserting and removing nodes, for adding and removing
head and tail nodes, for inserting nodes in a priority order, and for
searching for nodes by name.  The prototypes for Exec list handling
functions are as follows.

   Exec Functions
   --------------
   VOID AddHead( struct List *list, struct Node *node );
   VOID AddTail( struct List *list, struct Node *node );
   VOID Enqueue( struct List *list, struct Node *node );
   struct Node *FindName( struct List *list, UBYTE *name );
   VOID Insert( struct List *list, struct Node *node, struct Node *pred );
   VOID Remove( struct Node *node );
   struct Node *RemHead( struct List *list );
   struct Node *RemTail( struct List *list );

   Exec Support Functions in amiga.lib
   -----------------------------------
   VOID NewList( struct List *list );

In this discussion of the Exec list handling functions, header represents
a pointer to @{"List" link 23-1-3 23} header, and node represents pointer to a @{"Node" link 23-1-1 19}.

@{" Insertion and Removal " link 23-2-1}
@{" Special Case Insertion " link 23-2-2}
@{" Special Case Removal " link 23-2-3}
@{" MinList/MinNode Operations " link 23-2-4}
@{" Prioritized Insertion " link 23-2-5}
@{" Searching by Name " link 23-2-6}
@{" More on the Use of Named Lists " link 23-2-7}
@{" List Macros for Assembly Language Programmers " link 23-2-8}
@{" Empty Lists " link 23-2-9}
@{" Scanning a List " link 23-2-10}
@{" Important Note About Shared Lists " link 23-2-11}

@ENDNODE

@NODE 23-2-1 "23 / List Functions / Insertion and Removal"
The @{"Insert()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Insert()} function is used for inserting a new node into any position
in a list.  It always inserts the node following a specified node that is
already part of the list.  For example, Insert(@{"header" link 23-2 21},@{"node" link 23-2 21},pred) inserts
the node node after the node pred in the specified list.  If the pred node
points to the list header or is NULL, the new node will be inserted at the
head of the list.  Similarly, if the pred node points to the @{"lh_Tail" link 23-1-3 17} of
the list, the new node will be inserted at the tail of the list.  However,
both of these actions can be better accomplished with the functions
mentioned in the "@{"Special Case Insertion" link 23-2-2}" section below.

The @{"Remove()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Remove()} function is used to remove a specified node from a list.  For
example, Remove(@{"node" link 23-2 21}) will remove the specified node from whatever list it
is in.  To be removed, a node must actually be in a list. If you attempt
to remove a node that is not in a list, you will cause serious system
problems.

@ENDNODE

@NODE 23-2-2 "23 / List Functions / Special Case Insertion"
Although the @{"Insert()" link 23-2-1} function allows new nodes to be inserted at the head
and the tail of a list, the @{"AddHead()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddHead()} and @{"AddTail()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddTail()} functions will do so
with higher efficiency.  Adding to the head or tail of a list is common
practice in first-in-first-out (@{"FIFO" link 23-2-3}) or last-in-first-out (@{"LIFO" link 23-2-3} or stack)
operations.  For example, AddHead(@{"header" link 23-2 21},@{"node" link 23-2 21}) would insert the node at
the head of the specified list.

@ENDNODE

@NODE 23-2-3 "23 / List Functions / Special Case Removal"
The two functions @{"RemHead()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemHead()} and @{"RemTail()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemTail()} are used in combination with
@{"AddHead()" link 23-2-2} and @{"AddTail()" link 23-2-2} to create special list ordering.  When you combine
AddTail() and RemHead(), you produce a first-in-first-out (FIFO) list.
When you combine AddHead() and RemHead() a last-in-first-out (LIFO or
stack) list is produced.  RemTail() exists for symmetry. Other
combinations of these functions can also be used productively.

Both @{"RemHead()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemHead()} and @{"RemTail()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemTail()} remove a @{"node" link 23-2 21} from the list, and return a
pointer to the removed node. If the list is empty, the function return a
NULL result.

@ENDNODE

@NODE 23-2-4 "23 / List Functions / MinList/MinNode Operations"
All of the above functions and macros will work with long or short format
node structures.  A @{"MinNode" link 23-1-1 7} structure contains only linkage information.
A full @{"Node" link 23-1-1 19} structure contains linkage information, as well as type,
priority and name fields.  The smaller MinNode is used where space and
memory alignment issues are important.  The larger Node is used for queues
or lists that require a name tag for each node.

@ENDNODE

@NODE 23-2-5 "23 / List Functions / Prioritized Insertion"
The list functions discussed so far do not make use of the priority field
in a @{"Node" link 23-1-1 19}. The @{"Enqueue()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Enqueue()} function is equivalent to @{"Insert()" link 23-2-1}, except it
inserts nodes into a list sorting them according to their priority.  It
keeps the higher-priority nodes towards the head of the list.  All nodes
passed to this function must have their priority and name assigned prior
to the call. Enqueue(@{"header" link 23-2 21},mynode) inserts mynode behind the lowest
priority node with a priority greater than or equal to mynode's. For
Enqueue() to work properly, the list must already be sort according to
priority.  Because the highest priority node is at the head of the list,
the @{"RemHead()" link 23-2-3} function will remove the highest-priority node.  Likewise,
@{"RemTail()" link 23-2-3} will remove the lowest-priority node.

    FIFO Is Used For The Same Priority.
    -----------------------------------
    If you add a node that has the same priority as another node in the
    queue, @{"Enqueue()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Enqueue()} will use @{"FIFO" link 23-2-3} ordering.  The new node is inserted
    following the last node of equal priority.

@ENDNODE

@NODE 23-2-6 "23 / List Functions / Searching by Name"
Because many lists contain nodes with symbolic names attached (via the
@{"ln_Name" link 23-1-1 38} field), it is possible to find a node by its name.  This naming
technique is used throughout Exec for such nodes as tasks, libraries,
devices, and resources.

The @{"FindName()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/FindName()} function searches a list for the first node with a given
name.  For example, FindName(@{"header" link 23-2 21}, "Furrbol") returns a pointer to the
first node named "Furrbol." If no such node exists, a NULL is returned.
The case of the name characters is significant; "foo" is different from
"Foo."

  _______________________________________________________________________
 |   ___________       ___________       ___________       ___________   |
 |  |           |____\|           |____\|           |____\|           |__|
 |  |  lh_Head  |/_  /|  ln_Succ  |/_  /|  ln_Succ  |/_  /|  ln_Succ  |/_
 |  |___________|\ \  |___________|\ \  |___________|\ \  |___________|\ |
 |_\|           |   \ |           |   \ |           |   \ |           |  |
   /| lh_Tail=0 |    \|  ln_Pred  |    \|  ln_Pred  |    \|  ln_Pred  |  |
    |___________|     |___________|     |___________|     |___________|  |
    |           |     |           |     |           |     |           |  |
    |lh_TailPred|__   |  ln_Type  |     |  ln_Type  |     |  ln_Type  |  |
    |_ _ _ _ _ _|  |  |___________|     |___________|     |___________|  |
    |     |     |  |  |           |     |           |     |           |  |
    |     |     |  |  |  ln_Pri   |     |  ln_Pri   |     |  ln_Pri   |  |
    |_ _ _|_ _ _|  |  |___________|     |___________|     |___________|  |
                   |  |           |     |           |     |           |  |
                   |  |  ln_Name  |     |  ln_Name  |     |  ln_Name  |  |
                   |  |_ _ _ _ _ _|     |_ _ _ _ _ _|     |_ _ _ _ _ _|  |
                   |  |           |     |           |     |           |  |
                   |  |   Node    |     |   Node    |     |   Node    |  |
                   |  |  Content  |     |  Content  |     |  Content  |  |
                   |  |_ _ _ _ _ _|     |_ _ _ _ _ _|     |_ _ _ _ _ _|  |
                   |_____________________________________________________|


      Figure 23-4: Complete Sample List Showing all Interconnections

@ENDNODE

@NODE 23-2-7 "23 / List Functions / More on the Use of Named Lists"
To find multiple occurrences of nodes with identical names, the @{"FindName()" link 23-2-6 5}
function is called multiple times.  For example, if you want to find all
the nodes with the name pointed to by name:

    VOID DisplayName(struct List *list,UBYTE *name)
    {
    struct Node *node;

        if (node = FindName(list,name))
            while (node)
            {
                printf("Found %s at location %lx\n",node->ln_Name,node);
                node = FindName((struct List *)node,name);
            }
        else printf("No node with name %s found.\n",name);
    }

Notice that the second search uses the node found by the first search. The
@{"FindName()" link 23-2-6 5} function never compares the specified name with that of the
starting node.  It always begins the search with the successor of the
starting point.

@ENDNODE

@NODE 23-2-8 "23 / List Functions / List Macros for Assembly Language Programmers"
Assembly language programmers may want to optimize their code by using
assembly code list macros.  Because these macros actually embed the
specified list operation into the code, they result in slightly faster
operations.  The file <exec/@{"lists.i" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 42}> contains the recommended set of
macros. For example, the following instructions implement the @{"REMOVE" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 133} macro:

    MOVE.L  LN_SUCC(A1),A0       ; get successor
    MOVE.L  LN_PRED(A1),A1       ; get predecessor
    MOVE.L  A0,LN_SUCC(A1)       ; fix up predecessor's succ pointer
    MOVE.L  A1,LN_PRED(A0)       ; fix up successor's pred pointer

@ENDNODE

@NODE 23-2-9 "23 / List Functions / Empty Lists"
It is often important to determine if a list is empty.  This can be done
in many ways, but only two are worth mentioning.  If either the
@{"lh_TailPred" link 23-1-3 20} field is pointing to the list header or the @{"ln_Succ" link 23-1-1 13} field of
the @{"lh_Head" link 23-1-3 14} is NULL, then the list is empty.

In C, for example, these methods would be written as follows:

    /* You can use this method...   */
    if (list->lh_TailPred == (struct Node *)list)
        printf("list is empty\n");

    /* Or you can use this method   */
    if (NULL == list->lh_Head->ln_Succ)
        printf("list is empty\n");

In assembly code, if A0 points to the list header, these methods would be
written as follows:

    ; Use this method...
    CMP.L   LH_TAILPRED(A0),A0
    BEQ     list_is_empty

    ; Or use this method
    MOVE.L  LH_HEAD(A0),A1
    TST.L   LN_SUCC(A1)
    BEQ     list_is_empty

Because LH_HEAD and LN_SUCC are both zero offsets, the second case may be
simplified or optimized by your assembler.

@ENDNODE

@NODE 23-2-10 "23 / List Functions / Scanning a List"
Occasionally a program may need to scan a list to locate a particular
node, find a node that has a field with a particular value, or just print
the list.  Because lists are linked in both the forward and backward
directions, the list can be scanned from either the head or tail.

Here is a code fragment that uses a for loop to print the names of all
nodes in a list:

    struct List *list;
    struct Node *node;

    for (node = list->lh_Head ; node->ln_Succ ; node = node->ln_Succ)
        printf("%lx -> %s\n",node,node->ln_Name);

A common mistake is to process the head or tail nodes.  Valid data nodes
have non-NULL successor and predecessor pointers.  The above loop exits
when @{"node->ln_Succ" link 23-1-1 13} is NULL.  Another common mistake is to free a node from
within a loop, then reference the free memory to obtain the next node
pointer.  An extra temporary pointer solves this second problem.

In assembly code, it is more efficient to use a look-ahead cache pointer
when scanning a list.  In this example the list is scanned until the first
zero-priority node is reached:

            MOVE.L  (A1),D1         ; first node
    scan:   MOVE.L  D1,A1
            MOVE.L  (A1),D1         ; lookahead to next
            BEQ.S   not_found       ; end of list...
            TST.B   LN_PRI(A1)
            BNE.S   scan
            ...                     ; found one

    not_found:

    @{" Exec List Example " link ADCD_v1.2:Reference_Library/Libraries/Lib_Examples/buildlist.c/MAIN}

@ENDNODE

@NODE 23-2-11 "23 / List Functions / Important Note About Shared Lists"
It is possible to run into contention problems with other tasks when
manipulating a list that is shared by more than one task.  None of the
standard Exec list functions arbitrates for access to the list.  For
example, if some other task happens to be modifying a list while your task
scans it, an inconsistent view of the list may be formed.  This can result
in a corrupted system.

Generally it is not permissible to read or write a shared list without
first locking out access from other tasks.  All users of a list must use
the same arbitration method.  Several arbitration techniques are used on
the Amiga.  Some lists are protected by a semaphore.  The @{"ObtainSemaphore()" link ADCD_v1.2:Reference_Library/Libraries/Lib_25/25-1-1-3}
call grants ownership of the list (see the "@{"Exec Semaphores" link ADCD_v1.2:Reference_Library/Libraries/Lib_25/MAIN}" chapter for
more information).  Some lists require special arbitration. For example,
you must use the Intuition @{"LockIBase" link ADCD_v1.2:Reference_Library/Libraries/Lib_11/11-1 6}(0) call before accessing any
Intuition lists.  Other lists may be accessed only during @{"Forbid()" link ADCD_v1.2:Reference_Library/Libraries/Lib_21/21-4-1 9} or
@{"Disable()" link ADCD_v1.2:Reference_Library/Libraries/Lib_21/21-4-2 6} (see the "@{"Exec Tasks" link ADCD_v1.2:Reference_Library/Libraries/Lib_21/21-4}" chapter for more information).

The preferred method for arbitrating use of a shared list is through
semaphores because a semaphores only holds off other tasks that are trying
to access the shared list.  Rather than suspending all multitasking.
Failure to lock a shared list before use will result in unreliable
operation.

Note that I/O functions including @{"printf()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/printf()} generally call @{"Wait()" link ADCD_v1.2:Reference_Library/Libraries/Lib_22/22-1-2} to wait
for I/O completion, and this allows other tasks to run.  Therefore, it is
not safe to print or Wait() while traversing a list unless the list is
fully controlled by your application, or if the list is otherwise
guaranteed not to change during multitasking.

@ENDNODE

@NODE 23-3 "23 Exec Lists and Queues / Function Reference"
The following charts give a brief description of the Exec list and queue
functions and assembler macros.  See the Amiga ROM Kernel Reference
Manual: Includes and Autodocs for details about each call.


             Table 23-1: Exec List and Queue Functions
  _________________________________________________________________
 |                                                                 |
 |  Exec Function  Description                                     |
 |=================================================================|
 |    @{"AddHead()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddHead()}  Insert a node at the head of a list.              |
 |    @{"AddTail()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/AddTail()}  Append a node to the tail of a list.              |
 |    @{"Enqueue()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Enqueue()}  Insert or append a node to a system queue.        |
 |   @{"FindName()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/FindName()}  Find a node with a given name in a system list.   |
 |     @{"Insert()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Insert()}  Insert a node into a list.                        |
 |  IsListEmpty  Test if list is empty                             |
 |    @{"NewList()" link ADCD_v1.2:Inc&AD2.1/autodocs/amiga_lib/NewList()}  Initialize a list structure for use.              |
 |    @{"RemHead()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemHead()}  Remove the head node from a list.                 |
 |     @{"Remove()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/Remove()}  Remove a node from a list.                        |
 |    @{"RemTail()" link ADCD_v1.2:Inc&AD2.1/autodocs/exec/RemTail()}  Remove the tail node from a list.                 |
 |_________________________________________________________________|


          Table 23-2: Exec List and Queue Assembler Macros
  _________________________________________________________________
 |                                                                 |
 |  Exec Function  Description                                     |
 |=================================================================|
 |      @{"NEWLIST" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 44}  Initialize a list header for use.                 |
 |      @{"TSTLIST" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 52}  Test if list is empty (list address in register). |
 |               No arbitration needed.                            |
 |      @{"TSTLST2" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 66}  Test is list is empty (from effective address of  |
 |               list).  Arbitration needed.                       |
 |         @{"SUCC" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 73}  Get next node in a list.                          |
 |         @{"PRED" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 78}  Get previous node in a list.                      |
 |      @{"IFEMPTY" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 83}  Branch if list is empty.                          |
 |   @{"IFNOTEMPTY" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 89}  Branch if list is not empty.                      |
 |      @{"TSTNODE" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 95}  Get next node, test if at end of list.            |
 |     @{"NEXTNODE" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 101}  Get next node, go to exit label if at end.        |
 |      @{"ADDHEAD" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 113}  Add node to head of list.                         |
 |      @{"ADDTAIL" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 122}  Add node to tail of list.                         |
 |       @{"REMOVE" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 133}  Remove node from a list.                          |
 |      @{"REMHEAD" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 141}  Remove node from head of list.                    |
 |     @{"REMHEADQ" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 152}  Remove node from head of list quickly.            |
 |      @{"REMTAIL" link ADCD_v1.2:Inc&AD2.1/includes/exec/lists.i/MAIN 162}  Remove node from tail of list.                    |
 |_________________________________________________________________|

@ENDNODE
