shithub: openh264

Download patch

ref: a3f606e58a09c371893a1ece7d5d6c5b9eeb10ba
parent: bc566f0923d099531a573220ae7e83056f52da52
author: Sijia Chen <[email protected]>
date: Thu Oct 15 07:17:29 EDT 2015

replacement of std::list for m_cBusyThreads
https://rbcommons.com/s/OpenH264/r/1320/

--- a/codec/build/iOS/common/common.xcodeproj/project.pbxproj
+++ b/codec/build/iOS/common/common.xcodeproj/project.pbxproj
@@ -58,6 +58,7 @@
 		0DD32A911B467C73009181A1 /* WelsThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WelsThread.h; sourceTree = "<group>"; };
 		0DD32A921B467C73009181A1 /* WelsThreadPool.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WelsThreadPool.h; sourceTree = "<group>"; };
 		0DD32A931B468F77009181A1 /* WelsThreadPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WelsThreadPool.cpp; sourceTree = "<group>"; };
+		0DEA477E1BB36FE100ADD134 /* WelsList.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WelsList.h; sourceTree = "<group>"; };
 		4C3406B218D96EA600DFA14A /* arm_arch_common_macro.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = arm_arch_common_macro.S; sourceTree = "<group>"; };
 		4C3406B318D96EA600DFA14A /* deblocking_neon.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = deblocking_neon.S; sourceTree = "<group>"; };
 		4C3406B418D96EA600DFA14A /* expand_picture_neon.S */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.asm; path = expand_picture_neon.S; sourceTree = "<group>"; };
@@ -147,6 +148,7 @@
 				4C3406C118D96EA600DFA14A /* typedefs.h */,
 				5BA8F2BE19603F3500011CE4 /* wels_common_defs.h */,
 				0DB71EF31BAB273500EABC51 /* WelsCircleQueue.h */,
+				0DEA477E1BB36FE100ADD134 /* WelsList.h */,
 				0DD32A8E1B467B83009181A1 /* WelsLock.h */,
 				0DD32A8F1B467C73009181A1 /* WelsTask.h */,
 				0DD32A901B467C73009181A1 /* WelsTaskThread.h */,
--- a/codec/common/inc/WelsCircleQueue.h
+++ b/codec/common/inc/WelsCircleQueue.h
@@ -91,7 +91,6 @@
             return true;
           }
         }
-
       }
     }
     return false;
@@ -121,7 +120,7 @@
       m_iCurrentListEnd = 0;
     }
     if (m_iCurrentListEnd == m_iCurrentListStart) {
-      int32_t ret = ExpandList();
+      int32_t ret = ExpandQueue();
       if (ret) {
         return 1;
       }
@@ -129,7 +128,7 @@
     return 0;
   }
 
