blob: a82e067f9093b6860582f8cd03eaeda29a1cda81 [file] [log] [blame]
/*-------------------------------------------------------------------------
*
* ipc_test.c
* Simplistic testbed for shared memory and semaphore code.
*
* This file allows for quick "smoke testing" of a PG semaphore or shared
* memory implementation, with less overhead than compiling up a whole
* installation. To use:
* 1. Run configure, then edit src/include/pg_config.h to select the
* USE_xxx_SEMAPHORES and USE_xxx_SHARED_MEMORY settings you want.
* Also, adjust the pg_sema.c and pg_shmem.c symlinks in
* src/backend/port/ if needed.
* 2. In src/backend/port/, do "gmake ipc_test".
* 3. Run ipc_test and see if it works.
* 4. If it seems to work, try building the whole system and running
* the parallel regression tests for a more complete test.
*
*
* Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/port/ipc_test.c,v 1.26 2009/01/01 17:23:46 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include <unistd.h>
#include "miscadmin.h"
#include "storage/ipc.h"
#include "storage/pg_sema.h"
#include "storage/pg_shmem.h"
/********* stuff needed to satisfy references in shmem/sema code *********/
volatile bool InterruptPending = false;
volatile bool QueryCancelPending = false;
volatile bool ProcDiePending = false;
volatile bool ImmediateInterruptOK = false;
volatile uint32 InterruptHoldoffCount = 0;
volatile uint32 CritSectionCount = 0;
bool IsUnderPostmaster = false;
bool assert_enabled = true;
int MaxBackends = 32;
int NBuffers = 64;
char *DataDir = ".";
#define MAX_ON_EXITS 20
static struct ONEXIT
{
pg_on_exit_callback function;
Datum arg;
} on_proc_exit_list[MAX_ON_EXITS], on_shmem_exit_list[MAX_ON_EXITS];
static int on_proc_exit_index,
on_shmem_exit_index;
void
proc_exit(int code)
{
shmem_exit(code);
while (--on_proc_exit_index >= 0)
(*on_proc_exit_list[on_proc_exit_index].function) (code,
on_proc_exit_list[on_proc_exit_index].arg);
exit(code);
}
void
shmem_exit(int code)
{
while (--on_shmem_exit_index >= 0)
(*on_shmem_exit_list[on_shmem_exit_index].function) (code,
on_shmem_exit_list[on_shmem_exit_index].arg);
on_shmem_exit_index = 0;
}
void
on_shmem_exit(pg_on_exit_callback function, Datum arg)
{
if (on_shmem_exit_index >= MAX_ON_EXITS)
elog(FATAL, "out of on_shmem_exit slots");
on_shmem_exit_list[on_shmem_exit_index].function = function;
on_shmem_exit_list[on_shmem_exit_index].arg = arg;
++on_shmem_exit_index;
}
void
on_exit_reset(void)
{
on_shmem_exit_index = 0;
on_proc_exit_index = 0;
}
void
RecordSharedMemoryInLockFile(unsigned long id1, unsigned long id2)
{
}
void
ProcessInterrupts(void)
{
}
int
ExceptionalCondition(const char *conditionName,
const char *errorType,
const char *fileName,
int lineNumber)
{
fprintf(stderr, "TRAP: %s(\"%s\", File: \"%s\", Line: %d)\n",
errorType, conditionName,
fileName, lineNumber);
abort();
return 0;
}
int
errcode_for_file_access(void)
{
return 0;
}
bool
errstart(int elevel, const char *filename, int lineno,
const char *funcname)
{
return (elevel >= ERROR);
}
void
errfinish(int dummy,...)
{
proc_exit(1);
}
void
elog_start(const char *filename, int lineno, const char *funcname)
{
}
void
elog_finish(int elevel, const char *fmt,...)
{
fprintf(stderr, "ERROR: %s\n", fmt);
proc_exit(1);
}
int
errcode(int sqlerrcode)
{
return 0; /* return value does not matter */
}
int
errmsg(const char *fmt,...)
{
fprintf(stderr, "ERROR: %s\n", fmt);
return 0; /* return value does not matter */
}
int
errmsg_internal(const char *fmt,...)
{
fprintf(stderr, "ERROR: %s\n", fmt);
return 0; /* return value does not matter */
}
int
errdetail(const char *fmt,...)
{
fprintf(stderr, "DETAIL: %s\n", fmt);
return 0; /* return value does not matter */
}
int
errdetail_log(const char *fmt,...)
{
fprintf(stderr, "DETAIL: %s\n", fmt);
return 0; /* return value does not matter */
}
int
errhint(const char *fmt,...)
{
fprintf(stderr, "HINT: %s\n", fmt);
return 0; /* return value does not matter */
}
/********* here's the actual test *********/
typedef struct MyStorage
{
PGShmemHeader header;
int flag;
PGSemaphoreData sem;
} MyStorage;
int
main(int argc, char **argv)
{
MyStorage *storage;
int cpid;
printf("Creating shared memory ... ");
fflush(stdout);
storage = (MyStorage *) PGSharedMemoryCreate(8192, false, 5433);
storage->flag = 1234;
printf("OK\n");
printf("Creating semaphores ... ");
fflush(stdout);
PGReserveSemaphores(2, 5433);
PGSemaphoreCreate(&storage->sem);
printf("OK\n");
/* sema initial value is 1, so lock should work */
printf("Testing Lock ... ");
fflush(stdout);
PGSemaphoreLock(&storage->sem, false);
printf("OK\n");
/* now sema value is 0, so trylock should fail */
printf("Testing TryLock ... ");
fflush(stdout);
if (PGSemaphoreTryLock(&storage->sem))
printf("unexpected result!\n");
else
printf("OK\n");
/* unlocking twice and then locking twice should work... */
printf("Testing Multiple Lock ... ");
fflush(stdout);
PGSemaphoreUnlock(&storage->sem);
PGSemaphoreUnlock(&storage->sem);
PGSemaphoreLock(&storage->sem, false);
PGSemaphoreLock(&storage->sem, false);
printf("OK\n");
/* check Reset too */
printf("Testing Reset ... ");
fflush(stdout);
PGSemaphoreUnlock(&storage->sem);
PGSemaphoreReset(&storage->sem);
if (PGSemaphoreTryLock(&storage->sem))
printf("unexpected result!\n");
else
printf("OK\n");
/* Fork a child process and see if it can communicate */
printf("Forking child process ... ");
fflush(stdout);
cpid = fork();
if (cpid == 0)
{
/* In child */
on_exit_reset();
sleep(3);
storage->flag++;
PGSemaphoreUnlock(&storage->sem);
proc_exit(0);
}
if (cpid < 0)
{
/* Fork failed */
printf("failed: %s\n", strerror(errno));
proc_exit(1);
}
printf("forked child pid %d OK\n", cpid);
if (storage->flag != 1234)
printf("Wrong value found in shared memory!\n");
printf("Waiting for child (should wait 3 sec here) ... ");
fflush(stdout);
PGSemaphoreLock(&storage->sem, false);
printf("OK\n");
if (storage->flag != 1235)
printf("Wrong value found in shared memory!\n");
/* Test shutdown */
printf("Running shmem_exit processing ... ");
fflush(stdout);
shmem_exit(0);
printf("OK\n");
printf("Tests complete.\n");
proc_exit(0);
return 0; /* not reached */
}