56.4. Различные соглашения по оформлению кода #

56.4.1. Стандарт C #

Код в PostgreSQL должен использовать только те возможности языка, что описаны в стандарте C99. Это означает, что код postgres должен успешно компилироваться компилятором, поддерживающим C99, возможно, за исключением нескольких платформозависимых мест.

Некоторые возможности, вошедшие в стандарт C99, в настоящее время использовать в коде ядра PostgreSQL нельзя. В данный момент это массивы переменного размера, перемежающиеся с кодом объявления, комментарии // и универсальные символьные имена. Данный запрет объясняется соображениями переносимости и исторически сложившейся практикой.

Возможности более поздних ревизий стандарта C или специфические особенности компилятора могут использоваться, только если предусмотрен и вариант компиляции без них.

Например, в настоящее время используются конструкции _Static_assert() и __builtin_constant_p, хотя они относятся к более новым ревизиям стандарта C и расширению GCC, соответственно. Но если они недоступны, мы переходим к совместимой с C99 замене, которая выполняет те же проверки, но выдаёт довольно непонятные сообщения, и не используем __builtin_constant_p.

56.4.2. Внедрённые функции и макросы, подобные функциям #

Допускается использование и макросов с аргументами, и функций 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. Это правило введено, потому что некоторые компиляторы генерируют указатели на символы, фигурирующие во внедрённых функциях, даже когда эти функции не используются.

56.4.3. Написание обработчиков сигналов #

Чтобы код мог выполняться внутри обработчика сигналов, его нужно написать очень аккуратно. Фундаментальная сложность состоит в том, что обработчик сигнала может прервать код в любой момент, если он не отключён. Если код внутри обработчика сигнала использует то же состояние, что и внешний основной код, это может привести к хаосу. В качестве примера представьте, что произойдёт, если обработчик сигнала попытается получить ту же блокировку, которой уже владеет прерванный код.

Если не предпринимать специальных мер, код в обработчиках сигналов может вызывать только безопасные с точки зрения асинхронных сигналов функции (как это определяется в 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, мог бы получить некорректное значение.

56.4.4. Вызов функций по указателям #

Вызов функции по указателю может записываться по-разному. Ясности ради, когда указатель на функцию — простая переменная, предпочтительным вариантом считается запись с явным разыменованием указателя, например:

(*emit_log_hook) (edata);

(хотя будет работать и просто emit_log_hook(edata)). Когда указатель на функции является частью структуры, дополнительные знаки пунктуации можно и обычно даже нужно опускать, например:

paramInfo->paramFetch(paramInfo, paramId);