/******************************************************************************
 *
 * PROJECT: Carnegie Mellon Planetary Rover Project
 *          Task Control Architecture 
 * 
 * (c) Copyright 1991 Christopher Fedor and Reid Simmons.  All rights reserved.
 * 
 * MODULE: memory
 *
 * FILE: x_ipcMem.c
 *
 * ABSTRACT:
 * 
 * Interface to memory management routines.
 *
 * REVISION HISTORY
 *
 * $Log: tcaMem.c,v $
 * Revision 1.1.1.1  2004/10/15 14:33:16  tomkol
 * Initial Import
 *
 * Revision 1.4  2003/04/20 02:28:13  nickr
 * Upgraded to IPC 3.7.6.
 * Reversed meaning of central -s to be default silent,
 * -s turns silent off.
 *
 * Revision 2.5  2002/06/25 16:46:08  reids
 * "Insure" complained that malloc cannot be paired with cfree...
 *
 * Revision 2.4  2002/01/03 20:52:18  reids
 * Version of IPC now supports multiple threads (Caveat: Currently only
 *   tested for Linux).
 * Also some minor changes to support Java version of IPC.
 *
 * Revision 2.3  2000/08/14 21:28:35  reids
 * Added support for making under Windows.
 *
 * Revision 2.2  2000/07/03 17:03:30  hersh
 * Removed all instances of "tca" in symbols and messages, plus changed
 * the names of all other symbols which conflicted with TCA.  This new
 * version of IPC should be able to interoperate TCA fully.  Client
 * programs can now link to both tca and ipc.
 *
 * Revision 2.1.1.1  1999/11/23 19:07:35  reids
 * Putting IPC Version 2.9.0 under local (CMU) CVS control.
 *
 * Revision 1.1.2.7  1997/01/27 20:10:04  udo
 * ipc_2_6_006 to r3_Dev merge
 *
 * Revision 1.1.2.5  1997/01/16 22:17:29  reids
 * Added "totalMemBytes" to improve the way usage stats are reported.
 *
 * Revision 1.1.2.4  1996/12/18 15:13:11  reids
 * Changed logging code to remove VxWorks dependence on varargs
 *
 * Revision 1.1.2.3  1996/10/29 14:54:09  reids
 * Use the right prototype for x_ipcMallocHnd
 *
 * Revision 1.1.2.2  1996/10/24 15:19:29  reids
 * Make everything use x_ipcMalloc/x_ipcFree.
 *
 * Revision 1.1.2.1  1996/10/18 18:18:31  reids
 * Fixed a few compiler warnings.
 *
 * Revision 1.1  1996/05/09 01:02:08  reids
 * Moved all the X_IPC files over to the IPC directory.
 * Fixed problem with sending NULL data.
 * Added function IPC_setCapacity.
 * VxWorks m68k version released.
 *
 * Revision 1.1  1996/03/03 04:32:52  reids
 * First release of IPC files.  X_IPC code (8.5), modified to support NM-DS1 IPC.
 *
 * Revision 1.19  1996/02/10  16:50:46  rich
 * Fixed header problems and a crash related to direct connections.
 *
 * Revision 1.18  1995/07/06  21:17:38  rich
 * Solaris and Linux changes.
 *
 * Revision 1.17  1995/06/15  14:58:44  rich
 * Fixed definition of the malloc routine.
 *
 * Revision 1.16  1995/06/14  03:22:48  rich
 * Added DBMALLOC_DIR.
 * More support for DOS.  Fixed some problems with direct connections.
 *
 * Revision 1.15  1995/06/05  23:59:20  rich
 * Improve support of detecting broken pipes.  Add support for OSF 2.
 * Add return types to the global variable routines.
 *
 * Revision 1.14  1995/05/31  19:37:08  rich
 * Fixed problem with reply data being freed early from replys.
 * Initial work on getting the PC version to work.
 *
 * Revision 1.13  1995/04/08  02:06:39  rich
 * Added x_ipc_waitForReplyFrom to be able to block on replies from only one
 * source.  Useful when querying for the msg info information.  Added a
 * x_ipcQueryCentral that only accepts input from the central server.  Fixed
 * timing problems with direct connections.
 *
 * Revision 1.12  1995/01/18  22:43:25  rich
 * X_IPC 7.9: Speed improvements.
 * Use unix sockets for communication on the same machine.
 * Eliminate copying.
 * Optimize loop for arrays, especially simple, primitive arrays.
 * Optimize the buffer size.
 *
 * Revision 1.11  1994/10/25  17:11:02  reids
 * Changed the logging functions to accept variable number of arguments.
 *
 * Revision 1.10  1994/05/31  03:24:03  rich
 * Removed CFLAGS_sun4.
 * Removed cfree and bzero from mem routines.
 * Set zero wait on both sides of the pipe.  Can connect to host using inet
 * number.
 *
 * Revision 1.9  1994/05/17  23:18:12  rich
 * Added global variables and associated routines.
 * Added some error checking.  The central connection is now set to -1
 * rather than zero to prevent x_ipc messages from being send to stdout.
 * Now compiles on the sgi machines.  Still need to have the endian and
 * alignment figured out automatically.
 *
 * Revision 1.8  1994/04/28  16:17:45  reids
 * Changes in X_IPC Version 7.6:
 *  1) New functions: x_ipcIgnoreLogging and x_ipcResumeLogging
 *  2) Code for MacIntosh (MPW) version of X_IPC
 *
 * Revision 1.7  1994/04/16  19:43:29  rich
 * First release of X_IPC for the DEC alpha.
 * Changes were needed because longs are 64 bits.
 * Fixed alignment assumption in the data message format.
 * Fixed the way offsets are calculated for variable length arrays.  This
 * was a problem even without 64 bit longs and pointers.
 *
 * Added the commit date to the version information printed out with the -v
 * option.
 *
 * Now uses standard defines for byte order
 * (BYTE_ORDER = BIG_ENDIAN, LITTLE_ENDIAN or PDP_ENDIAN)
 *
 * Defined alignment types: ALIGN_INT ALINE_LONGEST and ALIGN_WORD.
 *
 * *** WARNING ***
 * sending longs between alphas and non-alpha machines will probably not work.
 * *** WARNING ***
 *
 * Revision 1.6  1993/12/14  17:35:32  rich
 * Changed getMGlobal to GET_M_GLOBAL and changed getSGlobal to
 * GET_S_GLOBAL to conform to Chris' software standards.
 *
 * Patched problem with connecting between machines with different byte
 * orders.  The real fix requires changing the way formats are stored.
 * Searching for structural similar formats does not guarantee that you
 * find the right format.
 *
 * Revision 1.5  1993/11/21  20:19:53  rich
 * Added shared library for sun4c_411 sunos machines.
 * Added install to the makefile.
 * Fixed problems with global variables.
 *
 * Revision 1.4  1993/08/30  21:54:57  fedor
 * V7+V6+VXWORKS Everything compiles but there are initialization problems.
 *
 * Revision 1.3  1993/08/27  07:17:23  fedor
 * First Pass at V7 and V6+VXWORKS merge
 *
 * Revision 1.2  1993/05/26  23:19:46  rich
 * Fixed up the comments at the top of the file.
 *
 * Revision 1.1.1.1  1993/05/20  05:45:39  rich
 * Importing x_ipc version 8
 *
 * Revision 7.1  1993/05/20  00:32:47  rich
 * RTG - initial checkin of Chris Fedor's version 8 of x_ipc
 *
 * Revision 1.2  1993/05/19  17:26:21  fedor
 * Added Logging.
 *
 * 27-Oct-92 Richard Goodwin, School of Computer Science, CMU
 * Changed printf to fprintf(stderr... for warning messages.
 *
 * 7-oct-92 Richard Goodwin : Changed free() to cfree().  Try to make it 
 * easier to track down memory problems.  Also added an 
 *
 *  4-Jul-91 Christopher Fedor, School of Computer Science, CMU
 * Added x_ipcRegisterMallocHnd to complete the routines so that modules
 * can handle all memory allocation.
 *
 *  4-Jun-91 Christopher Fedor, School of Computer Science, CMU
 * Added x_ipcRegisterFreeMemHnd so that modules can be called to free
 * memory to satisfy a malloc request.
 *
 * 25-Oct-90 Christopher Fedor, School of Computer Science, CMU
 * Created.
 *
 * $Revision: 1.1.1.1 $
 * $Date: 2004/10/15 14:33:16 $
 * $Author: tomkol $
 *
 *****************************************************************************/

