54.4. 其他编码约定

C Standard

PostgreSQL 中的代码应仅依赖 C89 标准中可用的语言功能。这意味着合格的 C89 编译器必须能够编译 postgres,至少除了一些平台相关的部分外。如果提供了回退功能,则可以使用 C 标准更高版本的功能或编译器特定的功能。

例如,当前使用的是static inline_Static_assert(),即使它们来自 C 标准的较新版本。如果不可用,我们将分别退回到没有内联的定义函数,以及使用 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隐藏。该规则之所以存在,是因为即使没有使用该函数,某些编译器也会发出对内联函数中包含的符号的引用。

编写 signal 处理程序

为了适合在 signal 处理程序中运行,必须非常仔细地编写代码。根本的问题是,除非被阻止,否则 signal 处理程序可以随时中断代码。如果 signal 处理程序内部的代码使用的状态与外部混乱的代码使用的状态相同。例如,考虑如果 signal 处理程序尝试获取已在中断代码中持有的锁,会发生什么情况。

禁止 signal 处理程序中的特殊安排代码只能调用异步 signal 安全功能(如 POSIX 中定义)并访问类型为volatile sig_atomic_t的变量。 postgres中的一些功能也被认为是 signal 安全的,重要的是SetLatch()

在大多数情况下,signal 处理程序应该做的只是注意到 signal 已到达,并使用闩锁唤醒在处理程序外部运行的代码。此类处理程序的示例如下:

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

    got_SIGHUP = true;
    SetLatch(MyLatch);

    errno = save_errno;
}

errno被保存和恢复,因为SetLatch()可能会对其进行更改。如果没有这样做,则正在检查errno的被中断的代码可能会看到错误的值。

调用函数指针

为了清楚起见,如果指针是简单变量,则在调用指向函数时最好显式地取消引用函数指针,例如:

(*emit_log_hook) (edata);

(即使emit_log_hook(edata)也可以使用)。当函数指针是结构的一部分时,多余的标点符号可以并且通常应该省略,例如:

paramInfo->paramFetch(paramInfo, paramId);