PostgreSQL 中的代码应只依赖 C89 标准提供的语言特性。这意味着,至少除去少数依赖平台的部分,符合 C89 标准的编译器必须能够编译 postgres。如果提供了回退方案,则可以使用来自 C 标准后续修订的特性或编译器特定特性。
例如,目前使用了 static inline 和 _Static_assert(),尽管它们来自 C 标准的较新修订。如果这些特性不可用,我们会分别回退为定义不带 inline 的函数,以及使用一种兼容 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()。
在大多数情况下,信号处理器只应记录信号已经到达,并使用锁存器唤醒在信号处理器之外运行的代码。下面就是这样的一个处理器示例:
static void
handle_sighup(SIGNAL_ARGS)
{
int save_errno = errno;
got_SIGHUP = true;
SetLatch(MyLatch);
errno = save_errno;
}
errno被保存并恢复,是因为SetLatch()可能会改变它。如果不这样做,当前正在检查errno的被中断代码可能会看到错误的值。