Frage

I'm writing a library where a connecting library in another language only understands C. I need something similar to std::shared_ptr where ownership is shared. In my case manual reference counting would be ok.

C11 supports atomic operations. I've been trying to find an example of how to do this properly, but every example I've been able to find has to do with C++11, which has operator overloading.

Essentially what I'm looking to do is something like this:

typedef struct {
    union {
        int integer;
        // ...
        char* cstring;
        void* ptr;
    };
    enum {
        Undefined,
        Integer,
        String,
        // ...
    } type;
    int* refcount;
} value;

void value_retain(value v) {
    ++(*v.refcount);
}

void value_release(value v) {
    if(--(*v.refcount) == 0) {
        // free memory, depending on type...
    }
}

I'm assuming that I would need to change int* to atomic_int*. The function atomic_fetch_sub says that it returns "The value held previously be the atomic object pointed to by obj.". This leads me to believe my functions would look like this:

void value_retain(value v) {
    atomic_fetch_add(v.refcount, 1);
}

void value_release(value v) {
    if(atomic_fetch_sub(v.refcount, 1) == 1) {
        // free memory, depending on type...
    }
}

Is this even correct? My concern is that atomic_fetch_sub returns what the value was, not what the value is.

Also what does memory_order mean, and what should I be using for reference counting? Does it matter?

War es hilfreich?

Lösung

I think your design is correct except for the type of int* refcount; and the incorrect use of pointers. Right now you have nothing for it to point at, so you'll be invoking undefined behavior by passing the (indeterminate) pointer value to the atomic operations. I don't understand why you're using a pointer at all. The type should be:

int refcount;

and the code should be:

void value_retain(value v) {
    atomic_fetch_add(&v.refcount, 1);
}

void value_release(value v) {
    if(atomic_fetch_sub(&v.refcount, 1) == 1) {
        // free memory, depending on type...
    }
}

As for the order/barrier semantics, retain should be an acquire operation and release should be a release operation, but using full barriers for both is also ok.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top