-  int32_t ExpandList() {
+  int32_t ExpandQueue() {
     TNodeType** tmpCurrentTaskQueue = static_cast<TNodeType**> (malloc (m_iMaxNodeCount * 2 * sizeof (TNodeType*)));
     if (tmpCurrentTaskQueue == NULL) {
       return 1;
@@ -158,7 +157,6 @@
   int32_t m_iMaxNodeCount;
   TNodeType** m_pCurrentQueue;
 };
-
 
 }
 
--- /dev/null
+++ b/codec/common/inc/WelsList.h
@@ -1,0 +1,233 @@
+/*!
+ * \copy
+ *     Copyright (c)  2009-2015, Cisco Systems
+ *     All rights reserved.
+ *
+ *     Redistribution and use in source and binary forms, with or without
+ *     modification, are permitted provided that the following conditions
+ *     are met:
+ *
+ *        * Redistributions of source code must retain the above copyright
+ *          notice, this list of conditions and the following disclaimer.
+ *
+ *        * 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.
+ *
+ *     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.
+ *
+ *
+ * \file    WelsList
+ *
+ * \brief   for the list function needed in ThreadPool
+ *
+ * \date    9/27/2015 Created
+ *
+ *************************************************************************************
+ */
+
+
+#ifndef _WELS_LIST_H_
+#define _WELS_LIST_H_
+
+#include "typedefs.h"
+
+namespace WelsCommon {
+
+template<typename TNodeType>
+struct SNode {
+  TNodeType* pPointer;
+  SNode* pPrevNode;
+  SNode* pNextNode;
+};
+
+template<typename TNodeType>
+class CWelsList {
+ public:
+  CWelsList() {
+    m_iCurrentNodeCount = 0;
+    m_iMaxNodeCount = 50;
+    m_pCurrentList = static_cast<SNode<TNodeType>*> (malloc (m_iMaxNodeCount * sizeof (SNode<TNodeType>)));
+    //here using array storage to simulate list is to avoid the frequent malloc/free of Nodes which may cause fragmented memory
+    ResetStorage();
+  };
+  ~CWelsList() {
+    free (m_pCurrentList);
+  };
+
+  int32_t size() {
+    return m_iCurrentNodeCount;
+  }
+
+  bool push_back (TNodeType* pNode) {
+    m_pCurrent->pPointer = pNode;
+    if (0 == m_iCurrentNodeCount) {
+      m_pFirst = m_pCurrent;
+    }
+
+    m_iCurrentNodeCount++;
+    if (m_iCurrentNodeCount == m_iMaxNodeCount) {
+      if (!ExpandList()) {
+        return false;
+      }
+    }
+
+    SNode<TNodeType>* pNext = FindNextStorage();
+    m_pCurrent->pNextNode = pNext;
+    pNext->pPrevNode = m_pCurrent;
+    m_pCurrent = pNext;
+
+    return true;
+  }
+
+  TNodeType* begin() {
+    if (m_pFirst) {
+      return m_pFirst->pPointer;
+    }
+    return NULL;
+  }
+
+  void pop_front() {
+    if (m_iCurrentNodeCount == 0) {
+      return;
+    }
+
+    SNode<TNodeType>* pTemp = m_pFirst;
+    if (m_iCurrentNodeCount > 0) {
+      m_iCurrentNodeCount --;
+    }
+
+    if (0 == m_iCurrentNodeCount) {
+      ResetStorage();
+    } else {
+      m_pFirst = m_pFirst->pNextNode;
+      m_pFirst->pPrevNode = NULL;
+
+      CleanOneNode (pTemp);
+    }
+  }
+
+  bool erase (TNodeType* pNode) {
+    if (0 == m_iCurrentNodeCount) {
+      return false;
+    }
+
+    SNode<TNodeType>* pTemp = m_pFirst;
+    do {
+      if (pNode == pTemp->pPointer) {
+        if (pTemp->pPrevNode) {
+          pTemp->pPrevNode->pNextNode = pTemp->pNextNode;
+        } else {
+          m_pFirst = pTemp->pNextNode;
+        }
+
+        if (pTemp->pNextNode) {
+          pTemp->pNextNode->pPrevNode = pTemp->pPrevNode;
+        }
+
+        CleanOneNode (pTemp);
+        m_iCurrentNodeCount --;
+        return true;
+      }
+
+      if (pTemp->pNextNode) {
+        pTemp = pTemp->pNextNode;
+      } else {
+        break;
+      }
+    } while (pTemp->pPointer && pTemp->pNextNode);
+    return false;
+  }
+
+ private:
+  bool ExpandList() {
+    SNode<TNodeType>* tmpCurrentList = static_cast<SNode<TNodeType>*> (malloc (m_iMaxNodeCount * 2 * sizeof (
+                                         SNode<TNodeType>)));
+    if (tmpCurrentList == NULL) {
+      return false;
+    }
+    InitStorage (tmpCurrentList, (m_iMaxNodeCount * 2) - 1);
+
+    SNode<TNodeType>* pTemp = m_pFirst;
+    for (int i = 0; ((i < m_iMaxNodeCount) && pTemp); i++) {
+      tmpCurrentList[i].pPointer = pTemp->pPointer;
+      pTemp = pTemp->pNextNode;
+    }
+
+    free (m_pCurrentList);
+    m_pCurrentList = tmpCurrentList;
+    m_iCurrentNodeCount = m_iMaxNodeCount;
+    m_iMaxNodeCount = m_iMaxNodeCount * 2;
+    m_pFirst = m_pCurrentList;
+    m_pCurrent = & (m_pCurrentList[m_iCurrentNodeCount - 1]);
+    return true;
+  }
+
+  void InitStorage (SNode<TNodeType>* pList, const int32_t iMaxIndex) {
+    pList[0].pPrevNode = NULL;
+    pList[0].pPointer = NULL;
+    pList[0].pNextNode = & (pList[1]);
+    for (int i = 1; i < iMaxIndex; i++) {
+      pList[i].pPrevNode = & (pList[i - 1]);
+      pList[i].pPointer = NULL;
+      pList[i].pNextNode = & (pList[i + 1]);
+    }
+    pList[iMaxIndex].pPrevNode = & (pList[iMaxIndex - 1]);
+    pList[iMaxIndex].pPointer = NULL;
+    pList[iMaxIndex].pNextNode = NULL;
+  }
+
+  SNode<TNodeType>* FindNextStorage() {
+
+    if (NULL != m_pCurrent->pNextNode) {
+      if (NULL == m_pCurrent->pNextNode->pPointer) {
+        return (m_pCurrent->pNextNode);
+      }
+    }
+
+    for (int32_t i = 0; i < m_iMaxNodeCount; i++) {
+      if (NULL == m_pCurrentList[i].pPointer) {
+        return (&m_pCurrentList[i]);
+      }
+    }
+    return NULL;
+  }
+
+  void CleanOneNode (SNode<TNodeType>* pSNode) {
+    pSNode->pPointer = NULL;
+    pSNode->pPrevNode = NULL;
+    pSNode->pNextNode = NULL;
+  }
+
+  void ResetStorage() {
+    m_pFirst = NULL;
+    m_pCurrent = m_pCurrentList;
+    InitStorage (m_pCurrentList, m_iMaxNodeCount - 1);
+  }
+
+  int32_t m_iCurrentNodeCount;
+  int32_t m_iMaxNodeCount;
+  SNode<TNodeType>* m_pCurrentList;
+  SNode<TNodeType>* m_pFirst;
+  SNode<TNodeType>* m_pCurrent;
+};
+
+}
+
+
+#endif
+
+
+
--- a/codec/common/inc/WelsThreadPool.h
+++ b/codec/common/inc/WelsThreadPool.h
@@ -47,6 +47,7 @@
 #include "WelsTask.h"
 #include "WelsTaskThread.h"
 #include "WelsCircleQueue.h"
+#include "WelsList.h"
 
 namespace WelsCommon {
 
@@ -84,8 +85,8 @@
   WELS_THREAD_ERROR_CODE CreateIdleThread();
   void           DestroyThread (CWelsTaskThread* pThread);
   WELS_THREAD_ERROR_CODE AddThreadToIdleQueue (CWelsTaskThread* pThread);
-  WELS_THREAD_ERROR_CODE AddThreadToBusyMap (CWelsTaskThread* pThread);
-  WELS_THREAD_ERROR_CODE RemoveThreadFromBusyMap (CWelsTaskThread* pThread);
+  WELS_THREAD_ERROR_CODE AddThreadToBusyList (CWelsTaskThread* pThread);
+  WELS_THREAD_ERROR_CODE RemoveThreadFromBusyList (CWelsTaskThread* pThread);
   void           AddTaskToWaitedList (IWelsTask* pTask);
   CWelsTaskThread*   GetIdleThread();
   IWelsTask*         GetWaitedTask();
@@ -99,7 +100,7 @@
   //std::list<IWelsTask*>    m_cWaitedTasks;
   CWelsCircleQueue<IWelsTask>* m_cWaitedTasks;
   CWelsCircleQueue<CWelsTaskThread>* m_cIdleThreads;
-  std::map<uintptr_t, CWelsTaskThread*>  m_cBusyThreads;
+  CWelsList<CWelsTaskThread>* m_cBusyThreads;
   IWelsThreadPoolSink*   m_pSink;
 
   CWelsLock   m_cLockPool;
--- a/codec/common/src/WelsThreadPool.cpp
+++ b/codec/common/src/WelsThreadPool.cpp
@@ -52,6 +52,7 @@
   m_pSink (pSink) {
   m_cWaitedTasks = new CWelsCircleQueue<IWelsTask>();
   m_cIdleThreads = new CWelsCircleQueue<CWelsTaskThread>();
+  m_cBusyThreads = new CWelsList<CWelsTaskThread>();
   m_iMaxThreadNum = 0;
 
   Init (iMaxThreadNum);
@@ -63,16 +64,17 @@
 
   delete m_cWaitedTasks;
   delete m_cIdleThreads;
+  delete m_cBusyThreads;
 }
 
 WELS_THREAD_ERROR_CODE CWelsThreadPool::OnTaskStart (CWelsTaskThread* pThread, IWelsTask* pTask) {
-  AddThreadToBusyMap (pThread);
+  AddThreadToBusyList (pThread);
 
   return WELS_THREAD_ERROR_OK;
 }
 
 WELS_THREAD_ERROR_CODE CWelsThreadPool::OnTaskStop (CWelsTaskThread* pThread, IWelsTask* pTask) {
-  RemoveThreadFromBusyMap (pThread);
+  RemoveThreadFromBusyList (pThread);
   AddThreadToIdleQueue (pThread);
 
   if (m_pSink) {
@@ -198,36 +200,19 @@
   return WELS_THREAD_ERROR_OK;
 }
 
-WELS_THREAD_ERROR_CODE CWelsThreadPool::AddThreadToBusyMap (CWelsTaskThread* pThread) {
+WELS_THREAD_ERROR_CODE CWelsThreadPool::AddThreadToBusyList (CWelsTaskThread* pThread) {
   CWelsAutoLock cLock (m_cLockBusyTasks);
-
-  uintptr_t id = pThread->GetID();
-
-  std::map<uintptr_t, CWelsTaskThread*>::iterator iter = m_cBusyThreads.find (id);
-
-  if (iter != m_cBusyThreads.end()) {
-    return WELS_THREAD_ERROR_GENERAL;
-  }
-
-  m_cBusyThreads[id] = pThread;
-
+  m_cBusyThreads->push_back (pThread);
   return WELS_THREAD_ERROR_OK;
 }
 
-WELS_THREAD_ERROR_CODE CWelsThreadPool::RemoveThreadFromBusyMap (CWelsTaskThread* pThread) {
+WELS_THREAD_ERROR_CODE CWelsThreadPool::RemoveThreadFromBusyList (CWelsTaskThread* pThread) {
   CWelsAutoLock cLock (m_cLockBusyTasks);
-
-  uintptr_t  id = pThread->GetID();
-
-  std::map<uintptr_t, CWelsTaskThread*>::iterator iter = m_cBusyThreads.find (id);
-
-  if (iter != m_cBusyThreads.end()) {
-    m_cBusyThreads.erase (iter);
+  if (m_cBusyThreads->erase(pThread)) {
+      return WELS_THREAD_ERROR_OK;
   } else {
-    return WELS_THREAD_ERROR_GENERAL;
+        return WELS_THREAD_ERROR_GENERAL;
   }
-
-  return WELS_THREAD_ERROR_OK;
 }
 
 void  CWelsThreadPool::AddTaskToWaitedList (IWelsTask* pTask) {
@@ -250,7 +235,7 @@
 }
 
 int32_t  CWelsThreadPool::GetBusyThreadNum() {
-  return static_cast<int32_t> (m_cBusyThreads.size());
+  return static_cast<int32_t> (m_cBusyThreads->size());
 }
 
 int32_t  CWelsThreadPool::GetIdleThreadNum() {
--- /dev/null
+++ b/test/common/CWelsListTest.cpp
@@ -1,0 +1,222 @@
+#include <gtest/gtest.h>
+#include "WelsList.h"
+
+using namespace WelsCommon;
+
+TEST (CWelsList, CWelsListOne) {
+  CWelsList<int> cTestList;
+  int a = 0;
+
+  for (int i = 0; i < 60; i++) {
+    cTestList.push_back (&a);
+    EXPECT_TRUE (1 == cTestList.size()) << "after push size=" << cTestList.size() ;
+
+    cTestList.pop_front();
+    EXPECT_TRUE (0 == cTestList.size()) << "after pop size=" << cTestList.size() ;
+  }
+}
+
+TEST (CWelsList, CWelsListTen) {
+  CWelsList<int> cTestList;
+  int a = 0;
+  int* pPointer = &a;
+
+  for (int j = 0; j < 10; j++) {
+
+    for (int i = 0; i < 10; i++) {
+      EXPECT_TRUE (i == cTestList.size()) << "before push size=" << cTestList.size() ;
+      cTestList.push_back (pPointer);
+    }
+    EXPECT_TRUE (10 == cTestList.size()) << "after push size=" << cTestList.size() ;
+
+
+    for (int i = 9; i >= 0; i--) {
+      cTestList.pop_front();
+      EXPECT_TRUE (i == cTestList.size()) << "after pop size=" << cTestList.size() ;
+    }
+  }
+}
+
+TEST (CWelsList, CWelsListExpand) {
+  CWelsList<int> cTestList;
+  int a = 0;
+  int* pPointer = &a;
+
+  const int kiIncreaseNum = (rand() % 100) + 1;
+  const int kiDecreaseNum = rand() % kiIncreaseNum;
+
+  for (int j = 0; j < 10; j++) {
+
+    for (int i = 0; i < kiIncreaseNum; i++) {
+      EXPECT_TRUE (cTestList.push_back (pPointer));
+    }
+    EXPECT_TRUE (kiIncreaseNum + j * (kiIncreaseNum - kiDecreaseNum) == cTestList.size()) << "after push size=" <<
+        cTestList.size() ;
+
+    for (int i = kiDecreaseNum; i > 0; i--) {
+      cTestList.pop_front();
+    }
+    EXPECT_TRUE ((j + 1) * (kiIncreaseNum - kiDecreaseNum) == cTestList.size()) << "after pop size=" << cTestList.size() ;
+  }
+}
+
+TEST (CWelsList, CWelsListOverPop) {
+  CWelsList<int> cTestList;
+  int a = 0;
+  int* pPointer = &a;
+
+  const int kiDecreaseNum = 30000;//(rand() % 65535) + 1;
+  const int kiIncreaseNum = rand() % kiDecreaseNum;
+
+  EXPECT_TRUE (0 == cTestList.size());
+  cTestList.pop_front();
+  EXPECT_TRUE (0 == cTestList.size());
+
+  for (int i = 0; i < kiIncreaseNum; i++) {
+    EXPECT_TRUE (cTestList.push_back (pPointer));
+  }
+
+  for (int i = kiDecreaseNum; i > 0; i--) {
+    cTestList.pop_front();
+  }
+
+  EXPECT_TRUE (0 == cTestList.size());
+}
+
+
+void EraseOneInList (CWelsList<int>& cTestList, int* pPointer) {
+  int iPrevSize = cTestList.size();
+  EXPECT_TRUE (cTestList.erase (pPointer));
+  EXPECT_TRUE (cTestList.size() == (iPrevSize - 1));
+}
+
+TEST (CWelsList, CWelsListEraseOne) {
+#define TEST_LEN (4)
+  CWelsList<int> cTestList;
+  int a[TEST_LEN];
+  int* pPointer;
+
+  for (int i = 0; i < TEST_LEN; i++) {
+    a[i] = i;
+    cTestList.push_back (&a[i]);
+  }
+
+  EXPECT_TRUE (cTestList.size() == TEST_LEN);
+
+  int iEraseIdx = rand() % TEST_LEN;
+  EraseOneInList (cTestList, &a[iEraseIdx]);
+  EXPECT_TRUE (cTestList.size() == (TEST_LEN - 1));
+
+  for (int i = 0; i < TEST_LEN; i++) {
+    pPointer = cTestList.begin();
+    cTestList.pop_front();
+    if (!pPointer) {
+      EXPECT_TRUE (cTestList.size() == 0);
+      break;
+    }
+    if (i < iEraseIdx) {
+      EXPECT_TRUE (a[i] == (*pPointer));
+    } else {
+      EXPECT_TRUE (a[i + 1] == (*pPointer));
+    }
+  }
+
+  EXPECT_TRUE (0 == cTestList.size());
+}
+
+TEST (CWelsList, CWelsListEraseAll) {
+#define TEST_LEN (4)
+  CWelsList<int> cTestList;
+  int data[TEST_LEN];
+  int eraseidx[TEST_LEN] = {0};
+  int* pPointer;
+
+  for (int i = 0; i < TEST_LEN; i++) {
+    data[i] = i;
+    cTestList.push_back (&data[i]);
+  }
+  EXPECT_TRUE (cTestList.size() == TEST_LEN);
+
+  int iCurrentLen = TEST_LEN;
+  do {
+    int iEraseIdx = rand() % TEST_LEN;
+    if (0 == eraseidx[iEraseIdx]) {
+      eraseidx[iEraseIdx] = 1;
+      EraseOneInList (cTestList, &data[iEraseIdx]);
+      EXPECT_TRUE (cTestList.size() == (--iCurrentLen));
+    }
+    EXPECT_FALSE (cTestList.erase (&data[iEraseIdx]));
+
+    if (cTestList.size() == 0) {
+      break;
+    }
+
+    pPointer = cTestList.begin();
+    for (int i = 0; i < TEST_LEN; i++) {
+      if ((*pPointer) == data[i]) {
+        EXPECT_TRUE (eraseidx[i] == 0);
+        break;
+      }
+    }
+  } while (cTestList.size());
+  EXPECT_TRUE (0 == cTestList.size());
+}
+
+TEST (CWelsList, CWelsListEraseAndExpand) {
+#define TEST_LEN_10 (10)
+  CWelsList<int> cTestList;
+  int data[TEST_LEN_10];
+  int eraseidx[TEST_LEN_10] = {0};
+  int* pPointer;
+
+  for (int i = 0; i < TEST_LEN_10; i++) {
+    data[i] = i;
+    cTestList.push_back (&data[i]);
+  }
+  EXPECT_TRUE (cTestList.size() == TEST_LEN_10);
+
+  //erase some
+  int iCurrentLen = TEST_LEN_10;
+  do {
+    int iEraseIdx = rand() % TEST_LEN_10;
+    if (0 == eraseidx[iEraseIdx]) {
+      eraseidx[iEraseIdx] = 1;
+      EraseOneInList (cTestList, &data[iEraseIdx]);
+      EXPECT_TRUE (cTestList.size() == (--iCurrentLen));
+    }
+    EXPECT_FALSE (cTestList.erase (&data[iEraseIdx]));
+
+    if (cTestList.size() == 0) {
+      break;
+    }
+
+    pPointer = cTestList.begin();
+    for (int i = 0; i < TEST_LEN_10; i++) {
+      if ((*pPointer) == data[i]) {
+        EXPECT_TRUE (eraseidx[i] == 0);
+        break;
+      }
+    }
+  } while (iCurrentLen > (TEST_LEN_10 / 2));
+  EXPECT_TRUE (iCurrentLen == cTestList.size());
+
+  //expand
+  int iAddLen = rand() % 65535;
+  for (int i = 0; i < iAddLen; i++) {
+    EXPECT_TRUE (cTestList.push_back (&data[0]));
+  }
+  EXPECT_TRUE ((iCurrentLen + iAddLen) == cTestList.size());
+  EraseOneInList (cTestList, &data[0]);
+  EXPECT_TRUE ((iCurrentLen + iAddLen - 1) == cTestList.size()) << (iCurrentLen + iAddLen - 1)  << "_" <<
+      cTestList.size();
+
+  //clear up
+  do {
+    pPointer = cTestList.begin();
+    EXPECT_TRUE (NULL != pPointer);
+    cTestList.pop_front();
+  } while (cTestList.size());
+
+  EXPECT_TRUE (0 == cTestList.size());
+}
+
--- a/test/common/targets.mk
+++ b/test/common/targets.mk
@@ -2,6 +2,7 @@
 COMMON_UNITTEST_CPP_SRCS=\
 	$(COMMON_UNITTEST_SRCDIR)/ExpandPicture.cpp\
 	$(COMMON_UNITTEST_SRCDIR)/CWelsCircleQueue.cpp\
+	$(COMMON_UNITTEST_SRCDIR)/CWelsListTest.cpp\
 
 COMMON_UNITTEST_OBJS += $(COMMON_UNITTEST_CPP_SRCS:.cpp=.$(OBJ))