├── .gitignore ├── Makefile ├── README.md ├── include ├── BaseLib.h ├── Buffer.h ├── CSocket.h ├── Debug.h ├── Log.h ├── MemoryManager.h ├── Mint.h ├── MrswLock.h ├── MutexLock.h ├── Nonreentrant.h ├── Socket.h ├── Statistics.h ├── Thread.h ├── ThreadManager.h ├── ThreadPool.h ├── TonyLowDebug.h ├── Utils.h ├── confile.h ├── mylib.h └── std.h └── src ├── BaseLib.cpp ├── Buffer.cpp ├── CSocket.cpp ├── Debug.cpp ├── Log.cpp ├── MemoryManager.cpp ├── Mint.cpp ├── MrswLock.cpp ├── Nonreentrant.cpp ├── Statistics.cpp ├── ThreadPool.cpp ├── TonyLowDebug.cpp └── confile.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | 6 | # Compiled Dynamic libraries 7 | *.so 8 | *.dylib 9 | 10 | # Compiled Static libraries 11 | *.lai 12 | *.la 13 | *.a 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VPATH := ./src 2 | 3 | OBJECTS := confile.o Debug.o MemoryManager.o Mint.o MrswLock.o Nonreentrant.o Statistics.o TonyLowDebug.o CSocket.o BaseLib.o Buffer.o ThreadPool.o Log.o 4 | 5 | LIBNAME := libmy.a 6 | 7 | CC := g++ 8 | CFLAGS := -Wno-write-strings -I./include 9 | AR := ar 10 | #CFLAGS += -I./include 11 | 12 | mylib: $(OBJECTS) 13 | $(AR) rcs $(LIBNAME) $(OBJECTS) 14 | 15 | %.o:%.cpp 16 | $(CC) -c $(CFLAGS) $< -o $@ 17 | 18 | .PHONY:clean 19 | 20 | clean: 21 | rm -rf *.o 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | mylib 2 | ===== 3 | 4 | C/C++工具库,包涵一些常用的类,函数等 5 | -------------------------------------------------------------------------------- /include/BaseLib.h: -------------------------------------------------------------------------------- 1 | #ifndef __BASE_LIB_H_ 2 | #define __BASE_LIB_H_ 3 | 4 | #ifndef TONY_APP_LOG_PATH_NAME_SIZE 5 | #define TONY_APP_LOG_PATH_NAME_SIZE 100 6 | #endif 7 | #ifndef TONY_APP_TEMP_PATH_NAME_SIZE 8 | #define TONY_APP_TEMP_PATH_NAME_SIZE 100 9 | #endif 10 | 11 | #include 12 | 13 | typedef void (*_BASE_LIBRARY_PRINT_INFO_CALLBACK)(void* pCallParam); 14 | //static void BaseLibraryPrintInfoCallback(void* pCallParam); 15 | typedef void (*_APP_INFO_OUT_CALLBACK)(char* szInfo,void* pCallParam); 16 | //static void ApplicationInfomationOutCallback(char* szInfo,void* pCallParam); 17 | 18 | class CTonyBaseLibrary { 19 | public: 20 | CTonyBaseLibrary(char* szAppName, //应用名 21 | char* szLogPath, //日志路径 22 | char* szTempPath, //临时文件路径 23 | int nTaskPoolThreadMax = DEFAULT_THREAD_MAX, //任务池最大线程数 24 | bool bDebug2TTYFlag = true, //Debug 输出到屏幕开关 25 | _BASE_LIBRARY_PRINT_INFO_CALLBACK pPrintInfoCallback = null, //info 屏幕输出回调指针 26 | void* pPrintInfoCallbackParam = null, //info 回调参数指针 27 | _APP_INFO_OUT_CALLBACK pInfoOutCallback = null, //应用程序输出回调 28 | void* pInfoOutCallbackParam = null); //应用程序输出回调参数指针 29 | ~CTonyBaseLibrary(); //析构函数 30 | public: 31 | //应用名的备份保存 32 | char m_szAppName[TONY_APPLICATION_NAME_SIZE]; 33 | //日志路径 34 | char m_szLogPathName[TONY_APP_LOG_PATH_NAME_SIZE]; 35 | //临时文件路径 36 | char m_szTempPathName[TONY_APP_TEMP_PATH_NAME_SIZE]; 37 | //日志模块 38 | CTonyXiaoLog* m_pLog; 39 | //内存池 40 | CTonyMemoryPoolWithLock* m_pMemPool; 41 | //线程池 42 | CTonyXiaoTaskPool* m_pTaskPool; 43 | //线程池的运行体 44 | CTonyTaskRun* m_pTaskRun; 45 | //内核级Debug,每次运行写一个文件,覆盖上次的 46 | CTonyLowDebug* m_pDebug; 47 | private: 48 | //Info 打印任务 49 | static bool InfoPrintTaskCallback(void* pCallParam, int& nStatus); 50 | time_t m_tLastPrint; 51 | //打印信息的回调函数 52 | _BASE_LIBRARY_PRINT_INFO_CALLBACK m_pPrintInfoCallback; 53 | void* m_pPrintInfoCallbackParam; //回调函数参数指针 54 | }; 55 | #endif 56 | -------------------------------------------------------------------------------- /include/Buffer.h: -------------------------------------------------------------------------------- 1 | #ifndef __BUFFER_H_ 2 | #define __BUFFER_H_ 3 | 4 | #ifndef TONY_BUFFER_STRING_MAX 5 | #define TONY_BUFFER_STRING_MAX (1024) //变参处理字符串的最大长度 6 | #endif 7 | ///////////////////////////////////////////////////////////////////////// 8 | #define __DYNAMIC_BUFF_ 9 | ///////////////////////////////////////////////////////////////////////// 10 | //******************************动态buffer类****************************// 11 | ///////////////////////////////////////////////////////////////////////// 12 | #ifdef __DYNAMIC_BUFF_ 13 | //Buffer 类 14 | class CTonyBuffer { 15 | public: 16 | //构造函数,注意传入pMemPool 内存池对象指针。 17 | CTonyBuffer(CTonyMemoryPoolWithLock* pMemPool); 18 | ~CTonyBuffer(); 19 | public: 20 | //请注意,典型的工具类特征,所有内部变量,全部公有,方便应用层调用 21 | CTonyMemoryPoolWithLock* m_pMemPool; //内存池指针 22 | char* m_pData; //动态内存缓冲区指针 23 | int m_nDataLength; //内存缓冲区长度(代数据长度) 24 | public: 25 | //////////////////////////////////// 26 | //尺寸设置函数 27 | bool SetSize(int nSize); //设置新的大小 28 | bool InsertSpace2Head(int nAddBytes); //在前面插入空白 29 | bool AddSpace2Tail(int nAddBytes); //在后面插入空白 30 | void CutHead(int nBytes); //从前面剪切掉一段数据 31 | void CutTail(int nBytes); //从后面剪切掉一段数据 32 | //////////////////////////////////// 33 | //数值转换函数 34 | bool SetInt(int n); //将一个整数以二进制方式拷贝到缓冲区,带网络字节排序 35 | int GetInt(void); //以整数方式获得缓冲区的数值 36 | bool SetShort(short n); //将一个短整数以二进制方式拷贝到缓冲区,带网络字节排序 37 | short GetShort(void); //以短整型方式获得缓冲区的数值 38 | bool SetChar(char n); //将一个字节以二进制方式拷贝到缓冲区 39 | char GetChar(void); //以字节方式获得缓冲区的数值 40 | //////////////////////////////////// 41 | //二进制数据追加函数 42 | //追加数据到最后,返回新的数据长度 43 | int AddData(char* szData, int nDataLength); 44 | //插入数据到最前面,返回新的数据长度 45 | int InsertData2Head(char* szData, int nDataLength); 46 | //////////////////////////////////// 47 | //二进制数据拷贝函数 48 | //拷贝到一块目标缓冲区,受传入的缓冲区长度限制 49 | int BinCopyTo(char* szBuffer, int nBufferSize); 50 | //从一块来源缓冲区拷贝数据到本对象中 51 | int BinCopyFrom(char* szData, int nDataLength); 52 | //从另外一个Buffer 对象拷贝数据到本对象 53 | int BinCopyFrom(CTonyBuffer* pBuffer); 54 | //////////////////////////////////// 55 | //文本数据拷贝构建函数 56 | int StrCopyFrom(char* szString); 57 | int Printf(char* szFormat, ...); 58 | //////////////////////////////////// 59 | //数据比较函数 60 | int memcmp(char* szData, int nDataLength); 61 | int strcmp(char* szString); 62 | }; 63 | ///////////////////////////////////////////////////////////////////////// 64 | //******************************静态buffer类****************************// 65 | ///////////////////////////////////////////////////////////////////////// 66 | #else 67 | #ifndef TONY_SAFE_BUFFER_MAX_SIZE 68 | #define TONY_SAFE_BUFFER_MAX_SIZE (132*1024) //暂定132k,大家可以根据实际定义 69 | #endif 70 | class CTonyBuffer { 71 | public: 72 | //由于动静态Buffer 类api 完全一样,因此,公有函数构型必须保持一致。 73 | CTonyBuffer(CTonyBaseLibrary* pTonyBaseLib); 74 | CTonyBuffer(CTonyMemoryPoolWithLock* pMemPool); 75 | ~CTonyBuffer() { 76 | } //由于没有动态内存的释放任务,析构函数不做任何事 77 | public: 78 | //////////////////////////////////// 79 | //二进制数据拷贝函数 80 | int BinCopyTo(char* szBuffer, int nBufferSize); 81 | int BinCopyFrom(char* szData, int nDataLength); 82 | int BinCopyFrom(CTonyBuffer* pBuffer); 83 | //////////////////////////////////// 84 | //文本数据拷贝构建函数 85 | int StrCopyFrom(char* szString); 86 | int Printf(char* szFormat, ...); 87 | //////////////////////////////////// 88 | //尺寸设置函数 89 | //设置新的大小 90 | bool SetSize(int nSize); 91 | //在前面插入空白 92 | bool InsertSpace2Head(int nAddBytes); 93 | //在后面插入空白 94 | bool AddSpace2Tail(int nAddBytes); 95 | //从前面剪切掉一段数据 96 | void CutHead(int nBytes); 97 | //从后面剪切掉一段数据 98 | void CutTail(int nBytes); 99 | //////////////////////////////////// 100 | //数值转换函数 101 | bool SetInt(int n); 102 | int GetInt(void); 103 | bool SetShort(short n); 104 | short GetShort(void); 105 | bool SetChar(char n); 106 | char GetChar(void); 107 | //////////////////////////////////// 108 | //二进制数据追加函数 109 | //追加数据到最后,返回新的数据长度 110 | int AddData(char* szData, int nDataLength); 111 | //插入数据到最前面,返回新的数据长度 112 | int InsertData2Head(char* szData, int nDataLength); 113 | //////////////////////////////////// 114 | //数据比较函数 115 | int memcmp(char* szData, int nDataLength); 116 | int strcmp(char* szString); 117 | //////////////////////////////////// 118 | //服务函数 119 | bool IHaveData(void); 120 | public: 121 | char m_pData[TONY_SAFE_BUFFER_MAX_SIZE];//请注意这里,m_pData 变为静态数组 122 | int m_nDataLength; 123 | CTonyMemoryPoolWithLock* m_pMemPool;//保留MemPool,是为了Debug 方便 124 | }; 125 | #endif //end of __STATIC_BUFF_ 126 | ///////////////////////////////////////////////////////////////////////// 127 | //******************************pop buffer类***************************// 128 | ///////////////////////////////////////////////////////////////////////// 129 | //PopBuffer 队列管理变量结构体 130 | typedef struct _TONY_POP_BUFFER_HEAD_ { 131 | int m_nTokenCount; //内部包含的元素个数 132 | int m_nAllBytesCount; //使用的总字节数 133 | } STonyPopBufferHead; //定义的结构体变量类型 134 | //习惯性写法,定义了结构体,立即定义其长度常量 135 | const ULONG STonyPopBufferHeadSize = sizeof(STonyPopBufferHead); 136 | //由于PopBuffer 缓冲区的线性连续编址特性,这个队列头结构体被设计成位于缓冲区最开始的地方 137 | //因此,队列第一个元素真实的开始点,是缓冲区开始处向后偏移STonyPopBufferHeadSize 长度 138 | //这个宏定义出队列元素数据区开始指针(字符型指针) 139 | #define TONY_POP_BUFFER_TOKEN_DATA_BEGIN(p) \ 140 | (((char*)p)+STonyPopBufferTokenHeadSize) 141 | //每个Token 的头结构体 142 | typedef struct _TONY_POP_BUFFER_TOKEN_HEAD_ { 143 | int m_nDataLength; //标示该Token 的数据长度 144 | } STonyPopBufferTokenHead; //定义的结构体变量类型 145 | //结构体长度常量 146 | const ULONG STonyPopBufferTokenHeadSize = sizeof(STonyPopBufferTokenHead); 147 | //如果一笔准备推入队列的数据长度为n,则该Token 的总长度可以用该宏计算 148 | #define TONY_POP_BUFFER_TOKEN_LENGTH(n) (n+STonyPopBufferTokenHeadSize) 149 | //如果已知一个Token 的起始指针为p,则该Token 的数据区开始处可以用该宏计算 150 | #define TONY_POP_BUFFER_FIRST_TOKEN_BEGIN(p) \ 151 | (((char*)p)+STonyPopBufferHeadSize) 152 | #define TONY_POP_BUFFER_FIRST_TOKEN_BEGIN(p) \ 153 | (((char*)p)+STonyPopBufferHeadSize) 154 | 155 | //数据枚举回调函数,返回真,继续枚举,直到结束,否则直接结束循环 156 | typedef bool (*_TONY_ENUM_DATA_CALLBACK)(char* szData, //数据指针 157 | int nDataLength, //数据长度 158 | void* pCallParam); //代传的参数指针 159 | //这是笔者一个习惯,写出一个回调函数构型后,立即写个Demo,后续使用者可以直接拷贝使用 160 | //static bool EnumDataCallback(char* szData,int nDataLength,void* pCallParam); 161 | ///////////////////////////////////////////////////////////////////////////////// 162 | //基本的PopBuffer 类,本类同时兼顾粘合类和独立类两种特性 163 | class CTonyPopBuffer { 164 | public: 165 | //注意,这是粘合类的特有构造函数,内部缓冲区是外部传入, 166 | CTonyPopBuffer(char* szBuffer, //缓冲区指针 167 | int nBufferSize, //缓冲区尺寸 168 | bool bInitFlag = true); //是否初始化标志 169 | ~CTonyPopBuffer(); 170 | public: 171 | //实现“后粘合”的具体函数,即实现粘合的方法 172 | void Set(char* szBuffer, int nBufferSize); 173 | //清空整个数据区,仅仅是数据归零,缓冲区不释放 174 | void Clean(void); 175 | //内部信息打印函数,Debug 用,相当于前文的PrintfInfo 176 | void PrintInside(void); 177 | //能否正确工作的标志函数 178 | bool ICanWork(void); 179 | public: 180 | //队列最经典的功能,追加到末尾,此处兼容普通缓冲区和Buffer 类 181 | int AddLast(char* szData, int nDataLength); 182 | int AddLast(CTonyBuffer* pBuffer); 183 | public: 184 | //获得当前内部元素个数 185 | int GetTokenCount(void); 186 | //获得当前使用的所有字节数(包含管理字节) 187 | int GetAllBytes(void); 188 | //根据即将推送进队列的数据长度,判断内部剩余空间是否够用 189 | bool ICanSave(int nDataLength); 190 | public: 191 | //获得第一个元素的长度 192 | int GetFirstTokenLength(void); 193 | //获取第一个元素,这是普通buffer 版本,需要应用层保证缓冲区长度足够 194 | //用GetFirstTokenLength 可以查询第一个元素的长度 195 | int GetFirst(char* szBuffer, int nBufferSize); 196 | //获得第一个元素,这是Buffer 类版本 197 | int GetFirst(CTonyBuffer* pBuffer); 198 | //删除第一个元素 199 | bool DeleteFirst(void); 200 | //提取并删除第一元素,就是从队列中弹出第一个元素 201 | int GetAndDeleteFirst(char* szBuffer, int nBufferSize); 202 | int GetAndDeleteFirst(CTonyBuffer* pBuffer); 203 | public: 204 | //枚举遍历所有数据,提交回调函数处理,并且删除数据,返回经过处理的Token 个数 205 | int MoveAllData(_TONY_ENUM_DATA_CALLBACK pCallBack, PVOID pCallParam); 206 | public: 207 | char* m_pBuffer; //最关键的内部缓冲区指针 208 | int m_nBufferSize; //内部缓冲区长度 209 | private: 210 | //这是队列头的指针,注意,这个指针并不是动态申请的内存块,而是指向缓冲区m_pBuffer 头 211 | STonyPopBufferHead* m_pHead; 212 | }; 213 | 214 | ///////////////////////////////////////////////////////////////////////// 215 | //******************************mem Queue类***************************/// 216 | ///////////////////////////////////////////////////////////////////////// 217 | //MemQueue 链表节点数据结构 218 | typedef struct _TONY_XIAO_QUEUE_TOKEN_HEAD_ { 219 | int m_nDataLength; //存储的业务数据长度 220 | char* m_pBuffer; //指向业务数据块的指针 221 | struct _TONY_XIAO_QUEUE_TOKEN_HEAD_* m_pNext; //指向下一节点的指针 222 | } STonyXiaoQueueTokenHead; //定义的新的结构体变量类型 223 | //笔者习惯,写完结构体,立即声明其长度常量,方便后续的内存申请。 224 | const ULONG STonyXiaoQueueTokenHeadSize = sizeof(STonyXiaoQueueTokenHead); 225 | /////////////////////////////////////////////////////////////////////////// 226 | #ifndef TONY_CHAIN_TOKEN_MAX 227 | #define TONY_CHAIN_TOKEN_MAX 1024 228 | #endif 229 | #ifndef TONY_APPLICATION_NAME_SIZE 230 | #define TONY_APPLICATION_NAME_SIZE 50 231 | #endif 232 | ///////////////////////动态内存队列类//////////////////////////////////////// 233 | class CTonyXiaoMemoryQueue { 234 | private: 235 | //由于MemQueue 使用动态内存分配,理论上是没有限制的 236 | //但我们知道,程序中任何数据结构,都应该有边界,否则可能会造成bug 237 | //这里使用一个内部变量,强行界定边界值,为队列最大长度做个上限 238 | int m_nMaxToken; //最大的Token 限制 239 | int m_nTokenCount; //队列中有效Token 个数 240 | STonyXiaoQueueTokenHead* m_pHead; //队列头指针 241 | STonyXiaoQueueTokenHead* m_pLast; //加速因子,队列尾指针 242 | CTonyLowDebug* m_pDebug; //debug 对象指针 243 | CTonyMemoryPoolWithLock* m_pMemPool; //内存池指针(本类依赖内存池) 244 | char m_szAppName[TONY_APPLICATION_NAME_SIZE]; 245 | public: 246 | CTonyXiaoMemoryQueue(CTonyLowDebug* pDebug, //debug 对象指针 247 | CTonyMemoryPoolWithLock* pMemPool, //内存池指针 248 | char* szAppName, //应用名,这里代队列名 249 | int nMaxToken = TONY_CHAIN_TOKEN_MAX); //最大Token 上限 250 | ~CTonyXiaoMemoryQueue(); 251 | public: 252 | bool ICanWork(void); //这个太熟悉了吧,是否可以工作标志 253 | void CleanAll(void); //清除所有Token,队列清空 254 | int GetFirstLength(void); //获得第一个Token 数据长度 255 | //功能和目的同PopBuffer 256 | int GetTokenCount(void) { 257 | return m_nTokenCount; 258 | } //获得所有Token 总数,内联 259 | void PrintInside(void); //遍历打印所有队列内部Token 数据 260 | public: 261 | int AddLast(char* szData, //数据指针 262 | int nDataLength, //数据长度 263 | int nLimit = -1); //这是为了防止递归层度过深做的特殊限制 264 | int GetFirst(char* szBuffer, int nBufferSize); 265 | int GetFirst(CTonyBuffer* pBuffer); 266 | bool DeleteFirst(void); 267 | int GetAndDeleteFirst(char* szBuffer, int nBufferSize); 268 | int GetAndDeleteFirst(CTonyBuffer* pBuffer); 269 | public: 270 | //从前面弹出一批数据,以nBufferSize 能装下PopBuffer 的数据为准 271 | int PopFromFirst(char* szBuffer, int nBufferSize); 272 | //从PopBuffer 的数据区,弹出所有数据,追加到队列末尾 273 | int Push2Last(char* szData, int nDataLength); 274 | public: 275 | void Write2File(char* szFileName); //队列数据写入磁盘 276 | int ReadFromFile(char* szFileName); //队列数据从磁盘读出 277 | private: 278 | void PrintAToken(STonyXiaoQueueTokenHead* pToken); 279 | void WriteAToken2File(STonyXiaoQueueTokenHead* pToken, FILE* fp); 280 | int PopFromFirst4TonyPopBuffer(CTonyPopBuffer* pPopBuffer); 281 | int AddLast2ThisToken(STonyXiaoQueueTokenHead* pToken, char* szData, 282 | int nDataLength); 283 | STonyXiaoQueueTokenHead* GetAToken(void); 284 | bool DeleteATokenAndHisNext(STonyXiaoQueueTokenHead* pToken); 285 | static bool PushDataCallback(char* szData, int nDataLength, 286 | void* pCallParam); 287 | }; 288 | ///////////////////////////////////////////////////////////////////////// 289 | //*********************mem Queue线程安全封装类***************************/// 290 | ///////////////////////////////////////////////////////////////////////// 291 | //MemQueue 的线程安全封装类 292 | class CTonyMemoryQueueWithLock { 293 | public: 294 | CTonyMemoryQueueWithLock(CTonyLowDebug* pDebug, 295 | CTonyMemoryPoolWithLock* pMemPool, char* szAppName, int nMaxToken = 296 | TONY_CHAIN_TOKEN_MAX); 297 | ~CTonyMemoryQueueWithLock(); 298 | public: 299 | bool ICanWork(void); //对应各种公有方法名 300 | int AddLast(char* szData, int nDataLength); 301 | int GetFirst(CTonyBuffer* pBuffer); 302 | int GetFirst(char* szBuffer, int nBufferSize); 303 | int GetFirstLength(void); 304 | int GetTokenCount(void); 305 | int GetAndDeleteFirst(char* szBuffer, int nBufferSize); 306 | int PopFromFirst(char* szBuffer, int nBufferSize); 307 | int Push2Last(char* szData, int nDataLength); 308 | void CleanAll(void); 309 | bool DeleteFirst(void); 310 | void Write2File(char* szFileName); 311 | int ReadFromFile(char* szFileName); 312 | void PrintInside(void); 313 | private: 314 | CTonyMemoryPoolWithLock* m_pMemPool; 315 | CTonyXiaoMemoryQueue* m_pQueue; //MemQueue 的聚合对象 316 | CMultiReadSingleWriteLock m_Lock; //线程安全锁,为了保证效率, 317 | //此处使用了单写多读锁 318 | char m_szAppName[TONY_APPLICATION_NAME_SIZE]; //保存的AppName 319 | }; 320 | #endif 321 | -------------------------------------------------------------------------------- /include/CSocket.h: -------------------------------------------------------------------------------- 1 | #ifndef SOCKET_H_ 2 | #define SOCKET_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "std.h" 14 | #include "MemoryManager.h" 15 | #include "TonyLowDebug.h" 16 | 17 | //CTonyLowDebug *g_pSocketDebug = NULL; 18 | #define SOCKET_DEBUG g_pSocketDebug->Debug2File 19 | 20 | class Socket; 21 | class ServerSocket; 22 | 23 | //socket创建类 24 | //所有socket对象都应该由该对象创建 25 | class SocketBuilder { 26 | public: 27 | //@param CTonyLowDebug *pDebug 调试对象 28 | //@param CTonyMemoryPoolWithLock *pPool 内存池,用于管理跟踪fd 29 | SocketBuilder(CTonyLowDebug *pDebug, CTonyMemoryPoolWithLock *pPool); 30 | 31 | //创建tcp服务器端的socket 32 | //@param uint16_t port socket端口 33 | //@return ServerSocket对象指针 34 | ServerSocket* createTCPServerSocket(uint16_t port); 35 | 36 | //创建UDP或者TCP socket对象 37 | //@param int type SOCK_STREAM或者SOCK_DGRAM 38 | //@param uint16_t port socket绑定的端口号,0表示随机分配 39 | //@return Socket对象指针 40 | Socket* createSocket(int type, uint16_t port = 0); 41 | 42 | void close(Socket *sock); 43 | 44 | private: 45 | CTonyMemoryPoolWithLock *m_pPool; //用于管理socket的fd 46 | }; 47 | //Socket类 48 | //封装了一些基本的socket函数操作 49 | class Socket { 50 | public: 51 | //@param int type socket类型 52 | //@param uint16_t port 端口号,默认随机 53 | /* 54 | Socket(int type, uint16_t port = 0); 55 | Socket(int fd); 56 | virtual ~Socket(); 57 | */ 58 | int& getSocketFd() { return fd; }; 59 | 60 | //发送数据 61 | //@param void *buffer 数据缓冲区 62 | //@param int length 数据长度 63 | //@return 发送数据的长度,-1为发送错误 64 | virtual int send(void *buffer, int length) = 0; 65 | 66 | //接收数据 尽可能的接受需要接受的数据长度 67 | //@param void *buffer 接受数据的缓冲区 68 | //@param int length 需要接收数据的长度 69 | //@return 实际接受数据的长度,小于零表示接受错误 70 | virtual int recv(void *buffer, int length) = 0; 71 | 72 | //连接到服务器 73 | //@param const char *const ip 服务器ip 74 | //@param const uint16_t port 服务器端口 75 | int connect(const char * const ip, const uint16_t port); 76 | 77 | //判断创建的socket是否有效 78 | bool isValid() const { return fd > 0 ? true : false; } 79 | 80 | public: 81 | int fd; 82 | friend class ServerSocket; 83 | }; 84 | 85 | //tcp socket 86 | class TcpSocket : public Socket { 87 | public: 88 | TcpSocket(uint16_t port); 89 | TcpSocket(int fd); 90 | virtual ~TcpSocket(); 91 | 92 | virtual int send(void *buffer, int length); 93 | virtual int recv(void *buffer, int length); 94 | }; 95 | 96 | //udp socket 97 | class UdpSocket : public Socket { 98 | public: 99 | UdpSocket(uint16_t port); 100 | UdpSocket(int fd); 101 | virtual ~UdpSocket(); 102 | 103 | virtual int send(void *buffer, int length); 104 | virtual int recv(void *buffer, int length); 105 | }; 106 | 107 | //服务器端的socket 108 | class ServerSocket : public TcpSocket { 109 | public: 110 | ServerSocket(uint16_t port); 111 | virtual ~ServerSocket(){}; 112 | 113 | Socket* accept(struct sockaddr *addr); 114 | }; 115 | 116 | #endif 117 | -------------------------------------------------------------------------------- /include/Debug.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEBUG_H_ 2 | #define __DEBUG_H_ 3 | 4 | #ifdef WIN32 5 | #define Linux_Win_vsnprintf _vsnprintf 6 | #else // not WIN32 7 | #define Linux_Win_vsnprintf vsnprintf 8 | #endif 9 | //CON_DEBUG控制是否输出到屏幕 10 | #define CON_DEBUG 1 11 | #ifdef CON_DEBUG 12 | #define CON_PRINTF printf 13 | #else 14 | #define CON_PRINTF /\ 15 | /printf 16 | #endif//end of WIN32 17 | #define TONY_FORMAT(nPrintLength,szBuf,nBufferSize,szFormat) \ 18 | { \ 19 | va_list pArgList; \ 20 | va_start (pArgList,szFormat); \ 21 | nPrintLength+=Linux_Win_vsnprintf(szBuf+nPrintLength, \ 22 | nBufferSize-nPrintLength,szFormat,pArgList); \ 23 | va_end(pArgList); \ 24 | if(nPrintLength>(nBufferSize-1)) nPrintLength=nBufferSize-1; \ 25 | *(szBuf+nPrintLength)='\0'; \ 26 | } 27 | 28 | #define TONY_FORMAT_WITH_TIMESTAMP(nPrintLength,szBuf,nBufferSize,szFormat) \ 29 | { \ 30 | time_t t; \ 31 | struct tm *pTM=NULL; \ 32 | time(&t); \ 33 | pTM = localtime(&t); \ 34 | nPrintLength+=SafePrintf(szBuf,nBufferSize,"[%s",asctime(pTM)); \ 35 | szBuf[nPrintLength-1]='\0'; \ 36 | nPrintLength--; \ 37 | nPrintLength+=SafePrintf(szBuf+nPrintLength,nBufferSize-nPrintLength,"] "); \ 38 | TONY_FORMAT(nPrintLength,szBuf,nBufferSize,szFormat); \ 39 | } 40 | /////////////////////////////////////////////////////////////////////// 41 | #if defined(__cplusplus) || defined(c_plusplus) 42 | extern "C" { //纯C 输出开始 43 | #endif 44 | //安全的字符串拷贝函数 45 | //参数按顺序排:目的地址,源地址,拷贝的最大字节数 46 | extern void SafeStrcpy(char *pDest, char *pSource, int nCount); 47 | //安全的变参输出函数 48 | //szBuf: 用户指定的输出缓冲区 49 | //nMaxLength:用户指定的输出缓冲区尺寸 50 | //szFormat:格式化输出字符串(变参,可以是多个) 51 | //返回输出的字符总数(strlen 的长度,不包括最后的’\0’) 52 | extern int SafePrintf(char* szBuf, int nMaxLength, char *szFormat, ...); 53 | 54 | extern int SafePrintfWithTimestamp(char* szBuf,int nBufSize,char* szFormat, ...); 55 | 56 | #define TONY_LINE_MAX 1024 //最大一行输出的字符数 57 | extern int TonyPrintf(bool bWithTimestamp, char* szFormat, ...); //格式化字符串 58 | 59 | extern int TONY_PRINTF(char* szFormat, ...); //格式化字符串 60 | extern int TONY_XIAO_PRINTF(char* szFormat, ...); //格式化字符串 61 | //向指定的缓冲区输出一个时间戳字符串 62 | //szBuf: 用户指定的输出缓冲区 63 | //nMaxLength:用户指定的输出缓冲区尺寸 64 | //返回输出的字符总数(strlen 的长度,不包括最后的’\0’) 65 | extern int GetATimeStamp(char* szBuf, int nMaxLength); 66 | 67 | //向指定的缓冲区输出一个时间戳字符串 68 | // szFileName: 用户指定的输出文件 69 | // szMode:常见的文件打开方式描述字符串,一般建议”a+” 70 | //返回输出的字符总数(strlen 的长度,不包括最后的’\0’) 71 | #define DEBUG_BUFFER_LENGTH 1024 72 | extern int dbg2file(char* szFileName, char* szMode, char *szFormat, ...); 73 | 74 | //输出格式 75 | //0000 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 xxxxxxxxxxxxxxxx 76 | //以ASCII 方式显示数据内容 77 | extern int dbg_bin_ascii(char* pPrintBuffer, char* pBuffer, int nLength); 78 | //以十六进制方式显示指定数据区内容。 79 | extern int dbg_bin_hex(char* pPrintBuffer, char* pBuffer, int nLength); 80 | //****************这是主入口函数 81 | //以16 字节为一行,格式化输出二进制数据内容。 82 | extern void dbg_bin(char* pBuffer, int nLength); 83 | 84 | //****************这是主入口函数 85 | //以16 字节为一行,格式化输出二进制数据内容。 86 | extern void dbg2file4bin(char* szFileName, char* szMode, char* pBuffer, 87 | int nLength); 88 | 89 | #if defined(__cplusplus) || defined(c_plusplus) 90 | } //纯C 输出结束 91 | #endif /*defined(__cplusplus) || defined(c_plusplus)*/ 92 | 93 | #endif//end of __DEBUG_H_ 94 | -------------------------------------------------------------------------------- /include/Log.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOG_H_ 2 | #define __LOG_H_ 3 | 4 | #define LOG_FILE_SIZE_MAX (1*1024*1024*1024) //每个日志文件最大1G 5 | #define LOG_ITEM_LENGTH_MAX (2*1024) //单条log 最大长度2k 6 | #define LOG_FILE_CHANGE_NAME_PRE_SECONDS (60*60) //日志文件1 小时更换一次名字 7 | #define LOG_FILE_INFO_BUFFER_SIZE (256*1024) //日志目录最大长度(超长删除) 8 | #define LOF_FILE_DEFAULT_HOLD 72 //一般保留3 天的数据 9 | #define LOG_TIME_STRING_MAX 128 //时间戳字符串长度 10 | typedef void (*_APP_INFO_OUT_CALLBACK)(char* szInfo, void* pCallParam); 11 | //笔者的日志管理系统,由于很多系统都有自己的日志系统,此处以笔者的英文名修饰,避免重名 12 | #define FILENAME_STRING_LENGTH 256 //文件名长度 13 | class CTonyXiaoLog { 14 | public: 15 | //静态工具函数类 16 | //定制时间戳字符串 17 | static int MakeATimeString(char* szBuffer, int nBufferSize); 18 | public: 19 | //构造函数和析构函数 20 | CTonyXiaoLog(CTonyLowDebug* pDebug, //debug 对象指针(log 也需要debug) 21 | CTonyMemoryPoolWithLock* pMemPool, //内存池指针,内存队列要用 22 | char* szLogPath, //日志路径 23 | char* szAppName, //应用名(修饰日志文件) 24 | int nHoldFileMax = LOF_FILE_DEFAULT_HOLD, //保留多少个文件 25 | bool bSyslogFlag = true, //日志级别开关,true 打开,否则关闭 26 | bool bDebugFlag = true, bool bDebug2Flag = false, bool bDebug3Flag = 27 | false, bool bPrintf2ScrFlag = true); //是否打印到屏幕开关 28 | ~CTonyXiaoLog(); //析构函数 29 | public: 30 | //公有输出方法 31 | void _XGDebug4Bin(char* pBuffer, int nLength); //二进制输出 32 | int _XGSyslog(char *szFormat, ...); //Syslog 输出,变参函数 33 | int _XGDebug(char *szFormat, ...); //Debug 输出,变参函数 34 | int _XGDebug2(char *szFormat, ...); //Debug2 输出,变参函数 35 | int _XGDebug3(char *szFormat, ...); //Debug3 输出,变参函数 36 | public: 37 | //开关变量,对应构造函数的开关 38 | bool m_bSyslogFlag; 39 | bool m_bDebugFlag; 40 | bool m_bDebug2Flag; 41 | bool m_bDebug3Flag; 42 | private: 43 | //内部功能函数 44 | int _Printf(char *szFormat, ...); //最核心的打印输出模块,变参函数 45 | void DeleteFirstFile(void); //删除最老的文件 46 | void FixFileInfo(void); //修订文件名目录队列 47 | void MakeFileName(void); //根据时间和文件大小,定制文件名 48 | void GetFileName(void); //获得当前可用的文件名 49 | private: 50 | //内部私有变量区 51 | CMutexLock m_Lock; //线程安全锁 52 | char m_szFilePath[FILENAME_STRING_LENGTH]; //文件路径 53 | char m_szFileName[(FILENAME_STRING_LENGTH * 2)]; //文件名 54 | unsigned long m_nFileSize; //当前文件大小 55 | time_t m_tFileNameMake; //定制文件名的时间戳 56 | int m_nHoldFileMax; //保留文件的数量,构造函数传入 57 | _APP_INFO_OUT_CALLBACK m_pInfoOutCallback; //应用程序拦截输出回调函数 58 | void* m_pInfoOutCallbackParam; //透传的回调函数指针 59 | bool m_bPrintf2ScrFlag; //是否打印到屏幕的开关 60 | char m_szFileInfoName[FILENAME_STRING_LENGTH]; //文件名 61 | CTonyLowDebug* m_pDebug; //Debug 对象指针 62 | CTonyMemoryPoolWithLock* m_pMemPool; //内存池对象指针 63 | CTonyXiaoMemoryQueue* m_pFileInfoQueue; //文件名队列 64 | }; 65 | #endif 66 | -------------------------------------------------------------------------------- /include/MemoryManager.h: -------------------------------------------------------------------------------- 1 | #ifndef __MEMORYSTACK_H_ 2 | #define __MEMORYSTACK_H_ 3 | 4 | #ifndef ULONG 5 | #define ULONG unsigned long 6 | #endif 7 | 8 | #include "MrswLock.h" 9 | #include "Socket.h" 10 | #include "TonyLowDebug.h" 11 | 12 | typedef void* PVOID; 13 | 14 | typedef struct _TONY_MEM_BLOCK_HEAD_ { 15 | ULONG m_ulBlockSize; //内存块的尺寸 16 | struct _TONY_MEM_BLOCK_HEAD_* m_pNext; //指向下一链表元素的指针 17 | } STonyMemoryBlockHead; 18 | //本结构体的长度,经过计算,恒定为8Bytes 19 | const ULONG STonyMemoryBlockHeadSize = sizeof(STonyMemoryBlockHead); 20 | 21 | //根据一个应用程序数据块的长度,计算一个内存块的真实大小,即n+8 22 | #define TONY_MEM_BLOCK_SIZE(nDataLength) \ 23 | (nDataLength+STonyMemoryBlockHeadSize) 24 | //根据向系统申请的内存块,计算其应用程序数据内存的真实大小,即n-8 25 | #define TONY_MEM_BLOCK_DATA_SIZE(nBlockSize) \ 26 | (nBlockSize-STonyMemoryBlockHeadSize) 27 | //根据应用程序释放的指针,逆求真实的内存块指针,即p0=p1-8 28 | #define TONY_MEM_BLOCK_HEAD(pData) \ 29 | ((STonyMemoryBlockHead*)(((char*)pData)-STonyMemoryBlockHeadSize)) 30 | //根据一个内存块的真实指针,求数据内存块的指针,即p1=p0+8 31 | #define TONY_MEM_BLOCK_DATA(pHead) \ 32 | (((char*)pHead)+STonyMemoryBlockHeadSize) 33 | //最小内存块长度,16 Bytes,由于我们管理占用8 Bytes,这个最小长度不能再小了, 34 | //否则无意义,即使这样,我们最小的内存块,能分配给应用程序使用的,仅有8 Bytes。 35 | #define TONY_XIAO_MEMORY_STACK_BLOCK_MIN 16 36 | //这是管理的最大内存块长度,1M,如前文表中所示,超过此限制,内存池停止服务 37 | //改为直接向系统申请和释放。 38 | #define TONY_XIAO_MEMORY_STACK_MAX_SAVE_BLOCK_SIZE (1*1024*1024) 39 | ////////需要完善与注意//////////////TODO 40 | #define TONY_DEBUG m_pDebug->Debug2File 41 | ///////////////////////////////////////////////////////////////////// 42 | //************************内存栈基本单元*****************************/// 43 | ///////////////////////////////////////////////////////////////////// 44 | //内存栈最基本的管理单元,笔者习惯以Token 定名单位,单元,元素等结构,仅仅是习惯而已。 45 | //本类使用了一定的递归逻辑 46 | class CTonyMemoryStackToken { 47 | public: 48 | //构造函数和析构函数,注意,此处要求输入本内存块管理的基本内存大小,如64 Bytes 49 | //以及Debug 对象的指针,本模块需要利用Debug 进行中间的输出。 50 | CTonyMemoryStackToken(int nBlockSize, CTonyLowDebug* pDebug); 51 | ~CTonyMemoryStackToken(); 52 | public: 53 | //基本的申请函数Malloc,返回成功申请的内存块指针,注意,是p1,不是p0 54 | //注意其中的统计设计,这是为了Debug 和观察方便 55 | //另外,请注意,统计变量是传址操作,因此,可以在函数内部修改,回传信息 56 | // nAllBlockCount- nMemoryUse=内部的空闲内存块数量 57 | void* Malloc(int nSize, //应用程序申请的内存尺寸 58 | CMRSWint& nAllBlockCount, //统计变量,返回管理过的内存块总数 59 | CMRSWint& nMemoryUse); //统计变量,返回被应用程序使用的内存块数量 60 | //释放函数,此处的bCloseFlag 是一个特殊优化,即在程序退出时 61 | //释放的内存不会再返回内存栈,直接被free 掉,以加快程序退出速度。 62 | //注意,此处应用程序传进来的是p1,内部需要逆向计算出p0 实现操作。 63 | bool Free(void* pPoint, bool bCloseFlag); 64 | //这是纯粹的性能观察函数,这也是笔者开发底层模块的一个习惯 65 | //底层模块,由于很少有UI 交互的机会,因此,如果内部有什么问题,程序员很难观察到 66 | //由此可能导致bug,因此,一般对底层模块,笔者习惯性做一个打印函数 67 | //打印一些特殊信息,帮助程序员观察性能或bug。 68 | void PrintStack(void); 69 | private: 70 | //系统退出时,递归销毁所有内存块的函数。 71 | //笔者一般习惯以Brother(兄弟)或Next(下一个)来定名左枝, 72 | //以Son(儿子)来定名右枝 73 | void DestroySon(STonyMemoryBlockHead* pSon); 74 | private: 75 | //第一个儿子的指针,这就是链头了 76 | STonyMemoryBlockHead* m_pFirstSon; 77 | //这是指向兄弟节点,即左枝下一节点的指针 78 | CTonyMemoryStackToken* m_pNext; 79 | //最重要的设计,线程安全锁,注意,为了提升效率,这里使用了单写多读锁 80 | //根据本类定义,锁仅保护上述两个指针变量 81 | CMultiReadSingleWriteLock m_Lock; 82 | //内部保存一下本对象管理的内存块尺寸,就是构造函数传入的数据 83 | //供比对使用 84 | ULONG m_ulBlockSize; 85 | //性能统计变量,请注意,这些都是单写多读锁安全变量,本身是现成安全的。 86 | CMRSWint m_nBlockOutSide; //分配出去的内存块数量 87 | CMRSWint m_nBlockInSide; //内部保留的空闲内存块数量 88 | CTonyLowDebug* m_pDebug; //debug 对象指针 89 | }; 90 | /////////////////////////////////////////////////////////////////////////// 91 | //*****************************内存栈************************************/// 92 | /////////////////////////////////////////////////////////////////////////// 93 | class CTonyMemoryStack { 94 | public: 95 | CTonyMemoryStack(CTonyLowDebug* pDebug); //构造函数,需要传入Debug 对象 96 | ~CTonyMemoryStack(); 97 | public: 98 | //重定义指针指向的内存单元地址空间大小,给出旧指针,和新的空间尺寸 99 | //如果成功,返回有效的新指针,指向重定义大小之后的新空间,失败返回null 100 | //注意,本函数可以把旧空间的数据内容备份到新空间,但这不一定,取决与新空间大小 101 | void* ReMalloc(void* pPoint, //旧指针 102 | int nNewSize, //新的尺寸需求 103 | bool bCopyOldDataFlag = true); //是否备份旧数据的标志(默认“真”) 104 | //Malloc 和Free 函数,调用内存块管理单元方法完成 105 | void* Malloc(int nSize); 106 | bool Free(void* pPoint); 107 | //内部信息输出函数,下文会讲到其差别 108 | void PrintStack(void); 109 | void PrintInfo(void); 110 | //作为一项优化,如果app 设置这个关闭标志, 111 | //所有的free 动作都直接调用系统的free 完成,不再保留到stack 中。 112 | //加快退出速度。 113 | void SetCloseFlag(bool bFlag = true); 114 | CTonyLowDebug* m_pDebug; //debug 对象,此处为了优化公开 115 | private: 116 | CTonyMemoryStackToken* m_pHead; //注意,这是左枝开始的第一个节点 117 | CMRSWint m_pMaxPoint; //统计当前用到的最大内存指针 118 | CMRSWint m_nAllBlockCount; //统计所有在用的内存块 119 | CMRSWint m_nMemoryUse; //统计内存使用的总字节数 120 | CMRSWbool m_CloseFlag; //关闭标示 121 | }; 122 | //////////////////////////////////////////////////////////////////////// 123 | //*****************************内存注册模块*****************************// 124 | //////////////////////////////////////////////////////////////////////// 125 | //说明文字的最大长度,该长度在多个点被调用,因此使用条件编译声明 126 | #ifndef TONY_MEMORY_BLOCK_INFO_MAX_SIZE 127 | #define TONY_MEMORY_BLOCK_INFO_MAX_SIZE 124 128 | #endif 129 | #ifndef TONY_CLEAN_CHAR_BUFFER 130 | #define TONY_CLEAN_CHAR_BUFFER(p) (*((char*)p)='\0') 131 | #endif 132 | //内存注册类核心数据结构 133 | typedef struct _TONY_XIAO_MEMORY_REGISTER_ { 134 | void* m_pPoint; //保留的指针 135 | char m_szInfo[TONY_MEMORY_BLOCK_INFO_MAX_SIZE]; //说明文字 136 | } STonyXiaoMemoryRegister; //定义的变量类型 137 | //习惯性写法,声明了结构体后,立即定义其尺寸常量 138 | const ULONG STonyXiaoMemoryRegisterSize = sizeof(STonyXiaoMemoryRegister); 139 | 140 | //管理的最大指针个数,PC 平台一般建议10000,但嵌入式平台, 141 | //要根据具体情况分析,一般不建议超过1024 142 | #ifndef TONY_MEMORY_REGISTER_MAX 143 | #define TONY_MEMORY_REGISTER_MAX 10000 144 | #endif 145 | //内存注册类 146 | class CMemoryRegister { 147 | public: 148 | CMemoryRegister(CTonyLowDebug* pDebug); //构造函数传输debug 对象指针 149 | ~CMemoryRegister(); 150 | public: 151 | //添加一个指针及其说明文字 152 | void Add(void* pPoint, char* szInfo); 153 | //删除一个指针(该内存块被释放,失效) 154 | void Del(void* pPoint); 155 | //remalloc 的时候更新指针 156 | void Modeify(void* pOld, void* pNew); 157 | //打印信息函数,Debug 和观察性能用 158 | void PrintInfo(void); 159 | private: 160 | //这是一个内部工具函数,把一段指针和说明文字,拷贝到上面定义的结构体中 161 | void RegisterCopy(STonyXiaoMemoryRegister* pDest, void* pPoint, 162 | char* szInfo); 163 | 164 | private: 165 | CTonyLowDebug* m_pDebug; //Debug 对象 166 | //请注意,由于注册类不管是注册,还是反注册动作,均包含写动作 167 | //此时,使用单写多读锁意义不大,因此使用普通锁来完成 168 | CMutexLock m_Lock; //线程安全锁 169 | //请注意这里,这里使用了静态数组,主要原因是考虑到内存注册类 170 | //是内存池的一个功能,不能再有动态内存申请,以免造成新的不稳定因素。 171 | //不过请注意,这个数组占用栈空间,对于嵌入式设备,可能没有这么大,要关注不要越界。 172 | STonyXiaoMemoryRegister m_RegisterArray[TONY_MEMORY_REGISTER_MAX]; 173 | //这是数组使用标志,表示当前数组最大使用多少单元, 174 | //注意,这不是并发指针数,随着反注册的进行,数组中可能有空区 175 | int m_nUseMax; 176 | //另外一个统计,统计有史以来注册的最大指针,统计性能实用。 177 | void* m_pMaxPoint; 178 | //这是统计的当前在用指针数。 179 | int m_nPointCount; 180 | }; 181 | 182 | ////////////////////////////////////////////////////////////////////////// 183 | //**************************socket 注册管理类*****************************// 184 | ////////////////////////////////////////////////////////////////////////// 185 | //核心管理数据结构 186 | typedef struct _TONY_XIAO_SOCKET_REGISTER_ { 187 | Linux_Win_SOCKET m_nSocket; //保存的socket 188 | char m_szInfo[TONY_MEMORY_BLOCK_INFO_MAX_SIZE]; //说明文字,长度同上 189 | } STonyXiaoSocketRegister; 190 | //结构体尺寸常量 191 | const ULONG STonyXiaoSocketRegisterSize = sizeof(STonyXiaoSocketRegister); 192 | //判断socket 是否合法的通用函数,这个函数设置的目的是收拢所有的判断,方便以后修改 193 | bool SocketIsOK(Linux_Win_SOCKET nSocket); 194 | //真实地关闭一个socket 195 | void _Linux_Win_CloseSocket(Linux_Win_SOCKET nSocket); 196 | 197 | class CSocketRegister { 198 | public: 199 | CSocketRegister(CTonyLowDebug* pDebug); //构造函数,传入debug 对象指针 200 | ~CSocketRegister(); 201 | public: 202 | void PrintInfo(void); //信息打印函数 203 | public: 204 | //注册一个socket,以及其说明文字 205 | void Add(Linux_Win_SOCKET s, char* szInfo = null); 206 | //反注册一个socket,如果内部没找到,返回false 207 | bool Del(Linux_Win_SOCKET s); 208 | private: 209 | CTonyLowDebug* m_pDebug; //debug 对象指针 210 | CMutexLock m_Lock; //锁,这里同样使用普通锁 211 | //保存注册信息的结构体数组,大小也同内存指针注册类 212 | STonyXiaoSocketRegister m_RegisterArray[TONY_MEMORY_REGISTER_MAX]; 213 | //管理变量 214 | int m_nUseMax; 215 | //统计变量 216 | Linux_Win_SOCKET m_nMaxSocket; //注册过的最大socket, 217 | int m_nSocketUseCount; //在用的socket 总数 218 | }; 219 | ////////////////////////////////////////////////////////////////////////// 220 | //***************************內存池**************************************// 221 | ////////////////////////////////////////////////////////////////////////// 222 | //内存池类 223 | class CTonyMemoryPoolWithLock { 224 | public: 225 | //构造函数 226 | //考虑到某些成熟的代码模块,已经经过测试,确定没有bug,可以考虑在这里 227 | //关闭注册功能,这样,减少了每次内存申请和释放时,注册和反注册的逻辑,可以提升效率 228 | CTonyMemoryPoolWithLock(CTonyLowDebug* pDebug, //传入的Debug 对象指针 229 | bool bOpenRegisterFlag = true); //不开启注册功能的标志 230 | ~CTonyMemoryPoolWithLock(); 231 | ////////////////////////////////////////////////////////// 232 | //指针管理 233 | public: 234 | //注册指针,实现管理,注意需要说明文字 235 | void Register(void* pPoint, char* szInfo); 236 | void UnRegister(void* pPoint); 237 | private: 238 | CMemoryRegister* m_pRegister; //指针注册管理对象 239 | ////////////////////////////////////////////////////////// 240 | //Socket 管理 241 | public: 242 | //注册Socket,实现管理,注意需要说明文字 243 | void RegisterSocket(Linux_Win_SOCKET s, char* szInfo = null); 244 | //代应用程序执行Close Socket,所有Socket 的Close 由此处完成 245 | void CloseSocket(Linux_Win_SOCKET& s); //关闭Socket 246 | private: 247 | CSocketRegister* m_pSocketRegister; //Socket 注册管理对象 248 | ////////////////////////////////////////////////////////// 249 | //公共管理 250 | public: 251 | //作为一项优化,如果app 设置这个关闭标志, 252 | //所有的free 都直接free,不再保留到stack 中。 253 | //加快退出速度。 254 | void SetCloseFlag(bool bFlag = true); 255 | //重新分配一个指针的空间,默认拷贝原始数据到新空间。 256 | //考虑到大家可能会使用p=pMemPool->ReMalloc(p,1024);这种形式,为了防止内存泄露 257 | //如果pPoint 没有被找到,或者新指针分配失败,老指针会被free 258 | void* ReMalloc(void* pPoint, int nNewSize, bool bCopyOldDataFlag = true); 259 | //分配一个块,注意,需要一段说明文字,说明指针用途,方便注册跟踪 260 | void* Malloc(int nSize, char* szInfo = null); 261 | //释放一个块 262 | void Free(PVOID pBlock); 263 | //显示整棵内存树的内容 264 | void PrintTree(void); 265 | //关键信息显示 266 | void PrintInfo(void); 267 | void test(void); 268 | CTonyMemoryStack* m_pMemPool; //内存栈对象 269 | CTonyLowDebug* m_pDebug; 270 | }; 271 | #endif 272 | -------------------------------------------------------------------------------- /include/Mint.h: -------------------------------------------------------------------------------- 1 | #ifndef __MINT_H_ 2 | #define __MINT_H_ 3 | //这是.h 文件中的声明 4 | #include "MutexLock.h" 5 | 6 | typedef struct _MINT_ //这是整型的多线程安全单元 7 | { 8 | int m_nValue; //这是整型值 9 | MUTEX m_MyLock; //这是实现保护的C 语言锁变量 10 | } MINT, MBOOL; //int 型和bool 型同时实现 11 | //初始化一个线程安全变量,同时可以设置值,返回设置值 12 | extern int MvarInit(MINT& (mValue), int nValue = 0); 13 | //摧毁一个线程安全变量 14 | extern void MvarDestroy(MINT& (mValue)); 15 | //设置一个线程安全变量的值,返回设置的值 16 | extern int MvarSet(MINT& (mValue), int nValue); 17 | //得到一个线程安全变量的值 18 | extern int MvarGet(MINT& (mValue)); 19 | //线程安全变量做加法运算,默认+1 20 | extern int MvarADD(MINT& (mValue), int nValue = 1); 21 | //线程安全变量做减法运算,默认-1 22 | extern int MvarDEC(MINT& (mValue), int nValue = 1); 23 | //这里是.cpp 文件中的实现 24 | int MvarInit(MINT& mValue, int nValue) ; 25 | void MvarDestroy(MINT& mValue) ; 26 | int MvarSet(MINT& mValue, int nValue) ; 27 | int MvarGet(MINT& mValue) ; 28 | int MvarADD(MINT& mValue, int nValue) ; 29 | int MvarDEC(MINT& mValue, int nValue) ; 30 | 31 | //为了简化调用,笔者又定义了一批宏缩写 32 | #define XMI MvarInit 33 | #define XME MvarDestroy 34 | #define XMG MvarGet 35 | #define XMS MvarSet 36 | #define XMA MvarADD 37 | #define XMD MvarDEC 38 | 39 | class CMint //int 型多线程安全变量类 40 | { 41 | public: 42 | CMint(int nVlaue=0) {XMI(m_nValue,nVlaue);}~CMint(void) {XME(m_nValue);} 43 | public: 44 | int Get(void) { 45 | return XMG(m_nValue); 46 | } 47 | int Set(int nValue) { 48 | return XMS(m_nValue, nValue); 49 | } 50 | int Add(int nValue = 1) { 51 | return XMA(m_nValue, nValue); 52 | } 53 | int Dec(int nValue = 1) { 54 | return XMD(m_nValue, nValue); 55 | } 56 | private: 57 | MINT m_nValue; 58 | }; 59 | 60 | class CMbool //bool 型多线程安全的变量类 61 | { 62 | public: 63 | CMbool(bool nVlaue = false) { 64 | XMI(m_nValue, nVlaue); 65 | } 66 | ~CMbool(void) { 67 | XME(m_nValue); 68 | } 69 | public: 70 | //只提供Get 和Set 方法 71 | int Get(void) { 72 | return XMG(m_nValue); 73 | } 74 | int Set(bool nValue) { 75 | return XMS(m_nValue, nValue); 76 | } 77 | private: 78 | MBOOL m_nValue; 79 | }; 80 | 81 | #endif 82 | -------------------------------------------------------------------------------- /include/MrswLock.h: -------------------------------------------------------------------------------- 1 | #ifndef __MRSWLOCK_H_ 2 | #define __MRSWLOCK_H_ 3 | 4 | #include "MutexLock.h" 5 | 6 | #ifndef ULONG 7 | #define ULONG unsigned long 8 | #endif 9 | //定义结构体的习惯:typedef 定义出一种新的,针对本结构体的变量类型,方便后续使用 10 | typedef struct _TONY_XIAO_MULTI_READ_SINGLE_WRITE_LOCK_ { //注意变量命名,遵循匈牙利命名法在类中的命名规则 11 | int m_nReadCount; //读计数器 12 | bool m_bWriteFlag; //写标志 13 | MUTEX m_Lock; //作为单写多读锁本身,也应该是多线程安全的, 14 | //因此,需要增加一个锁变量 15 | } STonyXiaoMultiReadSingleWriteLock; //这是新的变量类型,可以定义变量 16 | //习惯性做法,定义结构体后,马上定义其尺寸常量,方便后续申请内存方便 17 | const ULONG STonyXiaoMultiReadSingleWriteLockSize = 18 | sizeof(STonyXiaoMultiReadSingleWriteLock); 19 | 20 | //高精度睡眠函数,采用内联模式加速调用 21 | inline void TonyXiaoMinSleep(void) { 22 | #ifdef WIN32 23 | Sleep(1); //Windows 下不做改变,沿用应用级Sleep 函数 24 | #else // not WIN32 25 | //Linux 下,使用nanosleep 实现高精度睡眠 26 | struct timespec slptm; 27 | slptm.tv_sec = 0; 28 | slptm.tv_nsec = 1000; //1000 ns = 1 us 29 | if (nanosleep(&slptm, NULL) == -1) 30 | usleep(1); 31 | #endif //WIN32 32 | } 33 | //Linux 下普通的睡眠函数对比 34 | #ifndef __Sleep 35 | #define Sleep(ms) usleep(ms*1000) 36 | #endif 37 | //大家可以看看笔者的函数命名习惯,基本遵循匈牙利命名法,即单词首字母大写 38 | //MRSW 是Multi Read and Signal Write(多读和单写)的缩写 39 | //MRSWLock 前缀表示单写多读锁 40 | //中间一个“_”分割符,后面是函数的功能描述Greate,创建,Destroy,摧毁,等等 41 | void MRSWLock_Create(STonyXiaoMultiReadSingleWriteLock* pLock) ; 42 | void MRSWLock_Destroy(STonyXiaoMultiReadSingleWriteLock* pLock) ; 43 | 44 | //获取写状态 45 | bool MRSWLock_GetWrite(STonyXiaoMultiReadSingleWriteLock* pLock) ; 46 | //获取读计数器 47 | int MRSWLock_GetRead(STonyXiaoMultiReadSingleWriteLock* pLock) ; 48 | 49 | //进入写操作函数 50 | void MRSWLock_EnableWrite(STonyXiaoMultiReadSingleWriteLock* pLock) ; 51 | void MRSWLock_DisableWrite(STonyXiaoMultiReadSingleWriteLock* pLock) ; 52 | 53 | //进入读函数,返回当前的读计数器值 54 | int MRSWLock_AddRead(STonyXiaoMultiReadSingleWriteLock* pLock) ; 55 | 56 | //返回读计数器变化后的结果 57 | int MRSWLock_DecRead(STonyXiaoMultiReadSingleWriteLock* pLock) ; 58 | void MRSWLock_Read2Write(STonyXiaoMultiReadSingleWriteLock* pLock) ; 59 | /////////////////////////////////////////////////////////////////////////// 60 | class CMultiReadSingleWriteLock { 61 | public: 62 | //构造函数和析构函数,自动调用结构体的初始化和摧毁 63 | CMultiReadSingleWriteLock() { 64 | MRSWLock_Create(&m_Lock); 65 | } 66 | ~CMultiReadSingleWriteLock() { 67 | MRSWLock_Destroy(&m_Lock); 68 | } 69 | public: 70 | //相应的公有方法,完全是调用C 语言的函数 71 | void EnableWrite(void) { 72 | MRSWLock_EnableWrite(&m_Lock); 73 | } 74 | void DisableWrite(void) { 75 | MRSWLock_DisableWrite(&m_Lock); 76 | } 77 | void Read2Write(void) { 78 | MRSWLock_Read2Write(&m_Lock); 79 | } 80 | void DecRead(void) { 81 | MRSWLock_DecRead(&m_Lock); 82 | } 83 | void AddRead(void) { 84 | MRSWLock_AddRead(&m_Lock); 85 | } 86 | bool GetWrite(void) { 87 | MRSWLock_GetWrite(&m_Lock); 88 | } 89 | int GetRead(void) { 90 | MRSWLock_GetRead(&m_Lock); 91 | } 92 | private: 93 | //私有结构体 94 | STonyXiaoMultiReadSingleWriteLock m_Lock; 95 | }; 96 | ////////////////////////////////////////////////////////////// 97 | //这里是类声明 98 | class CMRSWint { 99 | public: 100 | CMRSWint(); 101 | ~CMRSWint() { 102 | } //析构函数不做任何事 103 | public: 104 | int Get(void); 105 | int Set(int nValue); 106 | int Add(int nValue = 1); 107 | int Dec(int nValue = 1); 108 | int GetAndClean2Zero(void); 109 | int DecUnless0(int nValue = 1); //如果不是,-1 110 | private: 111 | int m_nValue; 112 | CMultiReadSingleWriteLock m_Lock; 113 | }; 114 | 115 | ///////////////////////////////////////////////////////// 116 | //bool 变量的实现更加简单 117 | class CMRSWbool { 118 | public: 119 | CMRSWbool() { 120 | } 121 | ~CMRSWbool() { 122 | } 123 | public: 124 | //得到变量的值,或者设置变量的值,均调用整型对象完成 125 | bool Get(void) { 126 | return (bool) m_nValue.Get(); 127 | } 128 | bool Set(bool bFlag) { 129 | return m_nValue.Set((int) bFlag); 130 | } 131 | private: 132 | CMRSWint m_nValue; //内部聚合一个上文定义的整型单写多读锁安全变量 133 | }; 134 | 135 | 136 | 137 | 138 | #endif 139 | -------------------------------------------------------------------------------- /include/MutexLock.h: -------------------------------------------------------------------------------- 1 | #ifndef __MUTEXLOCK_H_ 2 | #define __MUTEXLOCK_H_ 3 | 4 | #ifdef WIN32 //Windows 下定义 5 | //定义锁变量类型 6 | #define MUTEX CRITICAL_SECTION 7 | //定义初始化锁的功能 8 | #define MUTEXINIT(m) InitializeCriticalSection(m) 9 | //定义加锁 10 | #define MUTEXLOCK(m) EnterCriticalSection(m) 11 | //定义解锁 12 | #define MUTEXUNLOCK(m) LeaveCriticalSection(m) 13 | //定义摧毁锁变量操作 14 | #define MUTEXDESTROY(m) DeleteCriticalSection(m) 15 | #else //Linux 下定义 16 | //定义锁变量类型 17 | #define MUTEX pthread_mutex_t 18 | //定义初始化锁的功能 19 | #define MUTEXINIT(m) pthread_mutex_init(m, NULL) //TODO: check error 20 | //定义加锁 21 | #define MUTEXLOCK(m) pthread_mutex_lock(m) 22 | //定义解锁 23 | #define MUTEXUNLOCK(m) pthread_mutex_unlock(m) 24 | //定义摧毁锁变量操作 25 | #define MUTEXDESTROY(m) pthread_mutex_destroy(m) 26 | #endif 27 | 28 | class CMutexLock { 29 | 30 | 31 | public: 32 | CMutexLock(void) { 33 | MUTEXINIT(&m_Lock); 34 | } //构造函数,初始化锁 35 | ~CMutexLock(void) { 36 | MUTEXDESTROY(&m_Lock); 37 | } //析构函数,摧毁锁 38 | public: 39 | void Lock() { 40 | MUTEXLOCK(&m_Lock); 41 | } //加锁操作 42 | void Unlock() { 43 | MUTEXUNLOCK(&m_Lock); 44 | } //解锁操作 45 | private: 46 | MUTEX m_Lock; //锁变量(私有) 47 | }; 48 | 49 | #endif 50 | 51 | -------------------------------------------------------------------------------- /include/Nonreentrant.h: -------------------------------------------------------------------------------- 1 | #ifndef __NONREENTRANT_H_ 2 | #define __NONREENTRANT_H_ 3 | 4 | #include "MutexLock.h" 5 | 6 | //不可重入锁类声明 7 | class CNonReentrant { 8 | public: 9 | CNonReentrant(); 10 | ~CNonReentrant() { 11 | } //析构函数不做任何事 12 | public: 13 | //设置为真的时候 14 | // 如果没有设置进入标志,设置,并返回真 15 | // 如果已经设置进入标志,不设置,并返回假 16 | //设置为假的时候, 17 | // 总是成功并返回真 18 | bool Set(bool bRunFlag); 19 | private: 20 | CMutexLock m_Lock; //锁 21 | bool m_bAlreadRunFlag; //内部的变量值 22 | }; 23 | 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /include/Socket.h: -------------------------------------------------------------------------------- 1 | #ifndef __SOCKET_H_ 2 | #define __SOCKET_H_ 3 | #ifdef WIN32 //Windows 下定义 4 | #include 5 | //定义Socket 套接字变量类型 6 | #define Linux_Win_SOCKET SOCKET 7 | //标准的Socket 关闭函数,不过,由于后文资源池接管了关闭动作,此处隐去定义 8 | //#define Linux_Win_CloseSocket closesocket 9 | //协议族命名约定 10 | #define Linux_Win_F_INET AF_INET 11 | //非法的socket 表示值定义 12 | #define Linux_Win_InvalidSocket INVALID_SOCKET 13 | //标准socket 错误返回码 14 | #define Linux_Win_SocketError SOCKET_ERROR 15 | //setsockopt 第4 个变量类型定义 16 | #define Linux_Win_SetSockOptArg4UseType const char 17 | //getsockopt 第4 个变量类型定义 18 | #define Linux_Win_GetSockOptArg4UseType char 19 | //send recv 函数的最后一个参数类型 20 | #define Linux_Win_SendRecvLastArg 0 21 | //注意此处,所有的错误返回码定名,Windows 平台向向标准的伯克利socket 规范靠拢。 22 | #define EWOULDBLOCK WSAEWOULDBLOCK //10035 23 | #define EINPROGRESS WSAEINPROGRESS 24 | #define EALREADY WSAEALREADY 25 | #define ENOTSOCK WSAENOTSOCK 26 | #define EDESTADDRREQ WSAEDESTADDRREQ 27 | #define EMSGSIZE WSAEMSGSIZE 28 | #define EPROTOTYPE WSAEPROTOTYPE 29 | #define ENOPROTOOPT WSAENOPROTOOPT 30 | #define EPROTONOSUPPORT WSAEPROTONOSUPPORT 31 | #define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT 32 | #define EOPNOTSUPP WSAEOPNOTSUPP 33 | #define EPFNOSUPPORT WSAEPFNOSUPPORT 34 | #define EAFNOSUPPORT WSAEAFNOSUPPORT 35 | #define EADDRINUSE WSAEADDRINUSE 36 | #define EADDRNOTAVAIL WSAEADDRNOTAVAIL 37 | #define ENETDOWN WSAENETDOWN 38 | #define ENETUNREACH WSAENETUNREACH 39 | #define ENETRESET WSAENETRESET 40 | #define ECONNABORTED WSAECONNABORTED //10053 41 | #define ECONNRESET WSAECONNRESET //10054 42 | #define ENOBUFS WSAENOBUFS 43 | #define EISCONN WSAEISCONN 44 | #define ENOTCONN WSAENOTCONN 45 | #define ESHUTDOWN WSAESHUTDOWN 46 | #define ETOOMANYREFS WSAETOOMANYREFS 47 | #define ETIMEDOUT WSAETIMEDOUT 48 | #define ECONNREFUSED WSAECONNREFUSED 49 | #define ELOOP WSAELOOP 50 | #define EHOSTDOWN WSAEHOSTDOWN 51 | #define EHOSTUNREACH WSAEHOSTUNREACH 52 | #define EPROCLIM WSAEPROCLIM 53 | #define EUSERS WSAEUSERS 54 | #define EDQUOT WSAEDQUOT 55 | #define ESTALE WSAESTALE 56 | #define EREMOTE WSAEREMOTE 57 | 58 | #else //Linux 下定义 59 | //定义Socket 套接字变量类型 60 | #define Linux_Win_SOCKET int 61 | //标准的Socket 关闭函数,不过,由于后文资源池接管了关闭动作,此处隐去定义 62 | //#define Linux_Win_CloseSocket close 63 | //协议族命名约定 64 | #define Linux_Win_F_INET AF_INET 65 | //非法的socket 表示值定义 66 | #define Linux_Win_InvalidSocket -1 67 | //标准socket 错误返回码 68 | #define Linux_Win_SocketError -1 69 | //setsockopt 第4 个变量类型定义 70 | #define Linux_Win_SetSockOptArg4UseType void 71 | //getsockopt 第4 个变量类型定义 72 | #define Linux_Win_GetSockOptArg4UseType void 73 | //send recv 函数的最后一个参数类型 74 | #define Linux_Win_SendRecvLastArg MSG_NOSIGNAL 75 | #endif//end of WIN32 76 | #endif//end of __SOCKET_H_ -------------------------------------------------------------------------------- /include/Statistics.h: -------------------------------------------------------------------------------- 1 | #ifndef __STATISITCS_H_ 2 | #define __STATISITCS_H_ 3 | /////////////////////////////////// 4 | typedef struct _COUNT_SUB_ { 5 | unsigned long m_lXBegin; 6 | unsigned long m_lXEnd; 7 | } SCountSub; 8 | /////////////////////////////////// 9 | //基本统计平均值模块 10 | typedef struct _COUNT_ { 11 | SCountSub m_Sub; //ΔX 计算 12 | unsigned long m_Sum; //统计平均值 13 | } SCount; 14 | 15 | //////////////////////////////////////////////////////////////////////// 16 | #if defined(__cplusplus) || defined(c_plusplus) 17 | extern "C" { //纯C 输出开始 18 | #endif 19 | //获得ΔX 20 | extern unsigned long SCountSubGetX(SCountSub& CountSub); 21 | //设置初始值 22 | extern void SCountSubSetBegin(SCountSub& CountSub, unsigned long n) ; 23 | //设置结束值 24 | extern unsigned long SCountSubSetEnd(SCountSub& CountSub, unsigned long n) ; 25 | 26 | //初始化 27 | extern void SCountReset(SCount& Count, unsigned long n) ; 28 | //计算统计平均值 29 | extern unsigned long SCountSum(SCount& Count); 30 | //返回Sum 值 31 | extern unsigned long SCountGetSum(SCount& Count) ; 32 | 33 | //返回当前的ΔX 34 | extern unsigned long SCountGetX(SCount& Count) ; 35 | //设置开始 36 | extern void SCountSetBegin(SCount& Count, unsigned long n) ; 37 | //设置结束值,单纯更新,可以多次刷新 38 | extern unsigned long SCountSetEnd(SCount& Count, unsigned long n) ; 39 | 40 | #if defined(__cplusplus) || defined(c_plusplus) 41 | } //纯C 输出结束 42 | #endif /*defined(__cplusplus) || defined(c_plusplus)*/ 43 | 44 | ///////////////////////////////////////////////////////////////////////// 45 | //△计算模块类 46 | class CCountSub { 47 | public: 48 | CCountSub() { 49 | m_CountSub.m_lXBegin = 0; 50 | m_CountSub.m_lXEnd = 0; 51 | } 52 | ~CCountSub() { 53 | } 54 | public: 55 | //设置初始值 56 | unsigned long SetBegin(unsigned long n) { 57 | return m_CountSub.m_lXBegin = n; 58 | } 59 | //设置结束值 60 | unsigned long SetEnd(unsigned long n) { 61 | return m_CountSub.m_lXEnd = n; 62 | } 63 | //获得初始值 64 | unsigned long GetBegin(void) { 65 | return m_CountSub.m_lXBegin; 66 | } 67 | //获得结束值 68 | unsigned long GetEnd(void) { 69 | return m_CountSub.m_lXEnd; 70 | } 71 | //获得△ 72 | unsigned long GetX(void) { 73 | return m_CountSub.m_lXEnd - m_CountSub.m_lXBegin; 74 | } 75 | //把结束值赋给初始值,相当于△归零 76 | void E2B(void) { 77 | m_CountSub.m_lXBegin = m_CountSub.m_lXEnd; 78 | } 79 | //把结束值赋给初始值,然后给结束值赋新值,这在某些循环统计的场合非常有用 80 | void Push(unsigned long ulNew) { 81 | E2B(); 82 | m_CountSub.m_lXEnd = ulNew; 83 | } 84 | //与前文共用的数据描述结构体 85 | SCountSub m_CountSub; 86 | }; 87 | 88 | ///////////////////////////////////////////////////////////////////////////// 89 | //平均值类 90 | // 91 | class CDeltaTime { 92 | public: 93 | CDeltaTime() { 94 | TouchBegin(); 95 | } 96 | ~CDeltaTime() { 97 | } 98 | public: 99 | //Reset 功能,初始化统计时钟,且Begin=End 100 | void Reset(void) ; 101 | //触发开始时间计数 102 | void TouchBegin(void) ; 103 | //触发结束时间技术 104 | void TouchEnd(void) ; 105 | //利用上文的Push 功能,实现循环统计。即Now->End->Begin 106 | void Touch(void) ; 107 | //获得△ 108 | unsigned long GetDeltaT(void) { 109 | return m_CountSub.GetX(); 110 | } 111 | //平均值计算获得每秒钟的操作数,用户以累加器统计总的操作数,传递进来, 112 | //本函数自动根据内部的△计算平均值,除法计算提供double 的精度,并规避除0 错误 113 | double GetOperationsPreSecond(unsigned long ulOperationCount) { 114 | double dRet = 0.0; 115 | double dCount = (double) ulOperationCount; 116 | unsigned long ulDeltaSecond = GetDeltaT(); 117 | double dSeconds = (double) ulDeltaSecond; 118 | if (0 == ulDeltaSecond) 119 | return dRet; 120 | return dCount / dSeconds; 121 | } //以前文模块描述的基本统计模块 122 | CCountSub m_CountSub; 123 | }; 124 | //////////////////////////////////////////////////////////////////////////////////// 125 | ////带锁的时间平均值统计模块,所有函数功能同上,本层仅仅提供锁保护 126 | //class CDeltaTimeVSLock { 127 | //public: 128 | // CDeltaTimeVSLock() { 129 | // } 130 | // ~CDeltaTimeVSLock() { 131 | // } 132 | //public: 133 | // void Reset(void) { //Reset 为写动作,不能并发 134 | // m_Lock.EnableWrite(); 135 | // m_nDeltaT.Reset(); 136 | // m_Lock.DisableWrite(); 137 | // } 138 | // void TouchBegin(void) { //TouchBegin 为写动作,不能并发 139 | // m_Lock.EnableWrite(); 140 | // m_nDeltaT.TouchBegin(); 141 | // m_Lock.DisableWrite(); 142 | // } 143 | // void TouchEnd(void) { //TouchEnd 为写动作,不能并发 144 | // m_Lock.EnableWrite(); 145 | // m_nDeltaT.TouchEnd(); 146 | // m_Lock.DisableWrite(); 147 | // } 148 | // unsigned long GetDeltaT(void) { //GetDeltaT 为读动作,可以并发读 149 | // unsigned long nRet = 0; 150 | // m_Lock.AddRead(); 151 | // nRet = m_nDeltaT.GetDeltaT(); 152 | // m_Lock.DecRead(); 153 | // return nRet; 154 | // } 155 | // double GetOperationsPreSecond(unsigned long ulOperationCount) { //GetOterationPerSecond 为读动作,可以并发读 156 | // double dRet = 0.0; 157 | // m_Lock.AddRead(); 158 | // dRet = m_nDeltaT.GetOperationsPreSecond(ulOperationCount); 159 | // m_Lock.DecRead(); 160 | // return dRet; 161 | // } 162 | //private: 163 | // CDeltaTime m_nDeltaT; //基本统计因子 164 | // CMultiReadSingleWriteLock m_Lock; //单写多读锁对象 165 | //}; 166 | 167 | 168 | ////////////////////////////////////////////////////////////////////////////// 169 | class CCount { 170 | public: 171 | CCount() { 172 | SCountReset(0); 173 | } 174 | ~CCount() { 175 | } 176 | public: 177 | void SCountReset(unsigned long n) { 178 | m_Count.m_Sum = n; 179 | m_Count.m_Sub.m_lXBegin = 0; 180 | m_Count.m_Sub.m_lXEnd = 0; 181 | } 182 | unsigned long SCountSum(void) { 183 | unsigned long X = SCountSubGetX(m_Count.m_Sub); 184 | if (0 == m_Count.m_Sum) 185 | m_Count.m_Sum = X; //初值如果为0,则直接赋值,避免误差 186 | else 187 | m_Count.m_Sum = (m_Count.m_Sum + X) / 2; //计算统计平均值 188 | return m_Count.m_Sum; 189 | } 190 | unsigned long SCountSetSum(unsigned long n) { 191 | m_Count.m_Sum = n; 192 | return n; 193 | } 194 | unsigned long SCountGetSum(void) { 195 | return m_Count.m_Sum; 196 | } 197 | unsigned long SCountGetX(void) { 198 | return SCountSubGetX(m_Count.m_Sub); 199 | } 200 | void SCountSetBegin(unsigned long n) { 201 | SCountSubSetBegin(m_Count.m_Sub, n); 202 | } 203 | unsigned long SCountSetEnd(unsigned long n) { 204 | return SCountSubSetEnd(m_Count.m_Sub, n); 205 | } 206 | SCount m_Count; 207 | }; 208 | #endif -------------------------------------------------------------------------------- /include/Thread.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREAD_H_ 2 | #define __THREAD_H_ 3 | #ifdef WIN32 //Windows 下定义 4 | #include 5 | //线程句柄类型 6 | #define THREAD HANDLE 7 | //线程ID 类型 8 | #define THREADID unsigned 9 | //线程启动函数统一构型,注意函数型宏的使用 10 | #define THREADCREATE(func, args, thread, id) \ 11 | thread = (HANDLE)_beginthreadex(NULL, 0, func, (PVOID)args, 0, &id); 12 | //线程启动失败后返回错误码定义 13 | #define THREADCREATE_ERROR NULL 14 | //线程函数标准构型 15 | #define THREADFUNCDECL(func,args) unsigned __stdcall func(PVOID args) 16 | //工程中通常需要检测本次开机以来的毫秒数,Windows 和Linux 不一样,此处予以统一。 17 | #define _GetTimeOfDay GetTickCount 18 | //Windows 下最小睡眠精度,经实测为10ms 19 | #define MIN_SLEEP 10 20 | #else //Linux 下定义 21 | //线程句柄类型 22 | #define THREAD pthread_t 23 | //线程ID 类型 24 | #define THREADID unsigned //unused for Posix Threads 25 | //线程启动函数统一构型,注意函数型宏的使用 26 | #define THREADCREATE(func, args, thread, id) \ 27 | pthread_create(&thread, NULL, func, args); 28 | //线程启动失败后返回错误码定义 29 | #define THREADCREATE_ERROR -1 30 | //线程函数标准构型 31 | #define THREADFUNCDECL(func,args) void * func(void *args) 32 | //#define Sleep(ms) usleep((__useconds_t)(ms*1000)) 33 | //工程中通常需要检测本次开机以来的毫秒数,Windows 和Linux 不一样,此处予以统一。 34 | unsigned long GetTickCount(void); 35 | #include 36 | #define _GetTimeOfDay GetTickCount 37 | //Linux 下没有Sleep 函数,为了便于统一编程,仿照Windows 下定义该函数 38 | #define Sleep(ms) usleep(ms*1000) 39 | #define MIN_SLEEP 1 40 | #endif//end of WIN32 41 | ///////////////////////////////////////////////////////// 42 | #ifdef WIN32 //Windows 下定义 43 | //Windows 下有此函数,无需重复定义 44 | #else //Linux 下定义 45 | //获得本次开机以来毫秒数 46 | inline unsigned long GetTickCount(void) { 47 | unsigned long lRet = 0; 48 | struct timeval tv; 49 | gettimeofday(&tv, null); 50 | lRet = tv.tv_usec / 1000; 51 | return lRet; 52 | } 53 | #endif//end of WIN32 54 | #endif//end of __THREAD_H_ 55 | -------------------------------------------------------------------------------- /include/ThreadManager.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREADMANAGER_H_ 2 | #define __THREADMANAGER_H_ 3 | #include "MrswLock.h" 4 | 5 | //线程控制锁类声明和实现,完全采用内联模型,保证效率 6 | class CThreadManager { 7 | private: 8 | //大家可能注意到,线程控制锁,大多数情况下用于查询, 9 | //只有在线程起停时,才会改写内部的值, 10 | //因此,此处用的全部是单写多读变量 11 | CMRSWbool m_bThreadContinue; //线程持续的标志 12 | CMRSWint m_nThreadCount; //线程计数器 13 | //很多时候,我们做测试代码,可能需要多个线程并发 14 | //在Debug 打印输出时,可能会需要一个独立的线程ID, 15 | //线程ID 需要做唯一性分配,因此,将这个分配器做在线程安全锁中,能被所有线程看到 16 | //与业务无关,仅用于区别打印信息来自于哪个线程, 17 | //这里提供一个线程ID 提供器,程序员可以根据需要,在线程中使用, 18 | //这算是一个内嵌的debug 友好功能。 19 | CMRSWint m_nThreadID; 20 | public: 21 | CThreadManager() { 22 | } //由于多线程安全变量对象内置初始化,此处无需初始化 23 | ~CThreadManager() { 24 | CloseAll(); 25 | } //退出时自动关闭所有线程 26 | //启动逻辑,其实也是初始化逻辑 27 | //在使用线程控制锁前,请一定要先调用本接口函数 28 | void Open(void) { 29 | CloseAll(); //为防止多重启动,先执行一次Close 30 | //初始化线程控制变量 31 | m_bThreadContinue.Set(true); 32 | m_nThreadCount.Set(0); 33 | } //关闭所有线程逻辑 34 | void CloseAll(void) { //这个逻辑已经出现几次,此处不再赘述 35 | m_bThreadContinue.Set(false); 36 | while (m_nThreadCount.Get()) { 37 | TonyXiaoMinSleep(); 38 | } 39 | } //启动一个线程前,线程计数器+1的动作 40 | int AddAThread(void) { 41 | return m_nThreadCount.Add(); 42 | } 43 | //线程退出前,线程计数器-1 动作 44 | void DecAThread(void) { 45 | m_nThreadCount.Dec(); 46 | } 47 | //查询线程维持变量的值 48 | bool ThreadContinue(void) { 49 | return m_bThreadContinue.Get(); 50 | } 51 | //获得线程计数器的值 52 | int GetThreadCount(void) { 53 | return m_nThreadCount.Get(); 54 | } 55 | //分配一个线程ID,供Debug 使用 56 | int GetID(void) { 57 | return m_nThreadID.Add() - 1; 58 | } 59 | }; 60 | #endif 61 | -------------------------------------------------------------------------------- /include/ThreadPool.h: -------------------------------------------------------------------------------- 1 | #ifndef __THREAD_POOL_H_ 2 | #define __THREAD_POOL_H_ 3 | 4 | //线程池回调函数指针 5 | //每个回调函数有一次运行权 6 | //运行结束,线程并不退出,进入IDLE 状态 7 | typedef void (*_TPOOL_CALLBACK)(void* pCallParam, //这是主程序传进来的参数指针 8 | MBOOL& bThreadContinue); //如果系统要退出,线程池会修改这个参数的值为false 9 | //习惯写法,写出一个回调函数构型,立即给出示例,方便以后的应用程序拷贝使用 10 | //static void ThreadPoolCallback(void* pCallParam,MBOOL& bThreadContinue); 11 | #ifndef TONY_DEBUG 12 | #define TONY_DEBUG m_pDebug->Debug2File 13 | #endif 14 | ////////////////////////////////////////////////////////////////////////// 15 | #define OPEN_THREAD_DELAY 250 //新开线程的延迟ms 数,Windows 和Linux 系统, 16 | //开线程太快的话,会出现死线程 17 | //必须有一个延迟,经验参数建议>200ms 18 | #define WHILE_THREAD_COUNT 10 //最多n 条线程空闲等待任务(经验参数:10) 19 | //这不是硬性的,如果有线程已经IDLE, 20 | //可能会比这个多,但随后会动态调整 21 | #define DEFAULT_THREAD_SLEEP 500 //通常建议的线程睡眠参数 22 | #define THREAD_POOL_EXIT_CODE 10000 //线程池的线程,从开始设置退出码 23 | //总线程数上限 24 | #ifdef _ARM_ //ARM 嵌入式系统 25 | #define THIS_POOLTHREAD_MAX 30 //嵌入式系统下最大30 条线程 26 | #else //PC 机 27 | #ifdef WIN32 28 | #define THIS_POOLTHREAD_MAX 2000 //Windows 下最大2000 条线程 29 | #else // not WIN32 30 | #define THIS_POOLTHREAD_MAX 300 //Linux 下最大300 条线程 31 | #endif //WIN32 32 | #endif 33 | ////////////////////////////////////////////////////////////// 34 | #define TPOOL_THREAD_STATE_NOT_RUN 0 //线程池线程状态,线程未运行 35 | #define TPOOL_THREAD_STATE_IDLE 1 //线程池线程状态,线程运行,空闲 36 | #define TPOOL_THREAD_STATE_BUSY 2 //线程池线程状态,线程运行,有任务 37 | ////////////////////////////////////////////////////////////// 38 | #define _THREADPOOL_CAN_NOT_USE -2 //线程池未初始化,无法工作 39 | #define _THREADPOOL_OVERFLOW -1 //线程池溢出标志,无法注册 40 | #define _THREADPOOL_PLEASE_WAIT 0 //线程池目前没有备用线程,请等待 41 | #define _THREADPOOL_OK 1 //线程池注册成功 42 | ////////////////////////////////////////////////////////////// 43 | class CTonyThreadPool; 44 | typedef struct _THREAD_TOKEN_ { 45 | int m_nExitCode; //返回值,也是本线程在整个线程池的编号 46 | MINT m_nState; //线程状态机 47 | THREAD m_hThread; //线程句柄 48 | THREADID m_nThreadID; //线程ID 49 | _TPOOL_CALLBACK m_pCallback; //回调函数 50 | void* m_pCallParam; //回调函数参数 51 | CTonyThreadPool* m_pThreadPoolObject; //指向线程池对象的指针 52 | } SThreadToken; //定义的变量类型 53 | const unsigned long SThreadTokenSize = sizeof(SThreadToken); //结构体长度常量 54 | 55 | ////////////////////////////////////////////////////////////////// 56 | //*************************线程池类*******************************// 57 | /////////////////////////////////////////////////////////////////// 58 | //线程池类 59 | class CTonyThreadPool { 60 | public: 61 | CTonyThreadPool(CTonyLowDebug* pDebug); //需要传入Debug 对象指针 62 | ~CTonyThreadPool(); 63 | public: 64 | //注册一个新线程,返回状态值,状态值后文有叙述 65 | int ThreadPoolRegTask(_TPOOL_CALLBACK pCallback, //回调函数指针 66 | void* pParam, //代传的参数指针 67 | bool bWait4Success = true); //是否等待注册成功才返回 68 | bool TPAllThreadIsIdle(void); //检查所有线程是否空闲 69 | bool ThreadPoolIsContinue(void); //检查线程池运行状态 70 | private: 71 | //这里是真实的操作系统线程函数,其构型后文叙述 72 | static THREADFUNCDECL(ThreadPoolThread,pParam); //线程池服务线程 73 | static THREADFUNCDECL(ThreadPoolCtrlThread,pParam); //线程池管理线程 74 | private: 75 | //内部函数,检索没有使用的Token 76 | int Search4NotUseToken(void); 77 | //内部函数,获得一个空闲线程 78 | int GetAIdleThread(void); 79 | //这是完成具体注册动作的内部函数,后文详细叙述 80 | int ThreadPoolRegisterANewThread(_TPOOL_CALLBACK pCallback, void* pParam); 81 | int ThreadPoolRegisterANewThreadWhile(_TPOOL_CALLBACK pCallback, 82 | void* pParam); 83 | private: 84 | SThreadToken m_TToken[THIS_POOLTHREAD_MAX]; //线程池任务参数静态数组 85 | //这两个参数就是线程池安全退出控制参数,int 型的计数器+bool 型的线程持续标志 86 | MBOOL m_bThreadContinue; //所有Thread 继续标志 87 | MINT m_nThreadPoolThreadCount; //Thread 计数器 88 | //统计变量 89 | MINT m_nThreadPoolIdleThreadCount; //空闲的线程数量 90 | //线程池没有使用前述的C++锁类,而是直接使用C 的纯锁结构体完成 91 | MUTEX m_RegisterLock; //线程注册临界区 92 | CTonyLowDebug* m_pDebug; //Debug 对象指针 93 | }; 94 | ////////////////////////////////////////////////////////////////// 95 | //*************************任务池类*******************************// 96 | /////////////////////////////////////////////////////////////////// 97 | typedef void (*_TPOOL_CALLBACK)(void* pCallParam, //这是主程序透传进来的参数指针 98 | MBOOL& bThreadContinue); //如果系统要退出,线程池会修改这个参数的值为false 99 | typedef bool (*_TASKPOOL_CALLBACK)(void* pCallParam, //代传参数指针 100 | int& nStatus); //程序控制状态机 101 | #ifndef TASK_POOL_TOKEN_MAX 102 | //最多同时并发任务数 103 | #define TASK_POOL_TOKEN_MAX (16*1024) 104 | #endif 105 | #ifndef DEFAULT_THREAD_MAX 106 | //默认线程数 107 | #define DEFAULT_THREAD_MAX (30) 108 | #endif 109 | //任务池核心数据结构 110 | typedef struct _TASK_POOL_TOKEN_ { 111 | _TASKPOOL_CALLBACK m_pCallback; //回调函数指针 112 | void* m_pUserParam; //替用户传递的回调函数参数,可以为null 113 | int m_nUserStatus; //代替用户传递的一个状态值,初始值默认是0 114 | } STaskPoolToken; 115 | //数据结构长度 116 | const int STaskPoolTokenSize = sizeof(STaskPoolToken); 117 | //////////////////////////////////////////////////////////////////////////// 118 | //将一根指针注册到内存池,内存池默认在聚合工具类中 119 | #define TONY_REGISTER(pPoint,szInfo) \ 120 | m_pTonyBaseLib->m_pMemPool->Register(pPoint,szInfo) 121 | //将一根指针从内存池反注册,内存池默认在聚合工具类中 122 | #define TONY_UNREGISTER(pPoint) \ 123 | m_pTonyBaseLib->m_pMemPool->UnRegister(pPoint) 124 | //将一根对象指针先执行反注册,再摧毁,最后置空 125 | #define TONY_DEL_OBJ(p) \ 126 | if(p){TONY_UNREGISTER(p);delete p;p=null;} 127 | ////////////////////////////////////////////////////////////////////////// 128 | //依赖类 129 | class CTonyBaseLibrary; 130 | class CTonyThreadPool; 131 | class CTonyMemoryQueueWithLock; 132 | class CTonyMemoryPoolWithLock; 133 | class CTonyLowDebug; 134 | 135 | //////////////////////////////任务池类/////////////////////////////////////// 136 | class CTonyXiaoTaskPool { 137 | public: 138 | CTonyXiaoTaskPool(CTonyBaseLibrary* pTonyBaseLib, //依赖的工具聚合类指针 139 | int nMaxThread = DEFAULT_THREAD_MAX); //最大线程数 140 | ~CTonyXiaoTaskPool(); 141 | public: 142 | bool ICanWork(void); //防御性设计,可运行标志 143 | void PrintInfo(void); //内容打印,Debug 用 144 | public: 145 | //注册一个新任务,返回TaskID 146 | bool RegisterATask(_TASKPOOL_CALLBACK pCallback, //回调函数指针 147 | void* pUserParam = null); //回调函数参数 148 | private: 149 | //真实的内部注册函数 150 | bool RegisterATaskDoIt(STaskPoolToken* pToken, int nLimit = -1); 151 | //服务者线程 152 | bool TaskServiceThreadDoIt(STaskPoolToken& Task); 153 | static void TaskServiceThread(void* pCallParam, MBOOL& bThreadContinue); 154 | //管理者线程 155 | static void TaskCtrlThread(void* pCallParam, MBOOL& bThreadContinue); 156 | private: 157 | CMRSWbool m_bThreadContinue; //任务池自带线程管理变量 158 | CMRSWint m_nThreadCount; //可以自行退出所属线程 159 | CTonyMemoryQueueWithLock* m_pTaskQueue; //核心任务队列 160 | CTonyThreadPool* m_pThreadPool; //线程池指针 161 | private: 162 | int m_nMaxThread; //最大任务数的保存变量 163 | CMRSWint m_nThreadID; //任务ID 种子 164 | CTonyLowDebug* m_pDebug; //debug 对象指针 165 | CTonyBaseLibrary* m_pTonyBaseLib; //聚合工具类指针 166 | }; 167 | 168 | ////////////////////////////////////////////////////////////////// 169 | //************************任务描述工具类***************************// 170 | /////////////////////////////////////////////////////////////////// 171 | #ifndef TONY_MEMORY_BLOCK_INFO_MAX_SIZE 172 | #define TONY_MEMORY_BLOCK_INFO_MAX_SIZE 124 173 | #endif 174 | #define TONY_TASK_RUN_MAX_TASK 16 //最多步动作 175 | typedef struct _TONY_TASK_RUN_INFO_ { 176 | int m_nTaskCount; //总共多少步骤 177 | void* m_pCallParam; //共用的回调函数参数 178 | //动作回调函数数组 179 | _TASKPOOL_CALLBACK m_CallbackArray[TONY_TASK_RUN_MAX_TASK]; 180 | } STonyTaskRunInfo; 181 | const ULONG STonyTaskRunInfoSize = sizeof(STonyTaskRunInfo); 182 | class CTonyTaskRun; 183 | typedef struct _TonyTeskRunTaskCallback_Param_ { 184 | STonyTaskRunInfo m_Info; //任务描述结构体 185 | CTonyBaseLibrary* m_pTonyBaseLib; //基本聚合工具类指针 186 | CTonyTaskRun* m_pThis; //任务运行体对象指针 187 | int m_nRunIndex; //当前执行的步距 188 | char m_szAppName[TONY_MEMORY_BLOCK_INFO_MAX_SIZE]; //应用名 189 | } STonyTeskRunTaskCallbackParam; 190 | const ULONG STonyTeskRunTaskCallbackParamSize = //结构体长度常量 191 | sizeof(STonyTeskRunTaskCallbackParam); 192 | //////////////////////////////////////////////////////////////////////////// 193 | class CTonyTaskRunInfo { 194 | public: 195 | STonyTaskRunInfo m_Info; //作为工具类的数据区实体,保存数据 196 | STonyTaskRunInfo* m_pInfo; //作为粘合类的数据区指针,指向外部数据 197 | private: 198 | static void Init(STonyTaskRunInfo* pInfo) //初始化动作 199 | { 200 | pInfo->m_nTaskCount = 0; //动作计数器归零 201 | pInfo->m_pCallParam = null; //参数指针清空 202 | int i = 0; 203 | for (i = 0; i < TONY_TASK_RUN_MAX_TASK; i++) //清空16 个回调函数指针 204 | pInfo->m_CallbackArray[i] = null; 205 | } 206 | public: 207 | STonyTaskRunInfo* GetInfoPoint(void) { 208 | if (m_pInfo) //优先返回m_pInfo 209 | return m_pInfo; 210 | else 211 | return &m_Info; //否则返回m_Info 的地址 212 | } 213 | public: 214 | //这是使用STonyTeskRunTaskCallbackParam 结构传参,实现粘合的构造函数 215 | //使用m_pInfo 指针,指针指向STonyTeskRunTaskCallbackParam 中的m_Info 216 | CTonyTaskRunInfo(STonyTeskRunTaskCallbackParam* pParam) { 217 | m_pInfo = &(pParam->m_Info); 218 | Init(m_pInfo); //初始化 219 | } //这是直接粘合到外部的一个STonyTaskRunInfo 结构体实例的重载构造函数 220 | //使用m_pInfo 指针,指针指向传入的结构体指针 221 | CTonyTaskRunInfo(STonyTaskRunInfo* pInfo) { 222 | m_pInfo = pInfo; 223 | Init(m_pInfo); //初始化 224 | } //这是以工具类启动,不使用m_pInfo,而使用本类自带的数据区 225 | CTonyTaskRunInfo() { 226 | m_pInfo = null; 227 | Init(&m_Info); //初始化 228 | } 229 | ~ 230 | CTonyTaskRunInfo() { 231 | } //析构函数不摧毁数据 232 | public: 233 | void SetCallbackParam(void* pCallParam) { 234 | if (m_pInfo) //注意先后顺序,优先使用m_pInfo 235 | m_pInfo->m_pCallParam = pCallParam; 236 | else 237 | m_Info.m_pCallParam = pCallParam; 238 | } 239 | public: 240 | //这是添加一个回调函数,连同其参数的方法,调用了下面的单独回调函数添加方法 241 | bool AddTask(_TASKPOOL_CALLBACK pCallback, void* pCallParam) { 242 | if (pCallParam) 243 | SetCallbackParam(pCallParam); //有参数,则设置参数 244 | return AddTask(pCallback); //调用下一函数,处理回调指针 245 | } 246 | bool AddTask(_TASKPOOL_CALLBACK pCallback) { 247 | if (m_pInfo) //优先使用m_pInfo 248 | { //检查回调函数总数是否超限,是则返回false,拒绝添加 249 | if (TONY_TASK_RUN_MAX_TASK <= m_pInfo->m_nTaskCount) 250 | return false; 251 | //添加到数组末尾 252 | m_pInfo->m_CallbackArray[m_pInfo->m_nTaskCount] = pCallback; 253 | //数组计数器+1 254 | m_pInfo->m_nTaskCount++; 255 | return true; 256 | } else //无m_pInfo 可用,则使用m_Info 257 | { //检查回调函数总数是否超限,是则返回false,拒绝添加 258 | if (TONY_TASK_RUN_MAX_TASK <= m_Info.m_nTaskCount) 259 | return false; 260 | //添加到数组末尾 261 | m_Info.m_CallbackArray[m_Info.m_nTaskCount] = pCallback; 262 | //数组计数器+1 263 | m_Info.m_nTaskCount++; 264 | return true; 265 | } 266 | } 267 | public: 268 | void CopyFrom(STonyTaskRunInfo* pInfo) { 269 | char* pMyInfo = null; //查找有效的拷贝目标对象 270 | if (m_pInfo) 271 | pMyInfo = (char*) m_pInfo; 272 | else 273 | pMyInfo = (char*) &m_Info; 274 | //二进制格式拷贝 275 | memcpy(pMyInfo, (char*) pInfo, STonyTaskRunInfoSize); 276 | } 277 | }; 278 | //////////////////////////CTonyTaskRun//////////////////////////////////// 279 | class CTonyBaseLibrary; 280 | class CTonyTaskRun { 281 | public: 282 | //构造函数很简单,就是保存聚合工具类指针备用即可。 283 | CTonyTaskRun(CTonyBaseLibrary* pTonyBaseLib) { 284 | m_pTonyBaseLib = pTonyBaseLib; 285 | } 286 | //析构函数也是简单关闭所有任务。 287 | ~CTonyTaskRun() { 288 | StopAll(); 289 | } 290 | public: 291 | //启动一个任务,这里有多个重载,方便调用 292 | bool StartTask(_TASKPOOL_CALLBACK pCallback, //任务片段回调函数 293 | void* pCallParam = null, //回调函数参数指针 294 | char* szAppName = null); //应用名(可以为空) 295 | //利用Info 描述启动多次任务 296 | bool StartTask(STonyTaskRunInfo* pInfoStruct, //任务描述核心数据结构体 297 | char* szAppName = null); //应用名(可以为空) 298 | bool StartTask(CTonyTaskRunInfo* pInfoObject, //任务描述对象指针 299 | char* szAppName = null); //应用名(可以为空) 300 | //停止所有任务,退出时用,注意,这里是温和退出,每个任务的每个片段都将得到执行 301 | void StopAll(void); 302 | //工具函数,判断是否在运行中 303 | bool IsRunning(void) { 304 | return m_ThreadManager.ThreadContinue(); 305 | } 306 | //工具函数,获得线程总数计数 307 | int GetThreadCount(void) { 308 | return m_ThreadManager.GetThreadCount(); 309 | } 310 | //工具函数,打印内部信息,协助debug 或性能观测 311 | void PrintInfo(void); 312 | private: 313 | //最核心的任务执行回调函数,这实际上是任务池的一个任务回调 314 | //但其内部逻辑实现了代码片段到完整任务的转换 315 | static bool TonyTeskRunTaskCallback(void* pCallParam, int& nStatus); 316 | //这里使用了线程控制锁简化操作 317 | CThreadManager m_ThreadManager; 318 | //保存的聚合工具类指针 319 | CTonyBaseLibrary* m_pTonyBaseLib; 320 | }; 321 | 322 | #endif 323 | -------------------------------------------------------------------------------- /include/TonyLowDebug.h: -------------------------------------------------------------------------------- 1 | #ifndef __LOWDEBUG_H_ 2 | #define __LOWDEBUG_H_ 3 | 4 | #define DEBUG_BUFFER_LENGTH 1024 //debug 缓冲区大小 5 | #define TONY_DEBUG_FILENAME_LENGTH 256 //文件名长 6 | #define APP_INFO_OIT_STRING_MAX 1024 7 | 8 | //#define TONY_PRINTF printf 9 | //#define TONY_DEBUG printf 10 | //Windows 和Linux 下有个差别,其路径的间隔符,Linux 下符合Unix 标准,是”/”,而Windows下为”\”,这个小小的宏定义,解决这种纷争。 11 | #ifdef WIN32 12 | #define PATH_CHAR "\\" 13 | #else // not WIN32 14 | #define PATH_CHAR "/" 15 | #endif 16 | 17 | //注意参数的表意,从左至右依次为路径、文件名,构建的全名缓冲区,扩展名 18 | //Windows 和Linux 下有个差别,其路径的间隔符,Linux 下符合Unix 标准,是”/”,而Windows下为”\”,这个小小的宏定义,解决这种纷争。 19 | #ifdef WIN32 20 | #define PATH_CHAR "\\" 21 | #else // not WIN32 22 | #define PATH_CHAR "/" 23 | #endif 24 | 25 | //注意参数的表意,从左至右依次为路径、文件名,构建的全名缓冲区,扩展名 26 | #define FILENAME_STRING_LENGTH 256 //文件名长度统一为256 字节 27 | #define FULL_NAME(path,name,fullname,ext_name) \ 28 | { \ 29 | if(strlen(path)) \ 30 | { \ 31 | if(strlen(ext_name)) \ 32 | SafePrintf(fullname, \ 33 | FILENAME_STRING_LENGTH, \ 34 | "%s%s%s.%s", \ 35 | path, \ 36 | PATH_CHAR, \ 37 | name, \ 38 | ext_name); \ 39 | else \ 40 | SafePrintf(fullname, \ 41 | FILENAME_STRING_LENGTH, \ 42 | "%s%s%s", \ 43 | path, \ 44 | PATH_CHAR,name); \ 45 | } \ 46 | else \ 47 | { \ 48 | if(strlen(ext_name)) \ 49 | SafePrintf(fullname, \ 50 | FILENAME_STRING_LENGTH, \ 51 | "%s.%s", \ 52 | name, \ 53 | ext_name); \ 54 | else \ 55 | SafePrintf(fullname, \ 56 | FILENAME_STRING_LENGTH, \ 57 | "%s", \ 58 | name); \ 59 | } \ 60 | } 61 | #include "MutexLock.h" 62 | //回调函数构型 63 | typedef void (*_APP_INFO_OUT_CALLBACK)(char* szInfo, void* pCallParam); 64 | ///////////////////////////////////////////////////////////////////////////////// 65 | class CTonyLowDebug { 66 | public: 67 | //静态工具函数,删除一个文件 68 | static void DeleteAFile(char* szFileName); 69 | //过滤掉__FILE__前面的路径名,避免info 太长 70 | //从后向前过滤,直到找到一个'\\'或'/'为止, 71 | //返回该字符下一个字符,就是真实文件名起始位置 72 | //如果找不到,则返回首字符 73 | static char* GetTrueFileName(char* szBuffer); 74 | public: 75 | //主业务函数,输出字符串到文件或控制台,变参设计,方便使用。返回字节数,不计”\0” 76 | int Debug2File(char *szFormat, ...); 77 | //二进制输出一段内存区,格式参考前文的dbg4bin 78 | void Debug2File4Bin(char* pBuffer, int nLength); 79 | public: 80 | //构造函数和析构函数 81 | CTonyLowDebug(char* szPathName, //路径名 82 | char* szAppName, //文件名 83 | bool bPrint2TTYFlag = false, //是否打印到控制台屏幕 84 | _APP_INFO_OUT_CALLBACK pInfoOutCallback = null, //额外的输出回调 85 | void* pInfoOutCallbackParam = null); //回调函数参数 86 | ~CTonyLowDebug(); 87 | public: 88 | //这是一个额外加入的功能,很多时候,应用程序有自己的输出设备, 89 | //比如Windows 的一个窗口,这里提供一个回调函数,方便应用层 90 | //在需要的时候,抓取输出信息,打入自己的输出队列 91 | //至于public,是因为可能某些内部模块需要看一下这个指针,同时输出 92 | _APP_INFO_OUT_CALLBACK m_pInfoOutCallback; 93 | //所有的回调函数设计者,有义务帮调用者传一根指针 94 | void* m_pInfoOutCallbackParam; 95 | private: 96 | bool m_bPrint2TTYFlag; //内部保留的控制台输出标志 97 | char m_szFileName[TONY_DEBUG_FILENAME_LENGTH]; //拼接好的路径名+文件名 98 | CMutexLock m_Lock; //线程安全锁 99 | }; 100 | 101 | #endif 102 | -------------------------------------------------------------------------------- /include/Utils.h: -------------------------------------------------------------------------------- 1 | #ifndef __UTILS_H_ 2 | #define __UTILS_H_ 3 | 4 | //获得非零值 5 | inline int _GetNot0(void) { 6 | int nRet = rand(); 7 | if (!nRet) 8 | nRet++; //如果获得的随机数本身为0,返回1 9 | return nRet; 10 | } 11 | 12 | //获得0 13 | inline int _Get0(void) { 14 | int nRet = rand(); 15 | return nRet ^ nRet; //以异或方式求的0 16 | } 17 | 18 | //获得给定区间内的随机数 19 | inline int GetRandomBetween(int nBegin, int nEnd) { 20 | int n = _GetNot0(); //获得一个随机数 21 | int nBetween = 0; 22 | if (0 > nBegin) 23 | nBegin = -nBegin; //防御性设计防止Begin 为负值 24 | if (0 > nEnd) 25 | nEnd = -nEnd; //防御性设计防止End 为负值 26 | if (nBegin > nEnd) //调整Begin 和End 的顺序,保证End>Begin 27 | { 28 | nBetween = nEnd; 29 | nEnd = nBegin; 30 | nBegin = nBetween; 31 | } else if (nBegin == nEnd) //如果给定的Beggin 和End 相等,即范围为0 32 | nEnd = nBegin + 10; //强行定义范围区间为10 33 | nBetween = nEnd - nBegin; //求的区间 34 | n = n % nBetween; //通过求余运算限幅 35 | n += nBegin; //与Begin 累加,确保结果落于Begin 和End 之间 36 | return n; 37 | } 38 | 39 | #define TimeSetNow(t) time(&t) //设置给定的t 为当前时间 40 | //判断是否时间到了 41 | // tLast:最后一次更新时间 42 | //设定的最大时间 43 | inline bool TimeIsUp(time_t tLast, long lMax) { 44 | time_t tNow; 45 | TimeSetNow(tNow); 46 | long lDeltaT = (long) tNow - (long) tLast; 47 | if (lMax <= lDeltaT) 48 | return true; 49 | else 50 | return false; 51 | } 52 | #endif 53 | -------------------------------------------------------------------------------- /include/confile.h: -------------------------------------------------------------------------------- 1 | /** 2 | * INI配置文件管理函数库 3 | * Ini file parse functions. 4 | * By Hoverlees http://www.hoverlees.com me[at]hoverlees.com 5 | */ 6 | 7 | #ifndef _HOVERLEES_INI_CONFIG_H 8 | #define _HOVERLEES_INI_CONFIG_H 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | //#include 16 | 17 | typedef struct _CONFIG_BTREE_NODE{ 18 | char* key; 19 | void* data; 20 | struct _CONFIG_BTREE_NODE* left; 21 | struct _CONFIG_BTREE_NODE* right; 22 | char mem[2]; 23 | }CONFIG_BTREE_NODE; 24 | 25 | typedef struct _CONFIG_BTREE{ 26 | int numNodes; 27 | CONFIG_BTREE_NODE* root; 28 | }CONFIG_BTREE; 29 | 30 | typedef CONFIG_BTREE INI_CONFIG; 31 | 32 | typedef int (*CONFIG_BTREE_TRAVERSE_CB)(CONFIG_BTREE_NODE* node); 33 | typedef int (*CONFIG_BTREE_SAVE_TRAVERSE_CB)(FILE* fp,CONFIG_BTREE_NODE* node); 34 | 35 | /** 36 | * ini内容解析函数,从字符串解析配置 37 | * @param str 字符串 38 | * @param slen 字符串长度,可以为0,如果为0,函数自动计算字符串长度 39 | * @param isGBK 如果ini文件使用GBK字符集,设置成1,否则设置成0 40 | * @return 成功返回INI_CONFIG指针,失败返回null 41 | */ 42 | INI_CONFIG* ini_config_create_from_string(unsigned char* str,int slen,int isGBK); 43 | 44 | /** 45 | * ini内容解析函数,从文件解析配置 46 | * @param filename 配置文件名 47 | * @param isGBK 如果ini文件使用GBK字符集,设置成1,否则设置成0 48 | * @return 成功返回INI_CONFIG指针,失败返回null 49 | */ 50 | INI_CONFIG* ini_config_create_from_file(const char* filename,int isGBK); 51 | 52 | /** 53 | * 配置释放函数,释放所占用的内存及数据结构 54 | * @param config 配置对象指针 55 | * @return 成功返回1,失败返回0 56 | */ 57 | void ini_config_destroy(INI_CONFIG* config); 58 | /** 59 | * 获取配置整数值 60 | * @param config 配置对象指针 61 | * @param section 段名,没有段名时可以为NULL 62 | * @param key 键名 63 | * @param default_int 默认值 64 | * @return 如果配置中有此键对应的值,返回该值,否则返回参数指定的默认值  65 | */ 66 | int ini_config_get_int(INI_CONFIG* config,const char* section,const char* key,int default_int); 67 | /** 68 | * 获取配置字符串值 69 | * @param config 配置对象指针 70 | * @param section 段名,没有段名时可以为NULL 71 | * @param key 键名 72 | * @param default_string 默认值 73 | * @return 如果配置中有此键对应的值,返回该值,否则返回参数指定的默认值  74 | */ 75 | char* ini_config_get_string(INI_CONFIG* config,const char* section,const char* key,char* default_string); 76 | /** 77 | * 设置变量 78 | * @param config 配置对象指针 79 | * @param section 段名,没有段名时可以为NULL 80 | * @param key 键名 81 | * @param key_len 键长 82 | * @param value 值 83 | * @param value_len 值长度 84 | * @return 成功为1,失败为0 85 | */ 86 | int ini_config_set_string(INI_CONFIG* config,const char* section,const char* key,int key_len,const char* value,int value_len); 87 | /** 88 | * 设置变量 89 | * @param config 配置对象指针 90 | * @param section 段名,没有段名时可以为NULL 91 | * @param key 键名 92 | * @param key_len 键长 93 | * @param value 整数值 94 | * @return 成功为1,失败为0 95 | */ 96 | int ini_config_set_int(INI_CONFIG* config,const char* section,const char* key,int key_len,int value); 97 | /** 98 | * 保存配置到文件中 *提示,原先配置文件中的注释信息将不会保存. 99 | * @param config 配置对象指针 100 | * @param filename 保存到的文件 101 | * @return 成功为1,失败为0 102 | */ 103 | int ini_config_save(INI_CONFIG* config,const char* filename); 104 | /** 105 | * 类似于ini_config_save,只是参数是文件指针,此函数可以直接使用stdin,stdout,stderr. *提示:本函数不负责关闭fp. 106 | * @param config 配置对象指针 107 | * @param fp 文件指针 108 | * @return 成功为1,失败为0 109 | */ 110 | int ini_config_print(INI_CONFIG* config,FILE* fp); 111 | 112 | #endif -------------------------------------------------------------------------------- /include/mylib.h: -------------------------------------------------------------------------------- 1 | #ifndef __MYLIB_H_ 2 | #define __MYLIB_H_ 3 | #include "std.h" 4 | #include "Socket.h" 5 | #include "CSocket.h" 6 | #include "Thread.h" 7 | #include "Statistics.h" 8 | #include "Mint.h" 9 | #include "MutexLock.h" 10 | #include "MrswLock.h" 11 | #include "Nonreentrant.h" 12 | #include "Debug.h" 13 | #include "TonyLowDebug.h" 14 | #include "ThreadManager.h" 15 | #include "ThreadPool.h" 16 | #include "MemoryManager.h" 17 | #include "Buffer.h" 18 | #include "Log.h" 19 | #include "Utils.h" 20 | #include "BaseLib.h" 21 | #endif 22 | 23 | -------------------------------------------------------------------------------- /include/std.h: -------------------------------------------------------------------------------- 1 | #ifndef __STD_H_ 2 | #define __STD_H_ 3 | 4 | //通用包含 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #ifdef WIN32 //windows 下特定包含 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #else //Linux Unix 下特定包含 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #endif 39 | //////////////////////////////////////////////////////////////////// 40 | #ifdef WIN32 41 | #pragma warning (disable : 4800) 42 | #pragma warning (disable : 4996) 43 | #pragma warning (disable : 4200) 44 | #pragma warning (disable : 4244) 45 | #pragma warning (disable : 4010) 46 | #define Linux_Win_vsnprintf _vsnprintf 47 | #else // not WIN32 48 | #define Linux_Win_vsnprintf vsnprintf 49 | #endif 50 | ///////////////////////////////////////////////////////////////////// 51 | #endif 52 | #ifndef null 53 | #define null 0 54 | #endif 55 | -------------------------------------------------------------------------------- /src/BaseLib.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "Mint.h" 3 | #include "MutexLock.h" 4 | #include "Debug.h" 5 | #include "TonyLowDebug.h" 6 | #include "ThreadManager.h" 7 | #include "Thread.h" 8 | #include "ThreadPool.h" 9 | #include "MemoryManager.h" 10 | #include "Buffer.h" 11 | #include "Log.h" 12 | #include "Utils.h" 13 | #include "BaseLib.h" 14 | 15 | CTonyBaseLibrary::CTonyBaseLibrary(char* szAppName, char* szLogPath, 16 | char* szTempPath, int nTaskPoolThreadMax, bool bDebug2TTYFlag, 17 | _BASE_LIBRARY_PRINT_INFO_CALLBACK pPrintInfoCallback, 18 | void* pPrintInfoCallbackParam, _APP_INFO_OUT_CALLBACK pInfoOutCallback, 19 | void* pInfoOutCallbackParam) { 20 | m_pDebug = null; //各指针变量赋初值null 21 | m_pTaskPool = null; //这是预防某个初始化动作失败后 22 | m_pMemPool = null; //跳转,导致后续指针没有赋值 23 | m_pLog = null; //成为“野指针” 24 | //保存各个路径字符串的值 25 | SafeStrcpy(m_szAppName, szAppName, TONY_APPLICATION_NAME_SIZE); 26 | SafeStrcpy(m_szLogPathName, szLogPath, TONY_APP_LOG_PATH_NAME_SIZE); 27 | SafeStrcpy(m_szTempPathName, szTempPath, TONY_APP_TEMP_PATH_NAME_SIZE); 28 | //保存信息打印回调函数相关指针 29 | m_pPrintInfoCallback = pPrintInfoCallback; 30 | m_pPrintInfoCallbackParam = pPrintInfoCallbackParam; 31 | //初始化随机数种子 32 | srand((unsigned int) time(NULL)); 33 | #ifdef WIN32 34 | { //注意,大括号限定作用域,可以临时定义变量 35 | m_bSocketInitFlag=false; 36 | WORD wVersionRequested; 37 | int err; 38 | wVersionRequested = MAKEWORD( 2, 2 ); 39 | err = WSAStartup( wVersionRequested, &m_wsaData ); 40 | if ( err != 0 ) 41 | { 42 | TONY_DEBUG("Socket init fail!\n"); 43 | } else m_bSocketInitFlag=true; 44 | } 45 | #else // Non-Windows 46 | #endif 47 | m_pDebug = new CTonyLowDebug(m_szLogPathName, m_szAppName, bDebug2TTYFlag, 48 | pInfoOutCallback, pInfoOutCallbackParam); 49 | if (!m_pDebug) 50 | return; 51 | //开始第一步信息输出,Hello World,这是笔者习惯,每个程序一启动,打印这个信息 52 | TONY_DEBUG("------------------------------------------\n"); 53 | TONY_DEBUG("Hello World!\n"); 54 | m_pMemPool = new CTonyMemoryPoolWithLock(m_pDebug); 55 | if (!m_pMemPool) { //如果失败,这里已经能打印Debug 信息了 56 | TONY_DEBUG("CTonyBaseLibrary(): m_pMemPool new fail!\n"); 57 | return; 58 | } 59 | 60 | m_pLog = new CTonyXiaoLog(m_pDebug, m_pMemPool, m_szLogPathName, //注意,这里使用日志路径 61 | m_szAppName); 62 | if (m_pLog) { //注意,此时已经可以利用内存池的注册机制 63 | m_pMemPool->Register(m_pLog, "CTonyBaseLibrary::m_pLog"); 64 | } 65 | m_pTaskPool = new CTonyXiaoTaskPool(this, nTaskPoolThreadMax); 66 | if (m_pTaskPool) { //注意,此时已经可以利用内存池的注册机制 67 | m_pMemPool->Register(m_pTaskPool, "CTonyBaseLibrary::m_pTaskPool"); 68 | } 69 | m_pTaskRun = new CTonyTaskRun(this); 70 | if (m_pTaskRun) { //注意,此时已经可以利用内存池的注册机制 71 | m_pMemPool->Register(m_pTaskRun, "CTonyBaseLibrary::m_pTaskRun"); 72 | } 73 | TimeSetNow (m_tLastPrint); //计时因子 74 | //注意,任务回调函数的参数是本对象指针 75 | if (!m_pTaskRun->StartTask(InfoPrintTaskCallback, this)) { //失败则报警 76 | m_pLog->_XGSyslog("CTonyBaseLibrary:: start print info task fail!\n"); 77 | } 78 | //笔者习惯,聚合工具类启动完毕标志,也是应用程序逻辑开始启动标志 79 | TONY_DEBUG(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); 80 | } 81 | 82 | CTonyBaseLibrary::~CTonyBaseLibrary() { //笔者习惯,这是应用程序逻辑开始退出的标志,相对于构造函数中的输出 83 | TONY_DEBUG("<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"); 84 | //这是一个技巧,退出时,笔者喜欢按照功能模块的划分,打印一些数字 85 | //当某个模块线程出现bug,其退出逻辑很可能受到影响,导致退出时挂死 86 | //此时只要简单观察数字打到几,就可以轻松判定是下面哪一步出现了问题,方便debug 87 | TONY_XIAO_PRINTF("1\n"); 88 | //设置内存池的关闭标志,内存块的Free 动作将直接释放,不再推入内存栈,加快程序运行 89 | m_pMemPool->SetCloseFlag(); 90 | TONY_XIAO_PRINTF("2\n"); 91 | TONY_XIAO_PRINTF("3\n"); 92 | if (m_pTaskRun) //退出线程池运行体,注意其中反注册动作 93 | { 94 | m_pMemPool->UnRegister(m_pTaskRun); 95 | delete m_pTaskRun; 96 | m_pTaskRun = null; 97 | } 98 | TONY_XIAO_PRINTF("4\n"); 99 | TONY_XIAO_PRINTF("5\n"); 100 | if (m_pTaskPool) //退出线程池,注意其中反注册动作 101 | { 102 | m_pMemPool->UnRegister(m_pTaskPool); 103 | delete m_pTaskPool; 104 | m_pTaskPool = null; 105 | } 106 | TONY_XIAO_PRINTF("6\n"); 107 | if (m_pLog) //退出Log 日志模块,注意其中反注册动作 108 | { 109 | m_pMemPool->UnRegister(m_pLog); 110 | delete m_pLog; 111 | m_pLog = null; 112 | } 113 | TONY_XIAO_PRINTF("7\n"); 114 | if (m_pMemPool) //退出内存池 115 | { 116 | delete m_pMemPool; 117 | m_pMemPool = null; 118 | } 119 | TONY_XIAO_PRINTF("8\n"); 120 | TONY_DEBUG("Bye World!\n"); //笔者习惯,“再见,世界” 121 | TONY_DEBUG("------------------------------------------\n"); 122 | if (m_pDebug) //退出Debug 对象 123 | { 124 | delete m_pDebug; 125 | m_pDebug = null; 126 | } 127 | TONY_XIAO_PRINTF("9\n"); 128 | #ifdef WIN32 129 | if(m_bSocketInitFlag) 130 | { 131 | if ( LOBYTE( m_wsaData.wVersion ) != 2 || 132 | HIBYTE( m_wsaData.wVersion ) != 2 ) 133 | { 134 | WSACleanup( ); 135 | }m_bSocketInitFlag=false; 136 | } 137 | #else // Non-Windows 138 | #endif 139 | } 140 | #define MAIN_LOOP_DELAY 2 //2 秒一次main loop 141 | bool CTonyBaseLibrary::InfoPrintTaskCallback(void* pCallParam, int& nStatus) { //强制指针类型转换,获得本对象指针 142 | CTonyBaseLibrary* pThis = (CTonyBaseLibrary*) pCallParam; 143 | //检测是否已经等到2 秒,否则返回 144 | if (TimeIsUp(pThis->m_tLastPrint, MAIN_LOOP_DELAY)) { 145 | TimeSetNow(pThis->m_tLastPrint); //更新当前时间 146 | TONY_XIAO_PRINTF("*******************************************\n"); 147 | pThis->m_pTaskPool->PrintInfo(); //打印任务池信息 148 | pThis->m_pTaskRun->PrintInfo(); //打印任务池运行体信息 149 | pThis->m_pMemPool->PrintInfo(); //打印内存池信息 150 | if (pThis->m_pPrintInfoCallback) //回调应用层打印函数 151 | pThis->m_pPrintInfoCallback(pThis->m_pPrintInfoCallbackParam); 152 | TONY_XIAO_PRINTF("*******************************************\n"); 153 | TONY_XIAO_PRINTF("\n"); 154 | } 155 | return true; //返回真,永远循环 156 | } 157 | -------------------------------------------------------------------------------- /src/Buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "Debug.h" 3 | #include "TonyLowDebug.h" 4 | #include "MemoryManager.h" 5 | #include "Buffer.h" 6 | 7 | ///////////////////////////////////////////////////////////////////////// 8 | //******************************动态buffer类****************************// 9 | ///////////////////////////////////////////////////////////////////////// 10 | #ifdef __DYNAMIC_BUFF_ 11 | CTonyBuffer::CTonyBuffer(CTonyMemoryPoolWithLock* pMemPool) { 12 | m_pMemPool = pMemPool; //保存内存池指针 13 | m_pData = null;//注意此处,动态内存块指针并未分配,先保持空 14 | m_nDataLength = 0;//长度也保持为0 15 | } 16 | CTonyBuffer::~CTonyBuffer() { 17 | SetSize(0); //SetSize 为0,表示清除动态内存块,下文有介绍 18 | } 19 | //由于动态内存申请,可能会失败,因此返回bool 量,成功为真,失败返回假 20 | bool CTonyBuffer::SetSize(int nSize)//给定新的大小 21 | { 22 | if (!m_pMemPool) 23 | return false; //防御性设计,如果内存池指针为空,无法工作 24 | m_nDataLength = nSize;//先给缓冲区长度赋值 25 | if (0 == m_nDataLength)//注意析构函数中的SetSize(0),此处处理 26 | { //如果设置大小为0,表示释放缓冲区 27 | if (m_pData) { 28 | m_pMemPool->Free(m_pData); //内存池释放 29 | m_pData = null;//释放后立即赋初值null,避免下文误用 30 | } 31 | return true; //这也是操作成功,返回真 32 | } //此处开始 ,新设置的缓冲期长 度,一定不为0 33 | if (!m_pData)//如果原指针为空,表示没有数据,需要Malloc 34 | { 35 | //请注意这里对内存池Malloc 函数的调用,这也算是内存池的应用实例 36 | //内存池申请的指针都是void*,这符合C 语言malloc 的规范, 37 | //而本类的指针都是字符串型,因此需要做强制指针类型转换 38 | m_pData = (char*) m_pMemPool->Malloc(m_nDataLength,//申请的内存块长度 39 | "CTonyBuffer::m_pData");//注意,这里特别声明申请者身份 40 | //一旦发生内存泄漏,本对象退出时忘了释放 41 | //内存池的报警机制即会激活,打印这个字符串 42 | if (!m_pData)//请注意,这里二次判断,是判断申请是否成功 43 | { 44 | m_nDataLength = 0; //没有成功,则把长度赋为0 45 | return false;//申请失败,返回假 46 | } else 47 | return true; //成功就返回真 48 | } else //这是原有m_pData 不为空,就是已经有一个内存区的情况 49 | { 50 | //使用ReMalloc 函数做内存区域再调整,默认,拷贝原有内容 51 | //请注意,这里是内存池ReMalloc 功能的实例,将原有指针传入 52 | //经过ReMalloc 修饰后,返回新的指针,注意强制指针类型转换 53 | m_pData = (char*) m_pMemPool->ReMalloc(m_pData, m_nDataLength); 54 | if (!m_pData) { 55 | m_nDataLength = 0; //申请失败,返回假 56 | return false; 57 | } else 58 | return true; //成功返回真 59 | } 60 | } 61 | //在前面插入空白,同上,动态内存申请可能失败,所以返回bool 量 62 | bool CTonyBuffer::InsertSpace2Head(int nAddBytes) { 63 | bool bRet = false; //预设返回值 64 | int nNewSize = 0;//新的空间大小变量 65 | char* pBuffer = null;//先定义一个新的二进制缓冲区指针 66 | if (!m_pMemPool)//防御性设计,防止内存池为空 67 | goto CTonyBuffer_InsertSpace2Head_End_Process; 68 | nNewSize = m_nDataLength + nAddBytes;//求得新的长度 69 | //请注意这里,申请一个临时缓冲区,其长度等于原长度+增加的长度 70 | pBuffer = (char*) m_pMemPool->Malloc(nNewSize, 71 | "CTonyBuffer::InsertSpace2Head():pBuffer");//请注意这段说明文字 72 | if (!pBuffer)//缓冲区申请失败,跳转返回假 73 | goto CTonyBuffer_InsertSpace2Head_End_Process; 74 | //此处为防御性设计,如果原有缓冲区为空,则不做后续的拷贝动作 75 | if ((m_pData) && (m_nDataLength)) { //这是将原有缓冲区内容拷贝到临时缓冲区后半部分,与新增加字节构成一个整体 76 | memcpy(pBuffer + nAddBytes, m_pData, m_nDataLength); 77 | } //当所有准备工作完成,调用本对象的二进制拷贝函数,将临时缓冲区内容拷贝回本对象 78 | //二进制拷贝函数,后文有介绍,当然,其拷贝成功与否,也作为本函数的返回值 79 | bRet = BinCopyFrom(pBuffer, nNewSize); 80 | CTonyBuffer_InsertSpace2Head_End_Process: 81 | //不管是否成功拷贝,释放临时缓冲区 82 | if (pBuffer) { 83 | m_pMemPool->Free(pBuffer); 84 | pBuffer = null; 85 | } 86 | return bRet; //返回操作结果 87 | } 88 | //在后面插入空白 89 | bool CTonyBuffer::AddSpace2Tail(int nAddBytes) { 90 | return SetSize(m_nDataLength + nAddBytes); 91 | } 92 | //从前面剪切掉一段数据 93 | void CTonyBuffer::CutHead(int nBytes) { 94 | //防御性设计,如果给出的剪切空间大于原有缓冲区,则直接清空。 95 | if (m_nDataLength <= nBytes) 96 | SetSize(0); 97 | else { //这是从后向前Move,因此,直接调用memecpy 完成。 98 | memcpy(m_pData, m_pData + nBytes, m_nDataLength - nBytes); 99 | //大家请注意,这里笔者没有再SetSize,由于内存池的原理, 100 | //ReMalloc 一个比较小的空间,一般都是直接返回原指针, 101 | //因此,此处也不多此一举了,直接就把空间长度修改为较小的长度即可 102 | m_nDataLength -= nBytes; 103 | } 104 | } 105 | //从后面剪切掉一段数据 106 | void CTonyBuffer::CutTail(int nBytes) { 107 | //防御性设计,剪切太多,直接清空 108 | if (m_nDataLength <= nBytes) 109 | SetSize(0); 110 | //同上,减小直接修改长度参数 111 | else 112 | m_nDataLength -= nBytes; 113 | } 114 | //返回拷贝的字节数,拷贝失败,返回0 115 | int CTonyBuffer::BinCopyTo(char* szBuffer,//调用者给出缓冲区指针 116 | int nBufferSize)//调用者给出缓冲区大小 117 | { //防御性设计 118 | if (!m_pData) 119 | return 0;//如果内部无数据,返回0 120 | if (!m_nDataLength) 121 | return 0; 122 | if (nBufferSize < m_nDataLength) 123 | return 0;//如果给定缓冲区小于本数据缓冲区,返回0 124 | memcpy(szBuffer, m_pData, m_nDataLength);//执行拷贝动作 125 | return m_nDataLength;//返回拷贝的字节长度 126 | } 127 | //拷贝一个二进制缓冲区的数据 128 | int CTonyBuffer::BinCopyFrom(char* szData, int nDataLength) { 129 | //防御性设计,如果给定的参数非法,清空本地数据,返回0 130 | if ((!szData) || (0 >= nDataLength)) { 131 | SetSize(0); 132 | return 0; 133 | } 134 | if (!SetSize(nDataLength)) 135 | return 0; //重新设置 136 | memcpy(m_pData, szData, m_nDataLength); 137 | return m_nDataLength; 138 | } //拷贝同类,另外一个对象的数据 139 | int CTonyBuffer::BinCopyFrom(CTonyBuffer* pBuffer) { //这里调用上一函数完成功能,注意工具类的用法,直接访问目标内部数据区 140 | return BinCopyFrom(pBuffer->m_pData, pBuffer->m_nDataLength); 141 | } 142 | //拷贝一个整数到本对象,执行网络字节序,破坏原有数据 143 | bool CTonyBuffer::SetInt(int n) { 144 | int nSave = htonl(n); //以临时变量求得给定整数的网络字节序 145 | //拷贝到本地缓冲区,带SetSize 146 | return BinCopyFrom((char*) &nSave, sizeof(int)); 147 | } //以整数本地字节序求得缓冲区最开始4Bytes 构成的整数的值,求值失败,返回0 148 | int CTonyBuffer::GetInt(void) { 149 | //防御性设计,如果本对象没有存储数据,或者存储的数据不到一个整数位宽4Bytes,返回0 150 | if (!m_pData) 151 | return 0; 152 | if (!sizeof(int) > m_nDataLength) 153 | return 0; 154 | return ntohl(*(int*) m_pData);//以头4Bytes 数据,求得本地字节序返回 155 | } 156 | bool CTonyBuffer::SetShort(short n) { 157 | short sSave = htons(n); 158 | return BinCopyFrom((char*) &sSave, sizeof(short)); //拷贝的字节数变成短整型位宽 159 | } 160 | short CTonyBuffer::GetShort(void) { 161 | if (!m_pData) 162 | return 0; 163 | if (sizeof(short) > m_nDataLength) 164 | return 0; //注意,此处的范围变成短整型的位宽 165 | return ntohs(*(short*) m_pData); 166 | } 167 | bool CTonyBuffer::SetChar(char n) { 168 | return BinCopyFrom(&n, sizeof(char)); //位宽1Byte 169 | } 170 | char CTonyBuffer::GetChar(void) { 171 | if (!m_pData) 172 | return 0; 173 | if (sizeof(char) > m_nDataLength) 174 | return 0; //位宽1Byte 175 | return *(char*) m_pData; 176 | } 177 | int CTonyBuffer::AddData(char* szData, int nDataLength) { 178 | if ((!m_pData) || (0 >= m_nDataLength)) { //防御性设计,如果原有数据为空,则直接执行拷贝动作 179 | return BinCopyFrom(szData, nDataLength); 180 | } 181 | int nOldSize = m_nDataLength; //保留原有大小 182 | int nNewSize = m_nDataLength + nDataLength;//求得新的大小 183 | if (!SetSize(nNewSize)) 184 | return 0;//SetSize,其逻辑保留原有数据 185 | //注意,失败返回0 186 | memcpy(m_pData + nOldSize, szData, nDataLength);//拷贝新数据到原有数据末尾 187 | return m_nDataLength;//返回新的大小 188 | } 189 | int CTonyBuffer::InsertData2Head(char* szData, int nDataLength) { 190 | if ((!m_pData) || (0 >= m_nDataLength)) { //防御性设计,如果原有数据为空,则直接执行拷贝动作 191 | return BinCopyFrom(szData, nDataLength); 192 | } //先在前插入足够空区,保证后续拷贝动作能成功。 193 | //根据InsertSpace2Head 逻辑,原有数据可以获得保留 194 | if (!InsertSpace2Head(nDataLength)) 195 | return 0; 196 | //拷贝新数据到开始,注意拷贝的长度是新数据的长度,因此,不会破坏后续的原有数据 197 | memcpy(m_pData, szData, nDataLength); 198 | return m_nDataLength;//返回新的大小 199 | } 200 | int CTonyBuffer::StrCopyFrom(char* szString) { 201 | int n = strlen(szString); //先求出目标字符串的长度 202 | n++;//长度+1,包括’\0’的位置 203 | return BinCopyFrom(szString, n);//调用二进制拷贝函数完成动作 204 | } 205 | //变参设计,返回打印的字符串总字节数(包含’\0’的位宽) 206 | int CTonyBuffer::Printf(char* szFormat, ...) { 207 | //这一段在很多变参函数中都出现过,此处不再赘述 208 | char szBuf[TONY_BUFFER_STRING_MAX]; 209 | int nListCount = 0; 210 | va_list pArgList; 211 | va_start(pArgList, szFormat); 212 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 213 | TONY_BUFFER_STRING_MAX - nListCount, szFormat, pArgList); 214 | va_end(pArgList); 215 | if (nListCount > (TONY_BUFFER_STRING_MAX - 1)) 216 | nListCount = TONY_BUFFER_STRING_MAX - 1; 217 | *(szBuf + nListCount) = '\0'; 218 | //最后调用StrCopyFrom,将缓冲区内容拷贝到本对象 219 | return StrCopyFrom(szBuf); 220 | } 221 | //二进制比较 222 | int CTonyBuffer::memcmp(char* szData, int nDataLength) { //防御性设计 223 | if (!m_pData) 224 | return -1; 225 | if (!m_nDataLength) 226 | return -1; 227 | if (!szData) 228 | return -1; 229 | if (m_nDataLength < nDataLength) 230 | return -1; 231 | //使用C 标准库的memcmp 完成功能 232 | int nRet = ::memcmp(m_pData, szData, nDataLength); 233 | return nRet; 234 | } //文本型比较 235 | int CTonyBuffer::strcmp(char* szString) { //防御性设计 236 | if (!m_pData) 237 | return -1; 238 | if (!m_nDataLength) 239 | return -1; 240 | if (!szString) 241 | return -1; 242 | //使用C 标准库的strcmp 完成功能 243 | int nRet = ::strcmp(m_pData, szString); 244 | return nRet; 245 | } 246 | ///////////////////////////////////////////////////////////////////////// 247 | //******************************静态buffer类****************************// 248 | ///////////////////////////////////////////////////////////////////////// 249 | #else 250 | CTonyBuffer::CTonyBuffer(CTonyBaseLibrary* pTonyBaseLib) { 251 | m_pMemPool = pTonyBaseLib->m_pMemPool; //保留内存池对象 252 | m_nDataLength = 0; //数据长度为0 253 | } 254 | CTonyBuffer::CTonyBuffer(CTonyMemoryPoolWithLock* pMemPool) { 255 | m_pMemPool = pMemPool; //保留内存池对象 256 | m_nDataLength = 0; //数据长度为0 257 | } 258 | bool CTonyBuffer::IHaveData(void) //已有数据标志 259 | { //由于使用静态数组,m_pData 永远有意义,因此,此处仅判断m_nDataLength 的值 260 | if (0 >= m_nDataLength) 261 | return false; 262 | return true; 263 | } 264 | bool CTonyBuffer::SetSize(int nSize) { 265 | if (TONY_SAFE_BUFFER_MAX_SIZE < nSize) { //如果超界,报警,返回假,即告知调用者分配失败 266 | m_pMemPool->m_pDebug->Debug2File( 267 | "CTonyBuffer::SetSize(): ERROR! nSize=%d\n", nSize); 268 | return false; 269 | } 270 | m_nDataLength = nSize; //数据长度置为设置尺寸, 271 | //这其实还是把m_nDataLength 作为缓冲区大小提示 272 | return true; 273 | } 274 | bool CTonyBuffer::InsertSpace2Head(int nAddBytes) { 275 | if (0 >= m_nDataLength) { //如果没有原始数据,则视为设置缓冲区大小 276 | m_nDataLength = nAddBytes; 277 | return true; 278 | } //这个计算相对复杂,首先缓冲区最大尺寸恒定,因此,它与原有数据的差值,是新插入空区的 279 | //最大可能值,因此,此处必须做判断 280 | if (nAddBytes > (TONY_SAFE_BUFFER_MAX_SIZE - m_nDataLength)) { //条件不满足,则报警返回 281 | m_pMemPool->m_pDebug->Debug2File( 282 | "CTonyBuffer::InsertSpace2Head(): ERROR! nAddBytes=%d, m_nDataLength=%d, too big!\n", 283 | nAddBytes, m_nDataLength); 284 | return false; 285 | } 286 | { 287 | //注意此处的大括号,是限定变量szBuffer 的作用范围 288 | char szBuffer[TONY_SAFE_BUFFER_MAX_SIZE]; 289 | memset(szBuffer, '\0', TONY_SAFE_BUFFER_MAX_SIZE); 290 | //第一次,把原有数据拷贝到新缓冲区,已经偏移了nAddBytes 的位置 291 | memcpy(szBuffer + nAddBytes, m_pData, m_nDataLength); 292 | m_nDataLength += nAddBytes; 293 | //第二次,将新缓冲区有效数据拷贝回本对象缓冲区 294 | memcpy(m_pData, szBuffer, m_nDataLength); 295 | //之所以这么复杂的拷贝,主要就是为了规避前文所述的“从前拷贝”和“从后拷贝”问题 296 | } 297 | return true; 298 | } 299 | bool CTonyBuffer::AddSpace2Tail(int nAddBytes) //在后面插入空白 300 | { 301 | if (0 >= m_nDataLength) { //如果没有原始数据,则视为设置缓冲区大小 302 | m_nDataLength = nAddBytes; 303 | return true; 304 | } //判断新设置的尺寸大小是否合适 305 | if (nAddBytes > (TONY_SAFE_BUFFER_MAX_SIZE - m_nDataLength)) { 306 | m_pMemPool->m_pDebug->Debug2File( 307 | "CTonyBuffer::AddSpace2Tail(): ERROR! nAddBytes=%d, m_nDataLength=%d, too big!\n", 308 | nAddBytes, m_nDataLength); 309 | return false; 310 | } 311 | m_nDataLength += nAddBytes; //后面追加比较简单,修改m_nDataLength 的值即可 312 | return true; 313 | } 314 | void CTonyBuffer::CutHead(int nBytes) //从前面剪切掉一段数据 315 | { 316 | if (0 >= m_nDataLength) { //没有原始数据,剪切无意义,报警,宣告失败 317 | m_pMemPool->m_pDebug->Debug2File( 318 | "CTonyBuffer::CutHead(): ERROR! m_nDataLength=%d, too small!\n", 319 | m_nDataLength); 320 | m_nDataLength = 0; 321 | return; 322 | } 323 | if (nBytes > m_nDataLength) { //如果剪切的数据长度大于原始数据长度,报警 324 | m_pMemPool->m_pDebug->Debug2File( 325 | "CTonyBuffer::CutHead(): ERROR! m_nDataLength=%d, nBytes=%d, too small!\n", 326 | m_nDataLength, nBytes); 327 | m_nDataLength = 0; 328 | return; 329 | } 330 | m_nDataLength -= nBytes; //求出新的数据长度 331 | //后面数据向前拷贝,“挤出”原有数据 332 | memcpy(m_pData, m_pData + nBytes, m_nDataLength); 333 | return; 334 | } 335 | void CTonyBuffer::CutTail(int nBytes) //从后面剪切掉一段数据 336 | { 337 | if (0 >= m_nDataLength) { //没有原始数据,剪切无意义,报警,宣告失败 338 | m_pMemPool->m_pDebug->Debug2File( 339 | "CTonyBuffer::CutTail(): ERROR! m_nDataLength=%d, too small!\n", 340 | m_nDataLength); 341 | m_nDataLength = 0; 342 | return; 343 | } 344 | if (nBytes > m_nDataLength) { //如果剪切的数据长度大于原始数据长度,报警 345 | m_pMemPool->m_pDebug->Debug2File( 346 | "CTonyBuffer::CutTail(): ERROR! m_nDataLength=%d, nBytes=%d, too small!\n", 347 | m_nDataLength, nBytes); 348 | m_nDataLength = 0; 349 | return; 350 | } 351 | m_nDataLength -= nBytes; //缩短长度就是剪切尾部 352 | return; 353 | } 354 | int CTonyBuffer::BinCopyTo(char* szBuffer, int nBufferSize) { 355 | //防御性设计,条件不满足则报警,返回0,表示没有拷贝成功 356 | if (m_nDataLength > nBufferSize) { 357 | m_pMemPool->m_pDebug->Debug2File( 358 | "CTonyBuffer::BinCopyTo(): ERROR! nBufferSize=%d,m_nDataLength=%d\n", 359 | nBufferSize, m_nDataLength); 360 | return 0; 361 | } 362 | if (!szBuffer) { 363 | m_pMemPool->m_pDebug->Debug2File( 364 | "CTonyBuffer::BinCopyTo(): ERROR! szBuffer=null\n"); 365 | return 0; 366 | } //执行真的拷贝逻辑 367 | memcpy(szBuffer, m_pData, m_nDataLength); 368 | return m_nDataLength; //返回拷贝的字节数 369 | } 370 | int CTonyBuffer::BinCopyFrom(char* szData, int nDataLength) { 371 | //防御性设计 372 | if (TONY_SAFE_BUFFER_MAX_SIZE < nDataLength) { 373 | m_pMemPool->m_pDebug->Debug2File( 374 | "CTonyBuffer::BinCopyFrom(): ERROR! nDataLength=%d, too big!\n", 375 | nDataLength); 376 | return 0; 377 | } 378 | if (!szData) { 379 | m_pMemPool->m_pDebug->Debug2File( 380 | "CTonyBuffer::BinCopyTo(): ERROR! szData=null\n"); 381 | return 0; 382 | } 383 | if (0 >= nDataLength) { 384 | m_pMemPool->m_pDebug->Debug2File( 385 | "CTonyBuffer::BinCopyTo(): ERROR! 0>=nDataLength\n"); 386 | return 0; 387 | } //真实的拷贝动作 388 | memcpy(m_pData, szData, nDataLength); 389 | m_nDataLength = nDataLength; 390 | return m_nDataLength; //返回拷贝的字节数 391 | } 392 | int CTonyBuffer::BinCopyFrom(CTonyBuffer* pBuffer) { //拷贝另外一个buffer 393 | return BinCopyFrom(pBuffer->m_pData, pBuffer->m_nDataLength); 394 | } 395 | bool CTonyBuffer::SetInt(int n) //设置一个整数,网络格式 396 | { 397 | int nSave = htonl(n); 398 | return BinCopyFrom((char*) &nSave, sizeof(int)); 399 | } 400 | int CTonyBuffer::GetInt(void) //获得一个整数,返回本地格式 401 | { 402 | if (0 >= m_nDataLength) 403 | return 0; 404 | int* pData = (int*) m_pData; 405 | int nRet = *pData; 406 | return ntohl(nRet); 407 | } 408 | bool CTonyBuffer::SetShort(short n) //设置一个短整数,网络格式 409 | { 410 | short sSave = htons(n); 411 | return BinCopyFrom((char*) &sSave, sizeof(short)); 412 | } 413 | short CTonyBuffer::GetShort(void) //获得一个短整数,返回本地格式 414 | { 415 | if (0 >= m_nDataLength) 416 | return 0; 417 | short* pData = (short*) m_pData; 418 | short sRet = *pData; 419 | return ntohs(sRet); 420 | } 421 | bool CTonyBuffer::SetChar(char n) //设置一个字节 422 | { 423 | *m_pData = n; 424 | m_nDataLength = sizeof(char); 425 | return true; 426 | } 427 | char CTonyBuffer::GetChar(void) //得到m_pData 第一个字节的值 428 | { 429 | return *m_pData; 430 | } 431 | //二进制数据追加函数 432 | //追加数据到最后,返回新的数据长度 433 | int CTonyBuffer::AddData(char* szData, int nDataLength) { 434 | int nNewSize = m_nDataLength + nDataLength; //求得新的尺寸 435 | if (TONY_SAFE_BUFFER_MAX_SIZE < nNewSize) //防御性判断 436 | { 437 | m_pMemPool->m_pDebug->Debug2File( 438 | "CTonyBuffer::AddData(): ERROR! m_nDataLength=%d, nDataLength=%d, too big!\n", 439 | m_nDataLength, nDataLength); 440 | return 0; 441 | } //做真实的拷贝动作 442 | memcpy(m_pData + m_nDataLength, szData, nDataLength); 443 | m_nDataLength = nNewSize; 444 | return m_nDataLength; 445 | } 446 | //插入数据到最前面,返回新的数据长度 447 | int CTonyBuffer::InsertData2Head(char* szData, int nDataLength) { 448 | if (!InsertSpace2Head(nDataLength)) //先试图插入空白到最前 449 | { 450 | m_pMemPool->m_pDebug->Debug2File( 451 | "CTonyBuffer::InsertData2Head(): ERROR! m_nDataLength=%d, nDataLength=%d, too big!\n", 452 | m_nDataLength, nDataLength); 453 | return 0; 454 | } 455 | memcpy(m_pData, szData, nDataLength); //成功则拷贝 456 | return m_nDataLength; 457 | } 458 | int CTonyBuffer::StrCopyFrom(char* szString) //拷贝一个字符串到内部 459 | { 460 | int nDataLength = strlen(szString) + 1; //求出字符串数据长度, 461 | //+1 表示包含’\0’ 462 | return BinCopyFrom(szString, nDataLength); //调用BinCopyFrom 完成拷贝 463 | } 464 | int CTonyBuffer::Printf(char* szFormat, ...) //变参打印构造一个字符串 465 | { 466 | char szBuf[TONY_SAFE_BUFFER_MAX_SIZE]; //注意,最大长度为静态buffer 467 | //的最大长度 468 | int nListCount = 0; 469 | va_list pArgList; 470 | va_start(pArgList, szFormat); 471 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 472 | TONY_SAFE_BUFFER_MAX_SIZE - nListCount, szFormat, pArgList); 473 | va_end(pArgList); 474 | if (nListCount > (TONY_SAFE_BUFFER_MAX_SIZE - 1)) 475 | nListCount = TONY_SAFE_BUFFER_MAX_SIZE - 1; 476 | *(szBuf + nListCount) = '\0'; 477 | //以上为变参处理段落,已经多处出现,此处不再赘述 478 | return StrCopyFrom(szBuf); //调用StrCopyFrom 完成拷贝 479 | } 480 | int CTonyBuffer::memcmp(char* szData, int nDataLength) //二进制比较 481 | { //防御性设计 482 | if (0 >= m_nDataLength) 483 | return -1; 484 | if (!szData) 485 | return -1; 486 | if (m_nDataLength != nDataLength) 487 | return -1; 488 | int nRet = ::memcmp(m_pData, szData, nDataLength); //调用系统函数完成 489 | return nRet; 490 | } 491 | int CTonyBuffer::strcmp(char* szString) //字符串比较 492 | { //防御性设计 493 | if (0 >= m_nDataLength) 494 | return -1; 495 | if (!szString) 496 | return -1; 497 | int nRet = ::strcmp(m_pData, szString); //调用系统函数完成 498 | return nRet; 499 | } 500 | #endif 501 | ///////////////////////////////////////////////////////////////////////// 502 | //******************************pop buffer类***************************// 503 | ///////////////////////////////////////////////////////////////////////// 504 | CTonyPopBuffer::CTonyPopBuffer(char* szBuffer, int nBufferSize, 505 | bool bInitFlag) { 506 | m_pHead = null; //初始化各种管件变量 507 | m_pBuffer = null; 508 | m_nBufferSize = 0; 509 | Set(szBuffer, nBufferSize); //调用后粘合的Set 函数,实现粘合 510 | if (bInitFlag) 511 | Clean(); //如果需要初始化,则清空整个队列 512 | } 513 | CTonyPopBuffer::~CTonyPopBuffer() { 514 | } 515 | bool CTonyPopBuffer::ICanWork(void) { //依次检测所有关键变量 516 | if (!m_pBuffer) 517 | return false; 518 | if (!m_nBufferSize) 519 | return false; 520 | if (!m_pHead) 521 | return false; 522 | return true; 523 | } 524 | void CTonyPopBuffer::Set(char* szBuffer, int nBufferSize) { 525 | m_pBuffer = szBuffer; //挂接缓冲区 526 | m_nBufferSize = nBufferSize; 527 | m_pHead = (STonyPopBufferHead*) m_pBuffer; //定义队列头指针 528 | } 529 | void CTonyPopBuffer::Clean(void) { 530 | if (m_pHead) //注意,此处已经开始使用m_pHead 531 | { 532 | m_pHead->m_nTokenCount = 0; //所有Token 总数置为0 533 | //总消耗的字节数,置为队列头的长度。 534 | //这表示,PopBuffer 最后输出的总字节数,是包含这个队列头长度的。 535 | //同时也表示,即使PopBuffer 内部一个Token 都没有,其字节数也不为0 536 | m_pHead->m_nAllBytesCount = STonyPopBufferHeadSize; 537 | } 538 | } 539 | //格式化输出内部数据 540 | void CTonyPopBuffer::PrintInside(void) { 541 | if (!ICanWork()) { //防御性设计 542 | TONY_XIAO_PRINTF( 543 | "CTonyPopBuffer::PrintInside(): \ 544 | ERROR! m_pBuffer=null\n"); 545 | return; 546 | } //定义元素区开始的指针 547 | char* pTokenBegin = TONY_POP_BUFFER_FIRST_TOKEN_BEGIN(m_pBuffer); 548 | //定义元素的头指针 549 | STonyPopBufferTokenHead* pTokenHead = (STonyPopBufferTokenHead*) pTokenBegin; 550 | //利用pTokenHead,偏移计算本Token 的数据开始点 551 | //请注意,这里传入的pTokenHead,就不是字符串型,如果前文函数型宏定义中 552 | //没有做强制指针类型转换,此处已经出错。 553 | char* pTokenData = TONY_POP_BUFFER_TOKEN_DATA_BEGIN(pTokenBegin); 554 | int i = 0; 555 | //预打印整个队列的信息,即元素个数,字节数。 556 | TONY_XIAO_PRINTF("CTonyPopBuffer::PrintInside(): Token: %d Bytes: %d\n", 557 | m_pHead->m_nTokenCount, m_pHead->m_nAllBytesCount); 558 | for (i = 0; i < m_pHead->m_nTokenCount; i++) { 559 | //注意,由于队列中存储的数据,可能是文本型,也可能是二进制型 560 | //笔者在此准备了两条打印语句,使用时,根据情况选择 561 | TONY_XIAO_PRINTF("[%d] - %s\n", //格式化输出文本 562 | pTokenHead->m_nDataLength, pTokenData); 563 | //dbg_bin(pTokenData,pTokenHead->m_nDataLength); //格式化输出二进制 564 | //开始迭代计算,根据本Token 长度,计算下一Token 起始点 565 | pTokenBegin += TONY_POP_BUFFER_TOKEN_LENGTH(pTokenHead->m_nDataLength); 566 | //修订相关Token 头等参变量 567 | pTokenHead = (STonyPopBufferTokenHead*) pTokenBegin; 568 | pTokenData = TONY_POP_BUFFER_TOKEN_DATA_BEGIN(pTokenBegin); 569 | } 570 | } 571 | int CTonyPopBuffer::GetTokenCount(void) { 572 | if (!ICanWork()) 573 | return 0; //防御性设计 574 | return m_pHead->m_nTokenCount; //返回元素个数 575 | } 576 | int CTonyPopBuffer::GetAllBytes(void) { 577 | if (!ICanWork()) 578 | return 0; //防御性设计 579 | return m_pHead->m_nAllBytesCount; //返回所有占用的字节数 580 | } 581 | //检查剩余空间是否够存储给定的数据长度 582 | bool CTonyPopBuffer::ICanSave(int nDataLength) { 583 | int nLeaveBytes = 0; //准备变量,求得剩余空间 584 | if (!ICanWork()) 585 | return false; //防御性设计 586 | //剩余空间=缓冲区总长度-AllBytes,我们知道,AllBytes 里面已经包含了队列头长度 587 | //因此,这种计算是正确的。 588 | nLeaveBytes = m_nBufferSize - m_pHead->m_nAllBytesCount; 589 | //判断语句,注意,进入的长度,需要利用函数型宏进行修饰。 590 | //由于每个Token 有一个小的头,这个头的长度,需要叠加进来判断,否则就不准确 591 | if (TONY_POP_BUFFER_TOKEN_LENGTH(nDataLength) > (ULONG) nLeaveBytes) 592 | return false; 593 | else 594 | return true; 595 | } 596 | //针对普通缓冲区的AddLast 597 | int CTonyPopBuffer::AddLast(char* szData, int nDataLength) { 598 | int nRet = 0; //准备返回值 599 | //防御性设计 600 | if (!szData) 601 | goto CTonyPopBuffer_AddLast_End_Process; 602 | if (0 >= nDataLength) 603 | goto CTonyPopBuffer_AddLast_End_Process; 604 | if (!ICanWork()) 605 | goto CTonyPopBuffer_AddLast_End_Process; 606 | //如果剩余空间不够添加,则跳转返回0 607 | if (!ICanSave(nDataLength)) 608 | goto CTonyPopBuffer_AddLast_End_Process; 609 | { //请注意,这个大括号不是if 语句开始,上文if 语句如果成立,已经goto 跳转 610 | //此处主要是为了限定变量的作用域,gcc 中,不允许在goto 语句之后声明变量 611 | //此处的大括号,是为了重新开辟一个堆栈区,以便声明局部变量 612 | char* pTokenBegin = //利用AllBytes 求出队列最尾的偏移 613 | m_pBuffer + m_pHead->m_nAllBytesCount; 614 | STonyPopBufferTokenHead* pTokenHead = //强制指针类型转换为Token 头指针 615 | (STonyPopBufferTokenHead*) pTokenBegin; 616 | char* pTokenData = //求出Token 数据区的指针 617 | TONY_POP_BUFFER_TOKEN_DATA_BEGIN(pTokenBegin); 618 | //请注意具体的添加动作 619 | pTokenHead->m_nDataLength = nDataLength; //先给元素头中的长度赋值 620 | memcpy(pTokenData, szData, nDataLength); //memcpy 数据内容到缓冲区 621 | m_pHead->m_nTokenCount++; //元素个数+1 622 | //请注意,这里AllByutes 添加的是包括元素头的所有长度,而不仅仅是数据长度 623 | m_pHead->m_nAllBytesCount += //AllByutes 加上增加的长度 624 | TONY_POP_BUFFER_TOKEN_LENGTH(nDataLength); 625 | nRet = nDataLength; //但返回值纯数据长度 626 | } 627 | CTonyPopBuffer_AddLast_End_Process: //结束跳转点 628 | return nRet; //返回结果 629 | } //针对Buffer 类的AddLast 630 | int CTonyPopBuffer::AddLast(CTonyBuffer* pBuffer) { 631 | if (!pBuffer) 632 | return 0; //防御性设计 633 | //调用上一函数,实现功能 634 | return AddLast(pBuffer->m_pData, pBuffer->m_nDataLength); 635 | } 636 | int CTonyPopBuffer::GetFirstTokenLength(void) { 637 | if (!ICanWork()) 638 | return 0; //防御性设计 639 | char* pFirstTokenBegin = //利用宏计算第一个Token 起始点 640 | TONY_POP_BUFFER_FIRST_TOKEN_BEGIN(m_pBuffer); 641 | STonyPopBufferTokenHead* pFirstTokenHead = //获得Token 头指针 642 | (STonyPopBufferTokenHead*) pFirstTokenBegin; 643 | return pFirstTokenHead->m_nDataLength; //返回头中包含的数据长度 644 | } 645 | //以普通缓冲区方式获得第一个Token 数据,上层程序保证缓冲区足够大,并传入供检查 646 | int CTonyPopBuffer::GetFirst(char* szBuffer, int nBufferSize) { 647 | int nRet = 0; //准备返回参数 648 | //防御性设计 649 | if (!ICanWork()) 650 | goto CTonyPopBuffer_GetFirst_End_Process; 651 | //判定队列是否为空 652 | if (!GetTokenCount()) 653 | goto CTonyPopBuffer_GetFirst_End_Process; 654 | //判断给定的参数区是否合法 655 | if (GetFirstTokenLength() > nBufferSize) 656 | goto CTonyPopBuffer_GetFirst_End_Process; 657 | { //注意,这个大括号不是if 语句的大括号,是限定变量作用域 658 | char* pFirstTokenBegin = //寻找第一个Token 起始点 659 | TONY_POP_BUFFER_FIRST_TOKEN_BEGIN(m_pBuffer); 660 | STonyPopBufferTokenHead* pFirstTokenHead = //获得Token 头指针 661 | (STonyPopBufferTokenHead*) pFirstTokenBegin; 662 | char* pFirstTokenData = //获得Token 数据指针 663 | TONY_POP_BUFFER_TOKEN_DATA_BEGIN(pFirstTokenBegin); 664 | memcpy(szBuffer, pFirstTokenData, //拷贝数据到缓冲区 665 | pFirstTokenHead->m_nDataLength); 666 | nRet = pFirstTokenHead->m_nDataLength; //返回值设定 667 | } 668 | CTonyPopBuffer_GetFirst_End_Process: return nRet; 669 | } //以Buffer 类方式获得第一个Token 数据,本函数破坏Buffer 原有内容 670 | //由于Buffer 类本身是动态内存管理,因此,不存在缓冲区问题 671 | int CTonyPopBuffer::GetFirst(CTonyBuffer* pBuffer) { 672 | //防御性设计, 673 | if (!ICanWork()) 674 | return 0; 675 | if (!pBuffer->SetSize(GetFirstTokenLength())) 676 | return 0; 677 | if (!pBuffer->m_nDataLength) 678 | return 0; 679 | //调用上一函数,实现真实的GetFirst 功能。 680 | return GetFirst(pBuffer->m_pData, pBuffer->m_nDataLength); 681 | } 682 | //删除第一个元素,如果队列为空,删除会失败,返回false 683 | bool CTonyPopBuffer::DeleteFirst(void) { 684 | bool bRet = false; //准备返回值 685 | //防御性设计 686 | if (!ICanWork()) 687 | goto CTonyPopBuffer_DeleteFirst_End_Porcess; 688 | //如果队列为空,则返回 689 | if (!GetTokenCount()) 690 | goto CTonyPopBuffer_DeleteFirst_End_Porcess; 691 | { //同上,这不是if 的大括号,是限定变量作用域的大括号 692 | char* pFirstTokenBegin = //求出第一个Token 起始点 693 | TONY_POP_BUFFER_FIRST_TOKEN_BEGIN(m_pBuffer); 694 | STonyPopBufferTokenHead* pFirstTokenHead = //第一个Token 头指针 695 | (STonyPopBufferTokenHead*) pFirstTokenBegin; 696 | int nFirstTokenLength = //第一个Token 总长度 697 | TONY_POP_BUFFER_TOKEN_LENGTH(pFirstTokenHead->m_nDataLength); 698 | char* pSecondTokenBegin = //求出第二个Token 起始点 699 | pFirstTokenBegin + nFirstTokenLength; 700 | int nCopyBytesCount = //求出需要Move 的字节数 701 | m_pHead->m_nAllBytesCount - //队列总的Byte 数减去 702 | STonyPopBufferHeadSize - //队列头长度,再减去 703 | nFirstTokenLength; //第一个Token 的长度 704 | memcpy(pFirstTokenBegin, //Move 动作 705 | pSecondTokenBegin, //利用第二个Token 后面的数据 706 | nCopyBytesCount); //把第一个Token 从物理上覆盖掉 707 | m_pHead->m_nAllBytesCount -= //修订AllBytes, 708 | nFirstTokenLength; //减去第一个Token 长度 709 | m_pHead->m_nTokenCount--; //Token 总数-1 710 | bRet = true; //删除成功 711 | } 712 | CTonyPopBuffer_DeleteFirst_End_Porcess: return bRet; 713 | } 714 | //普通缓冲区方式,弹出第一个元素数据 715 | int CTonyPopBuffer::GetAndDeleteFirst(char* szBuffer, int nBufferSize) { 716 | if (!ICanWork()) 717 | return 0; //防御性设计 718 | //请注意,这里不校验获得是否会成功,依赖上层程序保证缓冲区够大。 719 | int nRet = GetFirst(szBuffer, nBufferSize); //先获得第一个元素数据 720 | DeleteFirst(); //再删除第一个元素 721 | return nRet; 722 | } 723 | //Buffer 类方式,弹出第一个元素数据 724 | int CTonyPopBuffer::GetAndDeleteFirst(CTonyBuffer* pBuffer) { 725 | if (!ICanWork()) 726 | return 0; //防御性设计 727 | int nRet = GetFirst(pBuffer); //获得第一个元素数据 728 | DeleteFirst(); //删除第一个元素 729 | return nRet; 730 | } 731 | //枚举遍历所有数据,提交回调函数处理,并且删除数据,返回经过处理的Token 个数 732 | //为了避免过多的动态内存分配细节,或者缓冲区的组织,本函数没有使用GetFirst 733 | int CTonyPopBuffer::MoveAllData(_TONY_ENUM_DATA_CALLBACK pCallBack, //回调函数指针 734 | PVOID pCallParam) //代传的void*参数指针 735 | { 736 | int nMovedTokenCount = 0; //统计变量返回值 737 | bool bCallbackRet = true; //这是记录回调函数返回值的变量 738 | if (!pCallBack) //防御性设计,如果回调函数为空,不做事 739 | goto CTonyPopBuffer_MoveAllData_End_Process; 740 | if (!ICanWork()) //如果条件不满足,不做事 741 | goto CTonyPopBuffer_MoveAllData_End_Process; 742 | while (m_pHead->m_nTokenCount) //以TokenCount 为while 循环的条件 743 | { 744 | char* pFirstTokenBegin = //求得第一个Token 开始点 745 | TONY_POP_BUFFER_FIRST_TOKEN_BEGIN(m_pBuffer); 746 | STonyPopBufferTokenHead* pFirstTokenHead = //求得第一个Token 的头指针 747 | (STonyPopBufferTokenHead*) pFirstTokenBegin; 748 | char* pFirstTokenData = //求得第一个Token 的数据指针 749 | TONY_POP_BUFFER_TOKEN_DATA_BEGIN(pFirstTokenBegin); 750 | //请注意此处回调,将第一个Token 数据传给回调函数,且获得回调函数返回值 751 | bCallbackRet = pCallBack(pFirstTokenData, 752 | pFirstTokenHead->m_nDataLength, pCallParam); 753 | DeleteFirst(); //删除第一个Token 754 | nMovedTokenCount++; //返回值+1 755 | if (!bCallbackRet) 756 | break; //如果回调返回假,中止循环 757 | } 758 | CTonyPopBuffer_MoveAllData_End_Process: return nMovedTokenCount; //返回处理的Token 个数 759 | } 760 | 761 | ///////////////////////////////////////////////////////////////////////// 762 | //******************************mem Queue类***************************/// 763 | ///////////////////////////////////////////////////////////////////////// 764 | //构造函数,初始化所有变量 765 | CTonyXiaoMemoryQueue::CTonyXiaoMemoryQueue(CTonyLowDebug* pDebug, 766 | CTonyMemoryPoolWithLock* pMemPool, char* szAppName, int nMaxToken) { 767 | m_nMaxToken = nMaxToken; //保留最大Token 上线 768 | m_pLast = null; //注意,此处加速因子m_pLast 被设置为null 769 | m_pDebug = pDebug; //保留debug 对象指针 770 | m_pMemPool = pMemPool; //保留内存池指针 771 | SafeStrcpy(m_szAppName, szAppName, //保留AppName 772 | TONY_APPLICATION_NAME_SIZE); 773 | m_pHead = null; //注意,m_pHead 被设置为null,表示无Token 774 | m_nTokenCount = 0; //Token 计数器被设置为0 775 | } //析构函数,清除所有的Token,释放申请的内存 776 | CTonyXiaoMemoryQueue::~CTonyXiaoMemoryQueue() { 777 | if (m_pHead) { 778 | DeleteATokenAndHisNext (m_pHead); //此处递归释放 779 | m_pHead = null; 780 | } 781 | } 782 | bool CTonyXiaoMemoryQueue::ICanWork(void) { 783 | if (!m_pDebug) 784 | return false; //检查debug 对象指针 785 | if (!m_pMemPool) 786 | return false; //检查内存池指针 787 | return true; 788 | } 789 | void CTonyXiaoMemoryQueue::CleanAll(void) { 790 | if (!ICanWork()) 791 | return; //防御性设计 792 | while (DeleteFirst()) { 793 | } //循环删除第一个对象,直到队列为空 794 | //注意,本类的DeleteFirst 返回删除结果 795 | } 796 | int CTonyXiaoMemoryQueue::GetFirstLength(void) { 797 | int nRet = 0; 798 | if (m_pHead) //如果ICanWork 为否,m_pHead 必然为null 799 | { 800 | nRet = m_pHead->m_nDataLength; //取出第一个Token 的长度 801 | } 802 | return nRet; 803 | } 804 | //私有函数,打印某一个指定的Token,并且递归其后所有的Token 805 | void CTonyXiaoMemoryQueue::PrintAToken(STonyXiaoQueueTokenHead* pToken) { 806 | if (!pToken) 807 | return; //防御型设计,递归结束标志 808 | TONY_XIAO_PRINTF( 809 | "Queue Token: pToken:%p, \ 810 | Buffer=%p, Length=%d, m_pNext=%p\n", 811 | pToken, //Token 指针 812 | pToken->m_pBuffer, //数据缓冲区指针 813 | pToken->m_nDataLength, //数据长度 814 | pToken->m_pNext); //下一Token 指针 815 | if (pToken->m_pBuffer) //以二进制方式格式化输出业务数据 816 | dbg_bin(pToken->m_pBuffer, pToken->m_nDataLength); 817 | if (pToken->m_pNext) //递归 818 | PrintAToken(pToken->m_pNext); 819 | } //打印所有的Token 内容,公有函数入口 820 | void CTonyXiaoMemoryQueue::PrintInside(void) { 821 | if (!ICanWork()) 822 | return; //防御性设计 823 | //输出队列关键信息 824 | TONY_XIAO_PRINTF("Queue: Token Count=%d, Head=0x%p, Last=0x%p\n", 825 | m_nTokenCount, //Token 总数 826 | m_pHead, //队列头Token 指针 827 | m_pLast); //队列尾Token 指针 828 | if (m_pHead) //从队列头开始递归 829 | PrintAToken (m_pHead); 830 | } 831 | //如果删除成功,返回真,否则返回假 832 | bool CTonyXiaoMemoryQueue::DeleteATokenAndHisNext( 833 | STonyXiaoQueueTokenHead* pToken) //纯C 调用,需给定Token 指针 834 | { 835 | bool bRet = false; //准备返回值 836 | if (!ICanWork()) //防御性设计 837 | goto CTonyXiaoMemoryQueue_DeleteATokenAndHisNext_End_Process; 838 | if (pToken->m_pNext) //注意,如果有m_pNext,递归删除 839 | { 840 | DeleteATokenAndHisNext(pToken->m_pNext); 841 | pToken->m_pNext = null; 842 | } 843 | if (pToken->m_pBuffer) //开始删除本对象 844 | { 845 | m_pMemPool->Free(pToken->m_pBuffer); //向内存池释放数据缓冲区 846 | pToken->m_nDataLength = 0; //所有变量赋初值 847 | pToken->m_pBuffer = null; 848 | } 849 | m_pMemPool->Free((void*) pToken); //向内存池释放Token 头结构体缓冲区 850 | m_nTokenCount--; //Token 总计数器-1 851 | bRet = true; 852 | CTonyXiaoMemoryQueue_DeleteATokenAndHisNext_End_Process: m_pLast = null; //删除动作后,由于链表尾部发生变化 853 | //加速因子失效,因此需要清空 854 | return bRet; 855 | } 856 | //创建一个Token 头内存区域,并初始化,返回Token 指针 857 | STonyXiaoQueueTokenHead* CTonyXiaoMemoryQueue::GetAToken(void) { 858 | STonyXiaoQueueTokenHead* pToken = null; //准备返回值 859 | char* pTokenBuffer = null; //准备临时指针 860 | char szNameBuffer[256]; //准备说明文字缓冲区 861 | if (!ICanWork()) //防御性设计 862 | goto CTonyXiaoMemoryQueue_GetAToken_End_Process; 863 | //格式化说明文字,注意其中用到了AppName 864 | SafePrintf(szNameBuffer, 256, "%s::Token_Head", m_szAppName); 865 | //开始申请Token 头内存块 866 | pTokenBuffer = (char*) m_pMemPool->Malloc( //请注意对说明文字的使用 867 | STonyXiaoQueueTokenHeadSize, szNameBuffer); 868 | if (!pTokenBuffer) { //申请失败,返回null 869 | TONY_DEBUG("%s::GetAToken(): ma lloc new token fail!\n", m_szAppName); 870 | goto CTonyXiaoMemoryQueue_GetAToken_End_Process; 871 | } //强制指针类型转换,将申请到的二进制缓冲区指针转化成Token 头结构体指针 872 | pToken = (STonyXiaoQueueTokenHead*) pTokenBuffer; 873 | pToken->m_nDataLength = 0; //初始化过程,赋初值 874 | pToken->m_pNext = null; 875 | pToken->m_pBuffer = null; 876 | m_nTokenCount++; //Token 总数+1 877 | CTonyXiaoMemoryQueue_GetAToken_End_Process: return pToken; 878 | } 879 | //将数据保存到一个Token,如果该Token 已经保存数据, 880 | //则按照链表顺序向下寻找,直到找到一个空的Token,把数据放置其中。 881 | //如果成功,返回数据长度,如果失败,返回0 882 | int CTonyXiaoMemoryQueue::AddLast2ThisToken(STonyXiaoQueueTokenHead* pToken, //需要处理的Token 头指针 883 | char* szData, //需要存储数据的指针 884 | int nDataLength) //需要存储的数据的长度 885 | { 886 | int nRet = 0; //准备返回值 887 | char szNameBuffer[256]; //准备说明文字缓冲区 888 | if (!ICanWork()) //防御性设计 889 | goto CTonyXiaoMemoryQueue_AddLast2ThisToken_End_Process; 890 | if (!pToken->m_pBuffer) { 891 | //如果本Token 未包含有效数据,则保存到自己。 892 | SafePrintf(szNameBuffer, 256, //格式化内存块说明文字 893 | "%s::pToken->m_pBuffer", m_szAppName); 894 | pToken->m_pBuffer = (char*) m_pMemPool->Malloc( //向内存池申请内存块 895 | nDataLength, szNameBuffer); 896 | if (!pToken->m_pBuffer) { //申请失败,报警 897 | TONY_DEBUG( 898 | "%s::AddLast2ThisToken(): \ 899 | ma lloc pToken->m_pBuffer fail!\n", 900 | m_szAppName); 901 | goto CTonyXiaoMemoryQueue_AddLast2ThisToken_End_Process; 902 | } 903 | memcpy(pToken->m_pBuffer, szData, nDataLength); //拷贝业务数据到内存块 904 | pToken->m_nDataLength = nDataLength; //填充Token 头中的管理信息 905 | nRet = nDataLength; //给返回值赋值 906 | m_pLast = pToken; //保存加速因子(m_pLast 维护) 907 | goto CTonyXiaoMemoryQueue_AddLast2ThisToken_End_Process; 908 | } else { 909 | //保存在下家 910 | if (!pToken->m_pNext) { 911 | //如果指向下家的链指针为空,则利用GetAToken 创建一个头挂接 912 | pToken->m_pNext = GetAToken(); 913 | if (!pToken->m_pNext) { //创建失败报警 914 | TONY_DEBUG( 915 | "%s::AddLast2ThisToken(): ma lloc \ 916 | pToken->m_pNext fail!\n", 917 | m_szAppName); 918 | goto CTonyXiaoMemoryQueue_AddLast2ThisToken_End_Process; 919 | } 920 | } 921 | if (pToken->m_pNext) //递归调用 922 | nRet = AddLast2ThisToken(pToken->m_pNext, szData, nDataLength); 923 | } 924 | CTonyXiaoMemoryQueue_AddLast2ThisToken_End_Process: return nRet; 925 | } 926 | //返回添加的数据长度,失败返回0 927 | int CTonyXiaoMemoryQueue::AddLast(char* szData, //添加的数据指针 928 | int nDataLength, //数据长度 929 | int nLimit) //最大单元限制,-1 表示不限制 930 | { 931 | int nRet = 0; //准备返回值,初值为0 932 | if (!ICanWork()) //防御性设计,条件不符合,直接返回0 933 | goto CTonyXiaoMemoryQueue_AddLast_End_Process; 934 | if (0 >= nLimit) //应用限制值 935 | { //这是无限制 936 | if (m_nMaxToken <= m_nTokenCount) //无限制时,以m_nMaxToken 作为边界限制 937 | goto CTonyXiaoMemoryQueue_AddLast_End_Process; 938 | } else { 939 | //这是有限制 940 | if (nLimit <= m_nTokenCount) //如果有nLimit,则使用这个参数限制 941 | goto CTonyXiaoMemoryQueue_AddLast_End_Process; 942 | } 943 | if (!m_pHead) { 944 | m_pHead = GetAToken(); //这是链头第一次初始化 945 | if (!m_pHead) { 946 | TONY_DEBUG("%s::AddLast(): ma lloc m_pHead fail!\n", m_szAppName); 947 | goto CTonyXiaoMemoryQueue_AddLast_End_Process; 948 | } 949 | } 950 | if (m_pLast) //加速因子开始起作用,如果有值,直接跳入 951 | { 952 | nRet = AddLast2ThisToken(m_pLast, szData, nDataLength); 953 | } else if (m_pHead) //如果加速因子无值,按传统模式,遍历插入 954 | { 955 | nRet = AddLast2ThisToken(m_pHead, szData, nDataLength); 956 | } 957 | CTonyXiaoMemoryQueue_AddLast_End_Process: return nRet; 958 | } 959 | //针对普通缓冲区进行提取,应用层保证缓冲区够大 960 | //返回提取数据的真实长度(不一定是上层缓冲区的大小) 961 | int CTonyXiaoMemoryQueue::GetFirst(char* szBuffer, int nBufferSize) { 962 | int nRet = 0; //准备返回值变量 963 | //此处开始防御性设计 964 | if (!ICanWork()) 965 | goto CTonyXiaoMemoryQueue_GetFirst_End_Process; 966 | if (!m_pHead) //检查链表是否有数据 967 | goto CTonyXiaoMemoryQueue_GetFirst_End_Process; 968 | if (!m_pHead->m_pBuffer) //检查第一个Token 是否有数据 969 | { 970 | TONY_DEBUG("%s::GetFirst(): m_pHead->m_pBuffer=null\n", m_szAppName); 971 | goto CTonyXiaoMemoryQueue_GetFirst_End_Process; 972 | } 973 | if (m_pHead->m_nDataLength > nBufferSize) //检查给定的缓冲区是否足够 974 | { 975 | TONY_DEBUG("%s::GetFirst(): m_pHead->m_nDataLength > nBufferSize\n", 976 | m_szAppName); 977 | goto CTonyXiaoMemoryQueue_GetFirst_End_Process; 978 | } 979 | memcpy(szBuffer, m_pHead->m_pBuffer, m_pHead->m_nDataLength); //拷贝数据 980 | nRet = m_pHead->m_nDataLength; //返回值赋值 981 | CTonyXiaoMemoryQueue_GetFirst_End_Process: return nRet; 982 | } //这是针对Buffer 类对象的提取函数 983 | int CTonyXiaoMemoryQueue::GetFirst(CTonyBuffer* pBuffer) { 984 | if (!ICanWork()) 985 | return 0; //防御性设计 986 | if (!pBuffer) 987 | return 0; 988 | //调用上一函数完成功能 989 | return pBuffer->BinCopyFrom(m_pHead->m_pBuffer, m_pHead->m_nDataLength); 990 | } 991 | //删除第一个Token,有可能失败,因为队列可能为空,失败返回假,成功返回真 992 | bool CTonyXiaoMemoryQueue::DeleteFirst(void) { 993 | bool bRet = false; //准备返回值 994 | STonyXiaoQueueTokenHead* pSecond = null; //准备指向第二Token 的临时指针 995 | if (!ICanWork()) //防御性设计 996 | goto CTonyXiaoMemoryQueue_DeleteFirst_End_Process; 997 | if (!m_pHead) 998 | goto CTonyXiaoMemoryQueue_DeleteFirst_End_Process; 999 | //注意,先提取First 中保留的Socond 指针,做中间保护 1000 | pSecond = m_pHead->m_pNext; 1001 | //m_pHead 的m_pNext 赋为空值,这是割裂与队列其他元素的连接关系 1002 | m_pHead->m_pNext = null; 1003 | //然后调用DeleteATokenAndHisNext 完成删除,由于上面的割裂,因此不会影响其他Token 1004 | bRet = DeleteATokenAndHisNext(m_pHead); 1005 | if (bRet) { 1006 | m_pHead = pSecond; //重新给m_pHead 挂接第二Token 1007 | //完成对原有First 的挤出工作 1008 | //此时,原有Second 变为First 1009 | if (!m_pHead) //这里有一个特例, 1010 | m_pLast = null; //如果本次删除把该队列删空, 1011 | //需要清除加速因子,避免下次操作失败 1012 | } else { //如果删除失败(这种情况一般不太可能,属于保护动作,不会真实执行) 1013 | //将队列恢复原状,起码不至于在本函数内部造成崩溃,或内存泄漏,但打印报警 1014 | TONY_DEBUG("%s::DeleteFirst(): delete m_pHead fail!\n", m_szAppName); 1015 | m_pHead->m_pNext = pSecond; //删除失败,还得恢复回去 1016 | } 1017 | CTonyXiaoMemoryQueue_DeleteFirst_End_Process: return bRet; 1018 | } 1019 | int CTonyXiaoMemoryQueue::GetAndDeleteFirst(char* szBuffer, int nBufferSize) { 1020 | int nRet = GetFirst(szBuffer, nBufferSize); 1021 | if (nRet) 1022 | DeleteFirst(); 1023 | return nRet; 1024 | } 1025 | int CTonyXiaoMemoryQueue::GetAndDeleteFirst(CTonyBuffer* pBuffer) { 1026 | int nRet = GetFirst(pBuffer); 1027 | if (nRet) 1028 | DeleteFirst(); 1029 | return nRet; 1030 | } 1031 | //返回弹出的数据总长度,但请注意,不是PopBuffer 的AllBytes,仅仅是业务数据长度和 1032 | int CTonyXiaoMemoryQueue::PopFromFirst4TonyPopBuffer(CTonyPopBuffer* pPopBuffer) //需传入PopBuffer 指针 1033 | { 1034 | int nRet = 0; //准备返回值变量 1035 | if (!ICanWork()) //防御性设计 1036 | goto CTonyXiaoMemoryQueue_PopFromFirst4TonyPopBuffer_End_Process; 1037 | if (!m_pHead) 1038 | goto CTonyXiaoMemoryQueue_PopFromFirst4TonyPopBuffer_End_Process; 1039 | if (!m_pHead->m_pBuffer) //这也是防御性设计,检索First 是否有数据 1040 | { 1041 | TONY_DEBUG( 1042 | "%s::PopFromFirst4TonyPopBuffer(): \ 1043 | m_pHead->m_pBuffer=null\n", 1044 | m_szAppName); 1045 | goto CTonyXiaoMemoryQueue_PopFromFirst4TonyPopBuffer_End_Process; 1046 | } 1047 | if (!m_pHead->m_nDataLength) //防御性设计,检索First 数据尺寸是否合法 1048 | { 1049 | TONY_DEBUG( 1050 | "%s::PopFromFirst4TonyPopBuffer(): \ 1051 | m_pHead->m_nDataLength=0\n", 1052 | m_szAppName); 1053 | goto CTonyXiaoMemoryQueue_PopFromFirst4TonyPopBuffer_End_Process; 1054 | } 1055 | if (!pPopBuffer) //检查给定的PopBuffer 是否合法 1056 | { 1057 | TONY_DEBUG("%s::PopFromFirst4TonyPopBuffer(): pPopBuffer=null\n", 1058 | m_szAppName); 1059 | goto CTonyXiaoMemoryQueue_PopFromFirst4TonyPopBuffer_End_Process; 1060 | } //没有使用GetAndDeleteFirst 之类的函数,而是直接访问结构体内部变量 1061 | //就是预防如果PopBuffer 满了,弹出来的数据不好处理,这样也减少无谓的动态内存分配 1062 | nRet = pPopBuffer->AddLast(m_pHead->m_pBuffer, m_pHead->m_nDataLength); 1063 | if (m_pHead->m_nDataLength != nRet) { 1064 | //这是buffer 装满了 1065 | goto CTonyXiaoMemoryQueue_PopFromFirst4TonyPopBuffer_End_Process; 1066 | } 1067 | if (!DeleteFirst()) //删除First 1068 | { 1069 | TONY_DEBUG("%s::PopFromFirst4TonyPopBuffer(): DeleteFirst fail\n", 1070 | m_szAppName); 1071 | goto CTonyXiaoMemoryQueue_PopFromFirst4TonyPopBuffer_End_Process; 1072 | } 1073 | if (m_pHead) //注意此处的递归逻辑,继续向下弹 1074 | nRet += PopFromFirst4TonyPopBuffer(pPopBuffer); 1075 | CTonyXiaoMemoryQueue_PopFromFirst4TonyPopBuffer_End_Process: return nRet; 1076 | } 1077 | //向普通缓冲区,以PopBuffer 方式打包信令 1078 | int CTonyXiaoMemoryQueue::PopFromFirst(char* szBuffer, int nBufferSize) { 1079 | int nCopyBytes = 0; //准备返回值 1080 | //准备一个PopBuffer 对象,注意,粘合作用。 1081 | CTonyPopBuffer PopBuffer(szBuffer, nBufferSize); 1082 | if (!ICanWork()) //防御性设计 1083 | goto CTonyXiaoMemoryQueue_PopFromFirst_End_Process; 1084 | if (m_pHead) { //调用上一函数实现打包 1085 | nCopyBytes = PopFromFirst4TonyPopBuffer(&PopBuffer); 1086 | if (nCopyBytes) //如果弹出了数据(数据长度!=0) 1087 | //取PopBuffer 的AllBytes 作为返回值 1088 | nCopyBytes = PopBuffer.GetAllBytes(); //这就是实际需要发送的字节数 1089 | } 1090 | CTonyXiaoMemoryQueue_PopFromFirst_End_Process: return nCopyBytes; 1091 | } 1092 | //将受到的一段PopBuffer 格式化的信令编组,推入MemQueue 的队列末尾 1093 | //返回推入的数据字节常数,失败返回0 1094 | int CTonyXiaoMemoryQueue::Push2Last(char* szData, int nDataLength) { 1095 | int nRet = 0; 1096 | CTonyPopBuffer PopBuffer(szData, nDataLength, false); //此处粘合 1097 | if (!ICanWork()) //防御性设计 1098 | goto CTonyXiaoMemoryQueue_Push2Last_End_Process; 1099 | //开始调用PopBuffer 的功能,实现遍历循环 1100 | //请注意回调函数和this 指针参数 1101 | PopBuffer.MoveAllData(PushDataCallback, this); 1102 | CTonyXiaoMemoryQueue_Push2Last_End_Process: return nRet; 1103 | } 1104 | //回调函数,返回bool 值,如果返回false,遍历循环将退出,信令可能会丢失。 1105 | bool CTonyXiaoMemoryQueue::PushDataCallback(char* szData, //拆解出来的信令地址 1106 | int nDataLength, //信令数据长度 1107 | void* pCallParam) //代传的参数指针,就是前面的this 1108 | { 1109 | //笔者的习惯,一般类内回调函数,均以this 指针作为透传参数 1110 | //在回调函数内部,一般称之为pThis,以区别于普通成员函数的this 1111 | //回调函数一进入,第一件事就是强制指针类型转换,创建pThis,方便调用。 1112 | CTonyXiaoMemoryQueue* pThis = (CTonyXiaoMemoryQueue*) pCallParam; 1113 | //此处为调用实例,pThis 指针就是this,因此,调用的是本对象的AddLast 1114 | int nRet = pThis->AddLast(szData, nDataLength); 1115 | //成功,返回thre,遍历继续,直到数据遍历结束 1116 | if (nDataLength == nRet) 1117 | return true; 1118 | else { //失败,可能是队列满了,返回false,终止遍历, 1119 | pThis->TONY_DEBUG("%s::PushDataCallback(): I am full!\n", 1120 | pThis->m_szAppName); 1121 | return false; 1122 | } 1123 | } 1124 | //将指定Token 的数据写入文件 1125 | void CTonyXiaoMemoryQueue::WriteAToken2File(STonyXiaoQueueTokenHead* pToken, //指定的Token 指针 1126 | FILE* fp) //指定的文件指针 1127 | { 1128 | if (!ICanWork()) 1129 | return; //防御性设计 1130 | if (!fp) 1131 | return; 1132 | if (!pToken) 1133 | return; 1134 | if (!pToken->m_pBuffer) 1135 | return; 1136 | if (!pToken->m_nDataLength) 1137 | return; 1138 | //写入磁盘,由于磁盘写入,通常发生在服务器即将退出期间,这已经是数据保护最大的努力 1139 | //此时磁盘写入再出现bug,没有任何办法再保护数据,只能丢失信令 1140 | //因此,此处不再做校验,如果磁盘写入失败,即丢失信令 1141 | //先写入每个Token 的数据长度 1142 | fwrite((void*) &(pToken->m_nDataLength), sizeof(int), 1, fp); 1143 | //再写入每个Token 的实际数据 1144 | fwrite((void*) pToken->m_pBuffer, sizeof(char), pToken->m_nDataLength, fp); 1145 | if (pToken->m_pNext) //递归到下一Token 1146 | WriteAToken2File(pToken->m_pNext, fp); 1147 | } //这是公有方法入口函数 1148 | void CTonyXiaoMemoryQueue::Write2File(char* szFileName) //需给出文件名 1149 | { 1150 | FILE* fp = null; 1151 | if (!ICanWork()) 1152 | return; //防御性设计 1153 | if (!m_pHead) 1154 | return; 1155 | if (!GetTokenCount()) 1156 | return; //如果队列为空,直接返回 1157 | fp = fopen(szFileName, "wb"); //打开文件 1158 | if (fp) { 1159 | //首先,将队列控制信息写入磁盘,以便读回时直接使用 1160 | fwrite((void*) &m_nTokenCount, sizeof(int), 1, fp); 1161 | //开始调用上述递归函数,开始逐Token 写入 1162 | WriteAToken2File(m_pHead, fp); 1163 | fclose(fp); //关闭文件 1164 | } 1165 | } 1166 | //从一个磁盘Dump 文件中读入数据,返回读入的Token 总数 1167 | int CTonyXiaoMemoryQueue::ReadFromFile(char* szFileName) { 1168 | FILE* fp = null; 1169 | int n = 0; 1170 | int i = 0; 1171 | int nReadTokenCount = 0; 1172 | int nDataLength = 0; 1173 | char* pTempBuffer = null; 1174 | char szNameBuffer[256]; 1175 | if (!ICanWork()) //防御性设计 1176 | goto CTonyXiaoMemoryQueue_ReadFromFile_End_Process; 1177 | SafePrintf(szNameBuffer, 256, //构建内存申请说明文字 1178 | "%s::ReadFromFile::pTempBuffer", m_szAppName); 1179 | //申请临时缓冲区,由于临时缓冲区需要在循环中多次ReMalloc,因此,开始只申请1Byte 即可 1180 | pTempBuffer = (char*) m_pMemPool->Malloc(1, szNameBuffer); 1181 | fp = fopen(szFileName, "rb"); //打开文件 1182 | if (!fp) //失败则跳转返回 1183 | goto CTonyXiaoMemoryQueue_ReadFromFile_End_Process; 1184 | //读入队列控制头,即TokenCount 信息 1185 | n = fread((void*) &nReadTokenCount, sizeof(int), 1, fp); 1186 | if (1 > n) { 1187 | TONY_DEBUG("%s::ReadFromFile(): read token count fail!\n", m_szAppName); 1188 | goto CTonyXiaoMemoryQueue_ReadFromFile_End_Process; 1189 | } 1190 | for (i = 0; i < nReadTokenCount; i++) //开始逐一读出各个Token 1191 | { 1192 | //首先读出当前Token 的数据长度 1193 | n = fread((void*) &(nDataLength), sizeof(int), 1, fp); 1194 | if (1 > n) { 1195 | TONY_DEBUG("%s::ReadFromFile(): %d / %d, read data length fail!\n", 1196 | m_szAppName, i, nReadTokenCount); 1197 | goto CTonyXiaoMemoryQueue_ReadFromFile_End_Process; 1198 | } 1199 | if (0 > nDataLength) { 1200 | TONY_DEBUG("%s::ReadFromFile(): %d / %d, nDataLength=%d < 0!\n", 1201 | m_szAppName, i, nReadTokenCount, nDataLength); 1202 | goto CTonyXiaoMemoryQueue_ReadFromFile_End_Process; 1203 | } //调用ReMalloc,根据读入的nDataLength 重新分配缓冲区,以便读入数据 1204 | pTempBuffer = (char*) m_pMemPool->ReMalloc(pTempBuffer, //原有的临时缓冲区地址 1205 | nDataLength, //新的数据长度 1206 | false); //由于这个缓冲区马上被覆盖 1207 | //因此无需拷贝旧数据 1208 | if (!pTempBuffer) { 1209 | TONY_DEBUG("%s::ReadFromFile(): rema lloc pTempBuffer fail!\n", 1210 | m_szAppName); 1211 | goto CTonyXiaoMemoryQueue_ReadFromFile_End_Process; 1212 | } //准备缓冲区成功,开始读入该Token 数据 1213 | n = fread((void*) pTempBuffer, sizeof(char), nDataLength, fp); 1214 | if (nDataLength > n) { 1215 | TONY_DEBUG("%s::ReadFromFile(): read data fail!\n", m_szAppName); 1216 | goto CTonyXiaoMemoryQueue_ReadFromFile_End_Process; 1217 | } //读入成功,AddLast 添加到最后 1218 | if (!AddLast(pTempBuffer, nDataLength)) 1219 | break; 1220 | } 1221 | CTonyXiaoMemoryQueue_ReadFromFile_End_Process: //出错跳转标签 1222 | if (pTempBuffer) //清除临时缓冲区 1223 | { 1224 | m_pMemPool->Free(pTempBuffer); 1225 | pTempBuffer = null; 1226 | } 1227 | if (fp) //关闭文件 1228 | { 1229 | fclose(fp); 1230 | fp = null; 1231 | } 1232 | return nReadTokenCount; //返回读入的Token 总数 1233 | } 1234 | ///////////////////////////////////////////////////////////////////////// 1235 | //*********************mem Queue线程安全封装类***************************/// 1236 | ///////////////////////////////////////////////////////////////////////// 1237 | //构造函数,请注意,参数和MemQueue 完全一样 1238 | CTonyMemoryQueueWithLock::CTonyMemoryQueueWithLock(CTonyLowDebug* pDebug, 1239 | CTonyMemoryPoolWithLock* pMemPool, char* szAppName, int nMaxToken) { 1240 | //保存AppName 1241 | SafeStrcpy(m_szAppName, szAppName, TONY_APPLICATION_NAME_SIZE); 1242 | m_pMemPool = pMemPool; //保存内存池指针,析构函数要用 1243 | m_pQueue = new CTonyXiaoMemoryQueue(pDebug, //实例化封装对象 1244 | pMemPool, m_szAppName, nMaxToken); 1245 | if (m_pQueue) { 1246 | char szNameBuffer[256]; 1247 | //如果实例化成功,则试图在内存池注册它,以实现指针管理 1248 | //注册前,先利用AppName 构造说明文字 1249 | SafePrintf(szNameBuffer, 256, "%s::m_pQueue", m_szAppName); 1250 | m_pMemPool->Register(m_pQueue, szNameBuffer); 1251 | } 1252 | } //析构函数,摧毁封装对象爱那个 1253 | CTonyMemoryQueueWithLock::~CTonyMemoryQueueWithLock() { 1254 | m_Lock.EnableWrite(); //此处是带写函数,因此使用写锁 1255 | { 1256 | if (m_pQueue) { 1257 | m_pMemPool->UnRegister(m_pQueue); //这部反注册很重要,否则内存池报警 1258 | delete m_pQueue; //摧毁对象 1259 | m_pQueue = null; //习惯,变量摧毁后立即赋初值 1260 | } 1261 | } 1262 | m_Lock.DisableWrite(); 1263 | } 1264 | bool CTonyMemoryQueueWithLock::ICanWork(void) { 1265 | if (!m_pMemPool) 1266 | return false; 1267 | if (!m_pQueue) 1268 | return false; 1269 | bool bRet = false; 1270 | m_Lock.AddRead(); 1271 | { 1272 | bRet = m_pQueue->ICanWork(); 1273 | } 1274 | m_Lock.DecRead(); 1275 | return bRet; 1276 | } 1277 | int CTonyMemoryQueueWithLock::GetFirst(CTonyBuffer* pBuffer) { 1278 | int nRet = 0; 1279 | m_Lock.AddRead(); 1280 | { 1281 | nRet = m_pQueue->GetFirst(pBuffer); 1282 | } 1283 | m_Lock.DecRead(); 1284 | return nRet; 1285 | } 1286 | int CTonyMemoryQueueWithLock::GetFirst(char* szBuffer, int nBufferSize) { 1287 | int nRet = 0; 1288 | m_Lock.AddRead(); 1289 | { 1290 | nRet = m_pQueue->GetFirst(szBuffer, nBufferSize); 1291 | } 1292 | m_Lock.DecRead(); 1293 | return nRet; 1294 | } 1295 | int CTonyMemoryQueueWithLock::GetFirstLength(void) { 1296 | int nRet = 0; 1297 | m_Lock.AddRead(); 1298 | { 1299 | nRet = m_pQueue->GetFirstLength(); 1300 | } 1301 | m_Lock.DecRead(); 1302 | return nRet; 1303 | } 1304 | int CTonyMemoryQueueWithLock::GetTokenCount(void) { 1305 | int nRet = 0; 1306 | m_Lock.AddRead(); 1307 | { 1308 | nRet = m_pQueue->GetTokenCount(); 1309 | } 1310 | m_Lock.DecRead(); 1311 | return nRet; 1312 | } 1313 | void CTonyMemoryQueueWithLock::Write2File(char* szFileName) { 1314 | m_Lock.AddRead(); 1315 | { 1316 | m_pQueue->Write2File(szFileName); 1317 | } 1318 | m_Lock.DecRead(); 1319 | } 1320 | void CTonyMemoryQueueWithLock::PrintInside(void) { 1321 | m_Lock.AddRead(); 1322 | { 1323 | m_pQueue->PrintInside(); 1324 | } 1325 | m_Lock.DecRead(); 1326 | } 1327 | int CTonyMemoryQueueWithLock::AddLast(char* szData, int nDataLength) { 1328 | int nRet = 0; 1329 | m_Lock.EnableWrite(); 1330 | { 1331 | nRet = m_pQueue->AddLast(szData, nDataLength); 1332 | } 1333 | m_Lock.DisableWrite(); 1334 | return nRet; 1335 | } 1336 | bool CTonyMemoryQueueWithLock::DeleteFirst(void) { 1337 | bool bRet = 0; 1338 | m_Lock.EnableWrite(); 1339 | { 1340 | bRet = m_pQueue->DeleteFirst(); 1341 | } 1342 | m_Lock.DisableWrite(); 1343 | return bRet; 1344 | } 1345 | int CTonyMemoryQueueWithLock::GetAndDeleteFirst(char* szBuffer, 1346 | int nBufferSize) { 1347 | int nRet = 0; 1348 | m_Lock.EnableWrite(); 1349 | { 1350 | nRet = m_pQueue->GetAndDeleteFirst(szBuffer, nBufferSize); 1351 | } 1352 | m_Lock.DisableWrite(); 1353 | return nRet; 1354 | } 1355 | int CTonyMemoryQueueWithLock::PopFromFirst(char* szBuffer, int nBufferSize) { 1356 | int nRet = 0; 1357 | m_Lock.EnableWrite(); 1358 | { 1359 | nRet = m_pQueue->PopFromFirst(szBuffer, nBufferSize); 1360 | } 1361 | m_Lock.DisableWrite(); 1362 | return nRet; 1363 | } 1364 | int CTonyMemoryQueueWithLock::Push2Last(char* szData, int nDataLength) { 1365 | int nRet = 0; 1366 | m_Lock.EnableWrite(); 1367 | { 1368 | nRet = m_pQueue->Push2Last(szData, nDataLength); 1369 | } 1370 | m_Lock.DisableWrite(); 1371 | return nRet; 1372 | } 1373 | void CTonyMemoryQueueWithLock::CleanAll(void) { 1374 | m_Lock.EnableWrite(); 1375 | { 1376 | m_pQueue->CleanAll(); 1377 | } 1378 | m_Lock.DisableWrite(); 1379 | } 1380 | int CTonyMemoryQueueWithLock::ReadFromFile(char* szFileName) { 1381 | int nRet = 0; 1382 | m_Lock.EnableWrite(); 1383 | { 1384 | nRet = m_pQueue->ReadFromFile(szFileName); 1385 | } 1386 | m_Lock.DisableWrite(); 1387 | return nRet; 1388 | } 1389 | -------------------------------------------------------------------------------- /src/CSocket.cpp: -------------------------------------------------------------------------------- 1 | #include "CSocket.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | using namespace std; 7 | 8 | CTonyLowDebug *g_pSocketDebug = NULL; 9 | 10 | SocketBuilder::SocketBuilder(CTonyLowDebug *pDebug, CTonyMemoryPoolWithLock *pPool) { 11 | assert(pDebug != NULL); 12 | assert(pPool != NULL); 13 | 14 | g_pSocketDebug = pDebug; 15 | m_pPool = pPool; 16 | } 17 | 18 | ServerSocket* SocketBuilder::createTCPServerSocket(uint16_t port) { 19 | ServerSocket *sock = new ServerSocket(port); 20 | //regist the socket 21 | if (!sock->isValid()) { 22 | SOCKET_DEBUG("create server socket[port = %d] failt", port); 23 | return sock; 24 | } 25 | 26 | m_pPool->RegisterSocket(sock->getSocketFd(), "server socket"); 27 | 28 | return sock; 29 | } 30 | 31 | Socket* SocketBuilder::createSocket(int type, uint16_t port) { 32 | Socket *sock = NULL;// new Socket(port, m_pDebug); 33 | if (SOCK_STREAM == type) { 34 | sock = new TcpSocket(port); 35 | m_pPool->RegisterSocket(sock->getSocketFd(), "tcp"); 36 | } else { 37 | sock = new UdpSocket(port); 38 | m_pPool->RegisterSocket(sock->getSocketFd(), "udp"); 39 | } 40 | 41 | return sock; 42 | } 43 | 44 | void SocketBuilder::close(Socket *sock) { 45 | m_pPool->CloseSocket(sock->getSocketFd()); 46 | delete sock; 47 | } 48 | 49 | int Socket::connect(const char * const ip, const uint16_t port) { 50 | struct sockaddr_in addr; 51 | addr.sin_family = AF_INET; 52 | addr.sin_addr.s_addr = inet_addr(ip); 53 | addr.sin_port = htons(port); 54 | 55 | return ::connect(fd, (struct sockaddr*)&addr, sizeof(addr)); 56 | } 57 | 58 | TcpSocket::TcpSocket(uint16_t port) { 59 | fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 60 | int opt = 1; 61 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 62 | 63 | struct sockaddr_in addr; 64 | addr.sin_family = AF_INET; 65 | addr.sin_addr.s_addr = INADDR_ANY; 66 | addr.sin_port = htons(port); 67 | 68 | if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 69 | SOCKET_DEBUG("bind error"); 70 | } 71 | } 72 | 73 | TcpSocket::TcpSocket(int fd) { 74 | this->fd = fd; 75 | } 76 | 77 | TcpSocket::~TcpSocket() { 78 | if (fd > 0) close(fd); 79 | } 80 | 81 | int TcpSocket::send(void *buffer, int length) { 82 | if (buffer == NULL || length <= 0) { 83 | SOCKET_DEBUG("SOCKET: send param error \n"); 84 | } 85 | 86 | int curSendLength = 0;//当前已经发送的数据的长度 87 | //设置读取的延时时间,在当前时间内无法读取数据则抛出异常 88 | struct timeval delay = {5, 0}; 89 | 90 | //使用select机制来做数据发送超时处理 91 | fd_set sendSet; 92 | fd_set _sendSet; 93 | FD_ZERO(&sendSet); 94 | FD_SET(fd, &sendSet); 95 | 96 | do { 97 | _sendSet = sendSet; 98 | //select超时,亦也可能出错 99 | if (select(fd+1, NULL, &_sendSet, NULL, &delay) < 0) { 100 | SOCKET_DEBUG("SOCKET: send data out time \n"); 101 | break; 102 | } 103 | 104 | if (FD_ISSET(fd, &_sendSet)) { 105 | int sendLength = ::send(fd, (char *)buffer + curSendLength, 106 | length - curSendLength, 0); 107 | //非阻塞的时候发送失败可能是由于缓冲区满,此时只需继续等待 108 | if (sendLength < 0 && 109 | (ENOBUFS == errno || EWOULDBLOCK == errno)) { 110 | continue; 111 | } else if (sendLength > 0) { 112 | curSendLength += sendLength; 113 | } else { 114 | SOCKET_DEBUG("SOCKET:send data error \n"); 115 | break; 116 | } 117 | } 118 | 119 | } while(curSendLength < length); 120 | 121 | return curSendLength - length; //如果<0则表示发送出现异常 =0为正确 122 | } 123 | 124 | int TcpSocket::recv(void *buffer, int length) { 125 | if (NULL == buffer && length < 0 ) return -1; 126 | 127 | int curRecvLength = 0; 128 | 129 | struct timeval delay = {5, 0}; 130 | 131 | fd_set recvSet; 132 | fd_set _recvSet; 133 | FD_ZERO(&recvSet); 134 | FD_SET(fd, &recvSet); 135 | 136 | do { 137 | _recvSet = recvSet; 138 | 139 | if (select(fd+1, &recvSet, NULL, NULL, &delay) < 0) break; 140 | if (FD_ISSET(fd, &_recvSet)) { 141 | int recvLength = ::recv(fd, (char *)buffer + curRecvLength, 142 | length - curRecvLength, 0); 143 | if (recvLength < 0 && EWOULDBLOCK == errno ) { //缓冲区没有数据 144 | continue; 145 | } else if (recvLength > 0) { 146 | curRecvLength += recvLength; 147 | } else { 148 | break; 149 | } 150 | } 151 | } while(curRecvLength < length); 152 | 153 | //如果全部接收完成,则返回数据长度,否则返回-1 154 | int ret = (curRecvLength - length) == 0 ? curRecvLength : -1; 155 | return ret; 156 | } 157 | 158 | UdpSocket::UdpSocket(uint16_t port) { 159 | fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); 160 | int opt = 1; 161 | setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 162 | 163 | struct sockaddr_in addr; 164 | addr.sin_family = AF_INET; 165 | addr.sin_addr.s_addr = INADDR_ANY; 166 | addr.sin_port = htons(port); 167 | 168 | if (bind(fd, (struct sockaddr *) &addr, sizeof(addr)) < 0) { 169 | SOCKET_DEBUG("bind error"); 170 | } 171 | 172 | } 173 | 174 | UdpSocket::UdpSocket(int fd) { 175 | this->fd = fd; 176 | } 177 | 178 | UdpSocket::~UdpSocket() { 179 | if (fd > 0) close(fd); 180 | } 181 | 182 | int UdpSocket::send(void *buffer, int length) { 183 | return ::send(fd, buffer, length, 0); 184 | } 185 | 186 | int UdpSocket::recv(void *buffer, int length) { 187 | return ::recv(fd, buffer, length, 0); 188 | } 189 | 190 | ServerSocket::ServerSocket(uint16_t port) : TcpSocket(port) { 191 | listen(fd, 10); 192 | } 193 | 194 | Socket* ServerSocket::accept(struct sockaddr *addr) { 195 | int socklen = 0; 196 | int clientFd = ::accept(fd, addr, (socklen_t *)(&socklen)); 197 | Socket* sock = new TcpSocket(clientFd); 198 | 199 | return sock; 200 | } 201 | -------------------------------------------------------------------------------- /src/Debug.cpp: -------------------------------------------------------------------------------- 1 | #include "Debug.h" 2 | #include "std.h" 3 | //安全的字符串拷贝函数 4 | //参数按顺序排:目的地址,源地址,拷贝的最大字节数 5 | void SafeStrcpy(char *pDest, char *pSource, int nCount) { 6 | int nLen = (int) strlen(pSource) + 1; //获得源字符串长度,+1 保证’\0’ 7 | if (!pDest) 8 | goto SafeStrcpy_END_PROCESS; 9 | //保护措施,如果目标地址非法 10 | if (!pSource) 11 | goto SafeStrcpy_END_PROCESS; 12 | //则放弃服务,跳到结束点 13 | if (nLen > nCount) 14 | nLen = nCount; //避免读写出界的设计 15 | memcpy(pDest, pSource, nLen); //实施拷贝 16 | *(pDest + nLen - 1) = '\0'; //手工添加’\0’ 17 | SafeStrcpy_END_PROCESS: //这是结束点 18 | return; 19 | } 20 | 21 | inline int SafePrintf(char* szBuf,int nBufSize,char* szFormat, ...) 22 | { 23 | if(!szBuf) return 0; 24 | if(!nBufSize) return 0; 25 | if(!szFormat) return 0; 26 | int nRet=0; 27 | TONY_FORMAT(nRet,szBuf,nBufSize,szFormat); 28 | return nRet; 29 | } 30 | 31 | int SafePrintfWithTimestamp(char* szBuf,int nBufSize,char* szFormat, ...) 32 | { 33 | if(!szBuf) return 0; 34 | if(!nBufSize) return 0; 35 | if(!szFormat) return 0; 36 | int nRet=0; 37 | TONY_FORMAT_WITH_TIMESTAMP(nRet,szBuf,nBufSize,szFormat); 38 | return nRet; 39 | } 40 | 41 | 42 | //输出到控制台 43 | int TonyPrintf(bool bWithTimestamp, char* szFormat, ...) //格式化字符串 44 | { 45 | if(!szFormat) return 0; 46 | char szBuf[TONY_LINE_MAX]; 47 | int nLength=0; 48 | if(!bWithTimestamp) 49 | { //注意,由于内部是函数型宏,if...else这个大括号必不可少 50 | TONY_FORMAT(nLength,szBuf,TONY_LINE_MAX,szFormat); 51 | } //注意,由于内部是函数型宏,if...else这个大括号必不可少 52 | else 53 | { //注意,由于内部是函数型宏,if...else这个大括号必不可少 54 | TONY_FORMAT_WITH_TIMESTAMP(nLength,szBuf,TONY_LINE_MAX,szFormat); 55 | } //注意,由于内部是函数型宏,if...else这个大括号必不可少 56 | return printf(szBuf); 57 | } 58 | //输出到控制台 59 | int TONY_PRINTF(char* szFormat, ...) //格式化字符串 60 | { 61 | bool bWithTimestamp = false; 62 | if(!szFormat) return 0; 63 | char szBuf[TONY_LINE_MAX]; 64 | int nLength=0; 65 | if(!bWithTimestamp) 66 | { //注意,由于内部是函数型宏,if...else这个大括号必不可少 67 | TONY_FORMAT(nLength,szBuf,TONY_LINE_MAX,szFormat); 68 | } //注意,由于内部是函数型宏,if...else这个大括号必不可少 69 | else 70 | { //注意,由于内部是函数型宏,if...else这个大括号必不可少 71 | TONY_FORMAT_WITH_TIMESTAMP(nLength,szBuf,TONY_LINE_MAX,szFormat); 72 | } //注意,由于内部是函数型宏,if...else这个大括号必不可少 73 | return printf(szBuf); 74 | } 75 | //输出到控制台 76 | int TONY_XIAO_PRINTF(char* szFormat, ...) //格式化字符串 77 | { 78 | bool bWithTimestamp = true; 79 | if(!szFormat) return 0; 80 | char szBuf[TONY_LINE_MAX]; 81 | int nLength=0; 82 | if(!bWithTimestamp) 83 | { //注意,由于内部是函数型宏,if...else这个大括号必不可少 84 | TONY_FORMAT(nLength,szBuf,TONY_LINE_MAX,szFormat); 85 | } //注意,由于内部是函数型宏,if...else这个大括号必不可少 86 | else 87 | { //注意,由于内部是函数型宏,if...else这个大括号必不可少 88 | TONY_FORMAT_WITH_TIMESTAMP(nLength,szBuf,TONY_LINE_MAX,szFormat); 89 | } //注意,由于内部是函数型宏,if...else这个大括号必不可少 90 | return printf(szBuf); 91 | } 92 | ////安全的变参输出函数 93 | ////szBuf: 用户指定的输出缓冲区 94 | ////nMaxLength:用户指定的输出缓冲区尺寸 95 | ////szFormat:格式化输出字符串(变参,可以是多个) 96 | ////返回输出的字符总数(strlen 的长度,不包括最后的’\0’) 97 | //int SafePrintf(char* szBuf, int nMaxLength, char *szFormat, ...) { 98 | // int nListCount = 0; 99 | // va_list pArgList; 100 | // //此处做安全性防护,防止用户输入非法的缓冲区,导致程序在此崩溃。 101 | // if (!szBuf) 102 | // goto SafePrintf_END_PROCESS; 103 | // //此处开启系统循环,解析每条格式化输出字符串 104 | // va_start(pArgList, szFormat); 105 | // nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 106 | // nMaxLength - nListCount, szFormat, pArgList); 107 | // va_end(pArgList); 108 | // //实现缓冲区超限保护 109 | // if (nListCount > (nMaxLength - 1)) 110 | // nListCount = nMaxLength - 1; 111 | // //人工添加’\0’,确保输出100%标准C 字符串 112 | // *(szBuf + nListCount) = '\0'; 113 | // SafePrintf_END_PROCESS: return nListCount; 114 | //} 115 | 116 | //向指定的缓冲区输出一个时间戳字符串 117 | //szBuf: 用户指定的输出缓冲区 118 | //nMaxLength:用户指定的输出缓冲区尺寸 119 | //返回输出的字符总数(strlen 的长度,不包括最后的’\0’) 120 | int GetATimeStamp(char* szBuf, int nMaxLength) { 121 | time_t t; 122 | struct tm *pTM = NULL; 123 | int nLength = 0; 124 | time(&t); 125 | pTM = localtime(&t); 126 | nLength = SafePrintf(szBuf, nMaxLength, "%s", asctime(pTM)); 127 | //这里是个重要的技巧,asctime(pTM)产生的字符串最后会自动带上回车符, 128 | //这给后面很多的格式化输出带来不便 129 | //此处退格结束字符串,目的是过滤掉这个回车符。 130 | szBuf[nLength - 1] = '\0'; 131 | return nLength; 132 | } 133 | 134 | //向指定的缓冲区输出一个时间戳字符串 135 | // szFileName: 用户指定的输出文件 136 | // szMode:常见的文件打开方式描述字符串,一般建议”a+” 137 | //返回输出的字符总数(strlen 的长度,不包括最后的’\0’) 138 | #define DEBUG_BUFFER_LENGTH 1024 139 | int dbg2file(char* szFileName, char* szMode, char *szFormat, ...) { 140 | //前半段和SafePrintf 几乎一模一样 141 | char szBuf[DEBUG_BUFFER_LENGTH]; 142 | char szTime[256]; 143 | int nListCount = 0; 144 | va_list pArgList; 145 | va_start(pArgList, szFormat); 146 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 147 | DEBUG_BUFFER_LENGTH - nListCount, szFormat, pArgList); 148 | va_end(pArgList); 149 | if (nListCount > (DEBUG_BUFFER_LENGTH - 1)) 150 | nListCount = DEBUG_BUFFER_LENGTH - 1; 151 | *(szBuf + nListCount) = '\0'; 152 | //在此开始正式的输出到各个目标设备 153 | GetATimeStamp(szTime, 256); 154 | FILE* fp; 155 | fp = fopen(szFileName, szMode); 156 | if (fp) { 157 | nListCount = fprintf(fp, "[%s] %s", szTime, szBuf); //文件打印 158 | CON_PRINTF("[%s] %s", szTime, szBuf); //屏幕打印 159 | fclose(fp); 160 | } else 161 | nListCount = 0; 162 | return nListCount; 163 | } 164 | 165 | //输出格式 166 | //0000 - 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 xxxxxxxxxxxxxxxx 167 | //以ASCII 方式显示数据内容 168 | int dbg_bin_ascii(char* pPrintBuffer, char* pBuffer, int nLength) { 169 | //内部函数,只有笔者本人的函数调用的函数,一般不写防御性设计,保持简洁。 170 | int i; 171 | int nCount = 0; 172 | for (i = 0; i < nLength; i++) //请关注for 的写法,笔者所有的for 都是这个模式 173 | { 174 | //ASCII 字符表中,可显示字符代码>32 175 | if (32 <= *(pBuffer + i)) //请关注常量写左边 176 | nCount += SafePrintf(pPrintBuffer + nCount, 256, "%c", 177 | *(pBuffer + i)); 178 | else 179 | //不可显示字符以”.”代替占位,保持格式整齐,且避免出错 180 | nCount += SafePrintf(pPrintBuffer + nCount, 256, "."); 181 | //如果指定的内容不是可以显示的ASCII 字符,显示’.’ 182 | } 183 | return nCount; 184 | } 185 | //以十六进制方式显示指定数据区内容。 186 | int dbg_bin_hex(char* pPrintBuffer, char* pBuffer, int nLength) { 187 | int i = 0; 188 | int j = 0; 189 | int nCount = 0; 190 | //一个比较复杂的打印循环,虽然很长,但还是很简单 191 | for (i = 0; i < nLength; i++) { 192 | nCount += SafePrintf(pPrintBuffer + nCount, 256, "%02X ", 193 | (unsigned char) *(pBuffer + i)); 194 | j++; 195 | if (4 == j) { 196 | j = 0; 197 | nCount += SafePrintf(pPrintBuffer + nCount, 256, " "); 198 | } 199 | } 200 | if (16 > nLength) //每行 打印16字节 201 | { 202 | for (; i < 16; i++) { 203 | nCount += SafePrintf(pPrintBuffer + nCount, 256, " "); 204 | j++; 205 | if (4 == j) { 206 | j = 0; 207 | nCount += SafePrintf(pPrintBuffer + nCount, 256, " "); 208 | } 209 | } 210 | } 211 | return nCount; 212 | } 213 | 214 | //****************这是主入口函数 215 | //以16 字节为一行,格式化输出二进制数据内容。 216 | void dbg_bin(char* pBuffer, int nLength) { 217 | int nAddr = 0; 218 | int nLineCount = 0; 219 | int nBufferCount = nLength; 220 | int n = 0; 221 | char szLine[256]; //行缓冲 222 | if (0 < nLength) { 223 | while (1) { 224 | n = 0; 225 | n += SafePrintf(szLine + n, 256 - n, "%p - ", pBuffer + nAddr); 226 | nLineCount = 16; 227 | if (nBufferCount < nLineCount) 228 | nLineCount = nBufferCount; 229 | n += dbg_bin_hex(szLine + n, pBuffer + nAddr, nLineCount); 230 | n += dbg_bin_ascii(szLine + n, pBuffer + nAddr, nLineCount); 231 | CON_PRINTF("%s\n", szLine); 232 | nAddr += 16; 233 | nBufferCount -= 16; 234 | if (0 >= nBufferCount) 235 | break; 236 | } 237 | CON_PRINTF("\n"); 238 | } else 239 | CON_PRINTF("dbg_bin error length=%d\n", nLength); 240 | } 241 | 242 | //****************这是主入口函数 243 | //以16 字节为一行,格式化输出二进制数据内容。 244 | void dbg2file4bin(char* szFileName, char* szMode, char* pBuffer, int nLength) { 245 | int nAddr = 0; 246 | int nLineCount = 0; 247 | int nBufferCount = nLength; 248 | int n = 0; 249 | char szLine[256]; //行缓冲 250 | FILE* fp; 251 | if (0 < nLength) { 252 | while (1) { 253 | n = 0; 254 | n += SafePrintf(szLine + n, 256 - n, "%p - ", pBuffer + nAddr); 255 | nLineCount = 16; 256 | if (nBufferCount < nLineCount) 257 | nLineCount = nBufferCount; 258 | n += dbg_bin_hex(szLine + n, pBuffer + nAddr, nLineCount); 259 | n += dbg_bin_ascii(szLine + n, pBuffer + nAddr, nLineCount); 260 | fp = fopen(szFileName, szMode); 261 | if (fp) { 262 | fprintf(fp, "%s\n", szLine); //文件打印 263 | //CON_PRINTF("[%s] %s", szTime, szBuf); //屏幕打印 264 | fclose(fp); 265 | } 266 | 267 | nAddr += 16; 268 | nBufferCount -= 16; 269 | if (0 >= nBufferCount) 270 | break; 271 | } 272 | fp = fopen(szFileName, szMode); 273 | if (fp) { 274 | fprintf(fp, "\n"); //文件打印 275 | fclose(fp); 276 | } 277 | 278 | } else 279 | CON_PRINTF("dbg_bin error length=%d\n", nLength); 280 | } 281 | 282 | -------------------------------------------------------------------------------- /src/Log.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "MutexLock.h" 3 | #include "Debug.h" 4 | #include "TonyLowDebug.h" 5 | #include "MemoryManager.h" 6 | #include "Buffer.h" 7 | #include "Log.h" 8 | 9 | CTonyXiaoLog::CTonyXiaoLog( 10 | CTonyLowDebug* pDebug, //参数介绍略 11 | CTonyMemoryPoolWithLock* pMemPool, char* szLogPath, char* szAppName, 12 | int nHoldFileMax, bool bSyslogFlag, bool bDebugFlag, bool bDebug2Flag, 13 | bool bDebug3Flag, bool bPrintf2ScrFlag) { 14 | m_pDebug = pDebug; //保留Debug 对象指针 15 | m_pMemPool = pMemPool; //保留内存池指针 16 | //请注意,这里从Debug 对象中获得拦截回调函数信息 17 | m_pInfoOutCallback = m_pDebug->m_pInfoOutCallback; 18 | m_pInfoOutCallbackParam = m_pDebug->m_pInfoOutCallbackParam; 19 | //利用debug 输出启动标志 20 | TONY_DEBUG("CTonyXiaoLog: Start!\n"); 21 | //获得日志文件名基准字符串,这里主要使用输入的路径名和应用名生成基本名 22 | //如路径是“/var”,应用名是“test_project”, 23 | //则基准名为“/var/test_project”, 24 | //这样,以后的文件名,就是在这个基本名后加时间戳实现 25 | //如:/var/test_project_Thu_Jul_16_14_31_44_2009.log 26 | FULL_NAME(szLogPath, szAppName, m_szFilePath, ""); 27 | //获得日志文件名目录的保存文件名 28 | //如:/var/test_project.info 29 | FULL_NAME(szLogPath, szAppName, m_szFileInfoName, "info"); 30 | //清空当前文件名缓冲区 31 | TONY_CLEAN_CHAR_BUFFER (m_szFileName); 32 | //当前文件尺寸设置为0 33 | m_nFileSize = 0; 34 | m_bSyslogFlag = bSyslogFlag; //保存Debug 级别开关变量 35 | m_bDebugFlag = bDebugFlag; //为false 的级别将不会被输出 36 | m_bDebug2Flag = bDebug2Flag; 37 | m_bDebug3Flag = bDebug3Flag; 38 | m_bPrintf2ScrFlag = bPrintf2ScrFlag; //保存屏幕输出开关 39 | m_nHoldFileMax = nHoldFileMax; //保存最大保留文件个数 40 | m_pFileInfoQueue = new CTonyXiaoMemoryQueue( //实例化文件目录队列对象 41 | pDebug, m_pMemPool, "CTonyXiaoLog::m_pFileInfoQueue"); 42 | if (m_pFileInfoQueue) //如果创建成功,注册到内存池 43 | { 44 | m_pMemPool->Register(m_pFileInfoQueue, 45 | "CTonyXiaoLog::m_pFileInfoQueue"); 46 | } 47 | m_pFileInfoQueue->ReadFromFile(m_szFileInfoName); //读入上次保留的文件名信息 48 | MakeFileName(); //根据当前时间戳,定制一个文件名 49 | } 50 | CTonyXiaoLog::~CTonyXiaoLog() { 51 | if (m_pFileInfoQueue) //清楚文件目录队列对象 52 | { 53 | m_pFileInfoQueue->Write2File(m_szFileInfoName); //清除前先保留到磁盘 54 | m_pMemPool->UnRegister(m_pFileInfoQueue); //反注册对象指针 55 | delete m_pFileInfoQueue; //删除对象 56 | m_pFileInfoQueue = null; 57 | } 58 | TONY_DEBUG("CTonyXiaoLog: Stop!\n"); //Debug 输出 59 | } 60 | void CTonyXiaoLog::GetFileName(void) //获取当前文件名 61 | { 62 | time_t tNow; //当前时间戳变量 63 | unsigned long ulDeltaT = 0; //△t 变量 64 | if ('\0' == m_szFileName[0]) //如果是第一次启动,文件名为空 65 | { 66 | MakeFileName(); //无条件创造个文件名,返回 67 | goto CTonyXiaoLog_GetFileName_End_Porcess; 68 | } 69 | time(&tNow); //求得当前时间 70 | ulDeltaT = (unsigned long) tNow - m_tFileNameMake; //计算△t 71 | if (LOG_FILE_CHANGE_NAME_PRE_SECONDS <= ulDeltaT) { 72 | MakeFileName(); //如果△t 超过3600 秒,创造文件名返回 73 | goto CTonyXiaoLog_GetFileName_End_Porcess; 74 | } 75 | if (LOG_FILE_SIZE_MAX <= m_nFileSize) //如果当前文件大小尺寸超出1G 76 | { 77 | MakeFileName(); //创造文件名返回 78 | goto CTonyXiaoLog_GetFileName_End_Porcess; 79 | } 80 | CTonyXiaoLog_GetFileName_End_Porcess: return; 81 | } 82 | void CTonyXiaoLog::MakeFileName(void) //创造一个新文件名 83 | { 84 | char szTemp[LOG_ITEM_LENGTH_MAX]; //临时缓冲区 85 | MakeATimeString(szTemp, LOG_ITEM_LENGTH_MAX); 86 | //获得时间戳字符串 87 | FixFileInfo(); //维护文件总个数不超标(默认72 个) 88 | int nLen = SafePrintf( //注意看这句,利用构造函数中的种子名字 89 | m_szFileName, //加上时间戳,后面再加上“.log”后缀 90 | FILENAME_STRING_LENGTH * 2, //生成日志文件名 91 | "%s_%s.log", m_szFilePath, szTemp); 92 | nLen++; //习惯,长度+1,保留最后’\0’的位置 93 | //将新的文件名添加到队列 94 | int nAddLastRet = m_pFileInfoQueue->AddLast(m_szFileName, nLen); 95 | if (0 >= nAddLastRet) { //这是一个特殊的防护,如果队列满了(内存不够用),删除最开始三个文件名 96 | //释放内存空间,这是预防服务器业务太繁忙,导致内存不够用,队列无法添加的 97 | //规避措施,这也体现非关键模块为关键业务模块让路的思维 98 | DeleteFirstFile(); 99 | DeleteFirstFile(); 100 | DeleteFirstFile(); 101 | //删除三个之后,重新尝试添加 102 | nAddLastRet = m_pFileInfoQueue->AddLast(m_szFileName, nLen); 103 | //如果此时添加仍然失败,投降,日志发生一点错乱没有关系。 104 | } 105 | m_nFileSize = 0; //新文件创建,文件长度为0 106 | //下面逻辑,新创建一个文件,在文件头先打印一点文件名相关信息,帮助以后的跟踪查找 107 | time (&m_tFileNameMake); 108 | { //由于这是非业务打印,因此不希望输出到屏幕,这里临时将屏幕开关关闭 109 | bool bPrint2Scr = m_bPrintf2ScrFlag; 110 | m_bPrintf2ScrFlag = false; 111 | _Printf("Tony.Xiao. base libeary log file %s\n", m_szFileName); 112 | _Printf("-----------------------------------------------\n"); 113 | m_bPrintf2ScrFlag = bPrint2Scr; //输出完毕,屏幕开关恢复原值 114 | } 115 | } 116 | int CTonyXiaoLog::MakeATimeString(char* szBuffer, int nBufferSize) { 117 | int i = 0; 118 | time_t t; 119 | struct tm *pTM = NULL; 120 | int nLength = 0; 121 | if (LOG_TIME_STRING_MAX > nBufferSize) //防御性设计 122 | goto CTonyXiaoLog_MakeATimeString_End_Porcess; 123 | time(&t); //获得当前时间 124 | pTM = localtime(&t); //或当当前时区的时间戳字符串 125 | nLength = SafePrintf(szBuffer, LOG_ITEM_LENGTH_MAX, "%s", asctime(pTM)); 126 | //时间戳字符串入缓冲区 127 | //localtime 生成的字符串最后自带一个’\n’,即回车符,这不利于后续的打印 128 | //因此下面这行向前退一格,清除掉这个回车符。这是一个小经验。 129 | szBuffer[nLength - 1] = '\0'; 130 | //文件名有一定限制,一般不要有空格,’:’字符,这里过滤一下, 131 | //将时间戳中的非法字符都改变成各系统都能接受的’_’下划线 132 | for (i = 0; i < nLength; i++) { 133 | if (' ' == szBuffer[i]) 134 | szBuffer[i] = '_'; 135 | if (':' == szBuffer[i]) 136 | szBuffer[i] = '_'; 137 | } 138 | CTonyXiaoLog_MakeATimeString_End_Porcess: return nLength; //返回总长度 139 | } 140 | void CTonyXiaoLog::FixFileInfo(void) //维护文件总个数不超标 141 | { 142 | int nAddLastRet = 0; 143 | //请注意,这里不是if,而是一个while,如果因某种原因,超标很多个文件 144 | //利用这个循环技巧,很轻松地将超标文件删除到只有72 个, 145 | //很多时候,维护数组不超限,都是使用这个技巧 146 | while (m_pFileInfoQueue->GetTokenCount() >= m_nHoldFileMax) { 147 | DeleteFirstFile(); 148 | } 149 | } 150 | void CTonyXiaoLog::DeleteFirstFile(void) //删除第一个文件 151 | { 152 | char szFirstFile[FILENAME_STRING_LENGTH]; //文件名缓冲区 153 | int nFirstFileNameLen = 0; //文件名字符串长度 154 | //从文件名数组中,弹出第一个文件名 155 | nFirstFileNameLen = m_pFileInfoQueue->GetAndDeleteFirst(szFirstFile, 156 | FILENAME_STRING_LENGTH); 157 | if (0 >= nFirstFileNameLen) //失败返回 158 | goto CTonyXiaoLog_DeleteFirstFile_End_Process; 159 | CTonyLowDebug::DeleteAFile(szFirstFile); //真实地删除文件 160 | CTonyXiaoLog_DeleteFirstFile_End_Process: return; 161 | } 162 | int CTonyXiaoLog::_XGSyslog(char *szFormat, ...) { //这段比较经典,变参函数处理模块,不再赘述 163 | char szBuf[LOG_ITEM_LENGTH_MAX]; 164 | int nMaxLength = LOG_ITEM_LENGTH_MAX; 165 | int nListCount = 0; 166 | va_list pArgList; 167 | va_start(pArgList, szFormat); 168 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 169 | nMaxLength - nListCount, szFormat, pArgList); 170 | va_end(pArgList); 171 | if (nListCount > (nMaxLength - 1)) 172 | nListCount = nMaxLength - 1; 173 | *(szBuf + nListCount) = '\0'; 174 | if (m_bSyslogFlag) //如果开关打开 175 | { 176 | m_Lock.Lock(); //加锁 177 | { 178 | _Printf("%s", szBuf); //真实执行打印 179 | } 180 | m_Lock.Unlock(); //解锁 181 | } 182 | return nListCount; //返回长度 183 | } 184 | int CTonyXiaoLog::_XGDebug(char *szFormat, ...) { //这段比较经典,变参函数处理模块,不再赘述 185 | char szBuf[LOG_ITEM_LENGTH_MAX]; 186 | int nMaxLength = LOG_ITEM_LENGTH_MAX; 187 | int nListCount = 0; 188 | va_list pArgList; 189 | va_start(pArgList, szFormat); 190 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 191 | nMaxLength - nListCount, szFormat, pArgList); 192 | va_end(pArgList); 193 | if (nListCount > (nMaxLength - 1)) 194 | nListCount = nMaxLength - 1; 195 | *(szBuf + nListCount) = '\0'; 196 | if (m_bDebugFlag) //如果开关打开 197 | { 198 | m_Lock.Lock(); //加锁 199 | { 200 | _Printf("%s", szBuf); //真实执行打印; 201 | } 202 | m_Lock.Unlock(); //解锁 203 | } 204 | return nListCount; //返回长度 205 | } 206 | int CTonyXiaoLog::_XGDebug2(char *szFormat, ...) { //这段比较经典,变参函数处理模块,不再赘述 207 | char szBuf[LOG_ITEM_LENGTH_MAX]; 208 | int nMaxLength = LOG_ITEM_LENGTH_MAX; 209 | int nListCount = 0; 210 | va_list pArgList; 211 | va_start(pArgList, szFormat); 212 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 213 | nMaxLength - nListCount, szFormat, pArgList); 214 | va_end(pArgList); 215 | if (nListCount > (nMaxLength - 1)) 216 | nListCount = nMaxLength - 1; 217 | *(szBuf + nListCount) = '\0'; 218 | if (m_bDebug2Flag) //如果开关打开 219 | { 220 | m_Lock.Lock(); //加锁 221 | { 222 | _Printf("%s", szBuf); 223 | ; //真实执行打印 224 | } 225 | m_Lock.Unlock(); //解锁 226 | } 227 | return nListCount; //返回长度 228 | } 229 | int CTonyXiaoLog::_XGDebug3(char *szFormat, ...) { //这段比较经典,变参函数处理模块,不再赘述 230 | char szBuf[LOG_ITEM_LENGTH_MAX]; 231 | int nMaxLength = LOG_ITEM_LENGTH_MAX; 232 | int nListCount = 0; 233 | va_list pArgList; 234 | va_start(pArgList, szFormat); 235 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 236 | nMaxLength - nListCount, szFormat, pArgList); 237 | va_end(pArgList); 238 | if (nListCount > (nMaxLength - 1)) 239 | nListCount = nMaxLength - 1; 240 | *(szBuf + nListCount) = '\0'; 241 | if (m_bDebug3Flag) //如果开关打开 242 | { 243 | m_Lock.Lock(); //加锁 244 | { 245 | _Printf("%s", szBuf); //真实执行打印; 246 | } 247 | m_Lock.Unlock(); //解锁 248 | } 249 | return nListCount; //返回长度 250 | } 251 | int CTonyXiaoLog::_Printf(char *szFormat, ...) { 252 | char szTime[LOG_ITEM_LENGTH_MAX]; 253 | char szTemp[LOG_ITEM_LENGTH_MAX]; 254 | char szBuf[LOG_ITEM_LENGTH_MAX]; 255 | int nMaxLength = LOG_ITEM_LENGTH_MAX; 256 | int nListCount = 0; 257 | time_t t; 258 | struct tm *pTM = NULL; 259 | int nLength = 0; 260 | //获得当前时间戳,这在MakeATimeString 中已经有介绍 261 | time(&t); 262 | pTM = localtime(&t); 263 | nLength = SafePrintf(szTemp, LOG_ITEM_LENGTH_MAX, "%s", asctime(pTM)); 264 | szTemp[nLength - 1] = '\0'; 265 | SafePrintf(szTime, LOG_ITEM_LENGTH_MAX, "[%s] ", szTemp); 266 | //这段比较经典,变参函数处理模块,不再赘述 267 | va_list pArgList; 268 | va_start(pArgList, szFormat); 269 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 270 | nMaxLength - nListCount, szFormat, pArgList); 271 | va_end(pArgList); 272 | if (nListCount > (nMaxLength - 1)) 273 | nListCount = nMaxLength - 1; 274 | *(szBuf + nListCount) = '\0'; 275 | //得到当前使用的文件名 276 | GetFileName(); 277 | //调用debug 的功能函数,直接将信息输出到文件 278 | nListCount = dbg2file(m_szFileName, "a+", "%s%s", szTime, szBuf); 279 | if (m_bPrintf2ScrFlag) //如果屏幕输出开关打来 280 | { 281 | TONY_XIAO_PRINTF("%s%s", szTime, szBuf); //输出到屏幕 282 | } 283 | if (m_pInfoOutCallback) //如果拦截函数存在 284 | { //输出到拦截函数 285 | char szInfoOut[APP_INFO_OIT_STRING_MAX]; 286 | SafePrintf(szInfoOut, APP_INFO_OIT_STRING_MAX, "%s%s", szTime, szBuf); 287 | m_pInfoOutCallback(szInfoOut, m_pInfoOutCallbackParam); 288 | } 289 | m_nFileSize += nListCount; //这里很重要,修订文件长度 290 | //维护模块需要这个值判定文件大小 291 | //是否超标 292 | return nListCount; //返回输出的字节数 293 | } 294 | void CTonyXiaoLog::_XGDebug4Bin(char* pBuffer, int nLength) { 295 | m_Lock.Lock(); //加锁 296 | { 297 | GetFileName(); //获得文件名 298 | dbg2file4bin(m_szFileName, "a+", pBuffer, nLength); //输出到文件 299 | dbg_bin(pBuffer, nLength); //输出到屏幕 300 | } 301 | m_Lock.Unlock(); //解锁 302 | } 303 | -------------------------------------------------------------------------------- /src/MemoryManager.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "Socket.h" 3 | #include "Debug.h" 4 | #include "MutexLock.h" 5 | #include "MrswLock.h" 6 | #include "TonyLowDebug.h" 7 | #include "MemoryManager.h" 8 | 9 | ///////////////////////////////////////////////////////////////////// 10 | //************************内存栈基本单元*****************************/// 11 | ///////////////////////////////////////////////////////////////////// 12 | CTonyMemoryStackToken::CTonyMemoryStackToken(int nBlockSize, 13 | CTonyLowDebug* pDebug) { 14 | m_pDebug = pDebug; //保存Debug 对象指针 15 | m_ulBlockSize = (ULONG) nBlockSize; //保存本对象管理的内存块尺寸 16 | m_nBlockOutSide.Set(0); //统计变量初始化 17 | m_nBlockInSide.Set(0); 18 | m_Lock.EnableWrite(); //注意单写多读锁的使用,这是写锁 19 | { 20 | m_pFirstSon = null; //指针变量初始化 21 | m_pNext = null; //注意,这是在锁保护下进行的 22 | } 23 | m_Lock.DisableWrite(); 24 | } 25 | CTonyMemoryStackToken::~CTonyMemoryStackToken() { 26 | if (m_nBlockOutSide.Get()) { 27 | //注意此处,当检测到还有内存块在外被应用程序使用, 28 | //而本对象又需要析构,即内存池停止服务时,报警提醒程序员。 29 | TONY_DEBUG("Tony Memory Stack: lost %d * %d\n", m_ulBlockSize, 30 | m_nBlockOutSide.Get()); 31 | } 32 | m_Lock.EnableWrite(); 33 | { 34 | //此处,在锁保护下,摧毁所有的右枝和左枝,这是递归运算 35 | if (m_pFirstSon) 36 | DestroySon (m_pFirstSon); 37 | m_pFirstSon = null; 38 | if (m_pNext) 39 | delete m_pNext; 40 | m_pNext = null; 41 | } 42 | m_Lock.DisableWrite(); 43 | } 44 | 45 | //Malloc 逻辑 46 | void* CTonyMemoryStackToken::Malloc(int nSize, CMRSWint& nAllBlockCount, 47 | CMRSWint& nMemoryUse) { 48 | void* pRet = null; //准备返回的指针 49 | STonyMemoryBlockHead* pNew = null; //中间变量 50 | //请注意这个宏的使用 51 | //根据一个应用程序数据块的长度,计算一个内存块的真实大小,即n+8 52 | //#define TONY_MEM_BLOCK_SIZE(nDataLength) \ 53 | // (nDataLength+STonyMemoryBlockHeadSize) 54 | //这是申请大小+8,再和本对象管理大小比较,以此修订管理数据带来的偏差 55 | //这里还有一个技巧,m_ulBlockSize 没有使用锁保护,这是因为m_ulBlockSize 56 | //从构造函数填充后,所有的使用都是只读的,不再需要锁保护,以简化锁操作 57 | if ((TONY_MEM_BLOCK_SIZE(nSize)) < m_ulBlockSize) { 58 | //这表示本对象的大小可以满足申请需求,申请将从本节点右枝完成 59 | m_Lock.EnableWrite(); //加锁 60 | { 61 | //判断是否有空闲的内存块备用 62 | if (!m_pFirstSon) { 63 | //如果没有,需要向系统新申请一个内存块 64 | //注意这里的强制指针类型转换,申请的内存块,在本函数直接 65 | //转换成管理结构体指针,以便操作 66 | //注意申请的大小就是m_ulBlockSize,因此 67 | //本对象申请的所有内存块,都是一样大小的。 68 | pNew = (STonyMemoryBlockHead*) malloc(m_ulBlockSize); 69 | if (pNew) //安全检查 70 | { 71 | //统计函数+1,因为这个内存块马上会分配出去, 72 | //因此,修订m_nBlockOutSide+1,InSide 不加。 73 | m_nBlockOutSide.Add(); 74 | //这是帮助上层统计总内存字节数,注意,这是传址调用,数据会上传 75 | nMemoryUse.Add(m_ulBlockSize); 76 | //初始化新申请的内存块,填充大小信息 77 | //注意,下面的Free 要用到这个信息查找对应的左枝节点。 78 | pNew->m_ulBlockSize = m_ulBlockSize; 79 | //由于链表指针只有在管理时使用,分配出去的,暂时清空 80 | pNew->m_pNext = null; 81 | //这里最关键,请注意,使用了宏计算,p1=p0+8,求得p1, 82 | //返回给应用程序使用 83 | pRet = TONY_MEM_BLOCK_DATA(pNew); 84 | //统计变量,内存块总数+1 85 | nAllBlockCount.Add(); 86 | } //如果申请失败,则直接返回null 给应用程序 87 | } else { 88 | //这是有空闲内存块的情况 89 | //直接提取链表第一块,也就是栈上最新加入的内存块 90 | pNew = m_pFirstSon; 91 | //这是指针修订,注意,本类中m_pFirstSon 已经指向了原来第二块内存 92 | m_pFirstSon = pNew->m_pNext; 93 | //同上,分配出去的内存块,m_pNext 无用,清空 94 | pNew->m_pNext = null; 95 | //求出p1=p0+8,返回给应用程序使用 96 | pRet = TONY_MEM_BLOCK_DATA(pNew); 97 | //注意此处,这是把内部的内存块再分配重用 98 | //因此,OutSide+1,InSide-1 99 | m_nBlockOutSide.Add(); 100 | m_nBlockInSide.Dec(); 101 | } 102 | } 103 | m_Lock.DisableWrite(); //解 锁 104 | } else { 105 | m_Lock.EnableWrite(); //加写锁 106 | { 107 | //这是检测兄弟节点是否已经创建,如果没有,则创建之。 108 | //注意,此处对m_pNext 有写关系,因此,加写锁。 109 | if (!m_pNext) { 110 | m_pNext = new CTonyMemoryStackToken(m_ulBlockSize * 2, 111 | m_pDebug); 112 | } 113 | } 114 | m_Lock.DisableWrite(); //解写锁 115 | //此处锁读写模式转换非常重要,下面专门论述 116 | m_Lock.AddRead(); //加读锁 117 | { 118 | //将请求传递给兄弟节点的Malloc 函数处理 119 | if (m_pNext) 120 | pRet = m_pNext->Malloc(nSize, nAllBlockCount, nMemoryUse); 121 | //如果兄弟节点创建失败,表示系统内存分配也失败,返回空指针给应用程序 122 | } 123 | m_Lock.DecRead(); //解 读 锁 124 | } 125 | return pRet; 126 | } 127 | //Free 函数,允许代入CloseFlag 标志,注意,外部传入的指针为p1,需要逆求p0=p1-8 128 | //在某种情况下,Free 可能会失败,因此,需要返回一个bool 值,表示释放结果。 129 | bool CTonyMemoryStackToken::Free(void* pPoint, bool bCloseFlag) { 130 | bool bRet = false; //准备返回值变量 131 | //注意这里,这个pOld,已经是计算的p0=p1-8 132 | STonyMemoryBlockHead* pOld = TONY_MEM_BLOCK_HEAD(pPoint); 133 | //首先检测指定内存块大小是否由本对象管理。 134 | if (m_ulBlockSize == pOld->m_ulBlockSize) { 135 | //此处是判断内存块对象是否超限,如果超出限额,直接释放,不做重用处理。 136 | //因此,对于超限的内存块,其管理节点对象总是存在的,但对象的右枝为空。 137 | if (TONY_XIAO_MEMORY_STACK_MAX_SAVE_BLOCK_SIZE <= m_ulBlockSize) { 138 | free(pOld); 139 | m_nBlockOutSide.Dec(); //注意,这里修订OutSide 统计变量。 140 | } else if (bCloseFlag) { //当CloseFlag 为真,不再推入右枝,直接释放 141 | free(pOld); 142 | m_nBlockOutSide.Dec(); 143 | } else { //当所有条件不满足,正常推入右枝栈,准备重用内存块 144 | m_Lock.EnableWrite(); //加写锁 145 | { //请注意这里,典型的压栈操作,新加入内存块,放在第一个, 146 | //原有的内存块,被链接到新内存块的下一个,先进先出 147 | pOld->m_pNext = m_pFirstSon; 148 | m_pFirstSon = pOld; 149 | } 150 | m_Lock.DisableWrite(); //解除写锁 151 | m_nBlockOutSide.Dec(); //修订统计变量 152 | m_nBlockInSide.Add(); 153 | } //请注意返回值的位置,只要是本节点处理,都会返回true。 154 | bRet = true; 155 | } else { //这里有一个默认推定,请大家关注: 156 | //当一个程序,所有的动态内存申请都是由本内存池管理是, 157 | //内存块一定是被本内存池分配,因此,当内存块释放时, 158 | //其对应的左枝节点,即内存管理单元节点,一定存在 159 | //因此,Free 不再处理m_pNext 的创建问题,而是直接调用 160 | m_Lock.AddRead(); //加读锁,原因前面已经论述 161 | { //此处递归操作 162 | if (m_pNext) 163 | bRet = m_pNext->Free(pPoint, bCloseFlag); 164 | } 165 | m_Lock.DecRead(); //解读锁 166 | } 167 | return bRet; 168 | } 169 | 170 | //摧毁所有的子节点 171 | void CTonyMemoryStackToken::DestroySon(STonyMemoryBlockHead* pSon) { //本函数是典型的对象内私有函数,被其他线程安全的函数调用,因此,内部不加锁。 172 | STonyMemoryBlockHead* pObjNow = pSon; //中间变量 173 | STonyMemoryBlockHead* pOnjNext = null; 174 | while (1) //循环方式 175 | { 176 | if (!pObjNow) 177 | break; //循环跳出点 178 | pOnjNext = pObjNow->m_pNext; 179 | free(pObjNow); 180 | m_nBlockInSide.Dec(); 181 | pObjNow = pOnjNext; 182 | } 183 | } 184 | 185 | //内存管理单元的信息输出函数 186 | void CTonyMemoryStackToken::PrintStack(void) { 187 | //这是一个典型的递归函数,一次打印左枝上所有的内存管理单元的内容 188 | if (m_nBlockInSide.Get() + m_nBlockOutSide.Get()) { //有值则打印,无值不输出 189 | TONY_XIAO_PRINTF(" [%ld] stack: all=%d, out=%d, in=%d\n", m_ulBlockSize, //这是提示内存块的大小 190 | m_nBlockInSide.Get() + m_nBlockOutSide.Get(), //这是所有内存块的总数 191 | m_nBlockOutSide.Get(), //分配出去的内存块 192 | m_nBlockInSide.Get()); //内部保存备用的内存块 193 | } 194 | m_Lock.AddRead(); //加读锁 195 | if (m_pNext) { 196 | m_pNext->PrintStack(); //注意,这里递归 197 | } 198 | m_Lock.DecRead(); //解读锁 199 | } 200 | 201 | /////////////////////////////////////////////////////////////////////////// 202 | //*****************************内存栈************************************/// 203 | /////////////////////////////////////////////////////////////////////////// 204 | //构造函数 205 | CTonyMemoryStack::CTonyMemoryStack(CTonyLowDebug* pDebug) { 206 | m_CloseFlag.Set(false); //关闭标志清空 207 | m_pDebug = pDebug; //保存debug 对象指针 208 | m_pMaxPoint.Set(0); //统计变量赋初值 209 | m_nAllBlockCount.Set(0); 210 | m_nMemoryUse.Set(0); 211 | m_pHead = new CTonyMemoryStackToken( //以最小内存块尺寸16Bytes, 212 | TONY_XIAO_MEMORY_STACK_BLOCK_MIN, //构造左枝第一个节点 213 | m_pDebug); 214 | //注意,在整个运行期间,左枝节点只创建,不摧毁,因此,不会因为 215 | //动态对象而造成内存碎片 216 | } //析构函数 217 | CTonyMemoryStack::~CTonyMemoryStack() { 218 | //退出时,汇报一下使用的最大指针,方便程序员观察 219 | TONY_DEBUG("Memory Stack: Max Point= 0x%p\n", m_pMaxPoint.Get()); 220 | //清除左枝,这是递归删除,由每个节点的析构完成 221 | if (m_pHead) { 222 | delete m_pHead; 223 | m_pHead = null; 224 | } 225 | } //公有方法,设置Close 标志 226 | void CTonyMemoryStack::SetCloseFlag(bool bFlag) { 227 | m_CloseFlag.Set(true); 228 | } 229 | 230 | void* CTonyMemoryStack::Malloc(int nSize) { 231 | void* pRet = null; 232 | if (0 >= nSize) //防御性设计,申请长度<=0 的内存空间无意义 233 | { 234 | TONY_DEBUG("CTonyMemoryStack::Malloc(): ERROR! nSize=%d\n", nSize); 235 | return pRet; 236 | } 237 | if (m_pHead) { 238 | //注意,此处进入递归分配,上一小节逻辑,递归到合适的管理单元分配 239 | pRet = m_pHead->Malloc(nSize, m_nAllBlockCount, m_nMemoryUse); 240 | //统计功能,统计最大的指针 241 | if (m_pMaxPoint.Get() < (int) pRet) 242 | m_pMaxPoint.Set((int) pRet); 243 | } 244 | return pRet; 245 | } 246 | bool CTonyMemoryStack::Free(void* pPoint) { 247 | bool bRet = false; 248 | if (m_pHead) //递归调用左枝节点上的Free,完成Free 功能 249 | { 250 | bRet = m_pHead->Free(pPoint, m_CloseFlag.Get()); 251 | } 252 | return bRet; 253 | } 254 | //ReMalloc 功能,改变一个指针指向的内存区的尺寸。 255 | //假定该指针是本内存池分配的,因此,根据给出的指针p1,可以逆推算出p0=p1-8, 256 | //并由此获得原有尺寸的真实大小。 257 | void* CTonyMemoryStack::ReMalloc(void* pPoint, //原有指针,p1 258 | int nNewSize, //新尺寸 259 | bool bCopyOldDataFlag) //拷贝旧数据标志 260 | { 261 | void* pRet = null; 262 | STonyMemoryBlockHead* pOldToken = null; //旧的p0 263 | int nOldLen = 0; //旧的长度 264 | if (0 >= nNewSize) //防御性设计,防止申请非法的长度(<=0) 265 | { //请注意打印格式,以函数名开始,方便程序员观察Debug 信息时定位 266 | TONY_DEBUG("CTonyMemoryStack::ReMalloc(): ERROR! nNewSize=%d\n", 267 | nNewSize); 268 | goto CTonyMemoryStack_ReMalloc_Free_Old; 269 | } //请注意这里,调用宏计算p0=p1-8 270 | pOldToken = TONY_MEM_BLOCK_HEAD(pPoint); 271 | //求的原内存块的大小 272 | nOldLen = pOldToken->m_ulBlockSize; 273 | //比较新的长度和内存块总长度的大小, 274 | //特别注意,NewSize 被宏计算+8,求的是内存块大小,这是修订误差 275 | if (TONY_MEM_BLOCK_SIZE(nNewSize) <= (ULONG) nOldLen) { 276 | //我们知道,内存栈管理的内存块都是已经取模的内存块, 277 | //其长度通常都是16Bytes 的整倍数,并且比应用程序申请的内存要大 278 | //因此,很可能应用程序新申请的内存大小,并没有超过原内存块本身的大小 279 | //此时,就可以直接将原内存块返回继续使用,不会因此导致内存溢出等bug 280 | //比如,应用程序第一次申请一个260Bytes 的内存块,根据内存取模原则 281 | //内存池会分配一个512Bytes 的内存块给其使用,有效使用空间512-8=504Bytes 282 | //而当第二次,应用程序希望使用一个300Bytes 的内存区,还是小于504Bytes 283 | //此时,ReMalloc 会返回旧指针,让其继续使用,而不是重新申请,以提高效率。 284 | //另外,从这个逻辑我们也可以得知,如果调整的新尺寸本来就比原空间小 285 | //比如第二次调整的尺寸不是300Bytes,而是100Bytes, 286 | //也会因为这个原因,继续使用原内存,避免二次分配 287 | //这是一个典型的空间换时间优化,以一点内存的浪费,避免二次分配,提升效率 288 | //由于这是原内存返回,自然保留原有数据,本函数无需拷贝数据 289 | pRet = pPoint; 290 | //请注意这里,跳过了第一跳转点,直接跳到正常结束 291 | //即传入的指针不会被Free,而是二次使用。 292 | goto CTonyMemoryStack_ReMalloc_End_Process; 293 | } //当确定新的尺寸比较大,原有内存无法承受,则必须调用内存管理单元,重新申请一块 294 | pRet = m_pHead->Malloc(nNewSize, m_nAllBlockCount, m_nMemoryUse); 295 | //当新内存申请成功,根据拷贝表示拷贝数据 296 | if ((pRet) && (pPoint)) 297 | if (bCopyOldDataFlag) 298 | memcpy(pRet, pPoint, nOldLen); 299 | CTonyMemoryStack_ReMalloc_Free_Old: 300 | //第一跳转点,出错时,或正确结束时,均Free 旧指针 301 | m_pHead->Free(pPoint, m_CloseFlag.Get()); 302 | CTonyMemoryStack_ReMalloc_End_Process: 303 | //正常退出跳转点,返回新指针 304 | return pRet; 305 | } 306 | 307 | //打印关键变量的值 308 | void CTonyMemoryStack::PrintInfo(void) { 309 | TONY_XIAO_PRINTF("block=%d, use=%d kbytes, biggest=%p\n", 310 | m_nAllBlockCount.Get(), //所有内存块的总数 311 | m_nMemoryUse.Get() / 1024, //总内存使用大小(KBytes) 312 | m_pMaxPoint.Get()); //最大指针 313 | } //递归调用内存管理单元的对应方法,打印整个树的情况。 314 | void CTonyMemoryStack::PrintStack(void) { 315 | if (m_pHead) 316 | m_pHead->PrintStack(); 317 | } 318 | 319 | //////////////////////////////////////////////////////////////////////// 320 | //*****************************内存注册模块*****************************// 321 | //////////////////////////////////////////////////////////////////////// 322 | 323 | //这里使用了一个宏TONY_CLEAN_CHAR_BUFFER,其定义如下: 324 | 325 | //构造函数 326 | CMemoryRegister::CMemoryRegister(CTonyLowDebug* pDebug) { 327 | m_pDebug = pDebug; 328 | //初始化变量 329 | m_pMaxPoint = null; 330 | m_nUseMax = 0; 331 | m_nPointCount = 0; 332 | //请注意这里,我们的结构体中,并没有单独设计标示使用与否的变量 333 | //因此,一般是以保存的指针是否为null 来表示结构体是否被使用 334 | //这就要求,初始化时,先将所有的内存块指针均置为null,方便后续函数判读 335 | int i = 0; 336 | for (i = 0; i < TONY_MEMORY_REGISTER_MAX; i++) { 337 | m_RegisterArray[i].m_pPoint = null; 338 | TONY_CLEAN_CHAR_BUFFER(m_RegisterArray[i].m_szInfo); 339 | } 340 | } 341 | //析构函数 342 | CMemoryRegister::~CMemoryRegister() { 343 | int i = 0; 344 | m_Lock.Lock(); //加锁 345 | { 346 | //打印统计的最大指针,提醒程序员 347 | TONY_DEBUG("CMemoryRegister: Max Register Point= 0x%p\n", m_pMaxPoint); 348 | //检索所有使用的结构体,如果有指针非0,表示没有释放的,打印报警信息。 349 | //请注意,为了优化效率,这里循环的终点是m_nUseMax,不检查没有使用的部分 350 | for (i = 0; i < m_nUseMax; i++) { 351 | if (m_RegisterArray[i].m_pPoint) { 352 | //原则上,应该帮助应用程序释放,但考虑到,这个模块是基础支撑模块。 353 | //本析构函数执行时,进程即将关闭,因此,仅仅报警,不释放 354 | TONY_DEBUG("***** Memory Lost: [%p] - %s\n", 355 | m_RegisterArray[i].m_pPoint, 356 | m_RegisterArray[i].m_szInfo); 357 | } 358 | } 359 | } 360 | m_Lock.Unlock(); //解锁 361 | } 362 | 363 | void CMemoryRegister::RegisterCopy(STonyXiaoMemoryRegister* pDest, //目标结构体指针 364 | void* pPoint, //待拷贝的注册指针 365 | char* szInfo) //待拷贝的说明文字 366 | { 367 | pDest->m_pPoint = pPoint; 368 | if (szInfo) //由于szInfo 可以是null,因此需要加判断 369 | { //请注意这里对SafeStrcpy 的使用,拷贝永远是安全的,程序减少很多判断,显得很简洁 370 | SafeStrcpy(pDest->m_szInfo, szInfo, TONY_MEMORY_BLOCK_INFO_MAX_SIZE); 371 | } else { 372 | //TONY_CLEAN_CHAR_BUFFER(szInfo); //如果为空,则缓冲区置为空字符串 373 | SafeStrcpy(pDest->m_szInfo, "no info", TONY_MEMORY_BLOCK_INFO_MAX_SIZE); 374 | } 375 | } 376 | 377 | //注册方法 378 | void CMemoryRegister::Add(void* pPoint, char* szInfo) { 379 | int i = 0; 380 | m_Lock.Lock(); //加锁 381 | { 382 | //完成统计功能,如果新注册的指针比最大指针大,更新最大指针 383 | if (pPoint > m_pMaxPoint) 384 | m_pMaxPoint = pPoint; 385 | //循环遍历已经使用的数组区域,寻找因Del 导致的空区,重复使用 386 | //注意,循环的终点是m_nUseMax,而不是数组最大单元 387 | for (i = 0; i < m_nUseMax; i++) { 388 | //请注意,如果结构体保存的指针为空,判定未使用 389 | if (!m_RegisterArray[i].m_pPoint) { 390 | //统计在用指针总数 391 | m_nPointCount++; 392 | //调用拷贝功能函数,执行真实的拷贝动作,保存传入的信息,即注册 393 | RegisterCopy(&(m_RegisterArray[i]), pPoint, szInfo); 394 | //功能完成,直接跳到最后,返回 395 | goto CMemoryRegister_Add_End_Process; 396 | } 397 | } //这是第二逻辑段落,即使用区域无空区,需要增补到最尾 398 | //首先,检查是否数组越界,如果越界,报警 399 | if (TONY_MEMORY_REGISTER_MAX <= m_nUseMax) { 400 | //使用Debug 模块报警,原则上,程序员如果在Debug 信息中看到这一句 401 | //表示该程序并发的指针数超过了数组限制,可以考虑 402 | //增加TONY_MEMORY_REGISTER_MAX 的值。 403 | TONY_DEBUG("***ERROR*** CMemoryRegister is full!\n"); 404 | goto CMemoryRegister_Add_End_Process; 405 | } //这是讲注册内容拷贝到队列最尾的逻辑 406 | RegisterCopy(&(m_RegisterArray[m_nUseMax]), pPoint, szInfo); 407 | m_nPointCount++; 408 | m_nUseMax++; 409 | } 410 | CMemoryRegister_Add_End_Process: m_Lock.Unlock(); //解锁 411 | } 412 | //反注册函数 413 | void CMemoryRegister::Del(void* pPoint) { 414 | int i = 0; 415 | m_Lock.Lock(); //加锁 416 | { 417 | //寻找在用内存区域 418 | for (i = 0; i < m_nUseMax; i++) { 419 | if (pPoint == m_RegisterArray[i].m_pPoint) { 420 | //统计值-1 421 | m_nPointCount--; 422 | //清空逻辑,本结构体也就空闲出来,等待下次Add 重用 423 | m_RegisterArray[i].m_pPoint = null; 424 | TONY_CLEAN_CHAR_BUFFER(m_RegisterArray[i].m_szInfo); 425 | //跳出循环,返回 426 | goto CMemoryRegister_Del_End_Process; 427 | } 428 | } 429 | } 430 | CMemoryRegister_Del_End_Process: m_Lock.Unlock(); //解锁 431 | } 432 | //修改注册指针的值 433 | void CMemoryRegister::Modeify(void* pOld, void* pNew) { 434 | int i = 0; 435 | m_Lock.Lock(); 436 | { 437 | //由于要切换指针,因此需要做一次统计 438 | if (pOld > m_pMaxPoint) 439 | m_pMaxPoint = pOld; 440 | for (i = 0; i < m_nUseMax; i++) { 441 | //注意,所有的检索以内存块指针作为依据,相当于哈希上的key 442 | if (pOld == m_RegisterArray[i].m_pPoint) { 443 | //仅仅修订指针,不修订说明文字 444 | m_RegisterArray[i].m_pPoint = pNew; 445 | goto CMemoryRegister_Del_End_Process; 446 | } 447 | } 448 | } //这是一个显式的报警,如果一个指针没有找到,而应用层又来请求修改 449 | //这表示应用程序有bug,因此,debug 提醒程序员修改bug 450 | TONY_DEBUG( 451 | "***ERROR*** CMemoryRegister::Modeify(): I can\'t found point!\n"); 452 | CMemoryRegister_Del_End_Process: m_Lock.Unlock(); 453 | } 454 | //信息打印函数 455 | void CMemoryRegister::PrintInfo(void) { 456 | m_Lock.Lock(); 457 | { 458 | TONY_XIAO_PRINTF("memory pool: %d / %d, biggest=%p\n", m_nPointCount, //在用指针总数 459 | m_nUseMax + 1, //注册数组使用范围 460 | m_pMaxPoint); //统计的最大指针 461 | } 462 | m_Lock.Unlock(); 463 | } 464 | ////////////////////////////////////////////////////////////////////////// 465 | ////////////////////////////////////////////////////////////////////////// 466 | //**************************socket 注册管理类*****************************// 467 | ////////////////////////////////////////////////////////////////////////// 468 | //判断socket 是否合法的通用函数,这个函数设置的目的是收拢所有的判断,方便以后修改 469 | bool SocketIsOK(Linux_Win_SOCKET nSocket) { 470 | //某些系统的socket 判断,是以0~65535 的绝对值判断,这里实现,暂时不用 471 | // if(0>nSocket) return false; 472 | // if(65535 m_nMaxSocket) 535 | m_nMaxSocket = s; 536 | //先试图修改,遍历使用区域 537 | for (i = 0; i < m_nUseMax; i++) { 538 | if (m_RegisterArray[i].m_nSocket == s) { 539 | if (szInfo) //拷贝说明文字 540 | SafeStrcpy(m_RegisterArray[i].m_szInfo, szInfo, 541 | TONY_MEMORY_BLOCK_INFO_MAX_SIZE); 542 | //注意,修改不是添加,因此,这里的socket 使用统计不增加 543 | //m_nSocketUseCount++; 544 | goto CSocketRegister_Add_End_Process; 545 | } 546 | } //再试图插入 547 | for (i = 0; i < m_nUseMax; i++) { 548 | if (!SocketIsOK(m_RegisterArray[i].m_nSocket)) { 549 | m_RegisterArray[i].m_nSocket = s; 550 | if (szInfo) //拷贝说明文字 551 | SafeStrcpy(m_RegisterArray[i].m_szInfo, szInfo, 552 | TONY_MEMORY_BLOCK_INFO_MAX_SIZE); 553 | //这是实实在在的添加到空区,因此,统计变量+1 554 | m_nSocketUseCount++; 555 | goto CSocketRegister_Add_End_Process; 556 | } 557 | } //最后无空区可以使用呢,试图追加到最后 558 | if (TONY_MEMORY_REGISTER_MAX > m_nUseMax) { 559 | m_RegisterArray[m_nUseMax].m_nSocket = s; 560 | if (szInfo) //拷贝说明文字 561 | SafeStrcpy(m_RegisterArray[m_nUseMax].m_szInfo, szInfo, 562 | TONY_MEMORY_BLOCK_INFO_MAX_SIZE); 563 | //使用区域指针+1 564 | m_nUseMax++; 565 | //统计变量+1 566 | m_nSocketUseCount++; 567 | } //注册区满了,报警,并发的socket 数量超限,程序员有必要扩大缓冲区 568 | else 569 | TONY_DEBUG("CSocketRegister::Add(): Pool is full!\n"); 570 | } 571 | CSocketRegister_Add_End_Process: m_Lock.Unlock(); //解锁 572 | } 573 | 574 | //反注册函数,即将一个socket 的注册信息,从内部管理数据区删除 575 | bool CSocketRegister::Del(Linux_Win_SOCKET s) { 576 | bool bRet = false; 577 | int i = 0; 578 | m_Lock.Lock(); //加锁 579 | { 580 | for (i = 0; i < m_nUseMax; i++) { 581 | //遍历使用区,检索socket 582 | if (m_RegisterArray[i].m_nSocket == s) { 583 | //注意清空动作,把socket 置为Linux_Win_InvalidSocket 584 | //这项,下次Add 可以重复使用该空区 585 | m_RegisterArray[i].m_nSocket = Linux_Win_InvalidSocket; 586 | TONY_CLEAN_CHAR_BUFFER(m_RegisterArray[i].m_szInfo); 587 | //修订统计变量,并发socket 数量-1 588 | m_nSocketUseCount--; 589 | bRet = true; 590 | goto CSocketRegister_Del_End_Process; 591 | } 592 | } 593 | } 594 | CSocketRegister_Del_End_Process: m_Lock.Unlock(); //解锁 595 | return bRet; 596 | } 597 | //内部信息打印函数 598 | void CSocketRegister::PrintInfo(void) { 599 | m_Lock.Lock(); //加锁 600 | { 601 | TONY_XIAO_PRINTF("socket pool: %d / %d, biggest=%d\n", 602 | m_nSocketUseCount, //并发的socket 数量统计 603 | m_nUseMax + 1, //内存结构体数组使用量标示 604 | m_nMaxSocket); //统计到的最大socket 605 | } 606 | m_Lock.Unlock(); //解锁 607 | } 608 | ////////////////////////////////////////////////////////////////////////// 609 | //***************************內存池**************************************// 610 | ////////////////////////////////////////////////////////////////////////// 611 | //构造函数 612 | CTonyMemoryPoolWithLock::CTonyMemoryPoolWithLock(CTonyLowDebug* pDebug, 613 | bool bOpenRegisterFlag) { 614 | m_pDebug = pDebug; //保存debug 对象指针 615 | m_pMemPool = new CTonyMemoryStack(m_pDebug); //实例化内存栈对象 616 | m_pRegister = null; //初始化各个指针变量 617 | m_pSocketRegister = null; 618 | // m_pLockRegister = null; 619 | if (bOpenRegisterFlag) { //根据标志位,决定是否实例化各个注册对象 620 | m_pRegister = new CMemoryRegister(m_pDebug); 621 | m_pSocketRegister = new CSocketRegister(m_pDebug); 622 | } //打印内存池正确启动标志 623 | TONY_DEBUG("Memory Pool Open, register flag=%d\n", 624 | bOpenRegisterFlag); 625 | } 626 | //析构函数 627 | CTonyMemoryPoolWithLock::~CTonyMemoryPoolWithLock() { 628 | //依次摧毁各个子对象 629 | if (m_pRegister) //请读者注意删除对象的标准写法 630 | { 631 | delete m_pRegister; 632 | m_pRegister = null; 633 | } 634 | if (m_pSocketRegister) { 635 | delete m_pSocketRegister; 636 | m_pSocketRegister = null; 637 | } 638 | if (m_pMemPool) { 639 | delete m_pMemPool; 640 | m_pMemPool = null; 641 | } //打印内存池正确结束标志 642 | TONY_DEBUG("Memory Pool Close.\n"); 643 | } 644 | //设置退出标志,加速内存栈的释放过程 645 | void CTonyMemoryPoolWithLock::SetCloseFlag(bool bFlag) { 646 | if (m_pMemPool) 647 | m_pMemPool->SetCloseFlag(bFlag); 648 | } 649 | 650 | void CTonyMemoryPoolWithLock::test() { 651 | printf("this is in memory pool test"); 652 | } 653 | //Malloc 函数 654 | void* CTonyMemoryPoolWithLock::Malloc(int nSize, char* szInfo) { 655 | void* pRet = null; 656 | if (m_pMemPool) { 657 | pRet = m_pMemPool->Malloc(nSize); //调用内存栈实现内存分配 658 | if (pRet) Register(pRet, szInfo); //如果指针有效,自动注册 659 | } 660 | // printf("Malloc(int nSize, char *szInfo)\n"); 661 | return pRet; 662 | } 663 | //Free 函数 664 | void CTonyMemoryPoolWithLock::Free(PVOID pBlock) { 665 | if (m_pMemPool) m_pMemPool->Free(pBlock); //调用内存栈实现Free 666 | UnRegister(pBlock); //反注册指针 667 | } 668 | //ReMalloc 函数 669 | void* CTonyMemoryPoolWithLock::ReMalloc(void* pPoint, int nNewSize, 670 | bool bCopyOldDataFlag) { 671 | void* pRet = null; 672 | if (m_pMemPool) { //调用内存栈重定义内存区域大小 673 | pRet = m_pMemPool->ReMalloc(pPoint, nNewSize, bCopyOldDataFlag); 674 | if (m_pRegister) { 675 | if (pRet) //如果重分配成功,在注册对象中Modeify 新的指针 676 | m_pRegister->Modeify(pPoint, pRet); 677 | else 678 | //如果失败,由于老指针已经摧毁,因此需要从注册对象中反注册旧指针 679 | m_pRegister->Del(pPoint); 680 | } 681 | } 682 | return pRet; 683 | } 684 | //指针注册方法 685 | void CTonyMemoryPoolWithLock::Register(void* pPoint, char* szInfo) { 686 | if (m_pRegister) { 687 | m_pRegister->Add(pPoint, szInfo); 688 | } 689 | } 690 | //指针反注册方法 691 | void CTonyMemoryPoolWithLock::UnRegister(void* pPoint) { 692 | if (m_pRegister) { 693 | m_pRegister->Del(pPoint); 694 | } 695 | } 696 | //应用程序注册一个socket 开始管理,注意,一个socket,可能被注册多次,以修改其说明文字 697 | //本函数是可以多次重入的。 698 | void CTonyMemoryPoolWithLock::RegisterSocket(Linux_Win_SOCKET s, char* szInfo) { 699 | if (m_pSocketRegister) { 700 | m_pSocketRegister->Add(s, szInfo); 701 | } 702 | } 703 | 704 | //公有的关闭Socket 方法,反注册+关闭。 705 | void CTonyMemoryPoolWithLock::CloseSocket(Linux_Win_SOCKET& s) { 706 | if (SocketIsOK(s)) { 707 | if (m_pSocketRegister) { 708 | if (!m_pSocketRegister->Del(s)) { 709 | //这是一个很重要的报警,如果一个socket,经检查,没有在内部注册 710 | //但是应用程序却call 这个方法,就表示程序内存在没有注册的socket 情况 711 | //说明socket 的管理不完整,有遗漏,程序员有必要检查程序 712 | //还有一种可能,这个socket 被两个程序关闭,第一次关闭时, 713 | //其信息已经反注册,第二次就存在找不到的现象 714 | //这说明程序员对socket 的关闭有冗余行为,虽然不会有什么问题, 715 | //但建议最好修改。 716 | TONY_DEBUG( 717 | "CTonyMemoryPoolWithLock::CloseSocket(): \ 718 | Socket %d is not registered! but I have close it yet!\n", 719 | s); 720 | } 721 | } //真实地关闭socket 722 | _Linux_Win_CloseSocket(s); 723 | s = Linux_Win_InvalidSocket; //关闭完后,立即赋值 724 | } 725 | } 726 | //调用内存栈功能,打印详细跟踪信息 727 | void CTonyMemoryPoolWithLock::PrintTree(void) { 728 | if (m_pMemPool) 729 | m_pMemPool->PrintStack(); 730 | } //打印各个子对象信息 731 | void CTonyMemoryPoolWithLock::PrintInfo(void) { 732 | if (m_pSocketRegister) //打印Socket 注册对象信息 733 | m_pSocketRegister->PrintInfo(); 734 | if (m_pRegister) //打印指针注册对象信息 735 | m_pRegister->PrintInfo(); 736 | if (m_pMemPool) //打印内存栈对象信息 737 | m_pMemPool->PrintInfo(); 738 | } 739 | -------------------------------------------------------------------------------- /src/Mint.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "MutexLock.h" 3 | #include "Mint.h" 4 | 5 | 6 | //这里是.cpp 文件中的实现 7 | int MvarInit(MINT& mValue, int nValue) { 8 | MUTEXINIT(&mValue.m_MyLock); //初始化函数主要就是初始化锁变量 9 | mValue.m_nValue = nValue; //同时赋初值 10 | return nValue; 11 | } 12 | void MvarDestroy(MINT& mValue) { 13 | MUTEXLOCK(&mValue.m_MyLock); 14 | MUTEXUNLOCK(&mValue.m_MyLock); 15 | //还记得上面这个技巧吗?一次看似无意义的加锁和解锁行为 16 | //使摧毁函数可以等待其他线程中没有完成的访问,最大限度保证安全 17 | MUTEXDESTROY(&mValue.m_MyLock); //摧毁锁变量 18 | } 19 | int MvarSet(MINT& mValue, int nValue) { 20 | MUTEXLOCK(&mValue.m_MyLock); 21 | mValue.m_nValue = nValue; //锁保护内的赋值 22 | MUTEXUNLOCK(&mValue.m_MyLock); 23 | return nValue; 24 | } 25 | int MvarGet(MINT& mValue) { 26 | int nValue; 27 | MUTEXLOCK(&mValue.m_MyLock); 28 | nValue = mValue.m_nValue; //锁保护内的取值 29 | MUTEXUNLOCK(&mValue.m_MyLock); 30 | return nValue; 31 | } 32 | int MvarADD(MINT& mValue, int nValue) { 33 | int nRet; 34 | MUTEXLOCK(&mValue.m_MyLock); 35 | mValue.m_nValue += nValue; //锁保护内的累加动作 36 | nRet = mValue.m_nValue; 37 | MUTEXUNLOCK(&mValue.m_MyLock); 38 | return nRet; 39 | } 40 | int MvarDEC(MINT& mValue, int nValue) { 41 | int nRet; 42 | MUTEXLOCK(&mValue.m_MyLock); 43 | mValue.m_nValue -= nValue; //锁保护内的减法计算 44 | nRet = mValue.m_nValue; 45 | MUTEXUNLOCK(&mValue.m_MyLock); 46 | return nRet; 47 | } 48 | -------------------------------------------------------------------------------- /src/MrswLock.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "MutexLock.h" 3 | #include "Thread.h" 4 | #include "MrswLock.h" 5 | //大家可以看看笔者的函数命名习惯,基本遵循匈牙利命名法,即单词首字母大写 6 | //MRSW 是Multi Read and Signal Write(多读和单写)的缩写 7 | //MRSWLock 前缀表示单写多读锁 8 | //中间一个“_”分割符,后面是函数的功能描述Greate,创建,Destroy,摧毁,等等 9 | void MRSWLock_Create(STonyXiaoMultiReadSingleWriteLock* pLock) { 10 | MUTEXINIT(&(pLock->m_Lock)); //初始化内部锁 11 | pLock->m_nReadCount = 0; //初始化读计数器 12 | pLock->m_bWriteFlag = false; //初始化写标志 13 | } 14 | void MRSWLock_Destroy(STonyXiaoMultiReadSingleWriteLock* pLock) { 15 | MUTEXLOCK(&pLock->m_Lock); //还记得前文的技巧吗? 16 | MUTEXUNLOCK(&pLock->m_Lock); //利用一次空加锁解锁动作,规避风险 17 | MUTEXDESTROY(&(pLock->m_Lock)); //摧毁内部锁 18 | } 19 | 20 | //获取写状态 21 | bool MRSWLock_GetWrite(STonyXiaoMultiReadSingleWriteLock* pLock) { 22 | bool bRet = false; 23 | MUTEXLOCK(&(pLock->m_Lock)); 24 | { 25 | bRet = pLock->m_bWriteFlag; 26 | } 27 | MUTEXUNLOCK(&(pLock->m_Lock)); 28 | return bRet; 29 | } 30 | //获取读计数器 31 | int MRSWLock_GetRead(STonyXiaoMultiReadSingleWriteLock* pLock) { 32 | int nRet = 0; 33 | MUTEXLOCK(&(pLock->m_Lock)); 34 | { 35 | nRet = pLock->m_nReadCount; 36 | } 37 | MUTEXUNLOCK(&(pLock->m_Lock)); 38 | return nRet; 39 | } 40 | 41 | //进入写操作函数 42 | void MRSWLock_EnableWrite(STonyXiaoMultiReadSingleWriteLock* pLock) { 43 | while (1) //死循环等待 44 | { 45 | //第一重循环,检测“写”标志是否为“假”,尝试抢夺“写”权 46 | //请大家放心,即使有其他线程正在写,操作总会完成 47 | //因此,这个死循环不会永远死等,一般很快就会因为条件满足而跳出 48 | MUTEXLOCK(&(pLock->m_Lock)); //内部锁加锁,进入锁域 49 | { 50 | //在此域内,其他访问本单写多读锁的线程,会因为内部锁的作用被挂住 51 | //判断是否可以抢夺“写”权利 52 | if (!pLock->m_bWriteFlag) { 53 | //如果写标志为“假”,即可以抢夺“写”权利 54 | //注意,此时一定不要解内部锁,应该立即将写标志置为“真” 55 | pLock->m_bWriteFlag = true; 56 | //抢到“写”权后,本轮逻辑完毕,解除内部锁后,可以退出 57 | //关键:请注意,这里多一句解锁命令,为安全跳出使用 58 | MUTEXUNLOCK(&(pLock->m_Lock)); 59 | //注意退出方式,使用goto 精确定位 60 | goto MRSWLock_EnableWrite_Wait_Read_Clean; 61 | } //这是if 域结束 62 | //此处,“写”标志为真,表示其他线程已经抢到“写”权, 63 | //本线程必须悬挂等待。 64 | } //这是锁域结束 65 | //等待睡眠时,应该及时释放内部锁,避免其他线程被连锁挂死 66 | MUTEXUNLOCK(&(pLock->m_Lock)); 67 | //这是一个特殊的Sleep,下面会讲到 68 | TonyXiaoMinSleep(); 69 | } 70 | //这是第一阶段完成标志,程序运行到此,表示“写”权已经被本程序抢到手 71 | MRSWLock_EnableWrite_Wait_Read_Clean: 72 | //下面,开始等待其他的“读”操作完毕 73 | while (MRSWLock_GetRead(pLock)) //请务必关注这个调用 74 | //这是利用公有的取读状态方法来获取信息 75 | //前文已经说明,这个函数内部,是“资源锁”模型, 76 | //即函数内进行了内部锁加锁,是线程安全的, 77 | //退出函数,内部锁自动解锁 78 | //因此,本循环Sleep 期间,本函数没有挂住内部锁, 79 | //其他线程访问的其他公有方法,可以自由修改读计数器的值。 80 | //这个规避逻辑异常重要! 81 | { 82 | //第二重循环,等待所有的“读”操作退出 83 | //请放心,一旦“写”标志被置为“真”,新的“读”操作会被悬挂,不会进来 84 | //而老的读操作,迟早会工作完毕 85 | //因此,这个死循环,总能等到退出的时候,并不是死循环 86 | TonyXiaoMinSleep(); 87 | } 88 | } 89 | 90 | void MRSWLock_DisableWrite(STonyXiaoMultiReadSingleWriteLock* pLock) { 91 | MUTEXLOCK(&(pLock->m_Lock)); 92 | { 93 | pLock->m_bWriteFlag = false; 94 | } 95 | MUTEXUNLOCK(&(pLock->m_Lock)); 96 | } 97 | 98 | //进入读函数,返回当前的读计数器值 99 | int MRSWLock_AddRead(STonyXiaoMultiReadSingleWriteLock* pLock) { 100 | while (1) { 101 | //这里是死循环等待,不过,即使是其他线程在进行写操作, 102 | //操作总会完成,因此,总有机会碰到写标志为“假”,最终跳出循环 103 | MUTEXLOCK(&(pLock->m_Lock)); //加内部锁,进入锁域 104 | { 105 | if (!pLock->m_bWriteFlag) //检测写标志是否为“假” 106 | { 107 | //如果为“假”,表示可以开始读 108 | //此时,一定要先累加,再释放内部锁,避免由于空隙, 109 | //导致别的线程错误切入 110 | pLock->m_nReadCount++; 111 | MUTEXUNLOCK(&(pLock->m_Lock)); 112 | //返回读计数器的值, 113 | //注意:这个值可能不一定是刚刚累加的值 114 | //由于内部锁已经解除,别的读线程完全可能切进来 115 | //将这个值增加好几次 116 | return MRSWLock_GetRead(pLock); //这是本函数唯一跳出点 117 | } 118 | } //如果写标志为“真”只能循环等待 119 | MUTEXUNLOCK(&(pLock->m_Lock)); 120 | //使用特殊睡眠,后文有解释 121 | TonyXiaoMinSleep(); 122 | } 123 | } 124 | 125 | //返回读计数器变化后的结果 126 | int MRSWLock_DecRead(STonyXiaoMultiReadSingleWriteLock* pLock) { 127 | int nRet = 0; 128 | MUTEXLOCK(&(pLock->m_Lock)); 129 | { 130 | //这是一种习惯性保护,递减计算时,如果最小值是0,总是加个判断 131 | if (0 < (pLock->m_nReadCount)) 132 | pLock->m_nReadCount--; 133 | //注意,这里是直接获得读计数器的值,看起来,比进入读要准确一点 134 | nRet = pLock->m_nReadCount; 135 | } 136 | MUTEXUNLOCK(&(pLock->m_Lock)); 137 | return nRet; 138 | } 139 | 140 | void MRSWLock_Read2Write(STonyXiaoMultiReadSingleWriteLock* pLock) { 141 | while (1) //死循环,和进入写中的死循环一个道理 142 | { 143 | MUTEXLOCK(&(pLock->m_Lock)); 144 | { 145 | if (!pLock->m_bWriteFlag) { 146 | //注意这里,一旦检测到可以抢夺写权利 147 | //先把写标志置为“真” 148 | pLock->m_bWriteFlag = true; 149 | //切记,由于是读转写,以前进入读的时候,已经把计数器+1 150 | //这里一定要-1,否则会导致计数器永远不为0,系统挂死 151 | if (0 < (pLock->m_nReadCount)) 152 | pLock->m_nReadCount--; 153 | //如前,所有状态设置完成,解除内部锁,跳到下一步 154 | MUTEXUNLOCK(&(pLock->m_Lock)); 155 | goto MRSWLock_Read2Write_Wait_Read_Clean; 156 | } 157 | } //解除内部锁等待,前文已经说明 158 | MUTEXUNLOCK(&(pLock->m_Lock)); 159 | TonyXiaoMinSleep(); 160 | } //此处开始等待所有的读退出,同“进入写”的逻辑 161 | MRSWLock_Read2Write_Wait_Read_Clean: while (MRSWLock_GetRead(pLock)) { 162 | TonyXiaoMinSleep(); 163 | } 164 | } 165 | ///////////////////////////////////////////////////////////////////////// 166 | //这里是实施部分,请注意各个函数内的锁调用,与前文不同 167 | CMRSWint::CMRSWint() //构造函数初始化变量 168 | { 169 | m_Lock.EnableWrite(); 170 | m_nValue = 0; 171 | m_Lock.DisableWrite(); 172 | } 173 | int CMRSWint::Get(void) //得到变量的值 174 | { 175 | int nRet = 0; 176 | m_Lock.AddRead(); //请关注这里,这是读方式,即这个动作可以并发 177 | { 178 | nRet = m_nValue; 179 | } 180 | m_Lock.DecRead(); 181 | return nRet; 182 | } 183 | int CMRSWint::Set(int nValue) //设置变量的值 184 | { 185 | int nRet = 0; 186 | m_Lock.EnableWrite(); //注意,这里是写方式,表示是串行的 187 | { 188 | m_nValue = nValue; 189 | nRet = m_nValue; 190 | } 191 | m_Lock.DisableWrite(); 192 | return nRet; 193 | } 194 | int CMRSWint::Add(int nValue) //加法运算 195 | { 196 | int nRet = 0; 197 | m_Lock.EnableWrite(); //写方式,串行 198 | { 199 | m_nValue += nValue; 200 | nRet = m_nValue; 201 | } 202 | m_Lock.DisableWrite(); 203 | return nRet; 204 | } 205 | int CMRSWint::Dec(int nValue) //减法运算 206 | { 207 | int nRet = 0; 208 | m_Lock.EnableWrite(); //写方式,串行 209 | { 210 | m_nValue -= nValue; 211 | nRet = m_nValue; 212 | } 213 | m_Lock.DisableWrite(); 214 | return nRet; 215 | } 216 | -------------------------------------------------------------------------------- /src/Nonreentrant.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "MutexLock.h" 3 | 4 | #include "Nonreentrant.h" 5 | //不可重入锁类实现 6 | CNonReentrant::CNonReentrant() { //初始化内部变量 7 | m_bAlreadRunFlag = false; 8 | } //核心的Set 函数 9 | bool CNonReentrant::Set(bool bRunFlag) { 10 | //请注意,返回结果是设置动作的成功与否,和内部的bool 变量,没有任何关系 11 | bool bRet = false; 12 | if (bRunFlag) //需要设置为真的逻辑,比较复杂 13 | { 14 | m_Lock.Lock(); //进入锁域 15 | { 16 | if (!m_bAlreadRunFlag) { //如果原值为“假”,表示可以设置 17 | m_bAlreadRunFlag = true; //设置内部bool 变量 18 | bRet = true; //返回真,表示设置成功 19 | } //否则,不做任何事,即内部bool 变量不做设置,且返回假 20 | } 21 | m_Lock.Unlock(); //退出锁域 22 | } else { //这是设置为“假”的情况 23 | m_Lock.Lock(); //进入锁域 24 | { 25 | m_bAlreadRunFlag = false; //无条件设置内部bool 变量为“假” 26 | bRet = true; //由于这也是设置成功,因此返回真 27 | } 28 | m_Lock.Unlock(); //退出锁域 29 | } 30 | return bRet; 31 | } 32 | -------------------------------------------------------------------------------- /src/Statistics.cpp: -------------------------------------------------------------------------------- 1 | #include "Statistics.h" 2 | #include "std.h" 3 | //获得ΔX 4 | unsigned long SCountSubGetX(SCountSub& CountSub) { 5 | return CountSub.m_lXEnd - CountSub.m_lXBegin; 6 | } //设置初始值 7 | void SCountSubSetBegin(SCountSub& CountSub, unsigned long n) { 8 | CountSub.m_lXBegin = n; 9 | } //设置结束值 10 | unsigned long SCountSubSetEnd(SCountSub& CountSub, unsigned long n) { 11 | CountSub.m_lXEnd = n; 12 | return SCountSubGetX(CountSub); 13 | } 14 | 15 | //初始化 16 | void SCountReset(SCount& Count, unsigned long n) { 17 | Count.m_Sum = n; 18 | Count.m_Sub.m_lXBegin = 0; 19 | Count.m_Sub.m_lXEnd = 0; 20 | } //计算统计平均值 21 | unsigned long SCountSum(SCount& Count) { 22 | unsigned long X = SCountSubGetX(Count.m_Sub); 23 | if (0 == Count.m_Sum) 24 | Count.m_Sum = X; //初值如果为0,则直接赋值,避免误差 25 | else 26 | Count.m_Sum = (Count.m_Sum + X) / 2; //计算统计平均值 27 | return Count.m_Sum; 28 | } //返回Sum 值 29 | unsigned long SCountGetSum(SCount& Count) { 30 | return Count.m_Sum; 31 | } 32 | //返回当前的ΔX 33 | unsigned long SCountGetX(SCount& Count) { 34 | return SCountSubGetX(Count.m_Sub); 35 | } 36 | //设置开始 37 | void SCountSetBegin(SCount& Count, unsigned long n) { 38 | SCountSubSetBegin(Count.m_Sub, n); 39 | } 40 | //设置结束值,单纯更新,可以多次刷新 41 | unsigned long SCountSetEnd(SCount& Count, unsigned long n) { 42 | return SCountSubSetEnd(Count.m_Sub, n); 43 | } 44 | 45 | void CDeltaTime::Reset(void) { 46 | m_CountSub.SetEnd(m_CountSub.SetBegin((unsigned long) time(null))); 47 | } 48 | //触发开始时间计数 49 | void CDeltaTime::TouchBegin(void) { 50 | m_CountSub.SetBegin((unsigned long) time(null)); 51 | } 52 | //触发结束时间技术 53 | void CDeltaTime::TouchEnd(void) { 54 | m_CountSub.SetEnd((unsigned long) time(null)); 55 | } 56 | //利用上文的Push 功能,实现循环统计。即Now->End->Begin 57 | void CDeltaTime::Touch(void) { 58 | m_CountSub.Push((unsigned long) time(null)); 59 | } 60 | -------------------------------------------------------------------------------- /src/ThreadPool.cpp: -------------------------------------------------------------------------------- 1 | #include "std.h" 2 | #include "Mint.h" 3 | #include "MutexLock.h" 4 | #include "Debug.h" 5 | #include "TonyLowDebug.h" 6 | #include "ThreadManager.h" 7 | #include "Thread.h" 8 | #include "ThreadPool.h" 9 | #include "MemoryManager.h" 10 | #include "Buffer.h" 11 | #include "Log.h" 12 | #include "BaseLib.h" 13 | 14 | CTonyThreadPool::CTonyThreadPool(CTonyLowDebug* pDebug) { 15 | m_pDebug = pDebug; //保存Debug 对象指针 16 | TONY_DEBUG("CTonyThreadPool Start!\n"); //显示开启信息 17 | THREADID id; //注意,开启管理者线程的参变量 18 | THREAD t; //函数内变量,表示不保存 19 | //注意,这里面没有使用锁对象,而使用纯C 的锁结构体完成。 20 | MUTEXINIT (&m_RegisterLock); //初始化线程池总的锁对象 21 | XMI(m_bThreadContinue, true); //初始化bThreadContinue 变量 22 | XMI(m_nThreadPoolThreadCount, 0); //初始化nThreadCount 变量 23 | XMI(m_nThreadPoolIdleThreadCount, 0); //初始化空闲线程统计变量 24 | //初始化数组 25 | int i; 26 | for (i = 0; i < THIS_POOLTHREAD_MAX; i++) { 27 | //虽然管理者线程没有保存线程句柄,但考虑到应用线程以后潜在的需求, 28 | //在线程的任务结构体中,还是保留了线程句柄和线程ID 29 | m_TToken[i].m_hThread = 0; 30 | m_TToken[i].m_nThreadID = 0; 31 | //这主要是为了调试方便,为每条服务线程设置一个显式的退出码,Windows 下, 32 | //线程返回可以看到这个值 33 | m_TToken[i].m_nExitCode = THREAD_POOL_EXIT_CODE + i; 34 | //任务描述,回调函数指针和透传参数指针,注意,null 表示当前无任务, 35 | //服务线程即使启动,也是在Idle 下空转。 36 | m_TToken[i].m_pCallback = null; 37 | m_TToken[i].m_pCallParam = null; 38 | //这是一个很重要的描述,线程池自己的指针,原因后述。 39 | m_TToken[i].m_pThreadPoolObject = this; 40 | //初始化线程任务单元状态变量,注意,多线程安全的变量,C 模式 41 | XMI(m_TToken[i].m_nState, TPOOL_THREAD_STATE_NOT_RUN); 42 | } 43 | id = 0; //开启管理者线程 44 | t = 0; 45 | THREADCREATE(ThreadPoolCtrlThread, this, t, id); //注意,管理者线程的参数是this 46 | Sleep (OPEN_THREAD_DELAY); //强制等待管理者线程开启 47 | } 48 | CTonyThreadPool::~CTonyThreadPool() { 49 | //关闭线程,这个段落比较经典,前文已多次出现,此处不再赘述 50 | XMS(m_bThreadContinue, false); 51 | while (XMG (m_nThreadPoolThreadCount)) { 52 | Sleep (MIN_SLEEP); 53 | } //等待关闭所有线程 54 | //摧毁所有线程参数模块的状态变量 55 | int i; 56 | for (i = 0; i < THIS_POOLTHREAD_MAX; i++) { 57 | XME(m_TToken[i].m_nState); //只有这一个需要摧毁 58 | } 59 | XME (m_bThreadContinue); //摧毁各线程安全变量 60 | XME (m_nThreadPoolThreadCount); 61 | XME (m_nThreadPoolIdleThreadCount); 62 | MUTEXDESTROY (&m_RegisterLock); //摧毁基本锁 63 | TONY_DEBUG("CTonyThreadPool Stop!\n"); //显示退出信息 64 | } 65 | //寻找一个没有使用的Token 66 | //找到,返回编号 67 | //没有找到(全部用满了),返回-1 68 | int CTonyThreadPool::Search4NotUseToken(void) { 69 | int i; 70 | for (i = 0; i < THIS_POOLTHREAD_MAX; i++) //遍历整个数组查找 71 | { 72 | if (TPOOL_THREAD_STATE_NOT_RUN == XMG(m_TToken[i].m_nState)) 73 | return i; //找到,返回index 74 | } 75 | return -1; //找不到,返回-1 76 | } 77 | //管理者线程,注意:这里的函数声明是跨平台的线程函数定义,使用宏完成 78 | THREADFUNCDECL(CTonyThreadPool::ThreadPoolCtrlThread,pParam) 79 | { 80 | CTonyThreadPool* pThis=(CTonyThreadPool*)pParam; //老习惯,获得this 指针 81 | //注意,此处增加线程计数器,一般说来,线程池的使用者总是长期使用 82 | //即管理者线程总有实例化运行的机会,因此,线程计数器在其中+1,而不是放在构造函数 83 | XMA(pThis->m_nThreadPoolThreadCount); 84 | int nIdleThread=0;//空闲线程计数 85 | int nNotRunThread=0;//未运行线程计数 86 | //请注意这个死循环,参考bThreadContinue 87 | while(XMG(pThis->m_bThreadContinue)) 88 | { 89 | //获得当前空闲的线程数 90 | nIdleThread=XMG(pThis->m_nThreadPoolIdleThreadCount); 91 | if(WHILE_THREAD_COUNT>nIdleThread) 92 | { 93 | //如果备用的空闲线程不够10,需要添加,则启动新服务线程 94 | //启动前,需要先找到空闲的任务块,找不到,也不启动。 95 | nNotRunThread=pThis->Search4NotUseToken(); 96 | if(-1!=nNotRunThread) 97 | { 98 | //启动线程 99 | THREADCREATE(ThreadPoolThread,//服务者线程名 100 | //注意,此处把任务数据块指针作为参数传给服务者线程, 101 | //因此,每个服务者线程,仅能维护一个任务数据块 102 | &(pThis->m_TToken[nNotRunThread]), 103 | //注意,此处保存了服务者线程的句柄和ID 104 | pThis->m_TToken[nNotRunThread].m_hThread, 105 | pThis->m_TToken[nNotRunThread].m_nThreadID); 106 | //如果没有启动起来,下轮再说,此处不再报错 107 | } 108 | }Sleep(OPEN_THREAD_DELAY); //一定要等够 250ms 109 | }XMD(pThis->m_nThreadPoolThreadCount); //退出时,线程计数器-1 110 | #ifdef WIN32 //按照Linux 和Windows 两种方式退出 111 | return THREAD_POOL_EXIT_CODE-1; 112 | #else // not WIN32 113 | return null; 114 | #endif 115 | } 116 | //服务线程 117 | THREADFUNCDECL(CTonyThreadPool::ThreadPoolThread,pParam) 118 | { 119 | //由于历史原因,此处有一点歧义,此处的pThis 不是线程池对象指针 120 | //而是任务块的指针,由于线程池开发较早,出于“尊重”原则,此处没有做更动。 121 | //线程池对象指针,此处表示为:pThis->m_pThreadPoolObject 122 | SThreadToken* pThis=(SThreadToken*)pParam; 123 | //刚启动,设置为Idle 状态 124 | XMS(pThis->m_nState,TPOOL_THREAD_STATE_IDLE); 125 | //线程计数器+1 126 | XMA(pThis->m_pThreadPoolObject->m_nThreadPoolThreadCount); 127 | //Idle 线程计数器+1 128 | XMA(pThis->m_pThreadPoolObject->m_nThreadPoolIdleThreadCount); 129 | //注意这个守候循环,检索了bThreadContinue 参量,以便配合最终的退出动作 130 | while(XMG(pThis->m_pThreadPoolObject->m_bThreadContinue)) 131 | { 132 | //取任务 133 | switch(XMG(pThis->m_nState)) 134 | { case TPOOL_THREAD_STATE_NOT_RUN: 135 | //这个状态表示没有线程为任务块服务,但现在本线程已经启动 136 | //如果仍然看到这个状态,肯定出错了,表示外部程序状态设置不对 137 | //但这个错误不严重,自动进那个修补一下就OK 138 | //修补的方法就是设置为Idle 状态 139 | XMS(pThis->m_nState,TPOOL_THREAD_STATE_IDLE); 140 | //注意此处没有break 141 | case TPOOL_THREAD_STATE_IDLE://没有命令 142 | default: 143 | //这是正常睡眠 144 | break; 145 | case TPOOL_THREAD_STATE_BUSY://Register 下任务了 146 | //请注意一个细节,这里没有把Idle 计数器-1, 147 | //这是因为Register 函数做了这个动作,后文有详细描述 148 | if(pThis->m_pCallback)//检查是不是真的有任务 149 | { 150 | //将执行权交给新的任务(一次),请注意参数传递 151 | pThis->m_pCallback(pThis->m_pCallParam, 152 | pThis->m_pThreadPoolObject->m_bThreadContinue); 153 | //空闲线程数+1 154 | XMA(pThis->m_pThreadPoolObject->//----a 155 | m_nThreadPoolIdleThreadCount); 156 | }break; 157 | }; 158 | //检查空闲线程总数 159 | if(WHILE_THREAD_COUNTm_pThreadPoolObject->m_nThreadPoolIdleThreadCount)) 161 | break;//如果备用线程超出限额,则跳出死循环,退出自己 162 | //所有工作做完,把自己置为Idle 状态 163 | if(TPOOL_THREAD_STATE_IDLE!=XMG(pThis->m_nState))//----c 164 | XMS(pThis->m_nState,TPOOL_THREAD_STATE_IDLE); 165 | Sleep(DEFAULT_THREAD_SLEEP);//睡眠,等待下次任务 166 | } //退出流程 167 | //Idle 计数器-1 168 | XMD(pThis->m_pThreadPoolObject->m_nThreadPoolIdleThreadCount);//----d 169 | //线程总计数器-1 170 | XMD(pThis->m_pThreadPoolObject->m_nThreadPoolThreadCount); 171 | //把任务区块的状态设置为正确的值(没有线程为其服务) 172 | XMS(pThis->m_nState,TPOOL_THREAD_STATE_NOT_RUN); 173 | #ifdef WIN32 //两种方式退出 174 | return pThis->m_nExitCode; 175 | #else // not WIN32 176 | return null; 177 | #endif 178 | } 179 | 180 | //获得一个空闲的线程编号 181 | //如果无空闲,则返回-1 182 | int CTonyThreadPool::GetAIdleThread(void) { 183 | int nRet = -1; 184 | int i; 185 | for (i = 0; i < THIS_POOLTHREAD_MAX; i++) //遍历检索 186 | { //注意,仅仅检索Idle 一个状态 187 | if (TPOOL_THREAD_STATE_IDLE == XMG(m_TToken[i].m_nState)) { 188 | nRet = i; 189 | break; //找到跳出 190 | } 191 | } 192 | return nRet; //没找到,可能返回-1 193 | } 194 | //注册一个新任务 195 | //请注意,这里有临界区保护,线程安全 196 | //注册成功,返回_THREADPOOL_OK,否则返回其他错误码 197 | int CTonyThreadPool::ThreadPoolRegisterANewThread(_TPOOL_CALLBACK pCallback, //任务回调函数指针 198 | void* pParam) //透传的任务参数指针 199 | { 200 | int nRet = _THREADPOOL_PLEASE_WAIT; //返回值设置初值 201 | MUTEXLOCK (&m_RegisterLock); //加锁 202 | int nIdleThread = GetAIdleThread(); //取得Idle 的线程编号 203 | if (0 > nIdleThread) { //没有找到Idle 服务线程 204 | if (THIS_POOLTHREAD_MAX == XMG(m_nThreadPoolThreadCount)) { 205 | //没有空闲线程,而线程总数已经达到上限,返回OverFlow 标志 206 | nRet = _THREADPOOL_OVERFLOW; 207 | } else { 208 | //没有空闲线程,仅仅是还没有来得及开启,请调用者等待 209 | nRet = _THREADPOOL_PLEASE_WAIT; 210 | } 211 | } else { 212 | //找到空闲线程,添加任务 213 | m_TToken[nIdleThread].m_pCallback = pCallback; //这里可以看出如何将 214 | m_TToken[nIdleThread].m_pCallParam = pParam; //任务添加到任务区块 215 | XMS(m_TToken[nIdleThread].m_nState, //注意,本任务块被设置为 216 | TPOOL_THREAD_STATE_BUSY); //busy,因此,不会被再次 217 | //添加新的任务 218 | XMD (m_nThreadPoolIdleThreadCount); //前文所述,Idle 计数器-1 219 | nRet = _THREADPOOL_OK; //返回成功标志 220 | } 221 | MUTEXUNLOCK(&m_RegisterLock); //解锁 222 | return nRet; 223 | } 224 | //一定要注册成功,可以等待新的Idle 线程被管理者线程启动,除非遇到OverFlow,否则循环等待 225 | int CTonyThreadPool::ThreadPoolRegisterANewThreadWhile( 226 | _TPOOL_CALLBACK pCallback, //任务回调函数指针 227 | void* pParam) //透传的任务参数指针 228 | { 229 | int nRet; 230 | while (1) //死循环等待 231 | { //调用上一函数,开始注册 232 | nRet = ThreadPoolRegisterANewThread(pCallback, pParam); 233 | //注册成功,或者线程池溢出,都返回 234 | if (_THREADPOOL_PLEASE_WAIT != nRet) 235 | break; 236 | Sleep (OPEN_THREAD_DELAY); //最多等一会,新的线程就已经建立了 237 | } 238 | return nRet; 239 | } 240 | int CTonyThreadPool::ThreadPoolRegTask(_TPOOL_CALLBACK pCallback, //任务回调函数指针 241 | void* pParam, //透传的任务参数指针 242 | bool bWait4Success) //是否需要等待注册成功 243 | { 244 | if (bWait4Success) //根据标志,调用不同的函数 245 | return ThreadPoolRegisterANewThreadWhile(pCallback, pParam); 246 | else 247 | return ThreadPoolRegisterANewThread(pCallback, pParam); 248 | } 249 | ////////////////////////////////////////////////////////////////// 250 | //*************************任务池类*******************************// 251 | /////////////////////////////////////////////////////////////////// 252 | CTonyXiaoTaskPool::CTonyXiaoTaskPool(CTonyBaseLibrary* pTonyBaseLib, 253 | int nMaxThread) { 254 | m_pTonyBaseLib = pTonyBaseLib; //保存聚合工具类指针 255 | m_pDebug = m_pTonyBaseLib->m_pDebug; //从聚合工具类中获得debug 对象指针, 256 | //方便后续使用 257 | m_nMaxThread = nMaxThread; //保存最大线程数 258 | m_pTonyBaseLib->m_pLog->_XGSyslog("CTonyXiaoTaskPool(): Start!\n"); //打印启动信息 259 | m_bThreadContinue.Set(true); //线程管理变量初始化 260 | m_nThreadCount.Set(0); 261 | m_nThreadID.Set(0); //任务ID 种子赋初值0 262 | //实例化任务队列 263 | m_pTaskQueue = new CTonyMemoryQueueWithLock(m_pTonyBaseLib->m_pDebug, //传入debug 对象指针 264 | m_pTonyBaseLib->m_pMemPool, //传入内存池指针 265 | "Tony.XiaoTaskPool"); //应用名 266 | if (m_pTaskQueue) { //注册到内存池进行管理,预防退出时忘了释放 267 | TONY_REGISTER(m_pTaskQueue, "CTonyXiaoTaskPool::m_pTaskQueue"); 268 | } //实例化线程池 269 | m_pThreadPool = new CTonyThreadPool(m_pTonyBaseLib->m_pDebug); 270 | if (m_pThreadPool) { //注册到内存池进行管理 271 | TONY_REGISTER(m_pThreadPool, "CTonyXiaoTaskPool::m_pThreadPool"); 272 | } 273 | if (ICanWork()) //判断前面的new 动作是否完成 274 | { //启动管理线程 275 | if (!m_pThreadPool->ThreadPoolRegTask(TaskCtrlThread, this)) { //这是日志输出,后面章节有详细介绍 276 | ("CTonyXiaoTaskPool:: start ctrl thread %d fail!\n"); 277 | } else 278 | m_nThreadCount.Add(); //如果注册成功,线程计数器+1 279 | } 280 | } 281 | CTonyXiaoTaskPool::~CTonyXiaoTaskPool() { 282 | //以标准方式退出本对象所属所有线程 283 | m_bThreadContinue.Set(false); 284 | while (m_nThreadCount.Get()) { 285 | Sleep(100); 286 | } 287 | TONY_DEL_OBJ (m_pThreadPool); //删除对象的宏 288 | TONY_DEL_OBJ (m_pTaskQueue); 289 | ("CTonyXiaoTaskPool(): Stop!\n"); //打印退出信息 290 | } 291 | bool CTonyXiaoTaskPool::ICanWork(void) { //主要检查任务队列和线程池对象是否初始化 292 | if (!m_pThreadPool) 293 | return false; 294 | if (!m_pTaskQueue) 295 | return false; 296 | return true; 297 | } 298 | void CTonyXiaoTaskPool::PrintInfo(void) { //打印当前线程数和任务书,方便程序员观察 299 | TONY_XIAO_PRINTF("task pool: thread count=%d, task in queue=%d\n", 300 | m_nThreadCount.Get(), m_pTaskQueue->GetTokenCount()); 301 | } 302 | void CTonyXiaoTaskPool::TaskCtrlThread(void* pCallParam, //这是主程序传进来的参数指针 303 | MBOOL& bThreadContinue) //如果系统要退出,线程池会修改这个参数的值为false 304 | { 305 | CTonyXiaoTaskPool* pThis = (CTonyXiaoTaskPool*) pCallParam; 306 | int i = 0; 307 | for (i = 0; i < pThis->m_nMaxThread; i++) //根据最大线程数,循环启动服务线程 308 | { 309 | if (!pThis->m_pThreadPool->ThreadPoolRegTask(TaskServiceThread, 310 | pThis)) { //启动失败,打印报警信息,退出启动 311 | pThis->m_pTonyBaseLib->m_pLog->_XGSyslog( 312 | "CTonyXiaoTaskPool:: start service \ 313 | thread %d fail!\n", i); 314 | break; 315 | } else 316 | pThis->m_nThreadCount.Add(); //启动成功,线程数+1 317 | } 318 | pThis->m_nThreadCount.Dec(); //任务完成,自己退出,因此线程计数器-1 319 | } 320 | //传入任务数据结构STaskPoolToken 指针, 321 | //根据任务回调情况,返回真或者假,返回假,表示该任务已经结束 322 | bool CTonyXiaoTaskPool::TaskServiceThreadDoIt(STaskPoolToken& Task) { 323 | bool bCallbackRet = //执行回调函数,并获得返回值 324 | Task.m_pCallback(Task.m_pUserParam, Task.m_nUserStatus); 325 | if (!bCallbackRet) 326 | return bCallbackRet; //如果返回值为假,直接返回,表示任务结束 327 | //如果任务返回真,表示任务尚未结束,需要重新推回任务队列,等待下次运行 328 | bCallbackRet = RegisterATaskDoIt(&Task); //试图重新注册到任务队列 329 | if (!bCallbackRet) //如果注册失败,报警,表示任务丢失 330 | { 331 | TONY_DEBUG( 332 | "CTonyXiaoTaskPool::TaskServiceThreadDoIt(): a task need \ 333 | continue, but add 2 queue fail! task lost!\n"); 334 | //TONY_DEBUG_BIN((char*) &Task, STaskPoolTokenSize); 335 | } 336 | return bCallbackRet; //返回最后结果 337 | } 338 | void CTonyXiaoTaskPool::TaskServiceThread(void* pCallParam, //这是主程序传进来的参数指针 339 | MBOOL& bThreadContinue) //如果系统要退出,线程池会修改这个参数的值为false 340 | { 341 | int nQueueRet = 0; 342 | STaskPoolToken Task; 343 | char* szTask = (char*) &Task; 344 | //获得本对象指针 345 | CTonyXiaoTaskPool* pThis = (CTonyXiaoTaskPool*) pCallParam; 346 | int nID = pThis->m_nThreadID.Add() - 1; 347 | //这是打印本服务线程启动信号,通常注释掉不用,调试时可能使用。 348 | //pThis->XGSyslog("CTonyXiaoTaskPool::TaskServiceThread(): %d 349 | // Start 350 | // !\n 351 | // ",nID); 352 | while (XMG(bThreadContinue)) //注意本循环,标准的线程池线程守候循环 353 | { //额外增加的判断,以便判断本任务池退出标志 354 | if (!pThis->m_bThreadContinue.Get()) 355 | goto CTonyXiaoTaskPool_TaskServiceThread_End_Process; 356 | //尝试从任务队列中弹出第一个任务,并执行 357 | nQueueRet = pThis->m_pTaskQueue->GetAndDeleteFirst(szTask, 358 | STaskPoolTokenSize); 359 | if (STaskPoolTokenSize == nQueueRet) //如果弹出成功,表示有任务 360 | { 361 | pThis->TaskServiceThreadDoIt(Task); //调用上一函数执行 362 | } 363 | Sleep (MIN_SLEEP); //习惯性睡眠 364 | } 365 | CTonyXiaoTaskPool_TaskServiceThread_End_Process: pThis->m_nThreadCount.Dec(); //退出时,线程计数器-1 366 | //这是打印本服务线程停止信号,通常注释掉不用,调试时可能使用。 367 | //pThis->XGSyslog("CTonyXiaoTaskPool::TaskServiceThread(): %d 368 | // Stop 369 | // !\n 370 | // ",nID); 371 | } 372 | bool CTonyXiaoTaskPool::RegisterATaskDoIt(STaskPoolToken* pToken, int nLimit) { 373 | bool bRet = false; 374 | if (STaskPoolTokenSize == m_pTaskQueue->AddLast( //一次典型的队列AddLast 行为 375 | (char*) pToken, STaskPoolTokenSize), nLimit) 376 | bRet = true; 377 | return bRet; 378 | } 379 | bool CTonyXiaoTaskPool::RegisterATask(_TASKPOOL_CALLBACK pCallback, //传入回调函数指针 380 | void* pUserParam) //传入参数指针 381 | { 382 | STaskPoolToken Token; //准备一个结构体实例 383 | if (!ICanWork()) 384 | return false; 385 | if (!pCallback) 386 | return false; 387 | Token.m_pCallback = pCallback; //填充结构体 388 | Token.m_pUserParam = pUserParam; 389 | Token.m_nUserStatus = 0; //注意此处,状态机置为0 390 | return RegisterATaskDoIt(&Token, m_nMaxThread); //调用上面的函数,完成注册 391 | } 392 | 393 | ////////////////////////////////////////////////////////////////// 394 | //***********************CTonyTaskRun***************************// 395 | ////////////////////////////////////////////////////////////////// 396 | //本重载主要应对那些单任务片段的任务,即只有一个回调函数的任务,可以直接注入启动。 397 | bool CTonyTaskRun::StartTask(_TASKPOOL_CALLBACK pCallback, void* pCallParam, 398 | char* szAppName) { //先实例化一个任务描述对象,调用下面的函数完成注册任务 399 | CTonyTaskRunInfo InfoObj; 400 | InfoObj.AddTask(pCallback, pCallParam); //直接将任务数据添加到任务描述 401 | return StartTask(&InfoObj, szAppName); 402 | } //本重载主要应对使用任务描述对象,并完成了任务描述的应用,注入启动任务 403 | bool CTonyTaskRun::StartTask(CTonyTaskRunInfo* pInfoObject, char* szAppName) { 404 | return StartTask(pInfoObject->GetInfoPoint(), szAppName); 405 | } 406 | //本函数利用任务描述对象内部的数据结构体,完成最终的任务启动工作 407 | //请注意,本函数使用了远堆传参,来启动任务。远堆传参的参数,就是前文所述的结构体 408 | //STonyTeskRunTaskCallbackParam 409 | bool CTonyTaskRun::StartTask(STonyTaskRunInfo* pInfoStruct, char* szAppName) { 410 | bool bRet = false; //准备返回值 411 | if (!m_ThreadManager.ThreadContinue()) //如果本对象未启动,则启动 412 | m_ThreadManager.Open(); 413 | //请注意这里,开始准备远堆传参的参数结构体数据区,使用了内存池动态申请。 414 | //内存池的指针是从聚合工具类中获得的。 415 | STonyTeskRunTaskCallbackParam* pParam = 416 | (STonyTeskRunTaskCallbackParam*) m_pTonyBaseLib->m_pMemPool->Malloc( 417 | STonyTeskRunTaskCallbackParamSize, "CTonyTaskRun::pParam"); 418 | if (pParam) //内存申请成功,继续执行,否则返回假 419 | { 420 | pParam->m_pTonyBaseLib = m_pTonyBaseLib; //聚合工具类指针 421 | pParam->m_pThis = this; //本对象指针 422 | pParam->m_nRunIndex = 0; //状态机归0 423 | if (szAppName) //如果外部提供了应用名 424 | SafeStrcpy(pParam->m_szAppName, //拷贝应用名 425 | szAppName, TONY_MEMORY_BLOCK_INFO_MAX_SIZE); 426 | else 427 | //否则清空应用名 428 | TONY_CLEAN_CHAR_BUFFER(pParam->m_szAppName); 429 | //注意,这里使用了任务描述的粘合类特性,粘合到参数区的任务描述数据结构上 430 | CTonyTaskRunInfo InfoObj(&(pParam->m_Info)); 431 | //目的是调用CopyFrom 来拷贝外部传入的任务参数 432 | InfoObj.CopyFrom(pInfoStruct); 433 | //调用聚合工具类中的任务池对象,注册任务 434 | //注意,注册是是本类提供的标准任务执行体,该任务会解析任务描述 435 | //提供二次执行,真正执行应用层的任务调用。 436 | bRet = m_pTonyBaseLib->m_pTaskPool->RegisterATask( 437 | TonyTeskRunTaskCallback, //这是本对象的任务 438 | pParam); //应用层注册的任务在这里 439 | //会被二次解析执行 440 | if (bRet) { 441 | m_ThreadManager.AddAThread(); //如果成功,线程计数器+1 442 | if (szAppName) //如果有应用名,打印提示 443 | ("%s Start...\n", pParam->m_szAppName); 444 | } 445 | } 446 | return bRet; 447 | } 448 | void CTonyTaskRun::StopAll(void) { 449 | m_ThreadManager.CloseAll(); 450 | } 451 | void CTonyTaskRun::PrintInfo(void) { 452 | TONY_XIAO_PRINTF("task run: task count=%d\n", 453 | m_ThreadManager.GetThreadCount()); //打印线程总数 454 | } 455 | bool CTonyTaskRun::TonyTeskRunTaskCallback(void* pCallParam, int& nStatus) { 456 | bool bCallbackRet = false; //记录用户任务片段的调用结果 457 | bool bGotoNextStatus = true; //跳到下一状态的标志 458 | STonyTeskRunTaskCallbackParam* pParam = //强制指针类型转换,获得参数指针 459 | (STonyTeskRunTaskCallbackParam*) pCallParam; 460 | if (!pParam) 461 | return false; //防御性设计,如果没有参数,终止 462 | CTonyTaskRun* pThis = pParam->m_pThis; //方便调用,获得本类指针pThis 463 | switch (nStatus) //这个状态机是任务池提供的 464 | { 465 | case 0: ///这是本函数的主执行代码 466 | if (pParam->m_Info.m_nTaskCount > pParam->m_nRunIndex) { //只要应用层任务片段没有被轮询完毕,一直在本片段执行 467 | bGotoNextStatus = false; //这个=false,就是不希望跳转 468 | //这里的回调请仔细看 469 | bCallbackRet = //取得应用层片段的回调结果 470 | //根据应用层状态机pParam->m_nRunIndex,调用合适的片段 471 | pParam->m_Info.m_CallbackArray[pParam->m_nRunIndex]( 472 | pParam->m_Info.m_pCallParam, //这是透传的参数指针 473 | pParam->m_nRunIndex); //这里最关键,大家注意此处没有把 474 | //线程池状态机nStatus 传入应用层 475 | //代码模块,而是使用自己的状态机 476 | //这种“欺骗”,是透明设计的关键 477 | //根据任务池的定义,如果应用程序代码片段返回false,表示其希望跳到下一片段 478 | //因此,这里做应用程序状态机的+1 动作 479 | if (!bCallbackRet) 480 | pParam->m_nRunIndex++; 481 | //注意这个设计,这是非常关键的一步。当StopAll 被调用,本对象即将退出时 482 | //本函数检测到这个结果,并不是简单把nStatus+1 做退出 483 | //而是开始步进应用任务片段,即把应用程序的状态机pParam->m_nRunIndex 累加 484 | //这是强制执行完应用任务片段序列的每一步。 485 | //最终,本任务会因为应用任务执行完毕而关闭,而不是在此因为系统退出关闭 486 | //这样保证不至于产生资源泄漏等情况。 487 | if (!pThis->m_ThreadManager.ThreadContinue()) 488 | pParam->m_nRunIndex++; 489 | } //如果应用程序片段已经执行完毕,则本函数状态机+1,跳转到下一片段结束任务 490 | else 491 | bGotoNextStatus = true; 492 | break; 493 | default: //exit 494 | //如果有应用名,打印退出提示 495 | if (0 < strlen(pParam->m_szAppName)) 496 | pThis->m_pTonyBaseLib->m_pLog->_XGSyslog("%s Stop!\n", pParam->m_szAppName); 497 | pThis->m_ThreadManager.DecAThread(); //线程计数器-1 498 | pThis->m_pTonyBaseLib->m_pMemPool->Free(pParam); //释放参数结构体 499 | return false; //返回假,结束任务 500 | } 501 | if (bGotoNextStatus) 502 | nStatus++; //根据前文判断,调整本函数状态 503 | return true; //返回真,不退出任务 504 | } 505 | -------------------------------------------------------------------------------- /src/TonyLowDebug.cpp: -------------------------------------------------------------------------------- 1 | #include "Debug.h" 2 | #include "std.h" 3 | #include "MutexLock.h" 4 | #include "TonyLowDebug.h" 5 | 6 | CTonyLowDebug::CTonyLowDebug(char* szPathName, char* szAppName, 7 | bool bPrint2TTYFlag, _APP_INFO_OUT_CALLBACK pInfoOutCallback, 8 | void* pInfoOutCallbackParam) { 9 | //保留回调函数指针,供业务函数调用 10 | m_pInfoOutCallback = pInfoOutCallback; 11 | m_pInfoOutCallbackParam = pInfoOutCallbackParam; 12 | //保留输出到屏幕的标志,供业务函数参考 13 | m_bPrint2TTYFlag = bPrint2TTYFlag; 14 | //拼接输出文件名 15 | if (szAppName) 16 | FULL_NAME(szPathName, szAppName, m_szFileName, "dbg") 17 | else 18 | //允许不输出到文件 19 | m_szFileName[0] = '\0'; 20 | //先删除上次的运行痕迹,避免两次输出干扰 21 | DeleteAFile (m_szFileName); 22 | Debug2File("CTonyLowDebug: Start!\n"); //一个简单的声明,我开始工作了 23 | } 24 | 25 | CTonyLowDebug::~CTonyLowDebug() { 26 | Debug2File("CTonyLowDebug: Stop!\n"); 27 | } 28 | 29 | char* CTonyLowDebug::GetTrueFileName(char* szBuffer) { 30 | char* pRet = szBuffer; 31 | int nLen = strlen(szBuffer); 32 | int i = 0; 33 | for (i = nLen - 1; i >= 0; i--) //逆向检索,请注意,这是老代码,不太符合无错化原则 34 | { 35 | //基本逻辑,找到右数第一个路径间隔符跳出,以此作为文件名开始点 36 | if ('\\' == *(szBuffer + i)) //考虑到Windows 的路径间隔符 37 | { 38 | pRet = (szBuffer + i + 1); 39 | break; 40 | } 41 | if ('/' == *(szBuffer + i)) //考虑到Unix 和Linux 的路径间隔符 42 | { 43 | pRet = (szBuffer + i + 1); 44 | break; 45 | } 46 | } 47 | return pRet; //返回的是找到的点,缓冲区是同一缓冲区,该返回值不需要释放 48 | } 49 | 50 | void CTonyLowDebug::DeleteAFile(char* szFileName) { 51 | remove(szFileName); 52 | } 53 | 54 | int CTonyLowDebug::Debug2File(char *szFormat, ...) { 55 | //注意,这个类设计时,还没有内存池概念,因此,动态内存申请,原则上应该避免 56 | //以免出现内存碎片。此处使用静态数组实现buffer,在浮动栈建立。 57 | //这也是为什么这个类必须声明最大输出字符串长度的原因 58 | char szBuf[DEBUG_BUFFER_LENGTH]; //准备输出buffer 59 | char szTemp[DEBUG_BUFFER_LENGTH]; //时间戳置换的中间buffer 60 | char szTime[DEBUG_BUFFER_LENGTH]; //时间戳的buffer 61 | FILE* fp = NULL; //文件指针,以C 模式工作 62 | int nListCount = 0; 63 | va_list pArgList; 64 | time_t t; 65 | struct tm *pTM = NULL; 66 | int nLength = 0; 67 | //这是构建时间戳 68 | time(&t); 69 | pTM = localtime(&t); 70 | nLength = SafePrintf(szTemp, DEBUG_BUFFER_LENGTH, "%s", asctime(pTM)); 71 | szTemp[nLength - 1] = '\0'; 72 | SafePrintf(szTime, DEBUG_BUFFER_LENGTH, "[%s] ", szTemp); 73 | //注意,此处开始进入加锁,以此保证跨线程调用安全 74 | m_Lock.Lock(); 75 | { //习惯性写法,以大括号和缩进清晰界定加锁区域,醒目。 76 | //下面这个段落是从SafePrintf 拷贝出来的,一个逻辑的可重用性, 77 | //也可以根据需要,直接拷贝代码段实现,不一定非要写成宏或函数。 78 | va_start(pArgList, szFormat); 79 | nListCount += Linux_Win_vsnprintf(szBuf + nListCount, 80 | DEBUG_BUFFER_LENGTH - nListCount, szFormat, pArgList); 81 | va_end(pArgList); 82 | if (nListCount > (DEBUG_BUFFER_LENGTH - 1)) 83 | nListCount = DEBUG_BUFFER_LENGTH - 1; 84 | *(szBuf + nListCount) = '\0'; 85 | //开始真实的输出 86 | fp = fopen(m_szFileName, "a+"); 87 | //请注意下面逻辑,由于本函数使用了锁,因此只能有一个退出点 88 | //这里笔者没有使用goto,因此,必须使用if 嵌套,确保不会中间跳出 89 | if (fp) { //输出到文件 90 | nListCount = fprintf(fp, "%s%s", szTime, szBuf); 91 | if (m_bPrint2TTYFlag) { //根据需要输出至控制台 92 | TONY_PRINTF("%s%s", szTime, szBuf); 93 | if (m_pInfoOutCallback) { //注意此处,回调输出给需要的上层调用 94 | //注意,本段为后加,没有使用前文变量,目前是减少bug 率 95 | char szInfoOut[APP_INFO_OIT_STRING_MAX]; 96 | SafePrintf(szInfoOut, APP_INFO_OIT_STRING_MAX, "%s%s", 97 | szTime, szBuf); 98 | m_pInfoOutCallback(szInfoOut, //注意把输出字符串传给回调 99 | m_pInfoOutCallbackParam); //透传的指针 100 | } 101 | } 102 | fclose(fp); 103 | } else { 104 | nListCount = 0; 105 | } 106 | } 107 | m_Lock.Unlock(); //解锁 108 | return nListCount; //返回输出的字节数,所有字符串构造和输出函数的习惯 109 | } 110 | 111 | void CTonyLowDebug::Debug2File4Bin(char* pBuffer, int nLength) { 112 | m_Lock.Lock(); 113 | { //注意加锁区域,以及对前文的代码重用。 114 | //TODO: 实现dbg2file4bin 115 | dbg2file4bin(m_szFileName, "a+", pBuffer, nLength); 116 | if (m_bPrint2TTYFlag) 117 | dbg_bin(pBuffer, nLength); 118 | } 119 | m_Lock.Unlock(); 120 | } 121 | -------------------------------------------------------------------------------- /src/confile.c: -------------------------------------------------------------------------------- 1 | /** 2 | * INI配置文件管理函数库 3 | * Ini file parse functions. 4 | * By Hoverlees http://www.hoverlees.com me[at]hoverlees.com 5 | */ 6 | 7 | 8 | #include "confile.h" 9 | 10 | #define MAX_SECTION_SIZE 64 11 | typedef struct _PARSER{ 12 | int status; 13 | int pos; 14 | int start1; 15 | int end1; 16 | int start2; 17 | int end2; 18 | int row; 19 | int col; 20 | unsigned char* str; 21 | int slen; 22 | INI_CONFIG* config; 23 | char section_name[MAX_SECTION_SIZE]; 24 | }PARSER; 25 | typedef int (*PARSER_JUMP_FUNC)(PARSER* parser); 26 | 27 | #define PARSE_STATUS_GET_KEY_OR_SECTION 1 28 | #define PARSE_STATUS_GET_SECTION 2 29 | #define PARSE_STATUS_GET_VALUE 3 30 | #define PARSE_STATUS_COMMENT_LINE 4 31 | 32 | CONFIG_BTREE_NODE* config_btree_find_node(CONFIG_BTREE* config,const char* key); 33 | 34 | /** 35 | * 内部使用函数,实现内存段trim,返回第一个非空字符指针及字符串trim后的长度. 36 | */ 37 | char* mem_trim(char* src,int len,int* outlen){ 38 | int start,end; 39 | if(len==0) return NULL; 40 | start=0; 41 | end=len-1; 42 | while(startstart && (src[end]==' ' || src[end]=='\r' || src[end]=='\n')){ 46 | end--; 47 | } 48 | end=end+1; 49 | if(start==end) return NULL; 50 | src+=start; 51 | *outlen=end-start; 52 | return src; 53 | } 54 | 55 | /** 56 | * 下面是存储配置专用的二叉树实现. 57 | */ 58 | CONFIG_BTREE_NODE* config_btree_create_node(const char* key,int key_len,void* data,int data_len){ 59 | char* p; 60 | CONFIG_BTREE_NODE* node; 61 | if(key_len==0) key_len=strlen(key); 62 | if(data_len==0) data_len=strlen((char *)data)+1; 63 | node=(CONFIG_BTREE_NODE*) calloc(sizeof(CONFIG_BTREE_NODE)+key_len+data_len,1); 64 | if(node!=NULL){ 65 | p=node->mem; 66 | node->key=p; 67 | p+=(key_len+1); 68 | node->data=p; 69 | memcpy(node->key,key,key_len); 70 | memcpy(node->data,data,data_len); 71 | node->left=NULL; 72 | node->right=NULL; 73 | } 74 | return node; 75 | } 76 | int config_btree_free_node(CONFIG_BTREE_NODE* node){ 77 | free(node); 78 | return 1; 79 | } 80 | CONFIG_BTREE* config_btree_create(){ 81 | CONFIG_BTREE* ret=(CONFIG_BTREE*) calloc(sizeof(CONFIG_BTREE),1); 82 | return ret; 83 | } 84 | int config_btree_insert_node(CONFIG_BTREE* config,const char* key,int key_len,void* data,int data_len){ 85 | CONFIG_BTREE_NODE *p,**prev; 86 | CONFIG_BTREE_NODE* node; 87 | int comp; 88 | if(key_len==0) key_len=strlen(key); 89 | node=config_btree_create_node(key,key_len,data,data_len); 90 | if(node==NULL) return 0; 91 | p=config->root; 92 | prev=&config->root; 93 | if(!p){ 94 | config->root=node; 95 | config->numNodes++; 96 | return 1; 97 | } 98 | while(p){ 99 | comp=memcmp(key,p->key,key_len); 100 | if(comp>0){ 101 | if(p->right==NULL){ 102 | p->right=node; 103 | config->numNodes++; 104 | return 1; 105 | } 106 | prev=&p->right; 107 | p=p->right; 108 | } 109 | else if(comp<0){ 110 | if(p->left==NULL){ 111 | p->left=node; 112 | config->numNodes++; 113 | return 1; 114 | } 115 | prev=&p->left; 116 | p=p->left; 117 | } 118 | else{ 119 | node->left=p->left; 120 | node->right=p->right; 121 | *prev=node; 122 | config_btree_free_node(p); 123 | return 2; //update 124 | } 125 | } 126 | } 127 | 128 | int config_btree_insert_section(CONFIG_BTREE* config,const char* section_name){ 129 | CONFIG_BTREE* section=config_btree_create(); 130 | if(section==NULL) return 0; 131 | if(config_btree_insert_node(config,section_name,0,§ion,sizeof(void*))){ 132 | return 1; 133 | } 134 | else{ 135 | free(section); 136 | return 0; 137 | } 138 | } 139 | 140 | CONFIG_BTREE* config_btree_get_section(CONFIG_BTREE* config,const char* section_name){ 141 | CONFIG_BTREE* section; 142 | CONFIG_BTREE_NODE* node; 143 | node=config_btree_find_node(config,section_name); 144 | if(node==NULL) return NULL; 145 | memcpy(§ion,node->data,sizeof(void*)); 146 | return section; 147 | } 148 | 149 | int config_btree_insert_section_node(CONFIG_BTREE* config,const char* section_name,const char* key, 150 | int key_len,void* data,int data_len){ 151 | CONFIG_BTREE* section; 152 | CONFIG_BTREE_NODE* node; 153 | node=config_btree_find_node(config,section_name); 154 | if(node==NULL) return 0; 155 | memcpy(§ion,node->data,sizeof(void*)); 156 | return config_btree_insert_node(section,key,key_len,data,data_len); 157 | } 158 | 159 | CONFIG_BTREE_NODE* config_btree_find_node(CONFIG_BTREE* config,const char* key){ 160 | CONFIG_BTREE_NODE* p; 161 | int comp; 162 | p=config->root; 163 | while(p){ 164 | comp=strcmp(key,p->key); 165 | if(comp>0){ 166 | p=p->right; 167 | } 168 | else if(comp<0){ 169 | p=p->left; 170 | } 171 | else{ 172 | return p; 173 | } 174 | } 175 | return NULL; 176 | } 177 | 178 | int config_btree_delete_node(CONFIG_BTREE* config,const char* key){ 179 | CONFIG_BTREE_NODE* p; 180 | CONFIG_BTREE_NODE* temp; 181 | CONFIG_BTREE_NODE** prevTmpPos=NULL; 182 | CONFIG_BTREE_NODE** prevPos=NULL; 183 | int comp; 184 | prevPos=&config->root; 185 | p=config->root; 186 | while(p){ 187 | comp=strcmp(key,p->key); 188 | if(comp>0){ 189 | prevPos=&p->right; 190 | p=p->right; 191 | } 192 | else if(comp<0){ 193 | prevPos=&p->left; 194 | p=p->left; 195 | } 196 | else{ 197 | if(p->left){ 198 | temp=p->left; 199 | while(temp->right){ 200 | prevTmpPos=&temp->right; 201 | temp=temp->right; 202 | } 203 | if(prevTmpPos==NULL){ 204 | *prevPos=temp; 205 | temp->right=p->right; 206 | } 207 | else{ 208 | if(temp->left){ 209 | *prevTmpPos=temp->left; 210 | } 211 | else{ 212 | *prevTmpPos=NULL; 213 | } 214 | *prevPos=temp; 215 | temp->left=p->left; 216 | temp->right=p->right; 217 | } 218 | config_btree_free_node(p); 219 | } 220 | else if(p->right){ 221 | temp=p->right; 222 | while(temp->left){ 223 | prevTmpPos=&temp->left; 224 | temp=temp->left; 225 | } 226 | if(prevTmpPos==NULL){ 227 | *prevPos=temp; 228 | temp->left=p->left; 229 | } 230 | else{ 231 | if(temp->right){ 232 | *prevTmpPos=temp->right; 233 | } 234 | else{ 235 | *prevTmpPos=NULL; 236 | } 237 | *prevPos=temp; 238 | temp->left=p->left; 239 | temp->right=p->right; 240 | } 241 | config_btree_free_node(p); 242 | } 243 | else{ 244 | config_btree_free_node(p); 245 | *prevPos=NULL; 246 | } 247 | config->numNodes--; 248 | return 1; 249 | } 250 | } 251 | return 0; 252 | } 253 | 254 | int config_btree_inorder_traverse(CONFIG_BTREE_NODE* node,CONFIG_BTREE_TRAVERSE_CB callback){ 255 | if(node==NULL) return 1; 256 | if(!config_btree_inorder_traverse(node->left,callback)) return 0; 257 | if(!callback(node)) return 0; 258 | if(!config_btree_inorder_traverse(node->right,callback)) return 0; 259 | return 1; 260 | } 261 | 262 | int config_btree_inorder_save_traverse(CONFIG_BTREE_NODE* node,FILE* fp,CONFIG_BTREE_SAVE_TRAVERSE_CB callback){ 263 | if(node==NULL) return 1; 264 | if(!config_btree_inorder_save_traverse(node->left,fp,callback)) return 0; 265 | if(!callback(fp,node)) return 0; 266 | if(!config_btree_inorder_save_traverse(node->right,fp,callback)) return 0; 267 | return 1; 268 | } 269 | 270 | int config_btree_preorder_traverse(CONFIG_BTREE_NODE* node,CONFIG_BTREE_TRAVERSE_CB callback){ 271 | if(node==NULL) return 1; 272 | if(!callback(node)) return 0; 273 | if(!config_btree_preorder_traverse(node->left,callback)) return 0; 274 | if(!config_btree_preorder_traverse(node->right,callback)) return 0; 275 | return 1; 276 | } 277 | 278 | int config_btree_postorder_traverse(CONFIG_BTREE_NODE* node,CONFIG_BTREE_TRAVERSE_CB callback){ 279 | if(node==NULL) return 1; 280 | if(!config_btree_postorder_traverse(node->left,callback)) return 0; 281 | if(!config_btree_postorder_traverse(node->right,callback)) return 0; 282 | if(!callback(node)) return 0; 283 | return 1; 284 | } 285 | 286 | int config_btree_destroy_section(CONFIG_BTREE_NODE* node){ 287 | CONFIG_BTREE* section; 288 | if(!node) return 0; 289 | memcpy(§ion,node->data,sizeof(void*)); 290 | config_btree_postorder_traverse(section->root,config_btree_free_node); 291 | free(section); 292 | } 293 | 294 | int config_btree_destroy(CONFIG_BTREE* config){ 295 | if(!config) return 0; 296 | config_btree_postorder_traverse(config->root,config_btree_destroy_section); 297 | free(config); 298 | return 1; 299 | } 300 | 301 | /** 302 | * ini文件解析函数跳转表,此方式在大型解析的实现中非常高效. 303 | */ 304 | 305 | int parser_default_action(PARSER* parser){ 306 | parser->pos++; 307 | parser->col++; 308 | return 1; 309 | } 310 | int parse_default_gbk_action(PARSER* parser){ 311 | parser->pos+=2; 312 | parser->col+=2; 313 | return 1; 314 | } 315 | int parser_on_section_start(PARSER* parser){ 316 | if(parser->status==PARSE_STATUS_COMMENT_LINE){} 317 | else if(parser->status==PARSE_STATUS_GET_KEY_OR_SECTION){ 318 | parser->start1=parser->pos+1; 319 | parser->status=PARSE_STATUS_GET_SECTION; 320 | } 321 | parser->pos++; 322 | parser->col++; 323 | return 1; 324 | } 325 | int parser_on_section_end(PARSER* parser){ 326 | char* p; 327 | int len,r; 328 | if(parser->status==PARSE_STATUS_COMMENT_LINE){} 329 | else if(parser->status==PARSE_STATUS_GET_SECTION){ 330 | memset(parser->section_name,0,MAX_SECTION_SIZE); 331 | p=mem_trim((char *)(parser->str+parser->start1),parser->pos-parser->start1,&len); 332 | if(p==NULL){//section段名不能为空 333 | return 0; 334 | } 335 | memcpy(parser->section_name,p,len); 336 | r=config_btree_insert_section(parser->config,parser->section_name); 337 | if(r==0) return 0;//添加section失败 338 | parser->status=PARSE_STATUS_GET_KEY_OR_SECTION; 339 | parser->start1=parser->pos+1; 340 | } 341 | parser->pos++; 342 | parser->col++; 343 | return 1; 344 | } 345 | int parser_on_value_start(PARSER* parser){ 346 | char* p; 347 | int len,r; 348 | if(parser->status==PARSE_STATUS_GET_KEY_OR_SECTION){ 349 | parser->status=PARSE_STATUS_GET_VALUE; 350 | parser->end1=parser->pos; 351 | parser->start2=parser->pos+1; 352 | } 353 | parser->pos++; 354 | parser->col++; 355 | return 1; 356 | } 357 | int parser_on_new_line(PARSER* parser){ 358 | char *k,*v; 359 | int klen,vlen; 360 | switch(parser->status){ 361 | case PARSE_STATUS_COMMENT_LINE: 362 | break; 363 | case PARSE_STATUS_GET_VALUE: 364 | k=mem_trim((char*)(parser->str+parser->start1),parser->end1-parser->start1,&klen); 365 | v=mem_trim((char*)(parser->str+parser->start2),parser->pos-parser->start2,&vlen); 366 | if(k==NULL) return 0; 367 | if(v==NULL){ 368 | v=""; 369 | vlen=0; 370 | } 371 | if(!config_btree_insert_section_node(parser->config,parser->section_name,k,klen,v,vlen)) return 0; 372 | break; 373 | case PARSE_STATUS_GET_KEY_OR_SECTION: 374 | break; 375 | default: 376 | return 0; 377 | } 378 | parser->start1=parser->pos+1; 379 | parser->status=PARSE_STATUS_GET_KEY_OR_SECTION; 380 | parser->pos++; 381 | parser->col=1; 382 | parser->row++; 383 | return 1; 384 | } 385 | int parser_on_comment(PARSER* parser){ 386 | if(parser->col==1){ 387 | parser->status=PARSE_STATUS_COMMENT_LINE; 388 | } 389 | parser->col++; 390 | parser->pos++; 391 | return 1; 392 | } 393 | /** 394 | * 接下来是ini配置管理的上层函数. 395 | */ 396 | 397 | INI_CONFIG* ini_config_create_from_string(unsigned char* str,int slen,int isGBK){ 398 | int r; 399 | PARSER parser; 400 | PARSER_JUMP_FUNC funcs[256]; 401 | INI_CONFIG* config=config_btree_create(); 402 | if(slen==0) slen=strlen((char*)str); 403 | strcpy(parser.section_name,"default"); 404 | parser.pos=0; 405 | parser.status=PARSE_STATUS_GET_KEY_OR_SECTION; 406 | parser.start1=0; 407 | parser.str=str; 408 | parser.slen=slen; 409 | parser.row=1; 410 | parser.col=1; 411 | parser.config=config; 412 | //初始化解析跳转表 413 | if(isGBK){ 414 | for(r=0;r<127;r++){ 415 | funcs[r]=parser_default_action; 416 | } 417 | for(r=128;r<256;r++){ 418 | funcs[r]=parse_default_gbk_action; 419 | } 420 | } 421 | else{ 422 | for(r=0;r<256;r++){ 423 | funcs[r]=parser_default_action; 424 | } 425 | } 426 | funcs['[']=parser_on_section_start; 427 | funcs[']']=parser_on_section_end; 428 | funcs['=']=parser_on_value_start; 429 | funcs['\n']=parser_on_new_line; 430 | funcs[';']=parser_on_comment; 431 | 432 | if(config!=NULL){ 433 | r=config_btree_insert_section(config,parser.section_name); 434 | if(!r){ 435 | config_btree_destroy(config); 436 | return NULL; 437 | } 438 | } 439 | while(parser.posdata)); 485 | } 486 | 487 | char* ini_config_get_string(INI_CONFIG* config,const char* section,const char* key,char* default_string){ 488 | CONFIG_BTREE_NODE* node; 489 | INI_CONFIG* sec; 490 | if(section==NULL) section="default"; 491 | sec=config_btree_get_section(config,section); 492 | if(sec==NULL) return default_string; 493 | node=config_btree_find_node(sec,key); 494 | if(node==NULL) return default_string; 495 | return (char*)node->data; 496 | } 497 | 498 | int ini_config_set_string(INI_CONFIG* config,const char* section,const char* key,int key_len,const char* value,int value_len){ 499 | CONFIG_BTREE* sect; 500 | CONFIG_BTREE* node; 501 | int r; 502 | if(section==NULL) section="default"; 503 | sect=config_btree_get_section(config,section); 504 | if(sect==NULL){ 505 | r=config_btree_insert_section(config,section); 506 | if(!r) return 0; 507 | sect=config_btree_get_section(config,section); 508 | } 509 | return config_btree_insert_node(sect,key,key_len,(void*)value,value_len); 510 | } 511 | 512 | int ini_config_set_int(INI_CONFIG* config,const char* section,const char* key,int key_len,int value){ 513 | char number[32]; 514 | int len=sprintf(number,"%d",value); 515 | return ini_config_set_string(config,section,key,key_len,number,len); 516 | } 517 | 518 | int ini_config_save_traverse_value(FILE* fp,CONFIG_BTREE_NODE* node){ 519 | fprintf(fp,"%s=%s\n",node->key,(char*)node->data); 520 | return 1; 521 | } 522 | int ini_config_save_traverse_section(FILE* fp,CONFIG_BTREE_NODE* node){ 523 | CONFIG_BTREE* section; 524 | memcpy(§ion,node->data,sizeof(void*)); 525 | fprintf(fp,"[%s]\n",node->key); 526 | config_btree_inorder_save_traverse(section->root,fp,ini_config_save_traverse_value); 527 | return 1; 528 | } 529 | int ini_config_save(INI_CONFIG* config,const char* filename){ 530 | FILE* fp=fopen(filename,"w"); 531 | if(fp==NULL) return 0; 532 | config_btree_inorder_save_traverse(config->root,fp,ini_config_save_traverse_section); 533 | fclose(fp); 534 | return 1; 535 | } 536 | int ini_config_print(INI_CONFIG* config,FILE* fp){ 537 | if(fp==NULL) return 0; 538 | config_btree_inorder_save_traverse(config->root,fp,ini_config_save_traverse_section); 539 | return 1; 540 | } 541 | --------------------------------------------------------------------------------