/*
 * IPRPC - Inter Process Remote Procedure Call
 *
 * Copyright (C) 2012-2013 by Hiroyuki KAJIURA. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification, 
 * are permitted provided that the following conditions are met:
 *
 *     1:Redistributions of source code must retain the above copyright notice, 
 *       this list of conditions and the following disclaimer.
 *     2:Redistributions in binary form must reproduce the above copyright notice, 
 *       this list of conditions and the following disclaimer in the documentation 
 *       and/or other materials provided with the distribution.
 *     3:Neither the name of copyright owner nor the names of its contributors 
 *       may be used to endorse or promote products derived from this software 
 *       without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE 
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

#define	IPC_LIB_MAIN

#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif

#include	"ipcInfo.h"
#include	"ipcLog.h"
#include	"ipcType.h"
#include	"ipcSyscall.h"
#include	"ipcLib.h"
#include	"ipcShm.h"

#ifdef	USE_SEMAPHORE
#define	IPC_TACK_CNTL_SEM_INIT_NUM					(0)
#define	IPC_TACK_CNTL_SEM_PRIVATE					(0)
#define	IPC_TACK_CNTL_SEM_SHARED					(0)
#endif	/* USE_SEMAPHORE */

static uint32_t* RpcHashRandTable = (uint32_t*)NULL;

static uint32_t RPC_nextHashEntry(uint32_t hashId,uint32_t tblSize);
static uint32_t RPC_getHashVal(uint8_t *key,uint32_t keyLength,uint32_t tableSize);

static RpcBool_t RPC_checkTimeoutInternal(struct timeval *timeout,struct timeval *curTime);

#ifdef	USE_CANCEL_TASK
static void RPC_taskCntlCleanup(void *arg);
#endif	/* USE_CANCEL_TASK */

/* ----- RPC-LIB APIs ----- */

/*--- TASK CONTROL LIBRARY ---*/

