Le code dans PostgreSQL devrait seulement se baser sur les fonctionnalités disponibles dans le standard D89. Ceci signifie qu'un compilateur se conformant au standard C89 doit être caoable de compiler PostgreSQL, à l'exceptio possible de quelques parties dépendantes de la plateforme. Les fonctionnalités provenant de révisions ultérieures du standard C ou les fonctionnalités spécifiques des compilateurs peuvent être utilisées, si un contournement est fourni.
Par exemple static inline
et
_Static_assert()
sont actuellement utilisés, même si
elles proviennent de révisions ultérieures du standard C. Si elles ne sont
pas disponibles, nous définissons ces fonctions sans inline pour le premier
et en utilisant un remplaçant compatible C89 réalisant les mêmes
vérifications mais émettant des messages plutôt cryptiques.
Les macros avec arguments et les fonctions static inline
peuvent être utilisés. Ces dernières sont préférables s'il y a un risque
d'évaluations multiples si elles sont écrites en tant que macro, comme par
exemple le cas avec
#define Max(x, y) ((x) > (y) ? (x) : (y))
ou quand la macro deviendrait très longue. Dans d'autres cas, il est possible d'utiliser des macros ou au moins plus facilement. Par exemple parce que des expressions de types divers ont besoin d'être passées à la macro.
Quand la définition d'une fonction inline référence des symboles (autrement dit des variables, des fonctions) uniquement disponibles dans le moteur, la fonction pourrait ne pas être visible lorsqu'elle est incluse dans le code frontend.
#ifndef FRONTEND static inline MemoryContext MemoryContextSwitchTo(MemoryContext context) { MemoryContext old = CurrentMemoryContext; CurrentMemoryContext = context; return old; } #endif /* FRONTEND */
Dans cet exemple, CurrentMemoryContext
, qui est
seulement disponible dans le moteur, est référencé et la fonction
est donc cachée avec un #ifndef FRONTEND
. Cette règle
existe parce que certains compilateurs émettent des références aux symboles
contenus dans les fonctions inline même si la fonction n'est pas utilisée.
Pour pouvoir être exécuté à l'intérieur d'un gestionnaire de signal, le code doit être écrit avec beaucoup d'attention. Le problème fondamental est qu'un gestion de signal peut interrompre le code à tout moment, sauf s'il est bloqué. Si le code à l'intérieur d'un gestionnaire de signal utilise le même état que le code en dehors, un grand chaos peut survenir. Comme exemple, pensez à ce qui arriverait si un gestionnaire de signal essaie d'obtenir un verrou qui est déjà détenu par le code interrompu.
En dehors d'arrangements spéciaux, le code dans les gestionnaires de
signaux doit seulement appeler des fonctions saines de signal
asynchrone (d'après la définition de POSIX) et accèder à des variables
de type volatile sig_atomic_t
. Quelques fonctions
dans postgres
sont aussi déclarées comme saines pour
les signaux, notamment SetLatch()
.
Dans la plupart des cas, les gestionnaires de signaux ne devraient rien faire de plus que de noter qu'un signal est arrivé, et réveiller du code à l'extérieur du gestionnaire en utilisant un latch. Voici un exemple d'un tel gestionnaire :
static void handle_sighup(SIGNAL_ARGS) { int save_errno = errno; got_SIGHUP = true; SetLatch(MyLatch); errno = save_errno; }
errno
est sauvegardé puis restauré parce que
SetLatch()
pourrait le modifier. Si cela n'était pas
fait, le code interrompi qui était en train d'inspecter
errno
pourrait voir la mauvaise valeur.
Pour plus de clareté, il est préféré de déréférencer explicitement un pointeur de fonction lors de l'appel de cette fonction si le pointeur est une simple variable, par exemple :
(*emit_log_hook) (edata);
(même si emit_log_hook(edata)
fonctionnerait aussi).
Quand le pointeur de fonction fait partie d'une structure, la ponctuation
supplémentaire peut et devrait habituellement être omise. Par
exemple :
paramInfo->paramFetch(paramInfo, paramId);