text: /*-------------------------------------------------------------------------
   *
   * 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 */
  }
  