extern RpcResult_t RPC_CreateTaskControl(RpcBool_t IpcFlag,RpcTaskControl_t *tskCntl) {
	RpcResult_t ret;

	if(tskCntl == (RpcTaskControl_t*)NULL)
		return RPC_PARAM_ERROR;
#ifndef	USE_SEMAPHORE
	if((ret = RPC_MutexAttrInit(&(tskCntl->mutexAttr))) != RPC_SUCCESS) {
		return ret;
	}
#ifdef	_LINUX_
	if(RPC_MutexAttrSetType(&(tskCntl->mutexAttr), PTHREAD_MUTEX_ADAPTIVE_NP) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
#else	/* _LINUX_ */
	if(RPC_MutexAttrSetType(&(tskCntl->mutexAttr), PTHREAD_MUTEX_RECURSIVE) != RPC_SUCCESS) {
		return RPC_FATAL_ERROR;
	}
#endif	/* _LINUX_ */
	if((ret = RPC_MutexAttrSetPshared(&(tskCntl->mutexAttr), IpcFlag?PTHREAD_PROCESS_SHARED:PTHREAD_PROCESS_PRIVATE)) != RPC_SUCCESS) {
		return ret;
	}
	if((ret = RPC_InitMutex(&(tskCntl->mutexId),&(tskCntl->mutexAttr))) != RPC_SUCCESS) {
		return ret;
	}
	if((ret = RPC_CondAttrInit(&(tskCntl->condAttr))) != RPC_SUCCESS) {
		(void)RPC_MutexDestroy(&(tskCntl->mutexId));
		return ret;
	}
	if((ret = RPC_CondAttrSetPshared(&(tskCntl->condAttr), IpcFlag?PTHREAD_PROCESS_SHARED:PTHREAD_PROCESS_PRIVATE)) != RPC_SUCCESS) {
		(void)RPC_MutexDestroy(&(tskCntl->mutexId));
		return ret;
	}
	if((ret = RPC_InitCond(&(tskCntl->condId),&(tskCntl->condAttr))) != RPC_SUCCESS) {
		(void)RPC_MutexDestroy(&(tskCntl->mutexId));
		return ret;
	}
	tskCntl->cntlSignal = RPC_FALSE;
	tskCntl->inWaitTask = RPC_FALSE;
#else	/* USE_SEMAPHORE */
	if((ret = RPC_SemInit(&(tskCntl->semId),IpcFlag?IPC_TACK_CNTL_SEM_SHARED:IPC_TACK_CNTL_SEM_PRIVATE,IPC_TACK_CNTL_SEM_INIT_NUM)) != RPC_SUCCESS) {
		return ret;		
	}
#endif	/* USE_SEMAPHORE */
	return RPC_SUCCESS;
}

extern void RPC_DestroyTaskControl(RpcTaskControl_t *tskCntl) {
	if(tskCntl == (RpcTaskControl_t*)NULL)
		return;
#ifndef	USE_SEMAPHORE
	(void)RPC_CondDestroy(&(tskCntl->condId));
	(void)RPC_MutexDestroy(&(tskCntl->mutexId));
#else	/* USE_SEMAPHORE */
	(void)RPC_SemDestroy(&(tskCntl->semId));
#endif	/* USE_SEMAPHORE */
}

extern RpcResult_t RPC_TaskControlWait(RpcTaskControl_t *tskCntl) {
	RpcResult_t ret;

	if(tskCntl == (RpcTaskControl_t*)NULL)
		return RPC_PARAM_ERROR;
#ifndef	USE_SEMAPHORE
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"IN RPC_TaskControlWait tskCntl:0x%Lx; inWaitTask:%d; cntlSignal:%d;",
//	tskCntl,tskCntl->inWaitTask,tskCntl->cntlSignal);
	if((ret = RPC_MutexLock(&(tskCntl->mutexId))) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_MutexLock ret:%s",RPC_LogConvertResultCode(ret));
		return ret;			
	}
#ifdef	USE_CANCEL_TASK
	RPC_TaskPushCleanup(RPC_taskCntlCleanup,(void*)tskCntl);
#endif	/* USE_CANCEL_TASK */
	if(tskCntl->inWaitTask) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_ERROR,"A Task Is Already Waiting A Signal");
		(void)RPC_MutexUnlock(&(tskCntl->mutexId));
		return RPC_BUSY_ERROR;
	}
	tskCntl->inWaitTask = RPC_TRUE;
	if(!(tskCntl->cntlSignal)) {
		if((ret = RPC_CondWait(&(tskCntl->condId),&(tskCntl->mutexId))) != RPC_SUCCESS) {
			tskCntl->inWaitTask = RPC_FALSE;
			(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_CondWait ret:%s",RPC_LogConvertResultCode(ret));
			(void)RPC_MutexUnlock(&(tskCntl->mutexId));
			return ret;
		}
	}
	tskCntl->cntlSignal = RPC_FALSE;
	tskCntl->inWaitTask = RPC_FALSE;
#ifdef	USE_CANCEL_TASK
	RPC_TaskPopCleanup(0);
#endif	/* USE_CANCEL_TASK */
	(void)RPC_MutexUnlock(&(tskCntl->mutexId));
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"OUT RPC_TaskControlWait tskCntl:0x%Lx; inWaitTask:%d; cntlSignal:%d;",
//	tskCntl,tskCntl->inWaitTask,tskCntl->cntlSignal);
#else	/* USE_SEMAPHORE */
	if((ret = RPC_SemWait(&(tskCntl->semId))) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_SemWait ret:%s",RPC_LogConvertResultCode(ret));
		return ret;						
	}
#endif	/* USE_SEMAPHORE */
	return RPC_SUCCESS;
}

