libnih started life as a sort of growth on the side of Upstart; not really a standalone project, not really part of Upstart itself. Recently its come into its own as a stable library, and its available in Fedora 12 and up.
Central to libnih is nih_alloc, which began life as a talloc-like API. Since then, it has grown one important distinction: multiple parentage.
#include <nih/alloc.h>
#include <nih/list.h>
NihList *queue_a;
NihList *queue_b;
int
send_message (char *msg_text,
int send_to_a,
int send_to_b)
{
nih_local char *message = nih_strdup (NULL, msg_text);
if (! message)
return -1;
if (send_to_a)
send_to_queue (queue_a, message);
if (send_to_b)
send_to_queue (queue_b, message);
return 0;
}
int
send_to_queue (NihList *queue, char *message)
{
NihListEntry *entry = nih_list_entry_new (queue);
if (! entry)
return -1;
entry->str = message;
nih_ref (message, entry);
nih_list_add (queue, entry);
return 0;
}
Let's look at the libnih calls one by one:
- nih_strdup duplicates the given string into an libnih-allocated object. The first argument specifies an initial parent object for the allocation. All objects must have parents, and when an object has no more parents it is freed.
NULLcan be a parent, as it is here. - nih_list_entry_new creates a new
NihListobject. The first argument, again, specifies a parent. - nih_ref adds a parent to a libnih-allocated object.
- nih_list_add is part of libnih's
NihListlinked list API, and does what it sounds like.
We skipped the
nih_local keyword, but I'll come back to it.In our example, the new message string we create is created with one parent,
NULL. Each time it gets passed to send_to_queue, it gets one additional parent, in the form of a new list entry in the appropriate queue. Assuming we disposed of the list entries as we processed the queue, the object would loose one parent each time a queue finished with it. Also, since the list entries have the queue they are in as their only parent, they would be freed if we freed an entire queue, and our message string would lose them as parents.But what about that
NULL parent? How does it go away allowing the object to be freed? That's where nih_local comes in. nih_local uses a glibc extension to specify a destructor for the variable it is used with. That destructor will call nih_discard on whatever is in message. nih_discard in turn is equivalent to nih_unref (.., NULL), i.e. it removes one NULL parent from an object. In other words the NULL parent goes away as soon as send_message returns. If we called with both of the send_to arguments as 0, then message will be freed immediately. Otherwise message will be freed when both queues have dispensed with it.In addition to other allocation bells and whistles, like destructors, there's a lot more to libnih besides an allocator. It has everything from a hash table to a dbus API wrapper. It's also available for experimentation in F12 right now. Go check it out.
2 comments:
Absolutely love the name!
I was just beginning to build upstart when my system complained for libnih. I googled and stumbled across your post, and would have to admit that your post was particularly helpful in fostering interest. How often have a desired such garbage collection in C :)
I would study more of its API next. Thanks again.
Post a Comment