#include "globalM.h"

/******************************************************************************
 * Forward Declarations
 *****************************************************************************/

#if !defined(DBMALLOC)
#define SIZETYPE int
void *x_ipcDBMalloc(const char* file, int line, unsigned long amount);
void x_ipcDBFree(const char *file, int line, char *item);
#else
#undef x_ipcMalloc
void *x_ipcMalloc(unsigned long amount);
#endif


/******************************************************************************
 *
 * FUNCTION: void x_ipcRegisterFreeMemHnd(void *(*func)(u_int32), int retry)
 *
 * DESCRIPTION: 
 * Sets a function for freeing memory when malloc returns NULL.
 * The function is passed an unsigned int amount of the memory requested.
 * The number of times this routine is called is set by retry.
 * The default is 1.
 *
 * INPUTS: 
 * VOID_FN func;
 * int retry;
 *
 * OUTPUTS: void.
 *
 *****************************************************************************/

void x_ipcRegisterFreeMemHnd(void (*func)(u_int32), int retry)
{
  LOCK_M_MUTEX;
  GET_M_GLOBAL(freeMemRetryAmount) = retry;
  
  if (GET_M_GLOBAL(freeMemRetryAmount) < 1)
    GET_M_GLOBAL(freeMemRetryAmount) = 1;
  
  GET_M_GLOBAL(x_ipcFreeMemoryHnd) = func;
  UNLOCK_M_MUTEX;
}