extern RpcResult_t RPC_TaskControlTimedWait(RpcTaskControl_t *tskCntl, struct timespec *specTime) {
	RpcResult_t ret;

//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"IN RPC_TaskControlTimedWait");
	if((tskCntl == (RpcTaskControl_t*)NULL)||(specTime == (struct timespec*)NULL))
		return RPC_PARAM_ERROR;
#ifndef	USE_SEMAPHORE
	if((ret = RPC_MutexLock(&(tskCntl->mutexId))) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_MutexLock ret:%s",RPC_LogConvertResultCode(ret));
		return ret;			
	}
#ifdef	USE_CANCEL_TASK
	RPC_TaskPushCleanup(RPC_taskCntlCleanup,(void*)tskCntl);
#endif	/* USE_CANCEL_TASK */
	if(tskCntl->inWaitTask) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_ERROR,"Already Waiting A Task");
		(void)RPC_MutexUnlock(&(tskCntl->mutexId));
		return RPC_BUSY_ERROR;
	}
	tskCntl->inWaitTask = RPC_TRUE;
	if(!(tskCntl->cntlSignal)) {
		if((ret = RPC_CondWaitTimeout(&(tskCntl->condId),&(tskCntl->mutexId),specTime)) != RPC_SUCCESS) {
			tskCntl->inWaitTask = RPC_FALSE;
			if(ret == RPC_TIMEOUT_ERROR)
				(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_WARNING, " TIMEOUT in RPC_CondWaitTimeout ret:%s",RPC_LogConvertResultCode(ret));
			else
				(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_CondWaitTimeout ret:%s",RPC_LogConvertResultCode(ret));
			(void)RPC_MutexUnlock(&(tskCntl->mutexId));
			return ret;
		}
	}
	tskCntl->cntlSignal = RPC_FALSE;
	tskCntl->inWaitTask = RPC_FALSE;
#ifdef	USE_CANCEL_TASK
	RPC_TaskPopCleanup(0);
#endif	/* USE_CANCEL_TASK */
	(void)RPC_MutexUnlock(&(tskCntl->mutexId));
#else	/* USE_SEMAPHORE */
	if((ret = RPC_SemTimedWait(&(tskCntl->semId),specTime)) != RPC_SUCCESS) {
		if(ret == RPC_TIMEOUT_ERROR)
			(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_WARNING, " TIMEOUT in RPC_SemTimedWait ret:%s",RPC_LogConvertResultCode(ret));
		else
			(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_SemTimedWait ret:%s",RPC_LogConvertResultCode(ret));
		return ret;
	}
#endif	/* USE_SEMAPHORE*/
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"OUT RPC_TaskControlTimedWait");
	return RPC_SUCCESS;
}

extern RpcResult_t RPC_TaskControlSignal(RpcTaskControl_t *tskCntl) {
	RpcResult_t ret;

	if(tskCntl == (RpcTaskControl_t*)NULL)
		return RPC_PARAM_ERROR;
#ifndef	USE_SEMAPHORE
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"IN RPC_TaskControlSignal tskCntl:0x%Lx; inWaitTask:%d; cntlSignal:%d;",
//	tskCntl,tskCntl->inWaitTask,tskCntl->cntlSignal);
#if	1
	if((ret = RPC_MutexLock(&(tskCntl->mutexId))) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_MutexLock ret:%s",RPC_LogConvertResultCode(ret));
		return ret;
	}
	tskCntl->cntlSignal = RPC_TRUE;
	if((ret = RPC_CondSignal(&(tskCntl->condId))) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_CondSignal ret:%s",RPC_LogConvertResultCode(ret));
		(void)RPC_MutexUnlock(&(tskCntl->mutexId));
		return ret;
	}
	(void)RPC_MutexUnlock(&(tskCntl->mutexId));
#else	/* 1 */
	tskCntl->cntlSignal = RPC_TRUE;
	if((ret = RPC_CondSignal(&(tskCntl->condId))) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_CondSignal ret:%s",RPC_LogConvertResultCode(ret));
		return ret;
	}
#endif	/* 1 */
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"OUT RPC_TaskControlSignal tskCntl:0x%Lx; inWaitTask:%d; cntlSignal:%d;",
//	tskCntl,tskCntl->inWaitTask,tskCntl->cntlSignal);
#else	/* USE_SEMAPHORE */
	if((ret = RPC_SemPost(&(tskCntl->semId))) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_SemPost ret:%s",RPC_LogConvertResultCode(ret));
		return ret;
	}
#endif	/* USE_SEMAPHORE */
	return RPC_SUCCESS;
}

