Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

597 lines
15 KiB

  1. //=========== Copyright Valve Corporation, All rights reserved. ===============//
  2. //
  3. // Purpose:
  4. //=============================================================================//
  5. #ifndef PROTOBUFPOOL_H
  6. #define PROTOBUFPOOL_H
  7. #ifdef _WIN32
  8. #pragma once
  9. #endif
  10. #include "tier0/tslist.h"
  11. #include "tier1/fmtstr.h"
  12. #include "tier1/utlstring.h"
  13. #include "tier1/utlvector.h"
  14. #if !defined( SOURCE2_PANORAMA )
  15. #include "tier1/tsmempool.h"
  16. #include "misc.h"
  17. #endif
  18. #include "tier0/vprof.h"
  19. #include "protobuf-2.3.0/src/google/protobuf/message_lite.h"
  20. namespace panorama
  21. {
  22. #define PBMEM_POOL_LOW_TARGET 256
  23. #define PBMEM_POOL_HIGH_TARGET 512
  24. class IProtoBufMsgMemoryPool
  25. {
  26. public:
  27. virtual ~IProtoBufMsgMemoryPool() {}
  28. // Methods that need to be exposed out to examine memory
  29. virtual uint32 GetEstimatedSize() = 0;
  30. virtual uint32 GetCount() = 0;
  31. virtual CUtlString GetName() = 0;
  32. virtual uint32 GetAllocHitCount() = 0;
  33. virtual uint32 GetAllocMissCount() = 0;
  34. virtual void Free( void *pObjectVoid ) = 0;
  35. #ifdef DBGFLAG_VALIDATE
  36. virtual void Validate( CValidator &validator, const char *pchName ) = 0;
  37. #endif
  38. };
  39. #if defined( SOURCE2_PANORAMA )
  40. #include <tier0/memdbgon.h>
  41. #endif
  42. //-----------------------------------------------------------------------------
  43. // CProtoBufMsgMemoryPool - Implementation for allocation pools for protobufmsgs.
  44. // We create one of these per protobuf msg type, created on first construction of
  45. // an object of that type.
  46. //-----------------------------------------------------------------------------
  47. template< typename PB_OBJECT_TYPE >
  48. class CUIProtoBufMsgMemoryPool : public IProtoBufMsgMemoryPool
  49. {
  50. public:
  51. typedef typename CTSList< PB_OBJECT_TYPE * >::Node_t Node;
  52. CUIProtoBufMsgMemoryPool( uint32 unTargetLow, uint32 unTargetHigh )
  53. {
  54. m_unTargetCountLow = unTargetLow;
  55. m_unTargetCountHigh = unTargetHigh;
  56. m_unEstimatedSize = 0;
  57. m_unAllocHitCounter = 0;
  58. m_unAllocMissCounter = 0;
  59. m_pTListFreeObjects = new CTSList< PB_OBJECT_TYPE * >();
  60. }
  61. ~CUIProtoBufMsgMemoryPool()
  62. {
  63. Node *pObject = m_pTListFreeObjects->Pop();
  64. while ( pObject )
  65. {
  66. Destruct( pObject->elem );
  67. #if defined( SOURCE2_PANORAMA )
  68. free( pObject->elem );
  69. #else
  70. FreePv( pObject->elem );
  71. #endif
  72. delete pObject;
  73. pObject = m_pTListFreeObjects->Pop();
  74. }
  75. m_pTListFreeObjects->Purge();
  76. delete m_pTListFreeObjects;
  77. }
  78. CUtlString GetName()
  79. {
  80. return PB_OBJECT_TYPE::default_instance().GetTypeName().c_str();
  81. }
  82. uint32 GetEstimatedSize()
  83. {
  84. return m_pTListFreeObjects->Count()*m_unEstimatedSize;
  85. }
  86. uint32 GetCount()
  87. {
  88. return m_pTListFreeObjects->Count();
  89. }
  90. uint32 GetAllocHitCount()
  91. {
  92. return m_unAllocHitCounter;
  93. }
  94. uint32 GetAllocMissCount()
  95. {
  96. return m_unAllocMissCounter;
  97. }
  98. Node * AllocProtoBuf()
  99. {
  100. Node *pObject = m_pTListFreeObjects->Pop();
  101. if ( !pObject )
  102. {
  103. ++m_unAllocMissCounter;
  104. pObject = new Node;
  105. #if defined( SOURCE2_PANORAMA )
  106. pObject->elem = (PB_OBJECT_TYPE *)malloc( sizeof( PB_OBJECT_TYPE ) );
  107. #else
  108. pObject->elem = (PB_OBJECT_TYPE *)PvAllocNoLeakTracking( sizeof( PB_OBJECT_TYPE ) );
  109. #endif
  110. Construct( pObject->elem );
  111. }
  112. else
  113. {
  114. ++m_unAllocHitCounter;
  115. bool bFreeAnother = false;
  116. uint32 unCount = m_pTListFreeObjects->Count();
  117. // We'll free an extra cached msg every alloc if we are over the higher limit, and every 6th if we
  118. // are over the lower limit. This allows some elasticity to peaks in demand.
  119. if ( unCount > m_unTargetCountHigh )
  120. bFreeAnother = true;
  121. else if ( unCount > m_unTargetCountLow && m_unAllocHitCounter % 6 == 0 )
  122. bFreeAnother = true;
  123. if ( bFreeAnother )
  124. {
  125. // Pop an extra item, so we can get down to target count over time
  126. Node *pThrowAway = m_pTListFreeObjects->Pop();
  127. if ( pThrowAway )
  128. {
  129. Destruct( pThrowAway->elem );
  130. #if defined( SOURCE2_PANORAMA )
  131. free( pThrowAway->elem );
  132. #else
  133. FreePv( pThrowAway->elem );
  134. #endif
  135. delete pThrowAway;
  136. }
  137. }
  138. }
  139. return pObject;
  140. }
  141. inline void Free( void *pObjectVoid )
  142. {
  143. Free( (Node *)pObjectVoid );
  144. }
  145. void Free( Node *pObject )
  146. {
  147. if ( m_unFreeCounter++ % 2000 == 0 || m_unEstimatedSize == 0 )
  148. {
  149. m_unEstimatedSize = pObject->elem->SpaceUsed();
  150. }
  151. if ( m_unTargetCountHigh > 0 )
  152. {
  153. // We always cache for re-use on free, though we may throw out later on alloc to shrink the pool
  154. pObject->elem->Clear();
  155. m_pTListFreeObjects->Push( pObject );
  156. }
  157. else
  158. {
  159. Destruct( pObject->elem );
  160. #if defined( SOURCE2_PANORAMA )
  161. free( pObject->elem );
  162. #else
  163. FreePv( pObject->elem );
  164. #endif
  165. delete pObject;
  166. }
  167. }
  168. #ifdef DBGFLAG_VALIDATE
  169. void Validate( CValidator &validator, const char *pchName )
  170. {
  171. m_pTListFreeObjects->Validate( validator, "m_pTListFreeObjects" );
  172. validator.ClaimMemory_Aligned( m_pTListFreeObjects );
  173. }
  174. #endif // DBGFLAG_VALIDATE
  175. private:
  176. CTSList< PB_OBJECT_TYPE * > *m_pTListFreeObjects;
  177. // Not critical for these to be "right" so they don't need to be thread safe
  178. uint32 m_unEstimatedSize;
  179. uint32 m_unFreeCounter;
  180. // These counters are important to get correct, so interlocked in case of allocating on threads
  181. CInterlockedInt m_unAllocHitCounter;
  182. CInterlockedInt m_unAllocMissCounter;
  183. // Only set at construction, so not needed to be thread safe
  184. uint32 m_unTargetCountLow;
  185. uint32 m_unTargetCountHigh;
  186. };
  187. #if defined( SOURCE2_PANORAMA )
  188. #include <tier0/memdbgoff.h>
  189. #endif
  190. //-----------------------------------------------------------------------------
  191. // Purpose: Ref count wrapper around protobuf message objects
  192. //-----------------------------------------------------------------------------
  193. class CMsgLiteRefCount
  194. {
  195. public:
  196. CMsgLiteRefCount()
  197. {
  198. m_pMemoryPool = NULL;
  199. m_pTSListNode = NULL;
  200. m_cRef = 1;
  201. }
  202. // Has to be public, so we can use in memory pool, should always already be at zero ref count though
  203. ~CMsgLiteRefCount() { DbgAssert( 0 == m_cRef ); }
  204. inline int AddRef()
  205. {
  206. return ThreadInterlockedIncrement( &m_cRef );
  207. }
  208. int Release();
  209. inline google::protobuf::MessageLite *AccessMsg()
  210. {
  211. return (google::protobuf::MessageLite *)(((CTSList<void *>::Node_t *)m_pTSListNode)->elem);
  212. }
  213. IProtoBufMsgMemoryPool *m_pMemoryPool;
  214. void *m_pTSListNode;
  215. void *m_pSelfNode;
  216. #ifdef DBGFLAG_VALIDATE
  217. virtual void Validate( CValidator &validator, const tchar *pchName )
  218. {
  219. VALIDATE_SCOPE();
  220. if ( m_cRef != 0 )
  221. {
  222. CTSList<void *>::Node_t *pNode = (CTSList<void *>::Node_t *)m_pSelfNode;
  223. if ( pNode )
  224. {
  225. void *pvMem = MemAlloc_Unalign( pNode );
  226. if ( !validator.IsClaimed( pvMem ) )
  227. validator.ClaimMemory_Aligned( pNode );
  228. if ( !validator.IsClaimed( pNode->elem ) )
  229. validator.ClaimMemory( pNode->elem );
  230. }
  231. pNode = (CTSList<void *>::Node_t *)m_pTSListNode;
  232. if ( pNode )
  233. {
  234. void *pvMem = MemAlloc_Unalign( pNode );
  235. if ( !validator.IsClaimed( pvMem ) )
  236. validator.ClaimMemory_Aligned( pNode );
  237. }
  238. }
  239. }
  240. #endif
  241. private:
  242. friend class CUIProtoBufMsgMemoryPoolMgr;
  243. volatile int32 m_cRef;
  244. };
  245. //-----------------------------------------------------------------------------
  246. // CProtoBufMsgMemoryPoolMgr - Manages all the message pools for render messages proto buf objects.
  247. // Should have one global singleton instance of this which tracks all the pools
  248. // for individual message types.
  249. //-----------------------------------------------------------------------------
  250. class CUIProtoBufMsgMemoryPoolMgr
  251. {
  252. public:
  253. CUIProtoBufMsgMemoryPoolMgr()
  254. {
  255. m_pTSListMsgLiteRefCount = new CTSList<CMsgLiteRefCount*>;
  256. }
  257. ~CUIProtoBufMsgMemoryPoolMgr()
  258. {
  259. CTSList<CMsgLiteRefCount*>::Node_t *pNode = m_pTSListMsgLiteRefCount->Pop();
  260. while ( pNode )
  261. {
  262. delete pNode->elem;
  263. delete pNode;
  264. pNode = m_pTSListMsgLiteRefCount->Pop();
  265. }
  266. FOR_EACH_VEC( m_vecMsgPools, i )
  267. {
  268. delete m_vecMsgPools[i];
  269. }
  270. m_vecMsgPools.RemoveAll();
  271. }
  272. CTSList<CMsgLiteRefCount*>::Node_t * AllocMsgLiteRef()
  273. {
  274. CTSList<CMsgLiteRefCount*>::Node_t *pNode = m_pTSListMsgLiteRefCount->Pop();
  275. if ( !pNode )
  276. {
  277. pNode = new CTSList<CMsgLiteRefCount*>::Node_t;
  278. pNode->elem = new CMsgLiteRefCount();
  279. return pNode;
  280. }
  281. else
  282. {
  283. DbgAssert( pNode->elem->m_cRef == 0 );
  284. pNode->elem->m_cRef = 1;
  285. }
  286. return pNode;
  287. }
  288. void FreeMsgLiteRef( CTSList<CMsgLiteRefCount*>::Node_t * pRef )
  289. {
  290. #if defined(_DEBUG) && !defined(NO_MALLOC_OVERRIDE) && !defined( SOURCE2_PANORAMA )
  291. Assert( g_pMemAllocSteam->IsValid( pRef->elem ) );
  292. #endif
  293. m_pTSListMsgLiteRefCount->Push( pRef );
  294. }
  295. void RegisterPool( IProtoBufMsgMemoryPool * pPool )
  296. {
  297. m_vecMsgPools.AddToTail( pPool );
  298. }
  299. void DumpPoolInfo()
  300. {
  301. uint32 unTotalSize = 0;
  302. Msg( "CRenderMsgMemoryPoolMgr:\n" );
  303. Msg( " PoolName Count Est. Size Hit Rate\n" );
  304. Msg( " ----------------------------------------- ----------- ----------- -----------\n" );
  305. FOR_EACH_VEC( m_vecMsgPools, i )
  306. {
  307. uint32 unHitCount = m_vecMsgPools[i]->GetAllocHitCount();
  308. uint32 unMissCount = m_vecMsgPools[i]->GetAllocMissCount();
  309. float flHitRate = 0.0f;
  310. if ( unHitCount > 0 || unMissCount > 0 )
  311. {
  312. flHitRate = (float)unHitCount / (float)(unHitCount+unMissCount);
  313. flHitRate *= 100.0f;
  314. }
  315. uint32 unEstimate = m_vecMsgPools[i]->GetEstimatedSize();
  316. Msg( "%43s%12d%12s%12s\n", m_vecMsgPools[i]->GetName().String(), m_vecMsgPools[i]->GetCount(), V_pretifymem( (float)unEstimate, 2, true ), CFmtStr( "%f%%", flHitRate ).Access() );
  317. unTotalSize += unEstimate;
  318. }
  319. Msg( " -----------------------------------------------------------------------------\n" );
  320. Msg( " Total: %s\n", V_pretifymem( (float)unTotalSize, 2, true ) );
  321. Msg( " -----------------------------------------------------------------------------\n" );
  322. Msg( "TSList for CMsgLiteRef size: %s\n", V_pretifymem( (float)m_pTSListMsgLiteRefCount->Count(), 2, true ) );
  323. }
  324. #ifdef DBGFLAG_VALIDATE
  325. void Validate( CValidator &validator, const char *pchName )
  326. {
  327. ValidateObj( m_vecMsgPools );
  328. FOR_EACH_VEC( m_vecMsgPools, i )
  329. {
  330. ValidatePtr( m_vecMsgPools[i] );
  331. }
  332. validator.ClaimMemory_Aligned( m_pTSListMsgLiteRefCount );
  333. m_pTSListMsgLiteRefCount->Validate( validator, "m_pTSListMsgLiteRefCount" );
  334. CUtlVector< CTSList<CMsgLiteRefCount *>::Node_t * > vecTemp;
  335. CTSList<CMsgLiteRefCount *>::Node_t *pNode = m_pTSListMsgLiteRefCount->Pop();
  336. while ( pNode )
  337. {
  338. ValidatePtrIfNeeded( pNode->elem );
  339. vecTemp.AddToTail( pNode );
  340. pNode = m_pTSListMsgLiteRefCount->Pop();
  341. }
  342. FOR_EACH_VEC( vecTemp, i )
  343. {
  344. m_pTSListMsgLiteRefCount->Push( vecTemp[i] );
  345. }
  346. }
  347. #endif // DBGFLAG_VALIDATE
  348. private:
  349. CUtlVector< IProtoBufMsgMemoryPool * > m_vecMsgPools;
  350. CTSList<CMsgLiteRefCount *> *m_pTSListMsgLiteRefCount;
  351. };
  352. // Interface that rendermsgs of all types implement
  353. class IUIProtoBufMsg
  354. {
  355. public:
  356. virtual ~IUIProtoBufMsg() {}
  357. virtual void SerializeInProc( CUtlBuffer *pBuffer ) const = 0;
  358. };
  359. //-----------------------------------------------------------------------------
  360. // Purpose: Base class for protobuf objects
  361. //-----------------------------------------------------------------------------
  362. template< typename PB_OBJECT_TYPE >
  363. class CUIProtoBufMsg : public IUIProtoBufMsg
  364. {
  365. private:
  366. static bool s_bRegisteredWithMemoryPoolMgr;
  367. static CThreadMutex s_Mutex;
  368. static CUIProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *s_pMemoryPool;
  369. public:
  370. typedef typename CTSList<PB_OBJECT_TYPE *>::Node_t Node;
  371. // Called on construction of each object of this type, but only does work
  372. // once to setup memory pools for the class type.
  373. static void OneTimeInit()
  374. {
  375. // If we haven't done registration do so now
  376. if ( !s_bRegisteredWithMemoryPoolMgr )
  377. {
  378. // Get the lock and make sure we still haven't
  379. s_Mutex.Lock();
  380. if ( !s_bRegisteredWithMemoryPoolMgr )
  381. {
  382. s_pMemoryPool = new CUIProtoBufMsgMemoryPool< PB_OBJECT_TYPE >( PBMEM_POOL_LOW_TARGET, PBMEM_POOL_HIGH_TARGET );
  383. UIEngine()->MsgMemoryPoolMgr()->RegisterPool( s_pMemoryPool );
  384. s_bRegisteredWithMemoryPoolMgr = true;
  385. }
  386. s_Mutex.Unlock();
  387. }
  388. }
  389. CUIProtoBufMsg()
  390. {
  391. OneTimeInit();
  392. CTSList<CMsgLiteRefCount *>::Node_t *pRefCountNode = UIEngine()->MsgMemoryPoolMgr()->AllocMsgLiteRef();
  393. m_pMsgRefCount = pRefCountNode->elem;
  394. m_pMsgRefCount->m_pSelfNode = pRefCountNode;
  395. m_pMsgRefCount->m_pMemoryPool = s_pMemoryPool;
  396. Node *pNode = s_pMemoryPool->AllocProtoBuf();
  397. m_pMsgRefCount->m_pTSListNode = pNode;
  398. m_bIsValid = true;
  399. }
  400. // Expose memory pool for direct allocation of underlying PB msg objects
  401. static Node *AllocProtoBufMsgObject()
  402. {
  403. OneTimeInit();
  404. return s_pMemoryPool->AllocProtoBuf();
  405. }
  406. // Expose memory pool for direct allocation of underlying PB msg objects
  407. static void FreeProtoBufMsgObject( Node *pMsg )
  408. {
  409. s_pMemoryPool->Free( pMsg );
  410. }
  411. // Construct and deserialize in one
  412. CUIProtoBufMsg( CUtlBuffer *pBuffer )
  413. {
  414. OneTimeInit();
  415. m_pMsgRefCount = NULL;
  416. m_bIsValid = BDeserializeInProc( pBuffer );
  417. }
  418. // Destructor
  419. virtual ~CUIProtoBufMsg()
  420. {
  421. CleanupAllocations();
  422. }
  423. bool BIsValid() { return m_bIsValid; }
  424. inline void SerializeInProc( CUtlBuffer *pBuffer ) const
  425. {
  426. // Ensure enough for type, size, and serialized data
  427. pBuffer->EnsureCapacity( pBuffer->TellPut() + sizeof(uint32) + sizeof( uint64 ) );
  428. m_pMsgRefCount->AddRef();
  429. pBuffer->PutUnsignedInt( (int)m_eCmd );
  430. pBuffer->PutUnsignedInt64( (uint64)m_pMsgRefCount );
  431. }
  432. inline bool BDeserializeInProc( CUtlBuffer *pBuffer )
  433. {
  434. if ( pBuffer->GetBytesRemaining() < (int)sizeof(uint64) )
  435. return false;
  436. uint64 ulPtr = pBuffer->GetUnsignedInt64();
  437. if ( ulPtr == 0 )
  438. return false;
  439. CleanupAllocations();
  440. m_pMsgRefCount = (CMsgLiteRefCount*)ulPtr;
  441. m_pMsgRefCount->AddRef();
  442. return true;
  443. }
  444. void SerializeCrossProc( CUtlBuffer *pBuffer ) const
  445. {
  446. VPROF_BUDGET( "CUIProtoBufMsg::SerializeCrossProc", VPROF_BUDGETGROUP_TENFOOT );
  447. uint32 unSize = m_pMsgRefCount->AccessMsg()->ByteSize();
  448. // Ensure enough for type, size, and serialized data
  449. pBuffer->EnsureCapacity( pBuffer->TellPut() + sizeof(uint32) * 3 + unSize ); // bugbug cboyd - drop to * 2 whenpassthrough is removed below
  450. pBuffer->PutUnsignedInt( (int)m_eCmd );
  451. pBuffer->PutUnsignedInt( unSize );
  452. if ( unSize == 0 )
  453. return;
  454. uint8 *pBody = (uint8*)pBuffer->Base()+pBuffer->TellPut();
  455. m_pMsgRefCount->AccessMsg()->SerializeWithCachedSizesToArray( pBody );
  456. pBuffer->SeekPut( CUtlBuffer::SEEK_CURRENT, unSize );
  457. }
  458. bool BDeserializeCrossProc( CUtlBuffer *pBuffer )
  459. {
  460. VPROF_BUDGET( "CUIProtoBufMsg::BDeserialize", VPROF_BUDGETGROUP_TENFOOT );
  461. if ( pBuffer->GetBytesRemaining() < (int)sizeof(uint32) )
  462. return false;
  463. uint32 unSize = pBuffer->GetUnsignedInt();
  464. if ( unSize == 0 )
  465. return true;
  466. if ( pBuffer->GetBytesRemaining() < (int)unSize )
  467. return false;
  468. bool bSucccess = m_pMsgRefCount->AccessMsg()->ParseFromArray( (uint8*)pBuffer->Base()+pBuffer->TellGet(), unSize );
  469. pBuffer->SeekGet( CUtlBuffer::SEEK_CURRENT, unSize );
  470. return bSucccess;
  471. }
  472. // Accessors
  473. PB_OBJECT_TYPE &Body() { return *((PB_OBJECT_TYPE*)(m_pMsgRefCount->AccessMsg())); }
  474. const PB_OBJECT_TYPE &BodyConst() const { return *((const PB_OBJECT_TYPE*)(m_pMsgRefCount->AccessMsg())); }
  475. protected:
  476. CMsgLiteRefCount *m_pMsgRefCount;
  477. int m_eCmd;
  478. bool m_bIsValid;
  479. private:
  480. void CleanupAllocations()
  481. {
  482. SAFE_RELEASE( m_pMsgRefCount );
  483. }
  484. };
  485. // Statics
  486. template< typename PB_OBJECT_TYPE > bool CUIProtoBufMsg< PB_OBJECT_TYPE>::s_bRegisteredWithMemoryPoolMgr = false;
  487. template< typename PB_OBJECT_TYPE > CThreadMutex CUIProtoBufMsg< PB_OBJECT_TYPE>::s_Mutex;
  488. template< typename PB_OBJECT_TYPE > CUIProtoBufMsgMemoryPool< PB_OBJECT_TYPE > *CUIProtoBufMsg< PB_OBJECT_TYPE>::s_pMemoryPool = NULL;
  489. } // namespace panorama
  490. #endif //PROTOBUFPOOL_H