I'm working with some third-party library code that contains the following race, shown simplified:
struct Object
{
void (*func)(void *);
void *data;
};
void called_from_thread1(struct Object *obj)
{
obj->func(obj->data);
}
void called_from_thread2(struct Object *obj, void (*func)(void *), void *data)
{
obj->func = func;
obj->data = data;
}
Access from both threads is unsynchronized, so in general I'd analyze this as:
- a data race on
func
- a data race on
data
- inconsistent access to the
func
/data
pair (access could occur while only one is changed)
In this specific use case, however, it happens that the values are always changed back to the default value. Something like this:
void default_func(void *data) { /* snip */ };
void *default_data = 0; // Actually something valid
struct Object object = {
.func = default_func,
.data = &default_data,
};
Both thread 1 and thread 2 are spawned referencing the object
, and thread 2 sets the same object back to defaults similar to this:
called_from_thread1(&object);
// elsewhere...
called_from_thread2(&object, default_func, &default_data);
This is technically still a race (and the thread sanitizer reports it), but what are the possible consequences?
In this case, the function and data don't get out of sync (they're never actually changed), so logical inconsistency shouldn't be an issue.
Can the pointers be temporarily invalid as they are copied? For example, maybe copying a function pointer on an 8-bit processor could take two instructions if the address space is 16 bits wide? In this case I'm working with a BeagleBoneBlack (ARM Cortex A8?), and I'm guessing that pointers are copied in a single instruction and should always be accurate. However, I don't yet know enough about the processor to be certain.
What am I missing?
Thanks!