/******************************************************************************
 *
 * FUNCTION: void x_ipcRegisterMallocHnd(func, retry)
 *
 * DESCRIPTION: 
 * Registers a function to call in place of malloc.
 * The routine will be passed an unsigned int of the amount of storage needed.
 * The routine will be called a max of retry times if NULL is returned.
 * The default retry amount is 1.
 *
 * INPUTS: 
 * void *(*func)();
 * int32 retry;
 *
 * OUTPUTS:
 *
 *****************************************************************************/

#if defined(DBMALLOC)
void x_ipcRegisterMallocHnd(void *(*func)(const char*, int, SIZETYPE),
			  int retry)
#elif defined(__sgi)
void x_ipcRegisterMallocHnd(void *(*func)(unsigned int), int retry)
#elif defined(__TURBOC__)
void x_ipcRegisterMallocHnd(void *(*func)(unsigned long), long retry)
#else
void x_ipcRegisterMallocHnd(void *(*func)(size_t size), int retry)
#endif
{
  LOCK_M_MUTEX;
  GET_M_GLOBAL(mallocMemRetryAmount) = retry;
  
  if (GET_M_GLOBAL(mallocMemRetryAmount) < 1)
    GET_M_GLOBAL(mallocMemRetryAmount) = 1;
  
  if (func)
    GET_M_GLOBAL(x_ipcMallocMemHnd) = func;
  UNLOCK_M_MUTEX;
}


/******************************************************************************
 *
 * FUNCTION: void x_ipcFree(item)
 *
 * DESCRIPTION: 
 * An interface to free - should use x_ipcFreeData or x_ipcFreeReply in most cases.
 *
 * INPUTS: char *item;
 *
 * OUTPUTS: void.
 *
 *****************************************************************************/

#if defined(DBMALLOC)
#undef x_ipcFree
void x_ipcFree(void *item)
{
  x_ipcDBFree("Unknown", 9999, item);
}
void x_ipcDBFree(const char* file, int line, char *item)
#else
void x_ipcDBFree(const char* file, int line, char *item)
{
#ifdef UNUSED_PRAGMA
#pragma unused(file, line)
#endif
  x_ipcFree(item);
}
void x_ipcFree(void *item)
#endif
{
  if (item == NULL) return;

#if defined(THINK_C) || defined(macintosh) || defined(__sgi)
  free(item);
#elif defined(DBMALLOC)
  debug_free(file,line,item);
#elif defined(__TURBOC__)
  farfree(item);
#else 
  free(item);
#endif /* THINK_C || macintosh */
}


/******************************************************************************
 *
 * FUNCTION: char *x_ipcMalloc(amount)
 *
 * DESCRIPTION: Interface to malloc requests from x_ipc.
 *
 * INPUTS: u_int32 amount;
 *
 * OUTPUTS: char * - generic pointr to the memory
 *
 * NOTES: 
 * Stops everything if we have run out of memory. 
 * Note there may not be enough memory to print that we have run out.
 *
 *****************************************************************************/

