1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166
|
/*
* LIB/PRECOMMIT.C - Precommit caching
*
*/
#include "defs.h"
Prototype void InitPreCommit(void);
Prototype int PreCommit(const char *msgid, int postMode);
Prototype int SetPreCommitExpire(int pcex);
#define PC_HSIZE 8192 /* times sizeof(hash_t) */
#define PC_HMASK (PC_HSIZE - 1)
#define PC_EXPIRE 30 /* 30 second expiration */
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 = PC_EXPIRE * 20; /* 10 minute default */
int PCPid = -1;
int
SetPreCommitExpire(int pcex)
{
int oex = PCExpire;
PCExpire = pcex;
PCPostExpire = PCExpire * 20;
return(oex);
}
/*
* 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, PC_HSIZE * sizeof(pchash_t), SHM_R|SHM_W);
struct shmid_ds ds;
if (sid < 0) {
syslog(LOG_CRIT, "sysv shared memory alloc failed, is your machine configured with a high enough maximum segment size?");
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) {
syslog(LOG_CRIT, "sysv shmctl stat/rmid failed");
exit(1);
}
if (PCHAry == (pchash_t *)-1) {
PCHAry = NULL;
syslog(LOG_CRIT, "sysv shared memory map failed");
exit(1);
}
#endif
}
int
PreCommit(const char *msgid, int postMode)
{
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 (postMode)
return(0);
#endif
if (PCExpire == 0)
return(0);
if (PCPid == (pid_t)-1)
PCPid = getpid();
if (PCHAry == NULL) {
#if USE_PCOMMIT_SHM
syslog(LOG_CRIT, "unable to initialize precommit cache");
exit(1);
#else
struct stat st;
PCFd = xopen(O_RDWR|O_CREAT, 0644, "%s/pcommit.cache", NewsHome);
if (PCFd >= 0 && fstat(PCFd, &st) == 0) {
int prot = PROT_READ | (USE_PCOMMIT_RW_MAP * PROT_WRITE);
if (st.st_size < PC_HSIZE * sizeof(pchash_t))
ftruncate(PCFd, PC_HSIZE * sizeof(pchash_t));
PCHAry = xmap(NULL, PC_HSIZE * 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) & PC_HMASK;
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 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.
*/
r = -1;
#if USE_PCOMMIT_RW_MAP && DO_PCOMMIT_POSTCACHE
if (postMode && 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
*
* postMode 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 = (postMode) ? (pid_t)-1 : PCPid;
#if USE_PCOMMIT_RW_MAP
PCHAry[i] = npc;
#else
lseek(PCFd, i * sizeof(pchash_t), 0);
write(PCFd, &npc, sizeof(npc));
#endif
}
}
return(r);
}
|