Пока я читал реализацию ядра Linux для примитивов блокировки RCU, я дошел до следующего кода.
#define WRITE_ONCE(x, val) \
({ \
union { typeof(x) __val; char __c[1]; } __u = \
{ .__val = (__force typeof(x)) (val) }; \
__write_once_size(&(x), __u.__c, sizeof(x)); \
__u.__val; \
})
static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
switch (size) {
case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
default:
barrier();
__builtin_memcpy((void *)p, (const void *)res, size);
barrier();
}
}
Насколько я могу судить, объединение, заканчивающееся массивом char, имеющим размер предыдущей части объединения, позволяет получить доступ к памяти, назначенной объединению, в байтовой гранулярности.
Например,
#define UNION_PARTIAL \
int a; \
int b; \
char *c; \
union union_partial {
UNION_PARTIAL
};
union union_full {
UNION_PARTIAL
char bytes[sizeof(union union_partial)];
};
Однако, похоже, что объединение, используемое в макросе WRITE_ONCE, не объявлено, чтобы обеспечить точный гранулярный доступ к памяти объединения. Вместо этого можно использовать приведенный ниже код, но я не знаю, зачем нам нужен символ __c [1].
#define WRITE_ONCE(x, val) \
({ \
union { typeof(x) __val; char __c[1]; } __u = \
{ .__val = (__force typeof(x)) (val) }; \
__write_once_size(&(x), &(__u.val), sizeof(x)); \
__u.__val; \
})
только потому, что для программиста необходимо уменьшить нагрузку на & перед __u.val?