/*------------------------------------------------------------------------- * * 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-2013, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * src/backend/port/ipc_test.c * *------------------------------------------------------------------------- */ #include "postgres.h" #include #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 AddToDataDirLockFile(int target_line, const char *str) { } void ProcessInterrupts(void) { } void 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(); } int errcode_for_file_access(void) { return 0; } bool errstart(int elevel, const char *filename, int lineno, const char *funcname, const char *domain) { 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 */ }