Question

Is there a portable way in C to find out the mask for a bit field at compile time?

Ideally, I'd like to be able to atomically clear a field like this:

struct Reference {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
};

struct Reference myRef;
__sync_and_and_fetch(&myRef, age, ~AGE_MASK);

Otherwise I have to take out a lock on the struct, which is more heavyweight than I'd like.

Was it helpful?

Solution

Or, if you really wanted the mask:

union Reference {
  unsigned asWord;
  struct {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
  } asFields;
}

Reference agemask_ref;
agemask_ref.asFields = (typeof(agemask_ref.asFields)){0, -1, -1};
unsigned agemask = agemask_ref.asWord;

OTHER TIPS

You could do something like:

union Reference {
  unsigned asWord;
  struct {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
  } asFields;
}

To atomically clear a field of myRef, do

union Reference myRef;

union Reference oldr = myRef;
union Reference newr = oldr;
newr.asFields.age = 0;
compare_and_swap(&myRef.asWord, oldr.asWord, newr.asWord);

(+ unshown code to handle when compare_and_swap fails)

I don't know how to do it at compile time, but at runtime it should be a simple matter of union'ing an instance of your bitfield struct with an appropriately-sized unsigned int, and setting all fields to 0 except the one you care about which should be set to all 1s -- the value of the unsigned int is then the bitmask you want. You can do it for each field at startup, maybe with a macro to avoid some repetitive code. Might not that suffice?

I don't think is possible - even with offsetof() which works for byte offsets but does not seem to work for bitfields. I would redeclare the fields as enums/defines (0x01 0x02 etc) and manage the bits yourself, so you can get your atomic changes.

Yes, this can be done. You need to capture the value and do the operation. Then you need to use an atomic compare and swap (like InterlockedCompareExchange on Windows) to store the new value if the memory still contains the old value. If someone modified the value, you loop and try again. Note this is a standard pattern for doing any operation on a word-sized piece of data where an intrinsic isn't available.

The code below uses an int - as Keith pointed out you can use a union to be able to get the values of the struct as an int.

int oldValue, newValue;
do
{
    oldValue = myRef;
    newValue = oldValue & ~AGE_MASK;
} while (InterlockedCompareExchange(&myRef, newValue, oldValue) != oldValue);
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top