Libasync tutorial

Thomer M. Gil, 2002

Lesson 4: Gory details: strbuf, ptr, and ref

Prev Index Next

OK. So now that I whetted your appetite, there are some things you should know about libasync. Quoting from Using libasync:

One complication of programming with callbacks is dealing with freeing dynamically allocated memory. This is particularly true of strings. If a function takes an argument of type char * and does something asynchronously using the string, the caller may not be allowed to free the string until later. Keeping track of who is responsible for what strings and for how long is tricky and error-prone.

...

Strings are not the only data structure for which deallocation becomes tricky in asynchronous programming. Libasync therefore provides a generic mechanism for reference-counted deallocation of arbitrary dynamically allocated data structures. Refcounting is a simple form of automatic garbage collection: each time a refcounted object is referenced or goes out of scope its reference count is incremented or decremented, respectively. If the reference count of an object ever reaches zero, the object's destructor is called and it is deleted. Note that unlike real garbage collection in languages like Java, you cannot use reference-counted deallocation on data structures with pointer cycles.

Libasync provides you with strbuf objects, automatic reference counting and garbage collection. We'll treat these one by one.

Using strbuf

Strbuf allows you to build up a string by appending to it using the << operator.

examples/strbuf.C

 1:   #include "async.h"
 2:   
 3:   int
 4:   main(int argc, char *argv[])
 5:   {
 6:     async_init();
 7:     strbuf foo;
 8:     foo << "Hello, ";
 9:     foo << "World!\n";
10:     warn << foo;
11:   }

This example speaks for itself. One situation where you'll find strbuf most useful is when iteratively reading chunks of data from a file descriptor.

Using New

Libasync provides C++ support for debugging malloc. You should use the New operator where you would otherwise use new. New is a wrapper around new that includes line information, making it easy to debug memory leaks, etc. (Freely quoted from Using libasync.)

examples/new.C

 1:   #include "async.h"
 2:   
 3:   typedef struct {
 4:     int r;
 5:     int t;
 6:     int m;
 7:   } rtm;
 8:   
 9:   int
10:   main(int argc, char *argv[])
11:   {
12:     async_init();
13:     rtm *foo = New rtm();
14:     foo->r = 1;
15:     foo->t = 2;
16:     foo->m = 3;
17:   
18:     delete foo;
19:   }

Using ref

You can create reference counted object with the refcounted template. Refcounted can turn any ordinary C++ type into a reference counted type. It's pretty cool. Here's an example that allocates a new (reference counted) foo object, and passes it to a function using a delaycb.

examples/ref.C

 1:   #include "async.h"
 2:   
 3:   typedef struct {
 4:     int x, y, z;
 5:   } foo;
 6:   
 7:   
 8:   void
 9:   printfoo(ref<foo> p)
10:   {
11:     warn << "x : " << p->x << "\n";
12:     warn << "y : " << p->y << "\n";
13:     warn << "z : " << p->z << "\n";
14:     exit(0);
15:   }
16:   
17:   
18:   int
19:   main(int argc, char *argv[])
20:   {
21:     async_init();
22:     // ordinary C++ type
23:     ref<int> tmg = New refcounted<int>;
24:     *tmg = 3;
25:   
26:     // fancy type
27:     ref<foo> bar = New refcounted<foo>;
28:     bar->x = 1;
29:     bar->y = 2;
30:     bar->z = 3;
31:   
32:     delaycb(1, 0, wrap(printfoo, bar));
33:   
34:     amain();
35:   }

Notice that you can use a ref<foo> object, e.g. bar, as if it was a plain old C pointer. There's one thing you can never do, though: you cannot set a ref<...> object to zero. That's where ptr<...> enters the picture.

Using ptr

The only difference between ref and ptr is that ptr's can be zero, and ref's cannot. Here's a dead simple example that core dumps.

examples/ptr.C

 1:   #include "async.h"
 2:   
 3:   typedef struct {
 4:     int x, y, z;
 5:   } foo;
 6:   
 7:   
 8:   int
 9:   main(int argc, char *argv[])
10:   {
11:     async_init();
12:     ref<foo> bar = New refcounted<foo>;
13:     ptr<foo> rab = New refcounted<foo>;
14:   
15:     rab = 0; // fine!
16:   
17:     // wouldn't compile!
18:     // bar = 0;
19:     
20:     // core dumps
21:     bar = rab;
22:   }

You should be using ref's, unless you have a good excuse to use ptr's.

Please refer to Using libasync for the full story on reference counted objects.

Prev Index Next

Back to main.