3

Having populated a POSIX binary tree with tsearch, how is it possible to clean-up the entire tree? GCC provides tdestroy as an extension, but if you want to use POSIX-only functions how can you do it?

My current implementation uses twalk to walk the tree and, for endorder and leaf nodes, calls tdelete, but this understandably shows warnings about const-correctness:

static void free_tree(const void *node, const VISIT which, const int depth)
{
        struct search_entry *entry;
        switch (which) {
                case endorder:
                case leaf:
                        entry = *(struct search_entry **)node;
                        tdelete(entry->key, &node, search_entry_compare);
                        free(entry);
        }
}

What was the expected approach for POSIX-compliant applications?

Marcus Harrison
  • 819
  • 6
  • 19

2 Answers2

6

The POSIX description of the tsearch() family of functions has an informative Examples section which shows how the standard thinks you can delete all the elements of a tree (as part of a bigger, complete example of how to use the functions):

/* Delete all nodes in the tree */
while (root != NULL) {
    elementptr = *(struct element **)root;
    printf("deleting node: string = %s,  count = %d\n",
           elementptr->string,
           elementptr->count);
    tdelete((void *)elementptr, &root, delete_root);
    free(elementptr);
}

Basically, it repeatedly deletes the root node with tdelete() until there is no longer a root node to delete. The delete_root() function is also shown — it is a no-op that returns 0 for success.

We can debate the merits (or lack of them) for the cast in the call to tdelete().

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 3
    Too bad the Linux programmers manual doesn't contain something like this; all it has as of 4.16 is how to expose and use `tdestroy`. – S.S. Anne Aug 31 '19 at 20:25
  • Yeah, the `3posix` man page for `tdestroy` omits this, would be very useful. Thanks for the answer! – Marcus Harrison Sep 01 '19 at 04:18
1

The proposed solution of calling tdelete() from twalk()'s action conflicts with the POSIX requirement on the application that action and compar not change the tree. In practice, a use after free or incomplete cleanup is likely.

The best I can think of is either not using tsearch/tfind/tdelete/twalk or allocating new memory to store twalk() results and using tdelete() afterwards.

Not using tsearch/tfind/tdelete/twalk may also allow data structures such as radix tries and hash tables that tend to be more efficient on modern architectures than binary trees.

jilles
  • 10,509
  • 2
  • 26
  • 39
  • I would not avoid tsearch because "radix tries and hash tables [...] tend to be more efficient on modern architectures" unless you actually have a performance problem you're trying to solve and you've measured that it makes a difference. The tsearch API is really easy to use, optimal big-O, not dependent on type of keys (whereas radix is), and tends to make code very light and simple. – R.. GitHub STOP HELPING ICE Aug 31 '19 at 21:57