Saturday, March 31, 2012

Android's C++ reference counting

#include <stdio.h>
#include "android/os/Ref.h"

using namespace android::os;

class RefTest : public Ref {
public:
    RefTest(int32_t id) : mID(id) {
        printf("RefTest ctor: %d\n", mID);
    }

    virtual ~RefTest() {
        printf("RefTest dtor: %d\n", mID);
    }

    int32_t id() const {
        return mID;
    }

private:
    int32_t mID;
};

int main() {
    sp<RefTest> ref1 = new RefTest(1);

    {
    sp<RefTest> ref2 = new RefTest(2);
    }

    wp<RefTest> ref3 = ref1;
    sp<RefTest> ref4 = ref3.toStrongRef();
    if (ref4 == NULL) {
        printf("RefTest object is destroyed\n");
    } else {
        printf("RefTest object %d is still around\n",
            ref4->id());
    }
    ref4 = NULL;

    ref1 = NULL;
    ref4 = ref3.toStrongRef();
    if (ref4 == NULL) {
        printf("RefTest object is destroyed\n");
    } else {
        printf("RefTest object %d is still around\n",
            ref4->id());
    }

    return 0;
}

To use the reference counting mechanism for (non Android) C++ projects check out the Ref class of my native Android messaging and concurrency framework. It contains Google's C++ reference counting class from the Android project along with some fixes and refactorings for code readability.

To get started you have to inherit (virtual) public from the Ref base class so that each object of a particular class that derives from Ref contains reference counters and the necessary management methods to increment and decrement them.
Afterwards you are able to create instances of types like RefTest as in the example above and assign them to strong (smart) pointers of type sp<RefTest>. There also is a wp<> template class for weak pointers when you have to get around circles of strong references.

Each Ref object has a WeakRefImpl object that keeps two reference counters, one for weak references and one for strong references. Whenever the strong reference counter is incremented or decremented then the weak reference counter also gets incremented or decremented. But when you create a wp<> to some object only the object's weak reference counter is incremented. Therefore, managed objects live as long as there is some strong pointer referring to it. However, the managed object's WeakRefImpl object may live longer than the object itself because it lives as long as there is some weak pointer referring to the object. If that is the case and the object itself is already destroyed the weak pointer's toStrongRef method returns NULL to indicate that the object is not available anymore.

The Ref base class is thread-safe without needing expensive locks. It completely synchronizes the access to the reference counters using atomic operations. The sp<> and wp<> template classes are not thread-safe. So always make sure you copy the strong and weak pointers when you share managed objects with different threads.

The C++0x shared_ptr class is similar to Android's C++ reference counting class but there are also some differences. E.g. while C++0x keeps the reference counter outside of the managed object in the shared_ptr class Android's reference counting mechanism keeps the counter in the object itself.