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

Reply via email to