13,900,817 members
Technical Blog
alternative version

#### Stats

841 views
Posted 14 Mar 2019
Licenced MIT

# Bit Fields and Race Conditions

, 14 Mar 2019
Bit fields and race conditions

The following program, despite looking correct, is a race condition and has unpredictable behaviour:

```#include <iostream>
#include <mutex>
using namespace std;

const int COUNT = 10'000'000;

mutex aLock;
mutex bLock;

struct S
{
unsigned int a:9;
unsigned int b:7;
} __attribute__((packed));

int main(int argc, char** argv)
{
S s{};

scoped_lock lock(aLock);
for(int i = 0; i < COUNT; ++i)
{
s.a = 0;
s.a = 0b111111111;
}
});

scoped_lock lock(bLock);
for(int i = 0; i < COUNT; ++i)
{
s.b = 0;
s.b = 0b1111111;
}
});

t1.join();
t2.join();

cout << "sizeof(S) = " << sizeof(S) << ", " << s.a << ", " << s.b << endl;

return 1;
}```

The proof is in the output of running this code several times:

```sizeof(S) = 2, 511, 127
sizeof(S) = 2, 511, 127
sizeof(S) = 2, 0, 127
sizeof(S) = 2, 511, 0
sizeof(S) = 2, 511, 127
sizeof(S) = 2, 511, 127
sizeof(S) = 2, 511, 127
sizeof(S) = 2, 0, 127
sizeof(S) = 2, 0, 127
sizeof(S) = 2, 511, 127```

The race condition and the problem here is that the C++ standard states that adjacent bit fields are a single object in memory and may be packed to share the same byte. The CPU cannot operate on single bits, it must load at least 1 byte at a time, and because of the overlap, loading bits of `a` also loads the bits of `b`. So the write to bit field `a`, even though protected with its own mutex, also overwrites the value of `b`. And vice versa.

The fix is to use one mutex to protect all adjacent bit fields. I say all because you have no guarantee that the CPU will be able to load 1 byte at a time. It may be limited to working on 32-bit values at a time; depending on the architecture.

### Corrected Program

```#include <iostream>
#include <mutex>
using namespace std;

const int COUNT = 10'000'000;

mutex abLock;

struct S
{
unsigned int a:9;
unsigned int b:7;
} __attribute__((packed));

int main(int argc, char** argv)
{
S s{};

scoped_lock lock(abLock);
for(int i = 0; i < COUNT; ++i)
{
s.a = 0;
s.a = 0b111111111;
}
});

scoped_lock lock(abLock);
for(int i = 0; i < COUNT; ++i)
{
s.b = 0;
s.b = 0b1111111;
}
});

t1.join();
t2.join();

cout << "sizeof(S) = " << sizeof(S) << ", " << s.a << ", " << s.b << endl;

return 1;
}```

## Share

 Software Developer (Senior) United States
No Biography provided

## You may also be interested in...

 First Prev Next
 Please add more explanation and commentary Mike Diack15-Mar-19 3:12 Mike Diack 15-Mar-19 3:12
 formatting Stefan_Lang15-Mar-19 1:05 Stefan_Lang 15-Mar-19 1:05
 Last Visit: 24-Mar-19 1:36     Last Update: 24-Mar-19 1:36 Refresh 1