是否有#define来确定读取对齐的64位值是否为原子?

时间:2015-12-03 15:49:55

标签: postgresql posix c89

在C代码中,我在共享内存中有一个64位值,由连接到该共享内存的不同进程访问。目前读取和写入由自旋锁覆盖,但是在编译中,保证对齐的64位读取不会被“撕裂”(即,读取是原子的)我可以省略自旋锁覆盖,因为“略微陈旧”的版本价值很好。代码需要能够在各种硬件(包括32位和64位计算机; Intel,AMD,Sparc Solaris,IBM Power7和Power8以及许多其他计算机),操作系统环境(包括Linux,Windows和HP-UX)和编译器(gcc和clang,其中许多 - C89或更高版本);虽然如果我“喋喋不休”将螺旋锁保持在无法确定省略它们的安全性的环境中,那就没关系了。当然,更安全的平台,我可以确定它是安全的,更好。

确定这种对齐的64位读取是否保证来自单个64位写入的最安全,最便携的方法是什么?

根据目前为止的评论,最好显示一些相关代码。

在我的机器上,src/include/pg_config.h

/* src/include/pg_config.h.  Generated from pg_config.h.in by configure.  */
/* src/include/pg_config.h.in.  Generated from configure.in by autoheader.  */

[...]

/* Define to 1 if `long int' works and is 64 bits. */
#define HAVE_LONG_INT_64 1

许多这样的编译时常量是根据./configure运行时的内容定义的。

src/include/c.h

/*
 * 64-bit integers
 */
#ifdef HAVE_LONG_INT_64
/* Plain "long int" fits, use it */

#ifndef HAVE_INT64
typedef long int int64;
#endif
#ifndef HAVE_UINT64
typedef unsigned long int uint64;
#endif
#elif defined(HAVE_LONG_LONG_INT_64)
/* We have working support for "long long int", use that */

#ifndef HAVE_INT64
typedef long long int int64;
#endif
#ifndef HAVE_UINT64
typedef unsigned long long int uint64;
#endif
#else
/* neither HAVE_LONG_INT_64 nor HAVE_LONG_LONG_INT_64 */
#error must have a working 64-bit integer datatype
#endif

我正在努力改进的代码如下。 oldSnapshotControl是指向共享内存中结构的指针,threshold_timestamp是该结构中的一个字段,定义为int64。请注意函数注释中的XXX块。

/*
 * Get timestamp through which vacuum may have processed based on last stored
 * value for threshold_timestamp.
 *
 * XXX: If we can trust a read of an int64 value to be atomic, we can skip the
 * spinlock here.
 */
int64
GetOldSnapshotThresholdTimestamp(void)
{
    int64       threshold_timestamp;

    SpinLockAcquire(&oldSnapshotControl->mutex_threshold);
    threshold_timestamp = oldSnapshotControl->threshold_timestamp;
    SpinLockRelease(&oldSnapshotControl->mutex_threshold);

    return threshold_timestamp;
}

2 个答案:

答案 0 :(得分:0)

在C11之前,没有任何数据类型的读取,但可能sig_atomic_t被认为是原子的(在你解决它的意义上),它根本不是语言的一部分,而且该版本的C可以从来不保证。并且有很多平台,实际上它不会是原子的。

从C11开始,你就拥有语言中包含的原子(广义上),更新版本的gcc和clang支持该功能。如果您想要可移植代码,这将是未来的证明。

如果您对可移植性较差感到满意,可以使用以__sync_开头的gcc内置扩展,它基本上对基本整数类型执行原子操作。在这里,您将__GCC_HAVE_SYNC_COMPARE_AND_SWAP_XX的宏XX替换为字节数的宽度,以告诉您是否支持此类操作。

这些gcc扩展已存在很长时间了,clang也有它们。

答案 1 :(得分:0)

您可以使用sig_atomic_t

  

是对象的(可能是volatile限定的)整数类型   可以作为原子实体访问,即使存在   异步中断。

从C89开始发现。在<stdint.h>标头中,定义了以下宏

SIG_ATOMIC_MIN
SIG_ATOMIC_MAX

但是,仅来自C99(您的编译器可能具有类似的实现定义的宏)。然后只需#if SIG_ATOMIC_MAX == 64BIT_MAX;通过使用实现定义的宏64BIT_MAX或两者,可以使用limits.h - s和#if - s来找到#define

虽然您保证sig_atomic_t将是原子的,但它可能小于64位:例如,我的64位平台sig_atomic_t仍为int

最简单的解决方案可能是依赖外部工具(如cmake,autotools等),并让他们为您找到答案。