extern void IPC_ResetTaskControl(RpcTaskControl_t *tskCntl) {
#ifndef	USE_SEMAPHORE
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"IN IPC_ResetTaskControl tskCntl:0x%Lx; inWaitTask:%d; cntlSignal:%d;",
//	tskCntl,tskCntl->inWaitTask,tskCntl->cntlSignal);
	{RpcResult_t ret;
	if((ret = RPC_MutexLock(&(tskCntl->mutexId))) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_FATAL, " ERROR in RPC_MutexLock ret:%s",RPC_LogConvertResultCode(ret));
		return ;
	}
	if(tskCntl->inWaitTask) {
		(void)RPC_CondSignal(&(tskCntl->condId));
		tskCntl->inWaitTask = RPC_FALSE;
	}
	tskCntl->cntlSignal = RPC_FALSE;
	(void)RPC_MutexUnlock(&(tskCntl->mutexId));
	}
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"OUT IPC_ResetTaskControl tskCntl:0x%Lx; inWaitTask:%d; cntlSignal:%d;",
//	tskCntl,tskCntl->inWaitTask,tskCntl->cntlSignal);
#else	/* USE_SEMAPHORE */
	if(tskCntl == (RpcTaskControl_t*)NULL)
		return;
	{int32_t val;
	for(;;) {
		(void)RPC_SemGetValue(&(tskCntl->semId),&val);
		if(val <= 0)
			break;
		if(RPC_SemWaitTry(&(tskCntl->semId)) != RPC_SUCCESS) {
			break;
		}
	}
	}
#endif	/* USE_SEMAPHORE */
}
/*--- HASH LIBRARY ---*/

extern void RPC_RegisterHashTable(uint32_t *hashRandTable) {
	RpcHashRandTable = hashRandTable;
}

extern RpcResult_t RPC_AddHashEntry(RpcHashEntry_t *hashTblTop,void *entry,uint8_t *val, uint32_t valSize,uint32_t tblSize) {
	uint32_t hashId;RpcHashEntry_t *hashTbl;int i;

	if((hashTblTop == (RpcHashEntry_t*)NULL)||(entry == (void*)NULL)||(val == (uint8_t*)NULL)||(valSize == 0))
		return RPC_PARAM_ERROR;
	hashId = RPC_getHashVal((uint8_t*)val,valSize,tblSize);
	hashTbl = &(hashTblTop[hashId]);
	for(i = 0;i < tblSize;i++) {
		if(hashTbl->usedFlag == RPC_FALSE) {
			hashTbl->entry = entry;
			hashTbl->usedFlag = RPC_TRUE;
			return RPC_SUCCESS;
		}
		hashId = RPC_nextHashEntry(hashId,tblSize);
		hashTbl = &(hashTblTop[hashId]);
	}
	return RPC_NO_MORE_RESOURCE;
}

extern RpcResult_t RPC_DeleteHashEntry(RpcHashEntry_t *hashTblTop,uint8_t *val, uint32_t valSize,uint32_t tblSize,RPC_CompareHashVal_t compareHashVal) {
	uint32_t hashId,nextId;RpcHashEntry_t *hashTbl,*nextTbl;int i;

	if((hashTblTop == (RpcHashEntry_t*)NULL)||(val == (uint8_t*)NULL)||(valSize == 0)||(compareHashVal == (RPC_CompareHashVal_t)NULL))
		return RPC_PARAM_ERROR;
	hashId = RPC_getHashVal(val,valSize,tblSize);
	hashTbl = &(hashTblTop[hashId]);
	for(i = 0;i < tblSize;i++) {
		if(hashTbl->entry == (void*)NULL) {
			return RPC_PARAM_ERROR;
		}
		if(compareHashVal(hashTbl->entry,val,valSize)) {
			nextId = RPC_nextHashEntry(hashId,tblSize);
			nextTbl = &(hashTblTop[nextId]);
			if(nextTbl->entry == (void*)NULL) {
				hashTbl->entry = (void*)NULL;
				hashTbl->usedFlag = RPC_FALSE;
			}
			else {
				hashTbl->usedFlag = RPC_FALSE;
			}
			return RPC_SUCCESS;
		}
		hashId = RPC_nextHashEntry(hashId,tblSize);
		hashTbl = &(hashTblTop[hashId]);
	}
	return RPC_PARAM_ERROR;
}

