/**********************************************************************
 * Copyright (c) 2005, 2009 IBM Corporation and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * $Id: ossipcmemory.cpp,v 1.9 2009/05/20 03:35:11 kchan Exp $
 * 
 * Contributors: 
 * IBM - Initial API and implementation
 **********************************************************************/

/*******************************************************************************

   Source File Name = ossipcmemory.C

   Descriptive Name = OSSe IPC memory

   Function:
      Defines:

   Dependencies:
      None

   Restrictions:
      None

   Change Activity:
   Defect Date        Who Description
   ====== =========== === ==============================================
   xxxxxx mmm/dd/yyyy aha Initial Code Drop
   161626 Nov/29/2000 jpk Fixed two bugs in UNIX ipcMemAttach.
   161881 Nov/30/2000 pj  Various changes.
   162924 Dec/19/2000 jpk Replaced new/delete with malloc/free.
   163585 Jan/05/2001 jpk Various changes.
   164413 Jan/17/2001 jpk Added base address parameter.
   172437 Apr/19/2001 dns Added ossIPCMemGetRealHandle function
   186134 Jan/25/2002 dns replaced OSSExs with OSSLatch
   186134 Mar/01/2002 dns added ossIPCMemInitResSize function
   205955 May/29/2002 dns Added ossIPCMemSetUserCount  and
                          ossIPCMemAttachCount definitions
        Last Changed =    02/08/27  12:38:32

*******************************************************************************/

#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#ifdef SQLWINT
#include <windows.h>
#elif SQLUNIX
#include <errno.h>
#include <sys/ipc.h>
#include <sys/stat.h>
#include <sys/shm.h>
#endif

#include "osserror.h"
#include "osslatch.h"      /* 186134 */
#include "ossmemory.h"
#include "ossipc.h"
#include "ossipcmemory.h"

#if defined(__linux__) && defined(USE_PTHREAD_ATOMICS) && defined(ENW)
#include <pthread.h>
#endif

#define OSS_MARKER_MEM     ((OSSMarker)(0xDB2ADB2B))
#define OSS_MEM_HASH_SZ    64

/* bugzilla 99740 begin */
#ifdef SQLWINT
#define OSS_MEM_NAME	"Global\\OSSMEM%s"
#define OSS_MEM_NAME2	"Local\\OSSMEM%s"
#else
#define OSS_MEM_NAME	"OSSMEM%s"
#endif
/* bugzilla 99740 end */

#define OSS_RES_SIZE       (1024*1024*4)


/* Share resource size list */
struct OSSMemSize
{
  OSSMemSize * pNext ;
  size_t       resSize ;
  char         resName[1] ;
} ;

struct OSSMemBlk
{
  size_t    nextBlk ;
  size_t    prevBlk ;
  size_t    size ;
  OSSMarker marker ;
} ;

struct OSSMemCB
{
  OSSMarker marker ;
  size_t    nextCB ;
  size_t    prevCB ;
  OSSMemID  memID ;
  size_t    fixedSize ;
  size_t    maxBlksSize ;
  size_t    curBlksSize ;
  OSSLatch  blksExs ;      /* 186134 */
  size_t    rootBlk ;
} ;

struct OSSMemSet
{
  size_t   users ;
#ifdef SQLWINT
  DWORD    attacher;       /* process ID of attacher 205955 */
#endif
  OSSLatch usersExs ;      /* 186134 */
  size_t   setSize ;
  OSSLatch hashExs ;       /* 186134 */
  size_t   hashCBs[OSS_MEM_HASH_SZ] ;
} ;

struct OSSMemHdl
{
  OSSMarker   marker;
  OSSMemSet * pMemSet;
  OSSMemCB  * pMemCB;
  void      * pFixedArea;
#ifdef SQLWINT
  HANDLE      handle ;
#elif SQLUNIX
  int         id ;
#endif
} ;

#define ipcMemOffsetToCB( pMemSet, offset ) \
  ((OSSMemCB *)((0 == (offset)) ? 0 : (((char *)pMemSet) + (offset))))

#define ipcMemCBToOffset( pMemSet, pMemCB ) \
  ((size_t)((0 == (pMemCB)) ? 0 : ((char *)(pMemCB)) - ((char *)(pMemSet))))

#define ipcMemArea( pMemSet) ((void *)(pMemSet + 1))

#define ipcMemOffsetToBlk( pMemSet, offset ) \
  ((OSSMemBlk *)((0 == (offset)) ? 0 : (((char *)pMemSet) + (offset))))

#define ipcMemBlkToOffset( pMemSet, pMemBlk ) \
  ((size_t)((0 == (pMemBlk)) ? 0 : ((char*)(pMemBlk)) - ((char*)(pMemSet))))

#define ipcMemOffsetToPtr( pMemSet, offset ) \
  ((void*)((0 == (offset)) ? 0 : (((char*) pMemSet) + (offset))))

#define ipcMemPtrToOffset( pMemSet, pBlkPtr ) \
  ((size_t)((0 == (pBlkPtr)) ? 0 : ((char*)(pBlkPtr)) - ((char*)(pMemSet))))


/* Global variables */
/*  186134
static OSSExs       g_memSizesExs = { 0, } ;
*/
static OSSLatch     g_memSizesExs;      /* 186134 */
static OSSMemSize * g_pMemSizes   = NULL ;


/******************************************************************************

   Function
      isValidOSSMemHdl

   Function
      Returns true if the OSSMemHdl is valid.  Otherwise, the function
      return false.

******************************************************************************/
inline bool isValidOSSMemHdl( OSSMemHdl * pMemHdl )
{
   if ( ( NULL == pMemHdl )
         || ( OSS_MARKER_MEM != pMemHdl->marker ) )
   {
      return false ;
   }
   else
   {
      return true ;
   }
}


