/*
* LIB/PRECOMMIT.C - Precommit caching
*
* (c)Copyright 1998, Matthew Dillon, All Rights Reserved. Refer to
* the COPYRIGHT file in the base directory of this distribution
* for specific rights granted.
*
*/
#include "defs.h"
Prototype void InitPreCommit(void);
Prototype int PreCommit(const char *msgid, int flags);
Prototype void SetPCommitExpire(int pret, int post, int hsize);
Prototype int GetPCommit(int which);
#ifndef PC_HSIZE
#define PC_HSIZE 16384 /* times sizeof(pchash_t) */
#endif
#ifndef PC_EXPIRE
#define PC_EXPIRE 30 /* 30 second expiration */
#endif
#ifndef POC_EXPIRE
#define POC_EXPIRE 45*60 /* 45 minute expiration */
#endif
#define PC_HMASK (PC_HSIZE - 1)
typedef struct pchash_t {
hash_t pc_Hash; /* hash code */
time_t pc_Time; /* time of entry */
pid_t pc_Pid; /* process id, -1 if post commit */
} pchash_t;
pchash_t *PCHAry;
int PCFd = -1;
int PCExpire = PC_EXPIRE; /* 30 second default */
int PCPostExpire = POC_EXPIRE; /* 30 minute default */
int PCHSize = PC_HSIZE; /* History cache size */
int PCHMask = PC_HMASK; /* History cache hash mask */
int PCPid = -1;
void
SetPCommitExpire(int pret, int post, int hsize)
{
if (pret >= 0)
PCExpire = pret;
if (post >= 0)
PCPostExpire = post;
if (hsize >= 0) {
PCHSize = hsize;
PCHMask = hsize - 1;
}
}
/*
* InitPreCommit() is called by the master diablo to initialize any
* server-private (but inherited on fork) shared memory.
*
* We generate a private shared memory segment which is
* mapped, then immediately removed. The map is inherited
* on fork. If we don't remove it now, shm segments may
* build up on the machine.
*
* NOTE: InitPreCommit() may not open() any descriptors because
* our code is not designed to deal with the shared lseek
* for the descriptor on fork.
*/
void
InitPreCommit(void)
{
#if USE_PCOMMIT_SHM
int sid = shmget(IPC_PRIVATE, PCHSize * sizeof(pchash_t), SHM_R|SHM_W);
struct shmid_ds ds;
if (sid < 0) {
logit(LOG_CRIT, "sysv shared memory alloc of %d failed, is your machine configured with a high enough maximum segment size?",
PCHSize * sizeof(pchash_t)
);
exit(1);
}
PCHAry = (pchash_t *)shmat(sid, NULL, SHM_R|SHM_W);
if (shmctl(sid, IPC_STAT, &ds) < 0 || shmctl(sid, IPC_RMID, &ds) < 0) {
logit(LOG_CRIT, "sysv shmctl stat/rmid failed");
exit(1);
}
if (PCHAry == (pchash_t *)-1) {
PCHAry = NULL;
logit(LOG_CRIT, "sysv shared memory map failed");
exit(1);
}
#endif
}
int
PreCommit(const char *msgid, int flags)
{
int r = 0;
#if DO_PCOMMIT_POSTCACHE == 0
/*
* This option is turned on by default. If it is
* off, do not write the precommit cache to post-cache
* history lookup hits.
*/
if (flags & PC_POSTCOMM)
return(0);
#endif
if (PCExpire == 0)
return(0);
if (PCPid == (pid_t)-1)
PCPid = (int)getpid();
if (PCHAry == NULL) {
#if USE_PCOMMIT_SHM
logit(LOG_CRIT, "unable to initialize precommit cache");
exit(1);
#else
struct stat st;
PCFd = open(PatDbExpand(PCommitCachePat), O_RDWR|O_CREAT, 0644);
if (PCFd >= 0 && fstat(PCFd, &st) == 0) {
int prot = PROT_READ | (USE_PCOMMIT_RW_MAP * PROT_WRITE);
if (st.st_size < PCHSize * sizeof(pchash_t))
ftruncate(PCFd, PCHSize * sizeof(pchash_t));
PCHAry = xmap(NULL, PCHSize * sizeof(pchash_t), prot, MAP_SHARED, PCFd, 0);
}
#endif
}
if (PCHAry == NULL && PCFd >= 0) {
close(PCFd);
PCFd = -1;
}
if (PCHAry) {
hash_t hv = hhash(msgid);
int i = (hv.h1 ^ hv.h2) & PCHMask;
pchash_t *pc = &PCHAry[i];
time_t t = time(NULL);
int32 dt = t - pc->pc_Time;
if (pc->pc_Hash.h1 == hv.h1 &&
pc->pc_Hash.h2 == hv.h2 &&
dt >= 0 &&
((dt < PCExpire && pc->pc_Pid != PCPid) ||
(dt < PCPostExpire && pc->pc_Pid == (pid_t)-1))
) {
/*
* collision. Return -1 if postcommit or -2 if precommit.
* If we are posting a history cache
* hit/commit, change the pid to -1, which lengthens the expire
* time. We can do this because the message-id is already in
* the history file.
*/
if (pc->pc_Pid == (pid_t)-1)
r = -1;
else
r = -2;
/*
* If we are removing from precommit, then set pid to 0
* This stops any future cache hits for the Message-ID
* We don't care about the return code
*/
if (flags & PC_DELCOMM) {
pc->pc_Time = 0;
pc->pc_Pid = 0;
r = 0;
}
#if USE_PCOMMIT_RW_MAP && DO_PCOMMIT_POSTCACHE
if ((flags & PC_POSTCOMM) && pc->pc_Pid != (pid_t)-1) {
pc->pc_Pid = (pid_t)-1;
}
#endif
} else {
pchash_t npc;
/*
* enter new info, we don't care about collisions
*
* PC_POSTCOMM is set only when we are using the precommit
* cache as a post-commit 'in the history file' cache.
*/
npc.pc_Hash = hv;
npc.pc_Time = t - 1;
npc.pc_Pid = (flags & PC_POSTCOMM) ? (pid_t)-1 : PCPid;
if (flags & PC_DELCOMM) {
npc.pc_Time = 0;
npc.pc_Pid = 0;
r = 0;
}
#if USE_PCOMMIT_RW_MAP
PCHAry[i] = npc;
#else
lseek(PCFd, i * sizeof(pchash_t), 0);
write(PCFd, &npc, sizeof(npc));
#endif
}
}
return(r);
}
int
GetPCommit(int which)
{
switch (which) {
case 1: return(PCExpire);
case 2: return(PCPostExpire);
case 3: return(PCHSize);
}
return(0);
}
syntax highlighted by Code2HTML, v. 0.9.1