extern void *RPC_SearchEntryByHash(RpcHashEntry_t *hashTblTop, uint8_t *val, uint32_t valSize, uint32_t tblSize, RPC_CompareHashVal_t compareHashVal) {
	uint32_t hashId;RpcHashEntry_t *hashTbl;int i;

//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"IN RPC_SearchEntryByHash PID:0x%x; hashTop:0x%x; val:0x%x;(0x%x) valSize:%d; tblSize:%d;",
//		RPC_GetPid(),hashTblTop, val, (uint32_t)*val,valSize, tblSize);
	if((hashTblTop == (RpcHashEntry_t*)NULL)||(val == (uint8_t*)NULL)||(valSize == 0)||(compareHashVal == (RPC_CompareHashVal_t)NULL))
		return (void*)NULL;
	hashId = RPC_getHashVal(val,valSize,tblSize);
	hashTbl = &(hashTblTop[hashId]);
	for(i = 0;i < tblSize;i++) {
		if(hashTbl->entry == (void*)NULL) {
//			(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"OUT RPC_SearchEntryByHash NOT FOUND PID:0x%x; ",
//			RPC_GetPid());
			return (void*)NULL;
		}
		else if(hashTbl->usedFlag == RPC_TRUE) {
			if(compareHashVal(hashTbl->entry,val,valSize)) {
//				(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"OUT RPC_SearchEntryByHash FOUND PID:0x%x; ENTRY:0x%x;",
//					RPC_GetPid(), hashTbl->entry);
				return hashTbl->entry;
			}
		}
		hashId = RPC_nextHashEntry(hashId,tblSize);
		hashTbl = &(hashTblTop[hashId]);
	}
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG2,"OUT RPC_SearchEntryByHash NOT FOUND PID:0x%x; ",
//		RPC_GetPid());
	return (void*)NULL;
}

/*--- LIST LIBRARY --- */

extern void *RPC_AllocListData(RpcList_t **listTop,uint32_t entitySize,uint32_t allocId,RPC_AllocFunc_t allocFunc,RPC_FreeFunc_t freeFunc) {
	RpcList_t *list;

	if(listTop == (RpcList_t**)NULL)
		return (void*)NULL;
	if((list = (RpcList_t*)allocFunc(allocId,sizeof(RpcList_t))) == NULL) {
		return (void*)NULL;
	}
	if((list->entry = allocFunc(allocId,entitySize)) == NULL) {
		freeFunc(allocId,(void*)list);
		return (void*)NULL;
	}
	list->next = *listTop;
	*listTop = list;
	return list->entry;
}

extern void RPC_FreeListData(RpcList_t **listTop,void *delEntry,uint32_t allocId,RPC_FreeFunc_t freeFunc) {
	RpcList_t *list,*prev;

	if((listTop == (RpcList_t**)NULL)||(delEntry == (void*)NULL))
		return;
	prev = (RpcList_t*)NULL;
	for(list = *listTop;list != (RpcList_t*)NULL;list = (RpcList_t*)list->next) {
		if(delEntry == list->entry) {
			if(prev == (RpcList_t*)NULL) {
				(*listTop) = list->next;
			}
			else {
				prev->next = list->next;
			}
			freeFunc(allocId,(void*)list->entry);
			freeFunc(allocId,(void*)list);
			return;
		}
		prev = list;
	}
}

extern RpcList_t *RPC_RemoveListEntry(RpcList_t **listTop,void *remEntry) {
	RpcList_t *list,*prev;

	if((listTop == (RpcList_t**)NULL)||(remEntry == (void*)NULL))
		return;
	prev = (RpcList_t*)NULL;
	for(list = *listTop;list != (RpcList_t*)NULL;list = (RpcList_t*)list->next) {
		if(remEntry == list->entry) {
			if(prev == (RpcList_t*)NULL) {
				(*listTop) = list->next;
			}
			else {
				prev->next = list->next;
			}
			return list;
		}
		prev = list;
	}
	return (RpcList_t*)NULL;
}