/******************************************************************************

******************************************************************************/
inline int ipcHashMemID( OSSMemID memID )
{
#ifdef ENW
   unsigned char *pID = (unsigned char *) &memID;
#else
   char * pID = (char *)&memID ;
#endif
   int    hash = 0 ;

   for ( int i = 0; i < sizeof( memID ); i++ )
   {
      hash += pID[i] ;
   }

   hash = hash % OSS_MEM_HASH_SZ ;
   return hash ;
}


/******************************************************************************

   Name
      ipcMemAttach

   Function
      Attach or create an IPC memory resource.

   Inputs
      1. pResName

      3. createIfNotExists
         Specify true if the memory set should be created if it does
         not exist.

      4. pBaseAddress
         Points to the memory address in the calling process's address
         space where the memory resource should begin.  If pBaseAddress
         is NULL, then the starting address of the memory resource will
         be assigned by the operating system.

   Outputs
      2. pMemHdl

   Normal Returns
      OSS_OK

   Error Returns
      OSS_ERR_IPC_DUPLICATE
      OSS_ERR_IPC_CREATE_MAP_FAILED
      OSS_ERR_IPC_OPEN_MAP_FAILED
      OSS_ERR_IPC_MEM_MAP_FAILED
      OSS_ERR_IPC_GET_KEY_FAILED
	  OSS_ERR_IPC_SHMEM_GET_FAILED
      OSS_ERR_IPC_SHMEM_ATTACH_FAILED
	  OSS_ERR_IPC_NOMEMORY
	  OSS_ERR_NOTFOUND
	  OSS_ERR_FAILED

******************************************************************************/
static OSSErr ipcMemAttach(
      const char * pResName,
      OSSMemHdl  * pMemHdl,
      bool         createIfNotExists,
      const void * pBaseAddress )
{
   OSSErr      osserr = OSS_OK ;
   bool        initializeMem = true ;
   size_t      setSize = 0 ;
   OSSMemSet * pMemSet = NULL ;
   char        memName[256] ;
#ifdef SQLWINT
   SECURITY_ATTRIBUTES securityAttributes ;
   HANDLE      handle = 0 ;
#elif SQLUNIX
   key_t       shmKey   = 0 ;
   int         shmID    = -1 ;
#ifdef ENW
   int         errnoval = 0;
   /* 186134 - give everyone access */
   int         shmPerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH | IPC_CREAT | IPC_EXCL;
#else
   int         shmPerms = S_IRWXU | S_IRWXG ;
#endif
#endif

   /* Calculate shared resource size and setup resource name */
   if ( createIfNotExists )
   {
      setSize = ossIPCMemGetResSize( pResName ) ;
      setSize = ( setSize + 4095 ) & ~4095 ;
   }

	sprintf( memName, OSS_MEM_NAME, pResName ) ;

#ifdef OSS_DEBUG
         printf("IPCMemAttach:  Try to attach to shared memory %s\n",memName);
#endif

#ifdef SQLWINT
   /* 32-bit Windows implementation */

	/* Create/Open the named shared resource */
	memset( &securityAttributes, 0, sizeof( securityAttributes ) ) ;
	SetLastError( ERROR_SUCCESS ) ;
	if ( createIfNotExists )
	{
#if _DEBUG
		printf("Creating shared memory: %s\n", memName);
#endif
#ifdef _WIN64
		handle = CreateFileMapping( (HANDLE)0xFFFFFFFFFFFFFFFF,
#else
		handle = CreateFileMapping( (HANDLE)0xFFFFFFFF,
#endif
			&securityAttributes,
			PAGE_READWRITE,
			0,
			setSize,
			memName );
	}
	else
	{
#if _DEBUG
		printf("Opening shared memory: %s\n", memName);
#endif
		initializeMem = false ;
		handle = OpenFileMapping( FILE_MAP_READ | FILE_MAP_WRITE, FALSE, memName );
	}

	/* Bug 179871 */
#ifdef SQLWINT
	if(!handle) {
		int rc = GetLastError();
#if _DEBUG
		printf("Return code from CreateFileMapping() is %d\n", rc);
#endif
		/* On Vista and Server 2003 there might be problem creating Global file mapping as non-admin user */
		if(rc == ERROR_ACCESS_DENIED) {
			SetLastError(ERROR_SUCCESS) ;
			sprintf(memName, OSS_MEM_NAME2, pResName) ; /* Use the Local session namespace instead */

			if(createIfNotExists) {
#if _DEBUG
				printf("Creating shared memory: %s\n", memName);
#endif
#ifdef _WIN64
				handle = CreateFileMapping( (HANDLE)0xFFFFFFFFFFFFFFFF,
#else
				handle = CreateFileMapping( (HANDLE)0xFFFFFFFF,
#endif
					&securityAttributes,
					PAGE_READWRITE,
					0,
					setSize,
					memName );
			}
		}
		/* Sometimes open fails because it is looking under the Global namespace while it is created under Local */
		else if(rc == ERROR_FILE_NOT_FOUND) {
			SetLastError(ERROR_SUCCESS) ;
			sprintf(memName, OSS_MEM_NAME2, pResName) ; /* Use the Local session namespace instead */

#if _DEBUG
			printf("Opening shared memory: %s\n", memName);
#endif
			initializeMem = false ;
			handle = OpenFileMapping( FILE_MAP_READ | FILE_MAP_WRITE, FALSE, memName );
		}
	}
#endif
	/* Bug 179871 */

	if ( !handle )
	{
		int rc = GetLastError();
#if _DEBUG
		printf("Return code from CreateFileMapping() is %d\n", rc);
#endif

		if (createIfNotExists) {
			osserr = OSS_ERR_IPC_CREATE_MAP_FAILED ;
		}
		else {
			if ( rc == ERROR_FILE_NOT_FOUND ) 
				osserr = OSS_ERR_NOTFOUND;
			else
				osserr = OSS_ERR_IPC_OPEN_MAP_FAILED;
		}
		goto exit ;
	}

   /* Handle duplication / errors */
   if ( ERROR_ALREADY_EXISTS == GetLastError() )
   {
#if _DEBUG
		printf("Shared memory name %s already exist\n", memName);
#endif
      initializeMem = false ;
   }
   else if ( ERROR_SUCCESS != GetLastError() )
   {
#if _DEBUG
	   printf("Failed creating shared memory name %s, rc = %d\n", memName, GetLastError());
#endif
      if (createIfNotExists) {
         osserr = OSS_ERR_IPC_CREATE_MAP_FAILED ;
      }
	  else {
         osserr = OSS_ERR_IPC_OPEN_MAP_FAILED;
      }
      CloseHandle( handle ) ;
      goto exit ;
   }
#if _DEBUG
	printf("Successfully created shared memory: %s\n", memName);
#endif

	/* Map to process's address space */
   pMemSet = (OSSMemSet *)MapViewOfFileEx( handle,
                                           FILE_MAP_READ | FILE_MAP_WRITE,
                                           0, 0, 0, (void *)pBaseAddress ) ;
   if ( 0 == pMemSet )
   {
      CloseHandle( handle ) ;
      osserr = OSS_ERR_IPC_MEM_MAP_FAILED ;
      goto exit ;
   }

   pMemHdl->handle = handle ;

#elif SQLUNIX
   /* UNIX Implementation */

   /* Generate UNIX IPC Key based on resource name */
   osserr = ossIPCGetKey( &shmKey, memName, 'm', createIfNotExists ) ;
   if ( OSS_OK != osserr )
   {
      osserr = OSS_ERR_IPC_GET_KEY_FAILED;
      goto exit ;
   }

   /* Get IPC ID from IPC Key */
   if ( createIfNotExists )
   {
#ifdef ENW
/* 186134 begin */
      shmID = shmget( shmKey, setSize, shmPerms ) ;

      /* if the shared memory already exists then try to delete it */
      /* and create it again                                       */
      if ( ( -1 == shmID ) && ( EEXIST == errno ) )
      {
		  errnoval = errno;

         shmID = shmget( shmKey, 0, 0 ) ;
#ifdef OSS_DEBUG
         printf("IPCMemAttach:  about to delete shared memory %s because it already exists\n",memName);
#endif
         if (-1 == shmctl( shmID, IPC_RMID, 0 )) {
#ifdef OSS_DEBUG
            printf("IPCMemAttach:  shmctl(IPC_RMID) for mem %s failed - errno=%d %s\n", memName, errno, strerror(errno));
#endif
            struct shmid_ds shmCtl;
            if (-1 == shmctl(shmID, IPC_STAT, &shmCtl)) {
            	shmID = -1;
#ifdef OSS_DEBUG
            	printf("IPCMemAttach:  shmctl(IPC_STAT) for mem %s failed - errno=%d %s\n", memName, errno, strerror(errno));
#endif
            }
            else if (shmCtl.shm_nattch == 0) {
#ifdef OSS_DEBUG
            	printf("IPCMemAttach:  no attachments for mem %s, opening for read/write\n", memName);
#endif
            }
            else
            	shmID = -1;
         }
         else {
#ifdef OSS_DEBUG
            printf("IPCMemAttach:  about to create shared memory %s again after deleting it\n",memName);
#endif
            shmID = shmget( shmKey, setSize, shmPerms ) ;
			errnoval = errno;
#ifdef OSS_DEBUG
            if ( -1 == shmID ) {
               printf("IPCMemAttach:  creating shared memory again with name %s failed!\n", memName);
            }
#endif
         }
      }
      else if ( -1 == shmID ) {
		  errnoval = errno;
#ifdef OSS_DEBUG
         printf("IPCMemAttach:  shmget of size %d and perms %x failed with errno=%d %s\n", setSize, shmPerms, errno, strerror(errno));
#endif
	  }
/* 186134 */
#else
      shmID = shmget( shmKey, setSize, shmPerms | IPC_CREAT ) ;
      if ( ( -1 == shmID ) && ( EEXIST == errno ) )
      {
         initializeMem = false ;
         shmID = shmget( shmKey, 0, 0 ) ;
      }
#endif
   }
   else
   {
      initializeMem = false ;
      shmID = shmget( shmKey, 0, 0 ) ;
	  errnoval = errno;
   }

   if ( -1 == shmID )
   {
#ifdef OSS_DEBUG
      printf("IPCMemAttach:  shmget for mem %s failed with errno=%d %s\n", memName, errno, strerror(errno));
#endif
	  if (EEXIST == errnoval) 
		  osserr = OSS_ERR_IPC_DUPLICATE;
	  else if ( ENOENT == errnoval )
          osserr = OSS_ERR_NOTFOUND;
	  else if ( ENOMEM == errnoval || ENOSPC == errnoval )
		  osserr = OSS_ERR_IPC_NOMEMORY;
	  else 
	      osserr = OSS_ERR_IPC_SHMEM_GET_FAILED ;

      goto exit ;
   }

   /* Attach to the shared memory */
   pMemSet = (OSSMemSet *)shmat( shmID, pBaseAddress, 0 ) ;
   if ( (OSSMemSet *)-1 == pMemSet )
   {
#ifdef OSS_DEBUG
      printf("IPCMemAttach:  shmat for mem %s failed with errno=%d %s\n", memName, errno, strerror(errno));
#endif
      osserr = OSS_ERR_IPC_SHMEM_ATTACH_FAILED ;
      goto exit ;
   }

   pMemHdl->id = shmID ;

#else
   /* Other platforms not supported yet */
   osserr = OSS_ERR_FAILED ;
   goto exit ;
#endif   /* SQLWINT */

   /* Initialize memory set */
   if ( initializeMem )
   {
      memset( pMemSet, 0, sizeof( *pMemSet ) ) ;
      pMemSet->setSize = setSize ;
      ossMemInitialize( ipcMemArea( pMemSet ), setSize - sizeof( *pMemSet ) ) ;
      ossLatchInit( &(pMemSet->usersExs) ) ;      /* 186134 */
      ossLatchInit( &(pMemSet->hashExs) ) ;      /* 186134 */
   }
#ifdef SQLWINT
   else {
      /* Get the current process ID which is the ID of the process attaching to the shared memory */
		pMemSet->attacher = GetCurrentProcessId();
	}			
#endif

   /* Increment set usage count */
   ossLatchGet( &(pMemSet->usersExs) ) ;      /* 186134 */
   pMemSet->users++ ;
#ifdef OSS_DEBUG
   printf("IPCMemAttach:  users = %d for shared memory %s addr %x\n", pMemSet->users, memName, pMemSet);
#endif
   ossLatchRelease( &(pMemSet->usersExs) ) ;  /* 186134 */

   /* Initialize memory handle */
   pMemHdl->pMemSet = pMemSet ;

exit :
   return osserr ;
}


/******************************************************************************
   Detach from IPC memory resource
******************************************************************************/
static void ipcMemDetach( OSSMemHdl * pMemHdl )
{
   bool lastUser = false ;

   // Decrement set usage count
   ossLatchGet( &(pMemHdl->pMemSet->usersExs) ) ;       /* 186134 */
   pMemHdl->pMemSet->users--;
#ifdef OSS_DEBUG
   printf("ipcMemDetach:  users = %d of shared memory addr %x\n", pMemHdl->pMemSet->users, pMemHdl->pMemSet);
#endif
   lastUser = ( 0 == pMemHdl->pMemSet->users ) ;
   ossLatchRelease( &(pMemHdl->pMemSet->usersExs) ) ;   /* 186134 */

#ifdef SQLWINT
	UnmapViewOfFile( (void *)pMemHdl->pMemSet ) ;
	CloseHandle( pMemHdl->handle ) ;
	pMemHdl->handle = 0 ;
#else
   shmdt( (char *)pMemHdl->pMemSet ) ;
   if ( lastUser )
      shmctl( pMemHdl->id, IPC_RMID, 0 ) ;
   pMemHdl->id = 0 ;
#endif

  pMemHdl->pMemSet = NULL ;      /* Cleanup memory handle */
  return ;
}

/* 186134  begin */
void OSS_API ossIPCMemInitResSize(  )
{
   g_pMemSizes   = NULL ;
   ossLatchInit( &g_memSizesExs ) ;
}
/* 186134  end */

size_t OSS_API ossIPCMemGetResSize( const char * pResName )
{
   OSSMemSize * pSize ;

   if ( NULL == pResName )
      pResName = OSS_RES_NAME ;

   /* Lookup session by ID */
   ossLatchGet( &g_memSizesExs ) ;      /* 186134 */

   pSize = g_pMemSizes ;
   while ( ( NULL != pSize ) && ( 0 != strcmp( pSize->resName, pResName ) ) )
      pSize = pSize->pNext ;

   ossLatchRelease( &g_memSizesExs ) ;   /* 186134 */

   if ( NULL != pSize )
      return pSize->resSize ;
   else
      return OSS_RES_SIZE ;
}


void OSS_API ossIPCMemSetResSize( const char * pResName, size_t resSize )
{
   OSSMemSize * pSize ;

   if ( NULL == pResName ) pResName = OSS_RES_NAME ;
   if (    0 == resSize  )  resSize = OSS_RES_SIZE ;

   /* Lookup session by ID */
   ossLatchGet( &g_memSizesExs ) ;      /* 186134 */

   pSize = g_pMemSizes ;
   while ( ( NULL != pSize ) && ( 0 != strcmp( pSize->resName, pResName ) ) )
      pSize = pSize->pNext ;

   if ( NULL == pSize )
   {
      size_t size = strlen( pResName ) + sizeof( OSSMemSize ) ;
      pSize = ( OSSMemSize * )ossMemAlloc( 0, size ) ;
      pSize->resSize = resSize ;
      strcpy( pSize->resName, pResName ) ;
      pSize->pNext = g_pMemSizes ;
      g_pMemSizes = pSize ;
   }

   ossLatchRelease( &g_memSizesExs );      /* 186134 */
   return ;
}


OSSErr OSS_API ossIPCMemAuthorize( OSSMemRes memRes, OSSUid uid, OSSGid gid )
{
   OSSErr osserr = OSS_OK ;

#ifdef SQLUNIX
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;

   if ( !isValidOSSMemHdl( pMemHdl ) )
   {
      osserr == OSS_ERR_INVALID ;
      goto exit ;
   }

   struct shmid_ds shmCtl ;

   if ( -1 == shmctl( pMemHdl->id, IPC_STAT, &shmCtl ) )
   {
      osserr = OSS_ERR_FAILED ;
      goto exit ;
   }

   shmCtl.shm_perm.gid  = gid ;
   shmCtl.shm_perm.uid  = uid ;
   shmCtl.shm_perm.mode |= S_IRWXU ;

   if ( -1 == shmctl( pMemHdl->id, IPC_SET, &shmCtl ) )
   {
      osserr = OSS_ERR_FAILED ;
      goto exit ;
   }

exit :
#endif
  return osserr ;
}


/******************************************************************************

   Name
      ossIPCMemAttachCount

   Function
      Returns the number of current attaches to an IPC memory resource.

   Inputs
      1. memRes   - memory resource

      2. attcnt   - pointer to an int variable to hold the attach count


   Outputs
      2. attcnt

   Normal Returns
      OSS_OK

   Error Returns
      OSS_ERR_INVALID
	  OSS_ERR_FAILED

******************************************************************************/

OSSErr OSS_API ossIPCMemAttachCount( OSSMemRes memRes, int *attcnt )
{
   OSSErr osserr = OSS_OK ;

   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;

#ifdef SQLUNIX
   if ( !isValidOSSMemHdl( pMemHdl ) )
   {
      osserr == OSS_ERR_INVALID ;
      goto exit ;
   }

   struct shmid_ds shmCtl ;

   if ( -1 == shmctl( pMemHdl->id, IPC_STAT, &shmCtl ) )
   {
      osserr = OSS_ERR_FAILED ;
      goto exit ;
   }

#ifdef OSS_DEBUG
   printf("ossIPCMemAttachCount:  attach count = %d of shared memory addr %x\n", shmCtl.shm_nattch, pMemHdl->pMemSet);
#endif
   *attcnt = shmCtl.shm_nattch;

exit :
#else
   HANDLE hAttachProcess;
   hAttachProcess = OpenProcess(PROCESS_QUERY_INFORMATION, false, pMemHdl->pMemSet->attacher);

   if (hAttachProcess == NULL) {
	   DWORD errnum = GetLastError();
	   if (errnum == ERROR_INVALID_PARAMETER) {
		   /* assume that the attacher process ID is bad which means the attacher process has died */
			*attcnt = 1;
	   }
	   else {
			*attcnt = pMemHdl->pMemSet->users;
			printf("ossIPCMemAttachCount:  OpenProcess failed with error %d\n", errnum);
			osserr = OSS_ERR_FAILED ;
	   }
   }
   else {
		/* the process that attached to the shared memory still exists */
		*attcnt = pMemHdl->pMemSet->users;
		CloseHandle(hAttachProcess);
   }
#endif
  return osserr ;
}

/******************************************************************************

   Name
      ossIPCMemSetUserCount

   Function
      Sets the user count for an IPC memory resource.

   Inputs
      1. memRes   - memory resource

      2. usercnt  - user count value to set


******************************************************************************/

void OSS_API ossIPCMemSetUserCount( OSSMemRes memRes, int usercnt )
{

   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;

   ossLatchGet( &(pMemHdl->pMemSet->usersExs) ) ;       /* 186134 */
   pMemHdl->pMemSet->users = usercnt;
#ifdef OSS_DEBUG
   printf("ossIPCMemSetUserCount:  users = %d of shared memory addr %x\n", pMemHdl->pMemSet->users, pMemHdl->pMemSet);
#endif
   ossLatchRelease( &(pMemHdl->pMemSet->usersExs) ) ;   /* 186134 */
   return;
}


/******************************************************************************

   Name
      ossIPCMemCreate

   Function
      Create IPC memory resource

   Inputs
      2. pResName
         Optional resource name.

      3. memID
         Unique memory resource identifier.

      4. fixedSize
         The optional number of bytes to reserve in the fixed size area of the
         memory resource.  Specify 0 if the fixed size area is not required.

      5. maxBlksSize
         The maximum number of bytes that can be allocated in a single
         block.

      6. pBaseAddress
         Points to the memory address in the calling process's address
         space where the memory resource should begin.  If pBaseAddress
         is NULL, then the starting address of the memory resource will
         be assigned by the operating system.

   Outputs
      1. pMemRes
         IPC memory resource handle.

   Normal Returns
      OSS_OK

   Error Returns
      OSS_ERR_FAILED
      OSS_ERR_IPC_CREATE_MAP_FAILED
      OSS_ERR_IPC_MEM_MAP_FAILED
      OSS_ERR_IPC_GET_KEY_FAILED
	  OSS_ERR_IPC_SHMEM_GET_FAILED
      OSS_ERR_IPC_SHMEM_ATTACH_FAILED
      OSS_ERR_IPC_NOMEMORY
      OSS_ERR_IPC_DUPLICATE

 *****************************************************************************/
OSSErr OSS_API ossIPCMemCreate(
      OSSMemRes  * pMemRes,
      const char * pResName,
      OSSMemID     memID,
      size_t       fixedSize,
      size_t       maxBlksSize,
      const void * pBaseAddress )
{
   OSSErr      osserr = OSS_OK ;
   int         hash ;
   OSSMemHdl * pMemHdl  = NULL ;
   OSSMemSet * pMemSet  = NULL ;
   OSSMemCB  * pMemCB   = NULL ;
   OSSMemCB  * pFirstCB = NULL ;

   if ( NULL == pResName ) pResName = OSS_RES_NAME ;

   pMemHdl = (OSSMemHdl *)malloc( sizeof( OSSMemHdl ) ) ;
   memset( pMemHdl, 0, sizeof( *pMemHdl ) ) ;

   osserr = ipcMemAttach( pResName, pMemHdl, true, pBaseAddress ) ;
   if ( OSS_OK != osserr )
   {
      goto exit ;
   }

   pMemSet = pMemHdl->pMemSet ;

   hash = ipcHashMemID( memID ) ;

   ossLatchGet( &(pMemSet->hashExs) ) ;      /* 186134 */

   pMemCB = ipcMemOffsetToCB( pMemSet, pMemSet->hashCBs[hash] ) ;
   while ( ( NULL != pMemCB ) && ( memID != pMemCB->memID ) )
   {
      pMemCB = ipcMemOffsetToCB( pMemSet, pMemCB->nextCB ) ;
   }

   if ( NULL != pMemCB )
   {
      ossLatchRelease( &(pMemSet->hashExs) ) ;   /* 186134 */
      osserr = OSS_ERR_IPC_DUPLICATE ;
      goto exit ;
   }
   
   // printf("ossIPCMemCreate fixedSize is %d\n", fixedSize); // jgw-shmem

   pMemCB = (OSSMemCB *)ossMemAlloc( ipcMemArea( pMemSet ),
                                     sizeof( OSSMemCB ) + fixedSize ) ;
   if ( NULL == pMemCB )
   {
      ossLatchRelease( &(pMemSet->hashExs) ) ;    /* 186134 */
#ifdef OSS_DEBUG
      printf("ossIPCMemCreate: ossMemAlloc failed\n");
#endif
      osserr = OSS_ERR_IPC_NOMEMORY ;
      goto exit ;
   }

   memset( pMemCB, 0, sizeof( *pMemCB ) ) ;
   pMemCB->marker      = OSS_MARKER_MEM ;
   pMemCB->memID       = memID ;
   pMemCB->fixedSize   = fixedSize ;
   pMemCB->maxBlksSize = maxBlksSize ;
   ossLatchInit( &(pMemCB->blksExs) ) ;           /* 186134 */

   pMemCB->nextCB = pMemSet->hashCBs[hash] ;
   pMemSet->hashCBs[hash] = ipcMemCBToOffset( pMemSet, pMemCB ) ;

   pFirstCB = ipcMemOffsetToCB( pMemSet, pMemCB->nextCB ) ;
   if ( NULL != pFirstCB )
   {
      pFirstCB->prevCB = pMemSet->hashCBs[hash] ;
   }

   ossLatchRelease( &(pMemSet->hashExs) ) ;        /* 186134 */

   pMemHdl->marker     = OSS_MARKER_MEM ;
   pMemHdl->pMemSet    = pMemSet ;
   pMemHdl->pMemCB     = pMemCB ;
   pMemHdl->pFixedArea = (fixedSize) ? ( (char *)(pMemCB + 1) ) : 0 ;

exit :
   if ( OSS_OK != osserr )
   {
      if ( pMemHdl )
      {
         if ( pMemSet )
         {
            ipcMemDetach( pMemHdl ) ;
         }
         free( pMemHdl ) ;
         pMemHdl = NULL ;
      }
   }

   *pMemRes = (OSSMemRes)pMemHdl ;
   return osserr ;
}

/******************************************************************************

   Name
      ossIPCMemDestroy

   Function
      Destroys an IPC memory resource.

   Inputs
      1. memRes   - memory resource


   Normal Returns
      OSS_OK

   Error Returns
      OSS_ERR_INVALID

******************************************************************************/

OSSErr OSS_API ossIPCMemDestroy( OSSMemRes memRes )
{
   OSSErr      osserr  = OSS_OK ;
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;
   OSSMemSet * pMemSet = NULL ;
   OSSMemCB  * pMemCB  = NULL ;
   OSSMemCB  * pCB     = NULL ;
   OSSMemBlk * pBlk    = NULL ;
   OSSMemBlk * pMemBlk = NULL ;
   size_t      reqSize = 0 ;

   if ( !isValidOSSMemHdl( pMemHdl )
            || ( OSS_MARKER_MEM != pMemHdl->pMemCB->marker ) )
   {
      osserr = OSS_ERR_INVALID ;
      goto exit ;
   }

   pMemSet = pMemHdl->pMemSet ;
   pMemCB  = pMemHdl->pMemCB ;

   ossLatchGet( &(pMemSet->hashExs) ) ;      /* 186134 */

   if ( 0 == pMemCB->prevCB )
   {
      int hash = ipcHashMemID( pMemCB->memID ) ;
      pMemSet->hashCBs[hash] = pMemCB->nextCB ;
   }
   else
   {
      pCB = ipcMemOffsetToCB( pMemSet, pMemCB->prevCB ) ;
      pCB->nextCB = pMemCB->nextCB ;
   }

   if ( 0 != pMemCB->nextCB )
   {
      pCB = ipcMemOffsetToCB( pMemSet, pMemCB->nextCB ) ;
      pCB->prevCB = pMemCB->prevCB ;
   }

   ossLatchRelease( &(pMemSet->hashExs) ) ;      /* 186134 */

   pMemCB->marker  = 0 ;
   pMemHdl->marker = 0 ;

   ossLatchGet( &(pMemCB->blksExs) ) ;           /* 186134 */
   pMemBlk = ipcMemOffsetToBlk( pMemSet, pMemCB->rootBlk ) ;
   while ( 0 != ( pBlk = pMemBlk ) )
   {
      pMemBlk = ipcMemOffsetToBlk( pMemSet, pBlk->nextBlk ) ;
      reqSize = pBlk->size + sizeof( OSSMemBlk ) ;
      ossMemFree( ipcMemArea( pMemSet ), pBlk, reqSize ) ;
   }
   ossLatchRelease( &(pMemCB->blksExs) ) ;        /* 186134 */

   ossMemFree( ipcMemArea( pMemSet ), pMemCB,
               sizeof( OSSMemCB ) + pMemCB->fixedSize ) ;
   ipcMemDetach( pMemHdl ) ;
   free( pMemHdl ) ;

exit :
   return osserr ;
}


/******************************************************************************

   Name
      ossIPCMemClose

   Function
      Marks an IPC memory resource to be removed by OS when all users deatched.

   Inputs
      1. memRes   - memory resource


   Normal Returns
      OSS_OK

   Error Returns
      OSS_ERR_FAILED

******************************************************************************/
OSSErr OSS_API ossIPCMemClose(OSSMemRes memRes) {
   	OSSMemHdl *pMemHdl = (OSSMemHdl*) memRes;
	OSSErr	osserr = OSS_OK;

#if SQLUNIX
    if (-1 == shmctl(pMemHdl->id, IPC_RMID, 0)) osserr = OSS_ERR_FAILED;
#endif

	return osserr;
}

/******************************************************************************

   Name
      ossIPCMemAttach

   Function
      Attach to an IPC memory resource

   Inputs
      2. pResName
         Optional resource name.

      3. memID
         Unique memory resource identifier.

      4. pBaseAddress
         Points to the memory address in the calling process's address
         space where the memory resource should begin.  If pBaseAddress
         is NULL, then the starting address of the memory resource will
         be assigned by the operating system.

   Outputs
      1. pMemRes
         IPC memory resource handle.

   Normal Returns
      OSS_OK

   Error Returns
      OSS_ERR_FAILED
	  OSS_ERR_NOTFOUND
      OSS_ERR_IPC_NOTFOUND
      OSS_ERR_IPC_OPEN_MAP_FAILED
      OSS_ERR_IPC_MEM_MAP_FAILED
      OSS_ERR_IPC_GET_KEY_FAILED
	  OSS_ERR_IPC_SHMEM_GET_FAILED
      OSS_ERR_IPC_SHMEM_ATTACH_FAILED

 *****************************************************************************/
OSSErr OSS_API ossIPCMemAttach(
      OSSMemRes  * pMemRes,
      const char * pResName,
      OSSMemID     memID,
      const void * pBaseAddress )
{
   OSSErr      osserr = OSS_OK ;
   int         hash ;
   OSSMemHdl * pMemHdl  = NULL ;
   OSSMemSet * pMemSet  = NULL ;
   OSSMemCB  * pMemCB   = NULL ;
   OSSMemCB  * pFirstCB = NULL ;

   if ( NULL == pResName ) pResName = OSS_RES_NAME ;

   pMemHdl = (OSSMemHdl *)malloc( sizeof( OSSMemHdl ) ) ;
   memset( pMemHdl, 0, sizeof( *pMemHdl ) ) ;

   osserr = ipcMemAttach( pResName, pMemHdl, false, pBaseAddress ) ;
   if ( OSS_OK != osserr )
   {
      goto exit ;
   }

   pMemSet = pMemHdl->pMemSet ;

   hash = ipcHashMemID( memID ) ;

   ossLatchGet( &(pMemSet->hashExs) ) ;      /* 186134 */

   pMemCB = ipcMemOffsetToCB( pMemSet, pMemSet->hashCBs[hash] ) ;
   while ( ( NULL != pMemCB ) && ( memID != pMemCB->memID ) )
   {
      pMemCB = ipcMemOffsetToCB( pMemSet, pMemCB->nextCB ) ;
   }

   if ( NULL == pMemCB )
   {
      ossLatchRelease( &(pMemSet->hashExs) ) ;      /* 186134 */
      osserr = OSS_ERR_IPC_NOTFOUND ;
      goto exit ;
   }

   ossLatchRelease( &(pMemSet->hashExs) ) ;         /* 186134 */

   pMemHdl->marker     = OSS_MARKER_MEM ;
   pMemHdl->pMemSet    = pMemSet ;
   pMemHdl->pMemCB     = pMemCB ;
   pMemHdl->pFixedArea = ( pMemCB->fixedSize ) ? ( (char *)( pMemCB + 1 ) ) : 0 ;

exit :
   if ( OSS_OK != osserr )
   {
      if ( pMemHdl )
      {
         if ( pMemSet )
         {
            ipcMemDetach( pMemHdl ) ;
         }
         free( pMemHdl ) ;
         pMemHdl = NULL ;
      }
  }

  *pMemRes = (OSSMemRes)pMemHdl ;
  return osserr ;
}


OSSErr OSS_API ossIPCMemDetach( OSSMemRes memRes )
{
   OSSErr      osserr  = OSS_OK ;
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;
   OSSMemSet * pMemSet = NULL ;

   if ( !isValidOSSMemHdl( pMemHdl ) )
   {
      osserr = OSS_ERR_INVALID ;
      goto exit ;
   }

   pMemSet = pMemHdl->pMemSet ;
   pMemHdl->marker = 0 ;
   ipcMemDetach( pMemHdl ) ;
   free( pMemHdl ) ;

exit :
   return osserr ;
}


void * OSS_API ossIPCMemGetFixed( OSSMemRes memRes )
{
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;

   if ( isValidOSSMemHdl( pMemHdl ) )
   {
      return pMemHdl->pFixedArea ;
   }
   else
   {
      return NULL ;
   }
}

/* 172437 begin
ossIPCMemGetRealHandle - returns the real handle of the shared memory buffer
*/
#ifdef SQLWINT
HANDLE OSS_API ossIPCMemGetRealHandle( OSSMemRes memRes )
{
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;

   if ( isValidOSSMemHdl( pMemHdl ) )
   {
      return pMemHdl->handle ;
   }
   else
   {
      return NULL ;
   }
}
#elif SQLUNIX
int OSS_API ossIPCMemGetRealHandle( OSSMemRes memRes )
{
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;

   if ( isValidOSSMemHdl( pMemHdl ) )
   {
      return pMemHdl->id ;
   }
   else
   {
      return 0 ;   /* 186134 - change to return 0 instead of NULL to fix compile error */
   }
}
#endif
/* 172437 end */


OSSErr OSS_API ossIPCMemAllocBlk(
      OSSMemRes memRes,
      size_t    size,
      void   ** ppBlkPtr )
{
   OSSErr      osserr  = OSS_OK ;
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;
   OSSMemSet * pMemSet = NULL ;
   OSSMemCB  * pMemCB  = NULL ;
   OSSMemBlk * pMemBlk = NULL ;
   OSSMemBlk * pBlk    = NULL ;
   size_t      reqSize = 0 ;

   if ( !isValidOSSMemHdl( pMemHdl )
         || ( OSS_MARKER_MEM != pMemHdl->pMemCB->marker ) )
   {
      osserr = OSS_ERR_INVALID ;
      goto exit ;
   }

   pMemSet = pMemHdl->pMemSet ;
   pMemCB  = pMemHdl->pMemCB  ;

   if ( pMemCB->curBlksSize + size > pMemCB->maxBlksSize )
   {
      osserr = OSS_ERR_IPC_NOMEMORY ;
      goto exit ;
   }

   reqSize = size + sizeof( OSSMemBlk ) ;
   pMemBlk = (OSSMemBlk *)ossMemAlloc( ipcMemArea( pMemSet ), reqSize ) ;
   if ( NULL == pMemBlk )
   {
      osserr = OSS_ERR_IPC_NOMEMORY ;
      goto exit ;
   }

   ossLatchGet( &(pMemCB->blksExs) ) ;      /* 186134 */

   pMemBlk->size    = size ;
   pMemBlk->marker  = OSS_MARKER_MEM ;
   pMemBlk->nextBlk = pMemCB->rootBlk ;
   pMemBlk->prevBlk = 0 ;

   pBlk = ipcMemOffsetToBlk( pMemSet, pMemCB->rootBlk ) ;
   if ( NULL != pBlk )
   {
      pBlk->prevBlk = ipcMemBlkToOffset( pMemSet, pMemBlk ) ;
   }
   pMemCB->rootBlk = ipcMemBlkToOffset( pMemSet, pMemBlk ) ;
   pMemCB->curBlksSize += size ;

   ossLatchRelease( &(pMemCB->blksExs) ) ;     /* 186134 */

   *ppBlkPtr = (void *)( pMemBlk + 1 ) ;

exit :
   return osserr ;
}


void OSS_API ossIPCMemFreeBlk( OSSMemRes memRes, void * pBlkPtr )
{
   OSSMemHdl * pMemHdl = NULL ;
   OSSMemSet * pMemSet = NULL ;
   OSSMemCB  * pMemCB  = NULL ;
   OSSMemBlk * pMemBlk = NULL ;
   OSSMemBlk * pBlk    = NULL ;
   size_t      reqSize = 0 ;

   pMemHdl = (OSSMemHdl *)memRes ;
   pMemBlk = (OSSMemBlk *)pBlkPtr ;
   pMemBlk-- ;

   if ( !isValidOSSMemHdl( pMemHdl )
         || ( OSS_MARKER_MEM != pMemHdl->pMemCB->marker )
         || ( OSS_MARKER_MEM != pMemBlk->marker ) )
   {
      goto exit ;
   }

   pMemSet = pMemHdl->pMemSet ;
   pMemCB  = pMemHdl->pMemCB  ;

   ossLatchGet( &(pMemCB->blksExs) ) ;      /* 186134 */

   if ( 0 == pMemBlk->prevBlk )
   {
      pMemCB->rootBlk = pMemBlk->nextBlk ;
   }
   else
   {
      pBlk = ipcMemOffsetToBlk( pMemSet, pMemBlk->prevBlk ) ;
      pBlk->nextBlk = pMemBlk->nextBlk ;
   }

   if ( 0 != pMemBlk->nextBlk )
   {
      pBlk = ipcMemOffsetToBlk( pMemSet, pMemBlk->nextBlk ) ;
      pBlk->prevBlk = pMemBlk->prevBlk ;
   }

   pMemCB->curBlksSize -= pMemBlk->size ;
   pMemBlk->marker = 0 ;

   ossLatchRelease( &(pMemCB->blksExs) ) ;      /* 186134 */

   reqSize = pMemBlk->size + sizeof( OSSMemBlk ) ;
   ossMemFree( ipcMemArea( pMemSet ), pMemBlk, reqSize ) ;

exit :
   return ;
}


size_t OSS_API ossIPCMemBlk2Offset( OSSMemRes memRes, void * pBlkPtr )
{
   size_t      offset  = 0 ;
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;

   if ( isValidOSSMemHdl( pMemHdl ) )
   {
      offset = ipcMemPtrToOffset( pMemHdl->pMemSet, pBlkPtr ) ;
   }
   return offset ;
}


void * OSS_API ossIPCMemOffset2Blk( OSSMemRes memRes, size_t blkOffset )
{
   void      * pBlkPtr = NULL ;
   OSSMemHdl * pMemHdl = (OSSMemHdl *)memRes ;

   if ( isValidOSSMemHdl( pMemHdl ) )
   {
      pBlkPtr = ipcMemOffsetToPtr( pMemHdl->pMemSet, blkOffset ) ;
   }
   return pBlkPtr ;
}


#if defined(__linux__) && defined(USE_PTHREAD_ATOMICS) && defined(ENW)


void pthr_atom_mutexCreate(pthr_atom_critsec_t *lock) {
#ifdef _WIN32
	InitializeCriticalSection(lock);
#else
	pthread_mutex_init(lock, 0); /* unlocked by default */
#endif
}

void pthr_atom_mutexEnter(pthr_atom_critsec_t *lock) {
#ifdef _WIN32
	EnterCriticalSection(lock);
#else
	pthread_mutex_lock(lock);
#endif
}

void pthr_atom_mutexExit(pthr_atom_critsec_t *lock) {
#ifdef _WIN32
	LeaveCriticalSection(lock);
#else
	pthread_mutex_unlock(lock);
#endif
}

void pthr_atom_mutexDestroy(pthr_atom_critsec_t *lock) {
#ifdef _WIN32
	DeleteCriticalSection(lock);
#else
	pthread_mutex_destroy(lock);
#endif
}


/* Additional generic locking code for Linux */

	int sqloxchg(OSSLatch *latch, int * addr, int value) {
		int result = -1;
		int insideMutex = 0;		
		
		if(globalLatchLock != 0 && globalLatchLockInit) {
			
			insideMutex = 1;
			pthr_atom_mutexEnter(globalLatchLock);
		} else {
		}
		
		result = *addr;
	
		*addr = value;
		
		if(insideMutex) {
			pthr_atom_mutexExit(globalLatchLock);
		}
	
		
		return result;
	}

	void initLatchLock(OSSLatch *latch) {
		
		oss_unlock(latch);
	}	
	
#endif
