Hi!!
I was just browsing the message and saw yours. I have actually written a
shared memory system for PostgreSQL.
I've done some basic bench testing, and it seems to work, but I haven't
given it the big QA push yet.
My company, Mohawk Software, is going to release a bunch of PostgreSQL
extenssions for text search, shared memory, interfacing, etc.
Here's the source for the shared module. Mind you, it has not been through
rigerous QA yet!!! Also, this is the UNIX/Linux/SHM version, the Win32
version has not been written yet.
http://www.mohawksoft.org
/*
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
MA 02111-1307, USA
Copyright (C) 2004 Mark L. Woodward
If you want support or to professionally license this library, the author
can be reached at [EMAIL PROTECTED]
*/
#include "postgres.h"
#include "access/heapam.h"
#include "catalog/namespace.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
#include "commands/tablecmds.h"
#include "commands/sequence.h"
#include "miscadmin.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "sys/types.h"
#include "sys/mman.h"
#include "semaphore.h"
#include "fcntl.h"
#include "unistd.h"
#include "sys/ipc.h"
#include "sys/shm.h"
typedef int MEM_KEY ;
enum type
{
VSTR,
VINT,
VUNKNOWN
};
#define MEM_NAME 0x7DEAF2
#define MEM_PERM 0x1FF
#define VAR_MAGIC 0xDEADBEEF
#define MAX_VARS 32
#define MAX_NAME 16
#define MAX_SZVAL 256
#ifndef MIN
#define MIN(a,b) (((a)>(b))?(b):(a))
#endif
typedef struct shared_var
{
int inuse; // "inuse" flag, timestamp
int created; // When it was created
int ndx; // Index within system
int type; // Type of data
int nval; // Integer value
char name[MAX_NAME]; // Name of variable
char szval[MAX_SZVAL]; // String value
}SHAREDVAR;
typedef struct shared_memory_block
{
int magic; // Magic number
int nvars; // Peak number of variables
sem_t lock; // Semaphore lock
SHAREDVAR vars[MAX_VARS]; // Variables
}MEMBLOCK;
/* Internal functions */
static void *ShmOpen(MEM_KEY key, int size);
static void *ShmCreate(MEM_KEY key, int size);
static text *strtopg(char *string);
static char *pgtostr(char *buffer, int cb, text *text);
static int LowSetVal(char *name, int type, void *val);
static int LowAddVal(char *name, int nval);
static int LowSubVal(char *name, int nval);
static void *CreateMemSegment(MEM_KEY key);
static void *OpenMemSegment(MEM_KEY key);
static MEMBLOCK *GetMemBlock(MEM_KEY key);
static void ReleaseMemBlock(MEMBLOCK *p);
static SHAREDVAR *FindVar(MEMBLOCK *block, char *name);
static SHAREDVAR *AllocateVar(MEMBLOCK *block, char *name, int type);
static int FreeVar(MEMBLOCK *block, char *name);
/* PostgreSQL public functions */
Datum SetIntVal(PG_FUNCTION_ARGS);
Datum GetIntVal(PG_FUNCTION_ARGS);
Datum SetStrVal(PG_FUNCTION_ARGS);
Datum GetStrVal(PG_FUNCTION_ARGS);
Datum AddIntValue(PG_FUNCTION_ARGS);
Datum SubIntValue(PG_FUNCTION_ARGS);
Datum RemoveShared(PG_FUNCTION_ARGS);
Datum SwapTextValue(PG_FUNCTION_ARGS);
Datum SwapIntValue(PG_FUNCTION_ARGS);
Datum RemoveLRU(PG_FUNCTION_ARGS);
Datum GetLRU(PG_FUNCTION_ARGS);
Datum GetLastAccessShared(PG_FUNCTION_ARGS);
Datum GetCreatedShared(PG_FUNCTION_ARGS);
/* PostgreSQL function structs */
PG_FUNCTION_INFO_V1(SetIntVal);
PG_FUNCTION_INFO_V1(GetIntVal);
PG_FUNCTION_INFO_V1(SetStrVal);
PG_FUNCTION_INFO_V1(GetStrVal);
PG_FUNCTION_INFO_V1(AddIntValue);
PG_FUNCTION_INFO_V1(SubIntValue);
PG_FUNCTION_INFO_V1(RemoveShared);
PG_FUNCTION_INFO_V1(SwapTextValue);
PG_FUNCTION_INFO_V1(SwapIntValue);
PG_FUNCTION_INFO_V1(RemoveLRU);
PG_FUNCTION_INFO_V1(GetLRU);
PG_FUNCTION_INFO_V1(GetLastAccessShared);
PG_FUNCTION_INFO_V1(GetCreatedShared);
/* Static memblock pointer */
static MEMBLOCK * s_block = NULL;
/* Opens existing shared memory block */
static void *ShmOpen(MEM_KEY key, int size)
{
int n = shmget(key, size, 0660);
if(n == -1)
return NULL;
return shmat(n, NULL, SHM_R | SHM_W);
}
/* Creates a shared memory block */
static void *ShmCreate(MEM_KEY key, int size)
{
int n = shmget(key, size, 0660 | IPC_CREAT);
if(n == -1)
{
elog(ERROR, "Can't create shared memory: %s", strerror(errno));
return NULL;
}
return shmat(n, NULL, SHM_R | SHM_W);
}
/* Makes a postgres text * from a C string */
static text *strtopg(char *string)
{
int slen = strlen(string);
int cb = slen + VARHDRSZ;
text * t = (text *)palloc(cb);
if(t)
{
VARATT_SIZEP(t)=cb;
strncpy(VARDATA(t),string, slen);
}
return t;
}
/* Converts/copies postgres text to C string. */
static char *pgtostr(char *buffer, int cb, text *text)
{
int len = VARSIZE(text)-VARHDRSZ;
len = MIN(len,cb-1);
memcpy(buffer, VARDATA(text), len);
buffer[len]=0;
return buffer;
}
/* Create and init a shared memory segment */
static void *CreateMemSegment(MEM_KEY key)
{
void *p = ShmCreate(key, sizeof(MEMBLOCK));
if(p)
{
memset(p, 0, sizeof(MEMBLOCK));
s_block = (MEMBLOCK *)p;
s_block->nvars=0;
sem_init(&s_block->lock, 1, 1);
s_block->magic = VAR_MAGIC;
}
return p;
}
/* Open an existing memory segment */
static void *OpenMemSegment(MEM_KEY key)
{
void *p = ShmOpen(key, sizeof(MEMBLOCK));
if(p)
{
s_block = (MEMBLOCK *)p;
// Make sure race condition does not exist!
while(s_block->magic != VAR_MAGIC)
usleep(1);
}
return p;
}
/* Get and lock the formatted shared memory block */
static MEMBLOCK *GetMemBlock(MEM_KEY key)
{
if(s_block == NULL)
{
if(!OpenMemSegment(key))
{
if(!CreateMemSegment(key))
{
return NULL;
}
}
}
sem_wait(&s_block->lock);
return s_block;
}
/* Releases the formatted shared memory block */
static void ReleaseMemBlock(MEMBLOCK *p)
{
sem_post(&p->lock);
}
/* Find a variable in the memory block */
static SHAREDVAR *FindVar(MEMBLOCK *block, char *name)
{
int i;
SHAREDVAR *pvar=NULL;
for(i=0; i < block->nvars; i++)
{
SHAREDVAR *pvarT = &block->vars[i];
if(pvarT->inuse && strcmp(pvarT->name, name)==0)
{
pvar = pvarT;
break;
}
}
return pvar;
}
/* Find the least recent variable in the memory block */
static SHAREDVAR *FindLRUVar(MEMBLOCK *block)
{
int i;
SHAREDVAR *pvar=NULL;
for(i=0; i < block->nvars; i++)
{
SHAREDVAR *pvarT = &block->vars[i];
if(!pvar)
{
if(pvarT->inuse)
pvar = pvarT;
}
else if(pvarT->inuse && (pvarT->inuse < pvar->inuse))
{
pvar = pvarT;
}
}
return pvar;
}
/* Allocate a variable out of the shared memory block */
static SHAREDVAR *AllocateVar(MEMBLOCK *block, char *name, int type)
{
int i;
SHAREDVAR *pvar=NULL;
for(i=0; i < block->nvars; i++)
{
if(strcmp(block->vars[i].name, name)==0)
{
elog(ERROR, "Variable '%s' exists", name);
return NULL;
}
if(block->vars[i].inuse == 0)
{
pvar = &block->vars[i];
}
}
if(!pvar)
{
if(block->nvars >= MAX_VARS)
{
elog(ERROR, "Too many variables");
return NULL;
}
pvar = &block->vars[block->nvars];
pvar->ndx = block->nvars++;
}
strcpy(pvar->name, name);
pvar->type = type;
pvar->created = pvar->inuse = time(0);
return pvar;
}
/* Mark a variable as free, clear its name */
static int FreeVar(MEMBLOCK *block, char *name)
{
int i;
for(i=0; i < block->nvars; i++)
{
if(strcmp(block->vars[i].name, name)==0)
{
block->vars[i].name[0] = 0;
block->vars[i].created = block->vars[i].inuse = 0;
break;
}
}
return i;
}
/* Internal routine to allocate (if nessisary) and set a variable */
static int LowSetVal(char *name, int type, void *val)
{
SHAREDVAR *pvar;
MEMBLOCK *block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
return -1;
}
pvar = FindVar(block, name);
if(!pvar)
pvar = AllocateVar(block, name, type);
if(pvar)
{
pvar->inuse = time(0);
if(pvar->type != type)
{
elog(WARNING, "Value type being reassigned");
pvar->type = type;
}
if(type == VSTR)
{
strncpy(pvar->szval, (char *)val, sizeof(pvar->szval));
pvar->nval = atoi((char *)val); // You never REALLY know, you know?
}
else if(type == VINT)
{
pvar->nval = (int) val;
snprintf(pvar->szval, sizeof(pvar->szval), "%d", (int)val);
}
}
ReleaseMemBlock(block);
return (pvar) ? pvar->ndx : -1;
}
/* Funky internal routine to find a variable and add to it. This is for
* pseudo sequences and counters shafred across processes, but not on disk.*/
static int LowAddVal(char *name, int nval)
{
int retval=-1;
SHAREDVAR *pvar=NULL;
MEMBLOCK *block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
return -1;
}
pvar = FindVar(block, name);
if(!pvar)
elog(ERROR, "Variable %s does not exist", name);
else
{
pvar->inuse = time(0);
pvar->nval += nval;
snprintf(pvar->szval, sizeof(pvar->szval), "%d", nval);
retval = pvar->nval;
}
ReleaseMemBlock(block);
return retval;
}
/* Like LowAddVal, except used to subtract */
static int LowSubVal(char *name, int nval)
{
int retval=-1;
SHAREDVAR *pvar=NULL;
MEMBLOCK *block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
return -1;
}
pvar = FindVar(block, name);
if(!pvar)
elog(ERROR, "Variable %s does not exist", name);
else
{
pvar->inuse = time(0);
pvar->nval -= nval;
snprintf(pvar->szval, sizeof(pvar->szval), "%d", nval);
retval = pvar->nval;
}
ReleaseMemBlock(block);
return retval;
}
/* Public PG routine to set an integer value */
Datum SetIntVal(PG_FUNCTION_ARGS)
{
char name[MAX_NAME];
int val;
if(fcinfo->nargs != 2)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
val = PG_GETARG_INT32(1);
int retval = LowSetVal(name, VINT, (void *)val);
PG_RETURN_INT32(retval);
}
/* Public PG routine to set a text value */
Datum SetStrVal(PG_FUNCTION_ARGS)
{
char name[MAX_NAME];
char val[MAX_SZVAL];
if(fcinfo->nargs != 2)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
pgtostr(val, sizeof(val), PG_GETARG_TEXT_P(1));
int retval = LowSetVal(name, VSTR, (void *)val);
PG_RETURN_INT32(retval);
}
/* Public PG routine to get a text value */
Datum GetStrVal(PG_FUNCTION_ARGS)
{
MEMBLOCK *block;
SHAREDVAR *pvar=NULL;
char name[MAX_NAME];
text *retval = NULL;
if(fcinfo->nargs != 1)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_NULL();
}
pvar = FindVar(block, name);
if(pvar)
{
retval = strtopg(pvar->szval);
pvar->inuse = time(0);
}
ReleaseMemBlock(block);
if(retval)
{
PG_RETURN_NULL();
}
else
{
PG_RETURN_TEXT_P(retval);
}
}
/* Public PG routine to get an integer value */
Datum GetIntVal(PG_FUNCTION_ARGS)
{
MEMBLOCK *block;
char name[MAX_NAME];
SHAREDVAR * pvar;
int retval = -1;
if(fcinfo->nargs != 1)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_NULL();
}
pvar = FindVar(block, name);
if(pvar)
{
pvar->inuse = time(0);
retval = pvar->nval;
}
ReleaseMemBlock(block);
PG_RETURN_INT32(retval);
}
/* See LowAddVal, but this adds a value to a variable. The variable must already exist. */
Datum AddIntValue(PG_FUNCTION_ARGS)
{
char name[MAX_NAME];
int val;
if(fcinfo->nargs != 2)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
val = PG_GETARG_INT32(1);
val = LowAddVal(name, val);
PG_RETURN_INT32(val);
}
/* See AddIntValue, but this subtracts a value from a variable. The variable must already exist. */
Datum SubIntValue(PG_FUNCTION_ARGS)
{
char name[MAX_NAME];
int val;
if(fcinfo->nargs != 2)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
val = PG_GETARG_INT32(1);
val = LowSubVal(name, val);
PG_RETURN_INT32(val);
}
Datum SwapIntValue(PG_FUNCTION_ARGS)
{
MEMBLOCK *block;
SHAREDVAR * pvar;
char name[MAX_NAME];
int retval=-1;
int val;
if(fcinfo->nargs != 2)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
val = PG_GETARG_INT32(1);
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_INT32(-1);
}
pvar = FindVar(block, name);
if(!pvar)
{
elog(WARNING, "Variable not set");
}
else
{
pvar->inuse = time(0);
retval = pvar->nval;
pvar->nval = val;
snprintf(pvar->szval, sizeof(pvar->szval), "%d", val);
}
ReleaseMemBlock(block);
PG_RETURN_INT32(retval);
}
Datum SwapTextValue(PG_FUNCTION_ARGS)
{
MEMBLOCK *block;
SHAREDVAR * pvar;
char name[MAX_NAME];
char val[MAX_SZVAL];
char retval[MAX_SZVAL];
if(fcinfo->nargs != 2)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
pgtostr(val, sizeof(val), PG_GETARG_TEXT_P(1));
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_INT32(-1);
}
pvar = FindVar(block, name);
if(!pvar)
{
elog(WARNING, "Variable not set");
}
else
{
pvar->inuse = time(0);
memcpy(retval, pvar->szval, sizeof(retval));
memcpy(pvar->szval, val, sizeof(val));
pvar->nval = atoi(val);
}
ReleaseMemBlock(block);
PG_RETURN_TEXT_P(strtopg(retval));
}
/* Marks a variable as deleted. */
Datum RemoveShared(PG_FUNCTION_ARGS)
{
MEMBLOCK *block;
char name[MAX_NAME];
int retval;
if(fcinfo->nargs != 1)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_INT32(-1);
}
retval = FreeVar(block, name);
ReleaseMemBlock(block);
PG_RETURN_INT32(retval);
}
/* Marks a the LRU variable as deleted. */
Datum RemoveLRU(PG_FUNCTION_ARGS)
{
SHAREDVAR *pvar;
MEMBLOCK *block;
text *retval=NULL;
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_INT32(-1);
}
pvar = FindLRUVar(block);
if(pvar)
{
retval = strtopg(pvar->name);
pvar->inuse = 0;
pvar->name[0]=0;
}
ReleaseMemBlock(block);
if(retval)
{
PG_RETURN_TEXT_P(retval);
}
else
{
PG_RETURN_NULL();
}
}
/* Gets the LRU variable. */
Datum GetLRU(PG_FUNCTION_ARGS)
{
SHAREDVAR *pvar;
MEMBLOCK *block;
text *retval=NULL;
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_INT32(-1);
}
pvar = FindLRUVar(block);
if(pvar)
{
retval = strtopg(pvar->name);
}
ReleaseMemBlock(block);
if(retval)
{
PG_RETURN_TEXT_P(retval);
}
else
{
PG_RETURN_NULL();
}
}
/* Gets the last access time of variable */
Datum GetLastAccessShared(PG_FUNCTION_ARGS)
{
MEMBLOCK *block;
SHAREDVAR *pvar;
char name[MAX_NAME];
int retval = -1;
if(fcinfo->nargs != 1)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_INT32(-1);
}
pvar = FindVar(block, name);
if(pvar)
retval = pvar->inuse;
ReleaseMemBlock(block);
PG_RETURN_INT32(retval);
}
/* Gets the creation time of variable */
Datum GetCreatedShared(PG_FUNCTION_ARGS)
{
MEMBLOCK *block;
SHAREDVAR *pvar;
char name[MAX_NAME];
int retval = -1;
if(fcinfo->nargs != 1)
{
elog(ERROR, "Don't know how to use %d args", fcinfo->nargs);
PG_RETURN_INT32(-1);
}
pgtostr(name, sizeof(name), PG_GETARG_TEXT_P(0));
block = GetMemBlock(MEM_NAME);
if(!block)
{
elog(ERROR, "No shared memory block");
PG_RETURN_INT32(-1);
}
pvar = FindVar(block, name);
if(pvar)
retval = pvar->created;
ReleaseMemBlock(block);
PG_RETURN_INT32(retval);
}
---------------------------(end of broadcast)---------------------------
TIP 4: Have you searched our list archives?
http://archives.postgresql.org