extern void RPC_SearchAndFreeListData(RpcList_t **listTop,uint32_t id,uint32_t allocId,RPC_CompareFunc_t compareFunc,RPC_FreeFunc_t freeFunc) {
	RpcList_t *list,*prev;

	if(listTop == (RpcList_t**)NULL)
		return ;
	prev = (RpcList_t*)NULL;
	for(list = *listTop;list != (RpcList_t*)NULL;list = (RpcList_t*)list->next) {
		if(compareFunc(list,id)) {
			if(prev == (RpcList_t*)NULL) {
				(*listTop) = list->next;
			}
			else {
				prev->next = list->next;
			}
			freeFunc(allocId,(void*)list->entry);
			freeFunc(allocId,(void*)list);
			return;
		}
		prev = list;
	}
	return;
}

extern void *RPC_SearchListEntryById(RpcList_t *listTop,uint32_t id,RPC_CompareFunc_t compareFunc) {
	RpcList_t *list;

	for(list = listTop;list != (RpcList_t*)NULL;list = (RpcList_t*)list->next) {
		if(compareFunc(list,id)) {
			return (void*)list->entry;
		}
	}
	return (void*)NULL;
}

extern void *RPC_SearchListEntryByIds(RpcList_t *listTop,uint32_t id1,uint32_t id2,RPC_compareFuncByIds_t compareFunc) {
	RpcList_t *list;

	for(list = listTop;list != (RpcList_t*)NULL;list = (RpcList_t*)list->next) {
		if(compareFunc(list,id1,id2)) {
			return (void*)list->entry;
		}
	}
	return (void*)NULL;
}

extern void *RPC_SearchListEntryByString(RpcList_t *listTop,uint8_t *str,uint32_t strLen,RPC_compareFuncByString_t compareFunc) {
	RpcList_t *list;

	for(list = listTop;list != (RpcList_t*)NULL;list = (RpcList_t*)list->next) {
		if(compareFunc(list,str,strLen)) {
			return (void*)list->entry;
		}
	}
	return (void*)NULL;
}

#if	__WORDSIZE == 64
extern void *RPC_SearchListEntryByDwordId(RpcList_t *listTop, uint64_t id, RPC_CompareFuncDword_t compareFunc) {
	RpcList_t *list;

	for(list = listTop;list != (RpcList_t*)NULL;list = (RpcList_t*)list->next) {
		if(compareFunc(list,id)) {
			return (void*)list->entry;
		}
	}
	return (void*)NULL;
}
#endif	/* __WORDSIZE */

extern uint32_t RPC_CountNumOfListEntry(RpcList_t *listTop) {
	RpcList_t *list;uint32_t num = 0;

	for(list = listTop;list != (RpcList_t*)NULL;list = (RpcList_t*)list->next) {
		num++;
	}
	return num;
}

extern void *RPC_ShmAllocMemWrapper(uint32_t allocId,uint32_t size) {
	return IPC_AllocShmMem(allocId, size);
}

extern void RPC_ShmFreeMemWrapper(uint32_t allocId,void *area) {
	IPC_FreeShmMem(allocId,area);
}

extern void *RPC_LclAllocMemWrapper(uint32_t allocId,uint32_t size) {
	return RPC_AllocMem(size);
}

extern void RPC_LclFreeMemWrapper(uint32_t allocId,void *area) {
	RPC_FreeMem(area);
}

/* --- TIME LIBRARY --- */

extern RpcBool_t RPC_SetTimeout(uint32_t timeout, struct timeval *absTime) {
	struct timeval curTime;uint32_t errCode;

	if(absTime == (struct timeval*)NULL)
		return RPC_FALSE;
	if(RPC_GetTimeOfDay(&curTime,(struct timezone*)NULL,&errCode) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_ERROR,"ERROR IN  RPC_GetTimeOfDay ERR-CODE:%s;",RPC_LogConvertErrCode(errCode));
		return RPC_FALSE;
	}
//	(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_DEBUG3,"CUR TIME:%d.%d;",curTime.tv_sec,curTime.tv_usec);
	absTime->tv_sec = curTime.tv_sec;
	absTime->tv_usec = curTime.tv_usec + timeout*1000;
	absTime->tv_sec += ((absTime->tv_usec)/1000000);
	absTime->tv_usec = ((absTime->tv_usec)%1000000);
	return RPC_TRUE;
}

