53.4. 其他编码习惯

C 标准

PostgreSQL中的代码应该只依赖于 C89 标准中的语言特性。这意味着遵循 C89 的编译器肯定能编译 postgres,至少除开少数平台依赖问题之外。 如果提供了回退机制,也可以使用来自后续 C 标准版本的特性或者编译器相关的特性。

例如虽然static inline_StaticAssert()来自于新版的 C 标准,但是PostgreSQL中仍然用到了它们。如果它们不可用,我们分别会回退到定义没有内联的函数以及使用兼容 C89 的替代品来执行相同的检查(但是会发出晦涩的消息)。

类函数的宏以及内联函数

带有参数的宏以及static inline函数都可能被使用。当类似如下情况写作宏时会有多次计算风险或者宏可能非常长时,使用后者会更好。

#define Max(x, y)       ((x) > (y) ? (x) : (y))

在其他情况下只能使用宏,或者说使用宏至少更容易。例如,由于多种类型的表达式需要被传递给宏。

当一个内联函数的定义引用只在后端中可用的符号(即变量、函数)时,从前端代码引用该函数时该函数可能不可见。

#ifndef FRONTEND
static inline MemoryContext
MemoryContextSwitchTo(MemoryContext context)
{
    MemoryContext old = CurrentMemoryContext;

    CurrentMemoryContext = context;
    return old;
}
#endif   /* FRONTEND */

在这个例子中,CurrentMemoryContext只在后端中可用,但该函数引用了它并且该函数因此被#ifndef FRONTEND隐藏。之所以存在这条规则,是因为即使内联函数中包含的符号没有被使用,有些编译器也会发出对它们的引用。

编写信号处理器

为了能适合在信号处理器中运行,代码必须被非常仔细地编写。根本问题是,除非被阻塞,信号处理器能在任何时候打断代码。如果信号处理器内部的代码使用和外面代码相同的状态,很可能会出现混乱。例如,可以想想如果一个信号处理器试图取得已经在被打断代码中持有的锁时会发生什么。

除特殊安排的代码之外,在信号处理器中只应该调用对异步信号安全的函数(如 POSIX 中定义的那样)并且只访问volatile sig_atomic_t类型的变量。一些postgres中的函数也被视作是信号安全的,其中很重要的一个是SetLatch()

在大部分情况下,信号处理器应该只提示一个信号已经到达,并且使用一个 latch 唤醒运行在处理器之外的代码。这样一个处理器的例子如下:

static void
handle_sighup(SIGNAL_ARGS)
{
    int         save_errno = errno;

    got_SIGHUP = true;
    SetLatch(MyLatch);

    errno = save_errno;
}

errno会被保存并且恢复,因为SetLatch()可能会更改它。如果不这样做,当前正在观测errno的被中断代码可能会看到错误的值。