...
In this noncompliant code example, two executing threads simultaneously access two separate members of a global struct
.
Code Block | ||||
---|---|---|---|---|
| ||||
struct multi_threaded_flags { unsigned int flag1 : 2; unsigned int flag2 : 2; }; struct multi_threaded_flags flags; void thread1(void) { flags.flag1 = 1; } void thread2(void) { flags.flag2 = 2; } |
...
This compliant solution protects all accesses of the flags with a mutex, thereby preventing any thread scheduling interleaving from occurring. In addition, the flags are declared volatile
to ensure that the compiler will not attempt to move operations on them outside the mutex. Finally, the flags are embedded in a union alongside a long
, and a static assertion guarantees that the flags do not occupy more space than the long
. This prevents any data not checked by the mutex from being accessed or modified with the bit fields.
Code Block | ||||
---|---|---|---|---|
| ||||
struct multi_threaded_flags { volatile unsigned int flag1 : 2; volatile unsigned int flag2 : 2; }; union mtf_protect { struct multi_threaded_flags s; long padding; }; static_assert(sizeof(long) >= sizeof(struct multi_threaded_flags)); struct mtf_mutex { union mtf_protect u; pthread_mutex_t mutex; }; struct mtf_mutex flags; void thread1(void) { int result; if ((result = pthread_mutex_lock(&flags.mutex)) != 0) { /* Handle error */ } flags.u.s.flag1 = 1; if ((result = pthread_mutex_unlock(&flags.mutex)) != 0) { /* Handle error */ } } void thread2(void) { int result; if ((result = pthread_mutex_lock(&flags.mutex)) != 0) { /* Handle error */ } flags.u.s.flag2 = 2; if ((result = pthread_mutex_unlock(&flags.mutex)) != 0) { /* Handle error */ } } |
...