|
From: Will D. <wy...@ca...> - 2013-03-22 21:54:24
|
Hi,
In our application, we use bit fields a lot. For example:
class data {
unsigned int the_thread_1_data:1;
unsigned int the_thread_2_data:3;
...
};
When one thread is writing to the_thread_1_data, and another thread is reading or writing to the_thread_2_data, helgrind will flag racing condition. Is this a known issue in helgrind? Is there a way to get around this? I believe the operation is multithread safe.
Thanks,
--Will
|
|
From: John R. <jr...@bi...> - 2013-03-22 22:31:44
|
> In our application, we use bit fields a lot. For example:
>
>
>
> class data {
>
> unsigned int the_thread_1_data:1;
>
> unsigned int the_thread_2_data:3;
>
> …
>
> };
>
>
>
> When one thread is writing to the_thread_1_data, and another thread is reading or writing to the_thread_2_data, helgrind will flag racing condition. Is this a known issue in helgrind? Is there a way to get around this? I believe the operation is multithread safe.
>From the viewpoint of portability, bitfields are NEVER threadsafe!
This is particularly true on a RISC machine. The only way that one could
even imagine portable thread safety of a bitfield is for every update
to be performed via an atomic compare-and-swap instruction which covers
the enclosing addressable object ["unsigned int" above]. No compiler
is obligated to emit such code for Plain Old Data.
On a CISC machine such as x86, a one-bit field can be threadsafe
if the instructions are always "LOCK OR" or "LOCK AND". (or LOCK AND NOT,
which is not present on x86 until AVX2.) In particular, an assignment
from something which is not a literal constant cannot be threadsafe unless
the source is examined first for being 1 or 0, and then LOCK OR or LOCK AND
is used for the appropriate individual case.
A wider-than-one-bit field can be threadsafe similarly only if the values
are restricted to 0b0...0 or 0b1...1.
--
|
|
From: Will D. <wy...@ca...> - 2013-03-22 22:35:32
|
Even if two threads are writing to the same word, as long as thread 1 doesn't modify thread_2_data, it should be ok, right?
From: David Chapman [mailto:dcc...@ac...]
Sent: Friday, March 22, 2013 3:31 PM
To: Will Deng
Cc: val...@li...
Subject: Re: [Valgrind-users] Helgrind doesn't handle bit field correctly
On 3/22/2013 2:54 PM, Will Deng wrote:
In our application, we use bit fields a lot. For example:
class data {
unsigned int the_thread_1_data:1;
unsigned int the_thread_2_data:3;
...
};
When one thread is writing to the_thread_1_data, and another thread is reading or writing to the_thread_2_data, helgrind will flag racing condition. Is this a known issue in helgrind? Is there a way to get around this? I believe the operation is multithread safe.
Not safe. Bit fields are generally packed within the same word in memory, so you now have two threads trying to write the same word without a mutex.
Try an experiment: remove the field widths, so that each integer will be in a separate memory word. Helgrind should no longer report an error.
--
David Chapman dcc...@ac...<mailto:dcc...@ac...>
Chapman Consulting -- San Jose, CA
Software Development Done Right.
www.chapman-consulting-sj.com<http://www.chapman-consulting-sj.com>
|
|
From: David C. <dcc...@ac...> - 2013-03-22 22:46:26
|
On 3/22/2013 3:35 PM, Will Deng wrote:
>
> Even if two threads are writing to the same word, as long as thread 1
> doesn't modify thread_2_data, it should be ok, right?
>
Nope: the usual way for a processor to do this is to read the entire
word, modify the appropriate bits, and then write the entire word. This
is very definitely a race in your situation. Thread 1 would write the
old value of thread_2_data with the new value of thread_1_data, or vice
versa.
--
David Chapman dcc...@ac...
Chapman Consulting -- San Jose, CA
Software Development Done Right.
www.chapman-consulting-sj.com
|
|
From: Will D. <wy...@ca...> - 2013-03-22 22:47:57
|
You are right. How about one thread writes, but the other thread only reads?
From: David Chapman [mailto:dcc...@ac...]
Sent: Friday, March 22, 2013 3:46 PM
To: Will Deng
Cc: val...@li...
Subject: Re: [Valgrind-users] Helgrind doesn't handle bit field correctly
On 3/22/2013 3:35 PM, Will Deng wrote:
Even if two threads are writing to the same word, as long as thread 1 doesn't modify thread_2_data, it should be ok, right?
Nope: the usual way for a processor to do this is to read the entire word, modify the appropriate bits, and then write the entire word. This is very definitely a race in your situation. Thread 1 would write the old value of thread_2_data with the new value of thread_1_data, or vice versa.
--
David Chapman dcc...@ac...<mailto:dcc...@ac...>
Chapman Consulting -- San Jose, CA
Software Development Done Right.
www.chapman-consulting-sj.com<http://www.chapman-consulting-sj.com>
|
|
From: John R. <jr...@bi...> - 2013-03-22 23:07:38
|
> How about one thread writes, but the other thread only reads? Exactly one particular thread, whose identity never changes, can be a writer. Any other thread must never write to any portion of the enclosing addressable object. -- |
|
From: David C. <dcc...@ac...> - 2013-03-22 23:07:48
|
On 3/22/2013 2:54 PM, Will Deng wrote:
>
> In our application, we use bit fields a lot. For example:
>
> class data {
>
> unsigned int the_thread_1_data:1;
>
> unsigned int the_thread_2_data:3;
>
> ...
>
> };
>
> When one thread is writing to the_thread_1_data, and another thread is
> reading or writing to the_thread_2_data, helgrind will flag racing
> condition. Is this a known issue in helgrind? Is there a way to get
> around this? I believe the operation is multithread safe.
>
Not safe. Bit fields are generally packed within the same word in
memory, so you now have two threads trying to write the same word
without a mutex.
Try an experiment: remove the field widths, so that each integer will
be in a separate memory word. Helgrind should no longer report an error.
--
David Chapman dcc...@ac...
Chapman Consulting -- San Jose, CA
Software Development Done Right.
www.chapman-consulting-sj.com
|
|
From: David F. <fa...@kd...> - 2013-03-22 23:20:19
|
On Friday 22 March 2013 14:54:09 Will Deng wrote:
> Hi,
>
> In our application, we use bit fields a lot. For example:
>
> class data {
> unsigned int the_thread_1_data:1;
> unsigned int the_thread_2_data:3;
> ...
> };
>
> When one thread is writing to the_thread_1_data, and another thread is
> reading or writing to the_thread_2_data, helgrind will flag racing
> condition. Is this a known issue in helgrind? Is there a way to get around
> this? I believe the operation is multithread safe.
The C++11 standard says this is only safe if you insert
unsigned int separator:0;
between the two lines, in order to make these separate memory locations.
I have no idea how compilers are supposed to implement this though.
Maybe like gcc's __sync_fetch_and_{or|and} which is an atomic operation...
Anyway -- this requires a (compliant) C++11-enabled compiler.
--
David Faure, fa...@kd..., http://www.davidfaure.fr
Working on KDE, in particular KDE Frameworks 5
|
|
From: David C. <dcc...@ac...> - 2013-03-23 06:40:48
|
On 3/22/2013 4:52 PM, you wrote:
>
> When threads are writing to bit fields, there are mutex locks. It's an
> existing condition and it worked well from many years. So I wonder if
> there is a way to get around the "false" errors from Helgrind? Thanks.
>
Without seeing the precise code that is creating the locks and accessing
the variables, it is hard to be certain that it is in fact safe. Can
you reduce it to a minimal sample? Are you using only POSIX pthread
primitives, or properly describing the lock primitives you are using?
If you can create a small sample with no proprietary code that exhibits
the problems you are seeing, someone with more experience using Helgrind
(or one of the tool's authors) can comment on it.
One thing that may be causing problems is that Valgrind and its analysis
tools (such as Helgrind) are working with assembly language, not a
high-level language such as C. Thus it does not know the intent of your
code - that there are two independent sets of bit fields in the record,
and that threads will never (or at least should never) modify the
"wrong" bit fields. It only sees that a data word was written by one
thread, but another thread still has an old value. The question then
becomes "why does Helgrind think your code has an old value somewhere?"
--
David Chapman dcc...@ac...
Chapman Consulting -- San Jose, CA
Software Development Done Right.
www.chapman-consulting-sj.com
|
|
From: Will D. <wy...@ca...> - 2013-03-25 23:08:35
Attachments:
helgrind26240.log
|
#include <stdio.h>
#include <pthread.h>
class Data {
unsigned int the_red:16;
unsigned int the_black:16;
static pthread_mutex_t the_mutex;
static void lock() {
pthread_mutex_lock(&the_mutex);
}
static void unlock() {
pthread_mutex_unlock(&the_mutex);
}
public:
int red() {
return the_red;
}
void red(int r) {
the_red = r;
}
int black() {
return the_black;
}
void black(int b) {
the_black = b;
}
Data() {
the_red = 0;
the_black = 0;
}
static void *thread_worker(void *);
static void init() {
pthread_mutex_init(&the_mutex, 0);
}
static void cleanup() {
pthread_mutex_destroy(&the_mutex);
}
};
pthread_mutex_t Data::the_mutex;
// only read red, and write black
void *
Data::thread_worker(void *in_data)
{
const int max = 10000;
Data *data = (Data *)in_data;
int sum = 0;
int red = data->red();
for (int i = 0; i < max; i++) {
lock();
if (red % 2) {
data->black(data->black() + 1);
} else {
data->black(data->black() + 2);
}
unlock();
red = data->red() + i;
}
lock();
printf("RED = %d BLACK = %d\n", data->red(), data->black());
unlock();
return 0;
}
int main()
{
Data::init();
const int num_thread = 4;
pthread_t thread[num_thread];
pthread_attr_t attr[num_thread];;
Data data;
for (int i = 0; i < num_thread; i++) {
pthread_attr_init(attr + i);
pthread_create(thread + i, attr + i, Data::thread_worker, &data);
}
for (int i = 0; i < num_thread; i++) {
pthread_join(thread[i], 0);
pthread_attr_destroy(attr + i);
}
Data::cleanup();
return 0;
}
|