extern RpcBool_t RPC_CheckTimeout(struct timeval *timeout) {
	struct timeval curTime;

	if(timeout == (struct timeval*)NULL)
		return RPC_FALSE;
	return RPC_checkTimeoutInternal(timeout,&curTime);
}

extern RpcBool_t RPC_GetRestTimeout(struct timeval *absTime,uint32_t *restTimeout) {
	struct timeval curTime;

	if((absTime == (struct timeval*)NULL)||(restTimeout == (uint32_t*)NULL))
		return RPC_FALSE;
	if(!RPC_checkTimeoutInternal(absTime,&curTime)) {
		*restTimeout = 0;
		return RPC_FALSE;
	}
	*restTimeout = (uint32_t)((absTime->tv_sec - curTime.tv_sec)*1000 + (absTime->tv_usec - curTime.tv_usec)/1000);
	return RPC_TRUE;
}

extern void RPC_ConvertTime(struct timeval *timeVal,struct timespec *timeSpec) {
	if((timeVal == (struct timeval*)NULL)||(timeSpec == (struct timespec*)NULL))
		return;
	timeSpec->tv_sec = timeVal->tv_sec;
	timeSpec->tv_nsec = (timeVal->tv_usec) * 1000;
	timeSpec->tv_sec += ((timeSpec->tv_nsec)/1000000000);
	timeSpec->tv_nsec = ((timeSpec->tv_nsec)%1000000000);
}

extern void RPC_ConvertTimeoutToTimeval(uint32_t timeout,struct timeval *timeVal) {
	if(timeVal == (struct timeval*)NULL)
		return;
	timeVal->tv_sec = timeout /1000;
	timeVal->tv_usec = (timeout%1000)*1000000;
}

extern void RPC_ConvertTimevalToTimeout(struct timeval *timeVal,uint32_t *timeout) {
	if((timeVal == (struct timeval*)NULL)||(timeout == (uint32_t*)NULL))
		return;
	*timeout = timeVal->tv_sec * 1000;
	*timeout += (timeVal->tv_usec/1000);
}

/* ----- Internal Functions ----- */

static uint32_t RPC_nextHashEntry(uint32_t hashId,uint32_t tblSize) {
	return	((hashId+1) & (tblSize - 1));
}

static uint32_t RPC_getHashVal(uint8_t *key,uint32_t keyLength,uint32_t tableSize) {
	uint32_t i,j;uint32_t hashVal;

	if(RpcHashRandTable == NULL) {
		return 0;
	}
	hashVal = 0;
	i = j = 0;
	while( i < keyLength) {
		hashVal ^= RpcHashRandTable[j*IPC_HASH_RAND_NUM_SIZE_OF_TBL_NUM+key[i++]];
		j++;
		j &= (IPC_HASH_RAND_NUM_SIZE_OF_TBL_NUM -1);
	}
	return (hashVal & (tableSize - 1));
}

static RpcBool_t RPC_checkTimeoutInternal(struct timeval *timeout,struct timeval *curTime) {
	uint32_t errCode;

	if(RPC_GetTimeOfDay(curTime,(struct timezone*)NULL,&errCode) != RPC_SUCCESS) {
		(void)RPC_LogPrint(RPC_LOG_MODULE_LIB,RPC_LOG_LEVEL_ERROR,"ERROR IN  RPC_GetTimeOfDay ERR-CODE:%s;",RPC_LogConvertErrCode(errCode));
		return RPC_FALSE;
	}
	if(curTime->tv_sec > timeout->tv_sec)
		return RPC_FALSE;
	else if(curTime->tv_sec < timeout->tv_sec)
		return RPC_TRUE;
	else if(curTime->tv_usec >= timeout->tv_usec)
		return RPC_FALSE;
	else
		return RPC_TRUE;
}

#ifdef	USE_CANCEL_TASK
static void RPC_taskCntlCleanup(void *arg) {
#ifndef	USE_SEMAPHORE
	RpcTaskControl_t *tskCntl;

	tskCntl = (RpcTaskControl_t*)arg;
	(void)RPC_MutexUnlock(&(tskCntl->mutexId));
#endif	/* USE_SEMAPHORE */
}
#endif	/* USE_CANCEL_TASK */

/* ----- TEST Functions ----- */