#if defined(DBMALLOC)
void *x_ipcMalloc(unsigned long amount)
{
  return x_ipcDBMalloc("Unknown", 9999, amount);
}
void *x_ipcDBMalloc(const char* file, int line, SIZETYPE amount)
#elif defined(__TURBOC__)
void *x_ipcDBMalloc(const char* file, int line, unsigned long amount)
{
  return x_ipcMalloc(amount);
}
void *x_ipcMalloc(unsigned long amount)
#else
void *x_ipcDBMalloc(const char* file, int line, unsigned long amount)
{
#ifdef UNUSED_PRAGMA
#pragma unused(file, line)
#endif
  return x_ipcMalloc(amount);
}
void *x_ipcMalloc(size_t amount)
#endif
{
  int i, j;
  void *mem;
  
  mem = NULL;
  
  if (!mGlobalp() || !GET_M_GLOBAL(x_ipcMallocMemHnd)) {
    /* X_IPC_MOD_WARNING1("calling x_ipcMalloc(%d) before initialized\n", amount);*/
#if defined(DBMALLOC)
    mem = malloc(file, line, amount);
    /* check_alloc checked */
#else
    mem = malloc(amount);
    /* check_alloc checked */
#endif
    if (!mem) 
      X_IPC_MOD_ERROR1("x_ipcMalloc: NULL returned from malloc for request: %d\n", 
		       amount);
    return mem;

  } else {
    LOCK_M_MUTEX;

    if (GET_M_GLOBAL(x_ipcMallocMemHnd)) 
      for(i=0;!mem && i < GET_M_GLOBAL(mallocMemRetryAmount);i++) {
#if defined(DBMALLOC)
	mem = (*GET_M_GLOBAL(x_ipcMallocMemHnd))(file,line,amount);
#else
	mem = (*GET_M_GLOBAL(x_ipcMallocMemHnd))(amount);
#endif
      }

    if (mem) {
      GET_M_GLOBAL(totalMemRequest)++;
      GET_M_GLOBAL(totalMemBytes) += amount;
      UNLOCK_M_MUTEX;
      return(mem);
    }
  
    if (GET_M_GLOBAL(x_ipcFreeMemoryHnd))
      for(j=0;!mem && j < GET_M_GLOBAL(freeMemRetryAmount);j++) {
	(*GET_M_GLOBAL(x_ipcFreeMemoryHnd))(amount);
      
	if (GET_M_GLOBAL(x_ipcMallocMemHnd)) 
	  for(i=0;!mem && i < GET_M_GLOBAL(mallocMemRetryAmount);i++) {
#if defined(DBMALLOC)
	    mem = (*GET_M_GLOBAL(x_ipcMallocMemHnd))(file,line,amount);
#else
	    mem = (*GET_M_GLOBAL(x_ipcMallocMemHnd))(amount);
#endif
	  }
      }
    
    if (mem) {
      GET_M_GLOBAL(totalMemRequest)++;
      GET_M_GLOBAL(totalMemBytes) += amount;
      UNLOCK_M_MUTEX;
      return(mem);
    }
  
    UNLOCK_M_MUTEX;
    X_IPC_MOD_ERROR1("x_ipcMalloc: NULL returned from malloc for request: %d\n",
		     amount);
    return NULL;
  }
}


/******************************************************************************
 *
 * FUNCTION: void x_ipcStats(stream)
 *
 * DESCRIPTION: Quick hack to display some memory stats.
 *
 * INPUTS: none.
 *
 * OUTPUTS: void.
 *
 *****************************************************************************/

void x_ipcStats(FILE *stream)
{
  if (!mGlobalp())
    return;
  
  LOCK_M_MUTEX;
  fprintf(stream,"Cumulative Memory Usage:\n  Requests: %ld (%ld bytes)\n", 
	  GET_M_GLOBAL(totalMemRequest), GET_M_GLOBAL(totalMemBytes));
  UNLOCK_M_MUTEX;
  x_ipc_dataMsgDisplayStats(stream);
  FLUSH_IF_NEEDED(stream);
}
