SPARC上读取的原子性

时间:2014-07-29 03:06:10

标签: multithreading assembly sparc

我正在编写多线程应用程序并在SPARC平台上遇到问题。最终,我的问题归结为这个平台的原子性以及我如何获得这个结果。

一些伪代码有助于澄清我的问题:

// Global variable
typdef struct pkd_struct{
    uint16_t a;
    uint16_t b;
} __attribute__(packed) pkd_struct_t;

pkd_struct_t shared;

Thread 1:
swap_value() {
  pkd_struct_t prev = shared;
  printf("%d%d\n", prev.a, prev.b);
  ...
}

Thread 2:
use_value() {
  pkd_struct_t next;

  next.a = 0; next.b = 0;
  shared = next;
  printf("%d%d\n", shared.a, shared.b);
  ...
}

线程1和2正在访问共享变量" shared"。一个是设置,另一个是设置。如果线程2正在设置"共享"为零,我希望线程1在设置之前或之后读取计数 - 因为"共享"在4字节边界上对齐。但是,我偶尔会看到线程1读取0xFFFFFF00形式的值。即高阶24位是OLD,但低阶字节是NEW。看来我已经获得了中间价值。

查看反汇编,use_value函数只执行" ST"指令。鉴于数据是对齐的并且没有跨越字边界,这种行为是否有任何解释?如果ST确实不是使用这种方式的原子,这是否解释了我看到的结果(只改变了1个字节?!?)? x86没有问题。

更新1: 我发现了问题,但没有找到原因。 GCC似乎正在生成组件,该组件逐字节地读取共享(因此允许获得部分更新)。评论补充说,但我对SPARC装配并不十分满意。 %i0是指向共享变量的指针。

xxx+0xc:   ldub      [%i0], %g1             // ld unsigned byte g1 = [i0] -- 0 padded
xxx+0x10:  ...       
xxx+0x14:  ldub      [%i0 + 0x1], %g5       // ld unsigned byte g5 = [i0+1] -- 0 padded
xxx+0x18:  sllx      %g1, 0x18, %g1         // g1 = [i0+0] left shifted by 24 
xxx+0x1c:  ldub      [%i0 + 0x2], %g4       // ld unsigned byte g4 = [i0+2] -- 0 padded
xxx+0x20:  sllx      %g5, 0x10, %g5         // g5 = [i0+1] left shifted by 16
xxx+0x24:  or        %g5, %g1, %g5          // g5 = g5 OR g1
xxx+0x28:  sllx      %g4, 0x8, %g4          // g4 = [i0+2] left shifted by 8
xxx+0x2c:  or        %g4, %g5, %g4          // g4 = g4 OR g5
xxx+0x30:  ldub      [%i0 + 0x3], %g1       // ld unsigned byte g1 = [i0+3] -- 0 padded
xxx+0x34:  or        %g1, %g4, %g1          // g1 = g4 OR g1
xxx+0x38:  ...       
xxx+0x3c:  st        %g1, [%fp + 0x7df]     // store g1 on the stack

知道为什么GCC会生成这样的代码吗?

更新2:向示例代码添加更多信息。应用 - 我正在处理新旧代码的混合,并且难以区分相关内容。此外,我知道分享这样的变量一般都是非常沮丧的。但是,这实际上是在一个锁实现中,高级代码将使用它来提供原子性,并且使用pthread或特定于平台的锁定不是一个选项。

2 个答案:

答案 0 :(得分:2)

因为您已将类型声明为packed,所以它会获得一个字节对齐,这意味着它必须一次读取和写入一个字节,因为SPARC不允许未对齐的加载/存储。如果希望编译器使用字加载/存储指令,则需要为其提供4字节对齐:

typdef struct pkd_struct {
    uint16_t a;
    uint16_t b;
} __attribute__((packed, aligned(4))) pkd_struct_t;

请注意,packed对于此结构基本上没有意义,因此您可以将其保留。

答案 1 :(得分:-1)

在这里回答我自己的问题 - 这已经让我烦恼了太长时间,希望我能在某些时候拯救某人一些挫折感。

问题是虽然共享数据是对齐的,因为它是打包的,GCC会逐字节地读取它。

关于如何在SPARC(以及我假设的其他RISC平台上)上导致加载/存储膨胀的包装有一些讨论here,但在我的情况下它导致了竞争。