Postgresql 是否有一个#定义来确定读取对齐的64位值是否是原子的?

Postgresql 是否有一个#定义来确定读取对齐的64位值是否是原子的?,postgresql,posix,c89,Postgresql,Posix,C89,在C代码中,共享内存中有一个64位的值,由连接到该共享内存的不同进程访问。目前,自旋锁覆盖了读写操作,但在保证对齐的64位读取不会被“撕裂”(即,读取是原子的)的编译中,我可以忽略自旋锁覆盖,因为值的“稍微过时”版本是可以的。代码需要能够在多种硬件(包括32位和64位计算机;Intel、AMD、Sparc Solaris、IBM Power7和Power8以及许多其他硬件)、操作系统环境(包括Linux、Windows和HP-UX)和编译器(gcc和clang,其中许多是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。由configure从pg_config.h.in生成*/
/*src/include/pg_config.h.in。由autoheader从configure.in生成*/
[...]
/*如果“long int”有效且为64位,则定义为1*/
#定义HAVE_LONG_INT_64 1
许多这样的编译时常量是根据运行
/configure
时发现的内容定义的

src/include/c.h
中:

/*
*64位整数
*/
#如果定义有长整型64
/*普通“长整型”适合,使用它*/
#如果NDEF有INT64
typedef long int int64;
#恩迪夫
#如果NDEF有_UINT64
typedef无符号长整数uint64;
#恩迪夫
#已定义的elif(具有长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度长度64)
/*我们对“long-long-int”有工作支持,请使用*/
#如果NDEF有INT64
typedef long long int int64;
#恩迪夫
#如果NDEF有_UINT64
typedef无符号长整数uint64;
#恩迪夫
#否则
/*既没有长音也没有长音*/
#错误必须具有有效的64位整数数据类型
#恩迪夫
下面是我试图改进的代码
oldSnapshotControl
是指向共享内存中结构的指针,
threshold\u timestamp
是该结构中的一个字段,定义为
int64
。注意函数注释中的
XXX

/*
*根据上次存储的数据获取真空处理的时间戳
*阈值\u时间戳的值。
*
*XXX:如果我们可以相信对int64值的读取是原子的,那么我们可以跳过
*这里是旋转锁。
*/
int64
GetOldSnapshotThresholdTimestamp(无效)
{
int64阈值\u时间戳;
SpinLockAcquire(&oldSnapshotControl->mutex\u阈值);
threshold\u timestamp=oldSnapshotControl->threshold\u timestamp;
SpinLockRelease(&oldSnapshotControl->mutex\u阈值);
返回阈值时间戳;
}

在C11之前,没有任何数据类型的读取,但可能
sig_atomic_t
被保证是原子的(从您所述的意义上讲),它根本不是语言的一部分,而C作为该版本的语言永远无法保证这一点。事实上,周围有很多平台都不是原子平台

从C11开始,语言中就包含了原子(从更广泛的意义上讲),新版本的gcc和clang支持该特性。这是一种方法,如果你想要可移植的代码,那将是未来的证明

如果您对较少的可移植性感到满意,那么可以使用gcc的内置扩展,从基本上对基本整数类型执行原子操作的
\uuuu sync.
开始。这里有宏
\uuuu GCC\u have\u SYNC\u COMPARE\u和\u SWAP\u XX
,其中的
XX
替换为以字节为单位的宽度,这些宏告诉您是否支持这种操作


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

您可以使用
sig\u atomic\t

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

从C89开始发现。在
标题中,定义了以下宏

SIG_ATOMIC_MIN
SIG_ATOMIC_MAX
但是,只能从C99开始(您的编译器可能有类似的实现定义的宏)。然后简单地
#如果SIG_原子_MAX==64位_MAX
<通过使用实现定义的宏、
limits.h
或两者,以及两个
#if
-s和
#define
-s,可以依次找到code>64位的MAX

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


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

搜索
stdatomic.h
。最好直接使用
\u-Atomic
。stdatomic.h和_-Atomic在gcc中似乎不存在,因此这不是很好的移植性。此外,对
stdatomic.h
的支持是可选的,即使在C11中也是如此。@Olaf,没有人提到64位编译器,只有64位值。您可以在这样的系统上进行编译时的肯定测试。您需要执行预处理器检查,例如
#如果已定义(u STDC_VERSION_uuuuuuuuuuuuuuuuuuuuuuuuversion_uuuuuuuuu>=201112L)&&!已定义(\uu STDC\u NO\u原子学)
。但是,
\u原子的
-限定的整数类型不能保证与非限定的整数类型具有相同的大小、对齐方式或表示形式;但在C89上编译代码时必须没有警告或错误。我们根据硬件、操作系统定义了许多不同的常数。或者编译器。我对介绍原子CA等不感兴趣——我已经做过了