From 9ee21ecf904f9d7e34f2933b2d342457e9eeb631 Mon Sep 17 00:00:00 2001 From: nillerusr Date: Sun, 5 Jun 2022 01:44:42 +0300 Subject: [PATCH] amd64: fix multithread, fix vgui, fix physmodels --- common/GameUI/scriptobject.cpp | 3 +- datacache/mdlcache.cpp | 45 +- engine/cmodel_bsp.cpp | 2 +- engine/console.cpp | 2 +- engine/download.cpp | 8 +- engine/hltvdemo.cpp | 1 + engine/r_decal.cpp | 14 +- engine/spatialpartition.cpp | 6 +- engine/sys_dll.cpp | 2 +- engine/tmessage.cpp | 5 +- engine/vengineserver_impl.cpp | 1 + filesystem/basefilesystem.cpp | 2 +- game/client/detailobjectsystem.cpp | 25 +- game/server/basecombatcharacter.cpp | 15 +- game/server/basecombatcharacter.h | 12 +- game/server/hl2/npc_barnacle.cpp | 2 +- game/server/info_camera_link.cpp | 4 +- game/server/nav_mesh.h | 5 +- game/server/tactical_mission.cpp | 2 + game/shared/collisionproperty.cpp | 24 +- game/shared/hl2/hl2_gamerules.cpp | 2 +- game/shared/physics_saverestore.cpp | 2 +- game/shared/ragdoll_shared.cpp | 2 +- game/shared/saverestore.cpp | 29 +- game/shared/saverestore_utlmap.h | 4 +- materialsystem/cmaterialsystem.cpp | 11 +- materialsystem/cmaterialsystem.h | 6 +- materialsystem/ctexture.cpp | 2 +- materialsystem/imaterialsysteminternal.h | 2 +- materialsystem/texturemanager.cpp | 4 +- public/XZip.cpp | 2 +- public/builddisp.cpp | 6 +- public/datamap.h | 17 +- public/dt_send.cpp | 2 +- public/optimize.h | 2 +- public/phyfile.h | 3 +- public/studio.h | 2 +- public/tier0/platform.h | 2 +- public/tier0/threadtools.h | 1667 +++++++++---- public/tier0/threadtools.inl | 653 +++++ public/tier0/tslist.h | 171 +- public/tier1/stringpool.h | 442 +++- public/tier1/utlbuffer.h | 570 ++++- public/tier1/utllinkedlist.h | 70 +- public/tier1/utlmemory.h | 136 +- public/tier1/utlsymbol.h | 162 +- public/vgui_controls/BuildGroup.h | 1 - public/vstdlib/jobthread.h | 2 +- studiorender/r_studiodecal.cpp | 2 +- tier0/cpu.cpp | 50 +- tier0/dbg.cpp | 3 +- tier0/threadtools.cpp | 2873 ++++++++++++++-------- tier1/KeyValues.cpp | 4 +- tier1/stringpool.cpp | 228 +- tier1/utlbuffer.cpp | 481 ++-- tier1/utlsymbol.cpp | 253 +- vgui2/vgui_controls/BuildGroup.cpp | 15 +- vgui2/vgui_controls/Panel.cpp | 16 +- vphysics/physics_virtualmesh.cpp | 2 +- vphysics/vphysics_saverestore.cpp | 17 +- vstdlib/coroutine.cpp | 4 +- vstdlib/jobthread.cpp | 32 +- wscript | 13 +- 63 files changed, 5679 insertions(+), 2468 deletions(-) create mode 100644 public/tier0/threadtools.inl diff --git a/common/GameUI/scriptobject.cpp b/common/GameUI/scriptobject.cpp index e83f5fd5..f810fb8c 100644 --- a/common/GameUI/scriptobject.cpp +++ b/common/GameUI/scriptobject.cpp @@ -15,6 +15,7 @@ #include "filesystem.h" #include "tier1/convar.h" #include "cdll_int.h" +#include "vcrmode.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -1150,4 +1151,4 @@ void CInfoDescription::WriteFileHeader( FileHandle_t fp ) g_pFullFileSystem->FPrintf( fp, "//\r\n//\r\n// Cvar\t-\tSetting\r\n\r\n" ); } -//----------------------------------------------------------------------------- \ No newline at end of file +//----------------------------------------------------------------------------- diff --git a/datacache/mdlcache.cpp b/datacache/mdlcache.cpp index eb676a95..11e88ca5 100644 --- a/datacache/mdlcache.cpp +++ b/datacache/mdlcache.cpp @@ -1973,39 +1973,18 @@ studiohdr_t *CMDLCache::UnserializeMDL( MDLHandle_t handle, void *pData, int nDa // critical! store a back link to our data // this is fetched when re-establishing dependent cached data (vtx/vvd) -#ifndef PLATFORM_64BITS - pStudioHdrIn->SetVirtualModel( MDLHandleToVirtual( handle ) ); -#endif + pStudioHdrIn->SetVirtualModel( MDLHandleToVirtual( handle ) ); MdlCacheMsg( "MDLCache: Alloc studiohdr %s\n", GetModelName( handle ) ); // allocate cache space MemAlloc_PushAllocDbgInfo( "Models:StudioHdr", 0); -#ifdef PLATFORM_64BITS - studiohdr_t *pHdr = (studiohdr_t *)AllocData( MDLCACHE_STUDIOHDR, pStudioHdrIn->length + sizeof(studiohdr_shim64_index) ); -#else studiohdr_t *pHdr = (studiohdr_t *)AllocData( MDLCACHE_STUDIOHDR, pStudioHdrIn->length ); -#endif MemAlloc_PopAllocDbgInfo(); if ( !pHdr ) return NULL; -#ifdef PLATFORM_64BITS - // MoeMod : fix shim64 index - studiohdr_shim64_index *pHdrIndex = (studiohdr_shim64_index *)(((byte *)pHdr)+ pStudioHdrIn->length); - pHdrIndex->virtualModel = nullptr; - pHdrIndex->animblockModel = nullptr; - pHdrIndex->pVertexBase = nullptr; - pHdrIndex->pIndexBase = nullptr; - pStudioHdrIn->index_ptr_virtualModel = (byte *)&pHdrIndex->virtualModel - (byte *)pHdr; - pStudioHdrIn->index_ptr_animblockModel = (byte *)&pHdrIndex->animblockModel - (byte *)pHdr; - pStudioHdrIn->index_ptr_pVertexBase = (byte *)&pHdrIndex->pVertexBase - (byte *)pHdr; - pStudioHdrIn->index_ptr_pIndexBase = (byte *)&pHdrIndex->pIndexBase - (byte *)pHdr; - pStudioHdrIn->SetVirtualModel( MDLHandleToVirtual( handle ) ); - CacheData( &m_MDLDict[handle]->m_MDLCache, pHdr, pStudioHdrIn->length + sizeof(studiohdr_shim64_index), GetModelName( handle ), MDLCACHE_STUDIOHDR, MakeCacheID( handle, MDLCACHE_STUDIOHDR) ); -#else CacheData( &m_MDLDict[handle]->m_MDLCache, pHdr, pStudioHdrIn->length, GetModelName( handle ), MDLCACHE_STUDIOHDR, MakeCacheID( handle, MDLCACHE_STUDIOHDR) ); -#endif if ( mod_lock_mdls_on_load.GetBool() ) { @@ -2103,27 +2082,7 @@ bool CMDLCache::ReadMDLFile( MDLHandle_t handle, const char *pMDLFileName, CUtlB // critical! store a back link to our data // this is fetched when re-establishing dependent cached data (vtx/vvd) -#if PLATFORM_64BITS - int length = buf.Size(); - { - studiohdr_shim64_index shim; - buf.Put( &shim, sizeof(shim) ); - } - studiohdr_shim64_index *pHdrIndex = (studiohdr_shim64_index *)(((byte *)buf.PeekGet())+ length); - pStudioHdr = (studiohdr_t*)buf.PeekGet(); - - pHdrIndex->virtualModel = nullptr; - pHdrIndex->animblockModel = nullptr; - pHdrIndex->pVertexBase = nullptr; - pHdrIndex->pIndexBase = nullptr; - pStudioHdr->index_ptr_virtualModel = (byte *)&pHdrIndex->virtualModel - (byte *)pStudioHdr; - pStudioHdr->index_ptr_animblockModel = (byte *)&pHdrIndex->animblockModel - (byte *)pStudioHdr; - pStudioHdr->index_ptr_pVertexBase = (byte *)&pHdrIndex->pVertexBase - (byte *)pStudioHdr; - pStudioHdr->index_ptr_pIndexBase = (byte *)&pHdrIndex->pIndexBase - (byte *)pStudioHdr; - pStudioHdr->SetVirtualModel( MDLHandleToVirtual( handle ) ); -#else - pStudioHdr->SetVirtualModel( MDLHandleToVirtual( handle ) ); -#endif + pStudioHdr->SetVirtualModel( MDLHandleToVirtual( handle ) ); // Make sure all dependent files are valid if ( !VerifyHeaders( pStudioHdr ) ) diff --git a/engine/cmodel_bsp.cpp b/engine/cmodel_bsp.cpp index cdecce69..5d4faed3 100644 --- a/engine/cmodel_bsp.cpp +++ b/engine/cmodel_bsp.cpp @@ -288,7 +288,7 @@ bool CollisionBSPData_Load( const char *pName, CCollisionBSPData *pBSPData ) CollisionBSPData_LoadPhysics( pBSPData ); COM_TimestampedLog( " CollisionBSPData_LoadDispInfo" ); - CollisionBSPData_LoadDispInfo( pBSPData ); + CollisionBSPData_LoadDispInfo( pBSPData ); return true; } diff --git a/engine/console.cpp b/engine/console.cpp index b68d68b2..95dce891 100644 --- a/engine/console.cpp +++ b/engine/console.cpp @@ -479,7 +479,7 @@ Handles cursor positioning, line wrapping, etc */ static bool g_fColorPrintf = false; static bool g_bInColorPrint = false; -extern CThreadLocalInt<> g_bInSpew; +extern CTHREADLOCALINT g_bInSpew; void Con_Printf( const char *fmt, ... ); diff --git a/engine/download.cpp b/engine/download.cpp index eb58261c..f09f47dc 100644 --- a/engine/download.cpp +++ b/engine/download.cpp @@ -924,14 +924,14 @@ void CDownloadManager::StartNewDownload() m_lastPercent = 0; // Start the thread - uintp threadID; + uintp threadID; VCRHook_CreateThread(NULL, 0, #ifdef POSIX (void *) #endif DownloadThread, m_activeRequest, 0, &threadID ); - ThreadDetach( ( ThreadHandle_t )threadID ); + ReleaseThreadHandle( ( ThreadHandle_t )threadID ); } else { @@ -1072,14 +1072,14 @@ class CDownloadSystem : public IDownloadSystem public: virtual uintp CreateDownloadThread( RequestContext_t *pContext ) { - uintp nThreadID; + uintp nThreadID; VCRHook_CreateThread(NULL, 0, #ifdef POSIX (void*) #endif DownloadThread, pContext, 0, (uintp *)&nThreadID ); - ThreadDetach( ( ThreadHandle_t )nThreadID ); + ReleaseThreadHandle( ( ThreadHandle_t )nThreadID ); return nThreadID; } }; diff --git a/engine/hltvdemo.cpp b/engine/hltvdemo.cpp index 5dc25b44..02dd0594 100644 --- a/engine/hltvdemo.cpp +++ b/engine/hltvdemo.cpp @@ -22,6 +22,7 @@ #include "host.h" #include "server.h" #include "networkstringtableclient.h" +#include "vcrmode.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" diff --git a/engine/r_decal.cpp b/engine/r_decal.cpp index 1c76f4d0..923e6ba9 100644 --- a/engine/r_decal.cpp +++ b/engine/r_decal.cpp @@ -2003,7 +2003,7 @@ void R_DrawDecalsAllImmediate_GatherDecals( IMatRenderContext *pRenderContext, i intp iHead = g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].Element( iBucket ).m_iHead; - intp iElement = iHead; + intp iElement = iHead; while ( iElement != g_aDecalSortPool.InvalidIndex() ) { decal_t *pDecal = g_aDecalSortPool.Element( iElement ); @@ -2154,11 +2154,11 @@ void R_DrawDecalsAllImmediate( IMatRenderContext *pRenderContext, int iGroup, in { if ( g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].Element( iBucket ).m_nCheckCount != nCheckCount ) continue; - + intp iHead = g_aDecalSortTrees[iSortTree].m_aDecalSortBuckets[iGroup][iTreeType].Element( iBucket ).m_iHead; - + int nCount; - intp iElement = iHead; + intp iElement = iHead; while ( iElement != g_aDecalSortPool.InvalidIndex() ) { decal_t *pDecal = g_aDecalSortPool.Element( iElement ); @@ -2330,7 +2330,7 @@ void R_DrawDecalsAll_GatherDecals( IMatRenderContext *pRenderContext, int iGroup if ( bucket.m_nCheckCount != nCheckCount ) continue; - intp iHead = bucket.m_iHead; + intp iHead = bucket.m_iHead; if ( !g_aDecalSortPool.IsValidIndex( iHead ) ) continue; @@ -2647,7 +2647,7 @@ void R_DrawDecalsAll( IMatRenderContext *pRenderContext, int iGroup, int iTreeTy if ( bucket.m_nCheckCount != nCheckCount ) continue; - int iHead = bucket.m_iHead; + intp iHead = bucket.m_iHead; if ( !g_aDecalSortPool.IsValidIndex( iHead ) ) continue; @@ -2666,7 +2666,7 @@ void R_DrawDecalsAll( IMatRenderContext *pRenderContext, int iGroup, int iTreeTy bool bBatchInit = true; int nCount; - int iElement = iHead; + intp iElement = iHead; while ( iElement != g_aDecalSortPool.InvalidIndex() ) { decal_t *pDecal = g_aDecalSortPool.Element( iElement ); diff --git a/engine/spatialpartition.cpp b/engine/spatialpartition.cpp index 0b6b0b0a..68a51a13 100644 --- a/engine/spatialpartition.cpp +++ b/engine/spatialpartition.cpp @@ -282,7 +282,7 @@ private: CVoxelHash* m_pVoxelHash; CLeafList m_aLeafList; // Pool - Linked list(multilist) of leaves per entity. int m_TreeId; - CThreadLocalPtr m_pVisits; + CTHREADLOCALPTR(CPartitionVisits) m_pVisits; CSpatialPartition * m_pOwner; CUtlVector m_AvailableVisitBits; unsigned short m_nNextVisitBit; @@ -1775,7 +1775,7 @@ void CVoxelTree::Shutdown( void ) //----------------------------------------------------------------------------- void CVoxelTree::InsertIntoTree( SpatialPartitionHandle_t hPartition, const Vector& mins, const Vector& maxs ) { - bool bWasReading = ( m_pVisits != NULL ); + bool bWasReading = ( m_pVisits != static_cast(nullptr) ); if ( bWasReading ) { // If we're recursing in this thread, need to release our read lock to allow ourselves to write @@ -1832,7 +1832,7 @@ void CVoxelTree::RemoveFromTree( SpatialPartitionHandle_t hPartition ) int nLevel = info.m_nLevel[GetTreeId()]; if ( nLevel >= 0 ) { - bool bWasReading = ( m_pVisits != NULL ); + bool bWasReading = ( m_pVisits != static_cast(nullptr) ); if ( bWasReading ) { // If we're recursing in this thread, need to release our read lock to allow ourselves to write diff --git a/engine/sys_dll.cpp b/engine/sys_dll.cpp index a7795f44..2d8b30e2 100644 --- a/engine/sys_dll.cpp +++ b/engine/sys_dll.cpp @@ -797,7 +797,7 @@ void Sys_ShutdownAuthentication( void ) //----------------------------------------------------------------------------- // Debug library spew output //----------------------------------------------------------------------------- -CThreadLocalInt<> g_bInSpew; +CTHREADLOCALINT g_bInSpew; #include "tier1/fmtstr.h" diff --git a/engine/tmessage.cpp b/engine/tmessage.cpp index 3188f166..72c65f22 100644 --- a/engine/tmessage.cpp +++ b/engine/tmessage.cpp @@ -367,7 +367,8 @@ void TextMessageParse( byte *pMemFile, int fileSize ) client_textmessage_t textMessages[ MAX_MESSAGES ]; - int i, nameHeapSize, textHeapSize, messageSize, nameOffset; + int i, nameHeapSize, textHeapSize, messageSize; + intp nameOffset; lastNamePos = 0; lineNumber = 0; @@ -633,4 +634,4 @@ client_textmessage_t *TextMessageGet( const char *pName ) } return NULL; -} \ No newline at end of file +} diff --git a/engine/vengineserver_impl.cpp b/engine/vengineserver_impl.cpp index c10bc806..37dc07c6 100644 --- a/engine/vengineserver_impl.cpp +++ b/engine/vengineserver_impl.cpp @@ -48,6 +48,7 @@ #include "replay_internal.h" #include "replayserver.h" #include "replay/iserverengine.h" +#include "vcrmode.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" diff --git a/filesystem/basefilesystem.cpp b/filesystem/basefilesystem.cpp index e1ed24db..8a371b17 100644 --- a/filesystem/basefilesystem.cpp +++ b/filesystem/basefilesystem.cpp @@ -1804,7 +1804,7 @@ const char *CBaseFileSystem::GetWritePath( const char *pFilename, const char *pa //----------------------------------------------------------------------------- // Reads/writes files to utlbuffers. Attempts alignment fixups for optimal read //----------------------------------------------------------------------------- -CThreadLocal g_pszReadFilename; +CTHREADLOCAL(char *) g_pszReadFilename; bool CBaseFileSystem::ReadToBuffer( FileHandle_t fp, CUtlBuffer &buf, int nMaxBytes, FSAllocFunc_t pfnAlloc ) { SetBufferSize( fp, 0 ); // TODO: what if it's a pack file? restore buffer size? diff --git a/game/client/detailobjectsystem.cpp b/game/client/detailobjectsystem.cpp index 3c033714..ca2a6351 100644 --- a/game/client/detailobjectsystem.cpp +++ b/game/client/detailobjectsystem.cpp @@ -2331,7 +2331,16 @@ void CDetailObjectSystem::RenderFastSprites( const Vector &viewOrigin, const Vec color[2] = pquad->m_RGBColor[0][2]; color[3] = pColorsCasted[MANTISSA_LSB_OFFSET]; - DetailPropSpriteDict_t *pDict = pquad->m_pSpriteDefs[0]; + DetailPropSpriteDict_t *pDict; +#ifdef PLATFORM_64BITS + if( nSubIdx == 1 ) + pDict = ((FastSpriteQuadBuildoutBufferNonSIMDView_t*)((intp)pquad+4))->m_pSpriteDefs[0]; + else if( nSubIdx == 3 ) + pDict = ((FastSpriteQuadBuildoutBufferNonSIMDView_t*)((intp)pquad-4))->m_pSpriteDefs[0]; + else +#endif + pDict = pquad->m_pSpriteDefs[0]; + meshBuilder.Position3f( pquad->m_flX0[0], pquad->m_flY0[0], pquad->m_flZ0[0] ); meshBuilder.Color4ubv( color ); @@ -2545,6 +2554,7 @@ void CDetailObjectSystem::RenderFastTranslucentDetailObjectsInLeaf( const Vector int nToDraw = MIN( nCount, nQuadsRemaining ); nCount -= nToDraw; nQuadsRemaining -= nToDraw; + while( nToDraw-- ) { // draw the sucker @@ -2553,17 +2563,28 @@ void CDetailObjectSystem::RenderFastTranslucentDetailObjectsInLeaf( const Vector FastSpriteQuadBuildoutBufferNonSIMDView_t const *pquad = pQuadBuffer+nSIMDIdx; + // voodoo - since everything is in 4s, offset structure pointer by a couple of floats to handle sub-index pquad = (FastSpriteQuadBuildoutBufferNonSIMDView_t const *) ( ( (intp) ( pquad ) )+ ( nSubIdx << 2 ) ); + uint8 const *pColorsCasted = reinterpret_cast ( pquad->m_Alpha ); + uint8 color[4]; color[0] = pquad->m_RGBColor[0][0]; color[1] = pquad->m_RGBColor[0][1]; color[2] = pquad->m_RGBColor[0][2]; color[3] = pColorsCasted[MANTISSA_LSB_OFFSET]; - DetailPropSpriteDict_t *pDict = pquad->m_pSpriteDefs[0]; + DetailPropSpriteDict_t *pDict; +#ifdef PLATFORM_64BITS + if( nSubIdx == 1 ) + pDict = ((FastSpriteQuadBuildoutBufferNonSIMDView_t*)((intp)pquad+4))->m_pSpriteDefs[0]; + else if( nSubIdx == 3 ) + pDict = ((FastSpriteQuadBuildoutBufferNonSIMDView_t*)((intp)pquad-4))->m_pSpriteDefs[0]; + else +#endif + pDict = pquad->m_pSpriteDefs[0]; meshBuilder.Position3f( pquad->m_flX0[0], pquad->m_flY0[0], pquad->m_flZ0[0] ); meshBuilder.Color4ubv( color ); diff --git a/game/server/basecombatcharacter.cpp b/game/server/basecombatcharacter.cpp index 9f6a8674..698ebe2c 100644 --- a/game/server/basecombatcharacter.cpp +++ b/game/server/basecombatcharacter.cpp @@ -731,7 +731,10 @@ CBaseCombatCharacter::CBaseCombatCharacter( void ) } // not standing on a nav area yet +#ifdef MEXT_BOT m_lastNavArea = NULL; +#endif + m_registeredNavTeam = TEAM_INVALID; for (int i = 0; i < MAX_WEAPONS; i++) @@ -3481,17 +3484,20 @@ void CBaseCombatCharacter::UpdateLastKnownArea( void ) //----------------------------------------------------------------------------- bool CBaseCombatCharacter::IsAreaTraversable( const CNavArea *area ) const { +#ifdef NEXT_BOT return area ? !area->IsBlocked( GetTeamNumber() ) : false; +#endif + return false; } - //----------------------------------------------------------------------------- // Purpose: Leaving the nav mesh //----------------------------------------------------------------------------- void CBaseCombatCharacter::ClearLastKnownArea( void ) { +#ifdef NEXT_BOT OnNavAreaChanged( NULL, m_lastNavArea ); - + if ( m_lastNavArea ) { m_lastNavArea->DecrementPlayerCount( m_registeredNavTeam, entindex() ); @@ -3499,21 +3505,22 @@ void CBaseCombatCharacter::ClearLastKnownArea( void ) m_lastNavArea = NULL; m_registeredNavTeam = TEAM_INVALID; } +#endif } - //----------------------------------------------------------------------------- // Purpose: Handling editor removing the area we're standing upon //----------------------------------------------------------------------------- void CBaseCombatCharacter::OnNavAreaRemoved( CNavArea *removedArea ) { +#ifdef NEXT_BOT if ( m_lastNavArea == removedArea ) { ClearLastKnownArea(); } +#endif } - //----------------------------------------------------------------------------- // Purpose: Changing team, maintain associated data //----------------------------------------------------------------------------- diff --git a/game/server/basecombatcharacter.h b/game/server/basecombatcharacter.h index 3e92332b..b6860fd7 100644 --- a/game/server/basecombatcharacter.h +++ b/game/server/basecombatcharacter.h @@ -400,11 +400,19 @@ public: void SetPreventWeaponPickup( bool bPrevent ) { m_bPreventWeaponPickup = bPrevent; } bool m_bPreventWeaponPickup; - virtual CNavArea *GetLastKnownArea( void ) const { return m_lastNavArea; } // return the last nav area the player occupied - NULL if unknown - virtual bool IsAreaTraversable( const CNavArea *area ) const; // return true if we can use the given area + virtual CNavArea *GetLastKnownArea( void ) const + { +#ifdef NEXT_BOT + return m_lastNavArea; +#else + return NULL; +#endif + } // return the last nav area the player occupied - NULL if unknown + virtual void ClearLastKnownArea( void ); virtual void UpdateLastKnownArea( void ); // invoke this to update our last known nav area (since there is no think method chained to CBaseCombatCharacter) virtual void OnNavAreaChanged( CNavArea *enteredArea, CNavArea *leftArea ) { } // invoked (by UpdateLastKnownArea) when we enter a new nav area (or it is reset to NULL) + virtual bool IsAreaTraversable( const CNavArea *area ) const; // return true if we can use the given area virtual void OnNavAreaRemoved( CNavArea *removedArea ); // ----------------------- diff --git a/game/server/hl2/npc_barnacle.cpp b/game/server/hl2/npc_barnacle.cpp index 1c195b90..9e824351 100644 --- a/game/server/hl2/npc_barnacle.cpp +++ b/game/server/hl2/npc_barnacle.cpp @@ -1968,7 +1968,7 @@ void CNPC_Barnacle::OnTongueTipUpdated() //----------------------------------------------------------------------------- void CNPC_Barnacle::UpdateTongue( void ) { - if ( m_hTongueTip == NULL ) + if ( m_hTongueTip == NULL || m_hTongueTip->m_pSpring == NULL ) return; // Set the spring's length to that of the tongue's extension diff --git a/game/server/info_camera_link.cpp b/game/server/info_camera_link.cpp index bdc8a8c8..8724008d 100644 --- a/game/server/info_camera_link.cpp +++ b/game/server/info_camera_link.cpp @@ -150,8 +150,8 @@ void PointCameraSetupVisibility( CBaseEntity *pPlayer, int area, unsigned char * pCameraEnt->SetActive( false ); } - int nNext; - for ( int i = g_InfoCameraLinkList.Head(); i != g_InfoCameraLinkList.InvalidIndex(); i = nNext ) + intp nNext; + for ( intp i = g_InfoCameraLinkList.Head(); i != g_InfoCameraLinkList.InvalidIndex(); i = nNext ) { nNext = g_InfoCameraLinkList.Next( i ); diff --git a/game/server/nav_mesh.h b/game/server/nav_mesh.h index cb757709..80920dce 100644 --- a/game/server/nav_mesh.h +++ b/game/server/nav_mesh.h @@ -73,7 +73,6 @@ public: bool operator()( CBaseCombatCharacter *actor ) { actor->OnNavAreaRemoved( m_deadArea ); - return true; } }; @@ -200,12 +199,12 @@ public: { #if PLATFORM_64BITS COMPILE_TIME_ASSERT( sizeof(CNavArea *) == 8 ); - int64 key[2] = { (int64)item.pAreas[0] + (int64)item.pAreas[1]->GetID(), (int64)item.pAreas[1] + (int64)item.pAreas[0]->GetID() }; + int64 key[2] = { (int64)(item.pAreas[0] + item.pAreas[1]->GetID()), (int64)(item.pAreas[1] + item.pAreas[0]->GetID()) }; return Hash16( key ); #else COMPILE_TIME_ASSERT( sizeof(CNavArea *) == 4 ); int key[2] = { (int)(item.pAreas[0] + item.pAreas[1]->GetID()), (int)(item.pAreas[1] + item.pAreas[0]->GetID()) }; - return Hash8( key ); + return Hash8( key ); #endif } }; diff --git a/game/server/tactical_mission.cpp b/game/server/tactical_mission.cpp index 6cac72c7..33da872d 100644 --- a/game/server/tactical_mission.cpp +++ b/game/server/tactical_mission.cpp @@ -45,7 +45,9 @@ class CShowZone : public IForEachNavArea public: virtual bool Inspect( const CNavArea *area ) { +#ifdef NEXT_BOT area->DrawFilled( 255, 255, 0, 255, 9999.9f ); +#endif return true; } }; diff --git a/game/shared/collisionproperty.cpp b/game/shared/collisionproperty.cpp index 461deb14..d2f1b6c3 100644 --- a/game/shared/collisionproperty.cpp +++ b/game/shared/collisionproperty.cpp @@ -50,20 +50,22 @@ public: virtual void OnPostQuery( SpatialPartitionListMask_t listMask ); void AddEntity( CBaseEntity *pEntity ); - + ~CDirtySpatialPartitionEntityList(); void LockPartitionForRead() { - if ( m_readLockCount == 0 ) + int nThreadId = g_nThreadID; + if ( m_nReadLockCount[nThreadId] == 0 ) { m_partitionMutex.LockForRead(); } - m_readLockCount++; + m_nReadLockCount[nThreadId]++; } void UnlockPartitionForRead() { - m_readLockCount--; - if ( m_readLockCount == 0 ) + int nThreadId = g_nThreadID; + m_nReadLockCount[nThreadId]--; + if ( m_nReadLockCount[nThreadId] == 0 ) { m_partitionMutex.UnlockRead(); } @@ -71,6 +73,8 @@ public: private: + int m_nReadLockCount[MAX_THREADS_SUPPORTED]; + CTSListWithFreeList m_DirtyEntities; CThreadSpinRWLock m_partitionMutex; uint32 m_partitionWriteId; @@ -106,7 +110,7 @@ void UpdateDirtySpatialPartitionEntities() CDirtySpatialPartitionEntityList::CDirtySpatialPartitionEntityList( char const *name ) : CAutoGameSystem( name ) { m_DirtyEntities.Purge(); - m_readLockCount = 0; + memset( m_nReadLockCount, 0, sizeof( m_nReadLockCount ) ); } //----------------------------------------------------------------------------- @@ -164,7 +168,9 @@ void CDirtySpatialPartitionEntityList::OnPreQuery( SpatialPartitionListMask_t li if ( !( listMask & validMask ) ) return; - if ( m_partitionWriteId != 0 && m_partitionWriteId == ThreadGetCurrentId() ) + int nThreadID = g_nThreadID; + + if ( m_partitionWriteId != 0 && m_partitionWriteId == nThreadID + 1 ) return; #ifdef CLIENT_DLL @@ -180,11 +186,11 @@ void CDirtySpatialPartitionEntityList::OnPreQuery( SpatialPartitionListMask_t li // or became dirty due to some other thread or callback. Updating them may cause corruption further up the // stack (e.g. partition iterator). Ignoring the state change should be safe since it happened after the // trace was requested or was unable to be resolved in a previous attempt (still dirty). - if ( m_DirtyEntities.Count() && !m_readLockCount ) + if ( m_DirtyEntities.Count() && !m_nReadLockCount[nThreadID] ) { CUtlVector< CBaseHandle > vecStillDirty; m_partitionMutex.LockForWrite(); - m_partitionWriteId = ThreadGetCurrentId(); + m_partitionWriteId = nThreadID + 1; CTSListWithFreeList::Node_t *pCurrent, *pNext; while ( ( pCurrent = m_DirtyEntities.Detach() ) != NULL ) { diff --git a/game/shared/hl2/hl2_gamerules.cpp b/game/shared/hl2/hl2_gamerules.cpp index bdd5abec..cf7dd629 100644 --- a/game/shared/hl2/hl2_gamerules.cpp +++ b/game/shared/hl2/hl2_gamerules.cpp @@ -1814,7 +1814,7 @@ CAmmoDef *GetAmmoDef() def.AddAmmoType("Thumper", DMG_SONIC, TRACER_NONE, 10, 10, 2, 0, 0 ); def.AddAmmoType("Gravity", DMG_CLUB, TRACER_NONE, 0, 0, 8, 0, 0 ); // def.AddAmmoType("Extinguisher", DMG_BURN, TRACER_NONE, 0, 0, 100, 0, 0 ); - def.AddAmmoType("Battery", DMG_CLUB, TRACER_NONE, NULL, NULL, NULL, 0, 0 ); + def.AddAmmoType("Battery", DMG_CLUB, TRACER_NONE, 0, 0, 0, 0, 0 ); def.AddAmmoType("GaussEnergy", DMG_SHOCK, TRACER_NONE, "sk_jeep_gauss_damage", "sk_jeep_gauss_damage", "sk_max_gauss_round", BULLET_IMPULSE(650, 8000), 0 ); // hit like a 10kg weight at 400 in/s def.AddAmmoType("CombineCannon", DMG_BULLET, TRACER_LINE, "sk_npc_dmg_gunship_to_plr", "sk_npc_dmg_gunship", NULL, 1.5 * 750 * 12, 0 ); // hit like a 1.5kg weight at 750 ft/s def.AddAmmoType("AirboatGun", DMG_AIRBOAT, TRACER_LINE, "sk_plr_dmg_airboat", "sk_npc_dmg_airboat", NULL, BULLET_IMPULSE(10, 600), 0 ); diff --git a/game/shared/physics_saverestore.cpp b/game/shared/physics_saverestore.cpp index 434be1f4..43cad8a4 100644 --- a/game/shared/physics_saverestore.cpp +++ b/game/shared/physics_saverestore.cpp @@ -43,7 +43,7 @@ struct PhysBlockHeader_t BEGIN_SIMPLE_DATADESC( PhysBlockHeader_t ) DEFINE_FIELD( nSaved, FIELD_INTEGER ), // NOTE: We want to save the actual address here for remapping, so use an integer - DEFINE_FIELD( pWorldObject, FIELD_INTEGER ), + DEFINE_FIELD( pWorldObject, FIELD_POINTER ), END_DATADESC() #if defined(_STATIC_LINKED) && defined(CLIENT_DLL) diff --git a/game/shared/ragdoll_shared.cpp b/game/shared/ragdoll_shared.cpp index e77e9a75..b43109a4 100644 --- a/game/shared/ragdoll_shared.cpp +++ b/game/shared/ragdoll_shared.cpp @@ -40,7 +40,7 @@ void CRagdollLowViolenceManager::SetLowViolence( const char *pMapName ) #if !defined( CLIENT_DLL ) // the server doesn't worry about low violence during multiplayer games - if ( g_pGameRules->IsMultiplayer() ) + if ( g_pGameRules && g_pGameRules->IsMultiplayer() ) { m_bLowViolence = false; } diff --git a/game/shared/saverestore.cpp b/game/shared/saverestore.cpp index e1eb4f66..818def3a 100644 --- a/game/shared/saverestore.cpp +++ b/game/shared/saverestore.cpp @@ -91,7 +91,8 @@ static int gSizes[FIELD_TYPECOUNT] = FIELD_SIZE( FIELD_MATERIALINDEX ), FIELD_SIZE( FIELD_VECTOR2D ), - FIELD_SIZE( FIELD_INTEGER64 ), + FIELD_SIZE( FIELD_INTEGER64 ), + FIELD_SIZE( FIELD_POINTER ), }; @@ -687,7 +688,7 @@ bool CSave::ShouldSaveField( const void *pData, typedescription_t *pField ) int *pEHandle = (int *)pData; for ( int i = 0; i < pField->fieldSize; ++i, ++pEHandle ) { - if ( (*pEHandle) != 0xFFFFFFFF ) + if ( (*pEHandle) != INVALID_EHANDLE_INDEX ) return true; } } @@ -721,11 +722,11 @@ bool CSave::WriteBasicField( const char *pname, void *pData, datamap_t *pRootMap case FIELD_FLOAT: WriteFloat( pField->fieldName, (float *)pData, pField->fieldSize ); break; - + case FIELD_STRING: WriteString( pField->fieldName, (string_t *)pData, pField->fieldSize ); break; - + case FIELD_VECTOR: WriteVector( pField->fieldName, (Vector *)pData, pField->fieldSize ); break; @@ -1242,19 +1243,19 @@ bool CSave::WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, case FIELD_CLASSPTR: WriteEntityPtr( pField->fieldName, (CBaseEntity **)pData, pField->fieldSize ); break; - + case FIELD_EDICT: WriteEdictPtr( pField->fieldName, (edict_t **)pData, pField->fieldSize ); break; - + case FIELD_EHANDLE: WriteEHandle( pField->fieldName, (EHANDLE *)pData, pField->fieldSize ); break; - + case FIELD_POSITION_VECTOR: WritePositionVector( pField->fieldName, (Vector *)pData, pField->fieldSize ); break; - + case FIELD_TIME: WriteTime( pField->fieldName, (float *)pData, pField->fieldSize ); break; @@ -1262,7 +1263,7 @@ bool CSave::WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, case FIELD_TICK: WriteTick( pField->fieldName, (int *)pData, pField->fieldSize ); break; - + case FIELD_MODELINDEX: { int nModelIndex = *(int*)pData; @@ -1298,7 +1299,7 @@ bool CSave::WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, case FIELD_FUNCTION: WriteFunction( pRootMap, pField->fieldName, (inputfunc_t **)(char *)pData, pField->fieldSize ); break; - + case FIELD_VMATRIX: WriteVMatrix( pField->fieldName, (VMatrix *)pData, pField->fieldSize ); break; @@ -1314,6 +1315,10 @@ bool CSave::WriteGameField( const char *pname, void *pData, datamap_t *pRootMap, WriteInterval( pField->fieldName, (interval_t *)pData, pField->fieldSize ); break; + case FIELD_POINTER: + WriteData( pField->fieldName, sizeof(void*)*pField->fieldSize, (char *)pData ); + break; + default: Warning( "Bad field type\n" ); Assert(0); @@ -2154,6 +2159,10 @@ void CRestore::ReadGameField( const SaveRestoreRecordHeader_t &header, void *pDe ReadInterval( (interval_t *)pDest, pField->fieldSize, header.size ); break; + case FIELD_POINTER: + ReadData( (char *)pDest, sizeof(void*)*pField->fieldSize, header.size ); + break; + default: Warning( "Bad field type\n" ); Assert(0); diff --git a/game/shared/saverestore_utlmap.h b/game/shared/saverestore_utlmap.h index 06b624ab..d73dec6f 100644 --- a/game/shared/saverestore_utlmap.h +++ b/game/shared/saverestore_utlmap.h @@ -14,7 +14,7 @@ #pragma once #endif -template +template class CUtlMapDataOps : public CDefSaveRestoreOps { public: @@ -169,7 +169,7 @@ public: //------------------------------------- -template +template class CUtlMapDataopsInstantiator { public: diff --git a/materialsystem/cmaterialsystem.cpp b/materialsystem/cmaterialsystem.cpp index 0618d0c0..66edb8ac 100644 --- a/materialsystem/cmaterialsystem.cpp +++ b/materialsystem/cmaterialsystem.cpp @@ -517,7 +517,7 @@ void CMaterialSystem::CleanUpErrorMaterial() //----------------------------------------------------------------------------- CMaterialSystem::CMaterialSystem() { - m_nRenderThreadID = 0xFFFFFFFF; + m_nRenderThreadID = (uintp)-1; m_hAsyncLoadFileCache = NULL; m_ShaderHInst = 0; m_pMaterialProxyFactory = NULL; @@ -2785,8 +2785,8 @@ IMaterial* CMaterialSystem::FindMaterialEx( char const* pMaterialName, const cha { // We need lower-case symbols for this to work int nLen = Q_strlen( pMaterialName ) + 1; - char *pFixedNameTemp = (char*)stackalloc( nLen ); - char *pTemp = (char*)stackalloc( nLen ); + char *pFixedNameTemp = (char*)malloc( nLen ); + char *pTemp = (char*)malloc( nLen ); Q_strncpy( pFixedNameTemp, pMaterialName, nLen ); Q_strlower( pFixedNameTemp ); #ifdef POSIX @@ -2888,6 +2888,9 @@ IMaterial* CMaterialSystem::FindMaterialEx( char const* pMaterialName, const cha } } + free(pTemp); + free(pFixedNameTemp); + return g_pErrorMaterial->GetRealTimeVersion(); } @@ -3547,7 +3550,7 @@ void CMaterialSystem::ThreadExecuteQueuedContext( CMatQueuedRenderContext *pCont m_pRenderContext.Set( &m_HardwareRenderContext ); pContext->EndQueue( true ); m_pRenderContext.Set( pSavedRenderContext ); - m_nRenderThreadID = 0xFFFFFFFF; + m_nRenderThreadID = (uintp)-1; } IThreadPool *CMaterialSystem::CreateMatQueueThreadPool() diff --git a/materialsystem/cmaterialsystem.h b/materialsystem/cmaterialsystem.h index a5c890dc..60aad1fd 100644 --- a/materialsystem/cmaterialsystem.h +++ b/materialsystem/cmaterialsystem.h @@ -572,7 +572,7 @@ public: MaterialLock_t Lock(); void Unlock( MaterialLock_t ); CMatCallQueue * GetRenderCallQueue(); - uint GetRenderThreadId() const { return m_nRenderThreadID; } + ThreadId_t GetRenderThreadId() const { return m_nRenderThreadID; } void UnbindMaterial( IMaterial *pMaterial ); IMaterialProxy *DetermineProxyReplacements( IMaterial *pMaterial, KeyValues *pFallbackKeyValues ); @@ -617,7 +617,7 @@ private: CMaterialDict m_MaterialDict; CMatLightmaps m_Lightmaps; - CThreadLocal m_pRenderContext; + CTHREADLOCAL(IMatRenderContextInternal *) m_pRenderContext; CMatRenderContext m_HardwareRenderContext; CMatQueuedRenderContext m_QueuedRenderContexts[2]; @@ -698,7 +698,7 @@ private: const char * m_pForcedTextureLoadPathID; FileCacheHandle_t m_hAsyncLoadFileCache; - uint m_nRenderThreadID; + ThreadId_t m_nRenderThreadID; bool m_bAllocatingRenderTargets; bool m_bInStubMode; bool m_bGeneratedConfig; diff --git a/materialsystem/ctexture.cpp b/materialsystem/ctexture.cpp index e4353a0b..977d2deb 100644 --- a/materialsystem/ctexture.cpp +++ b/materialsystem/ctexture.cpp @@ -4039,7 +4039,7 @@ void CTexture::DeleteIfUnreferenced() if ( ThreadInMainThread() ) { // Render thread better not be active or bad things can happen. - Assert( MaterialSystem()->GetRenderThreadId() == 0xFFFFFFFF ); + Assert( MaterialSystem()->GetRenderThreadId() == (uintp)-1 ); TextureManager()->RemoveTexture( this ); return; } diff --git a/materialsystem/imaterialsysteminternal.h b/materialsystem/imaterialsysteminternal.h index dc55bfae..40f775d4 100644 --- a/materialsystem/imaterialsysteminternal.h +++ b/materialsystem/imaterialsysteminternal.h @@ -215,7 +215,7 @@ public: virtual CMatCallQueue *GetRenderCallQueue() = 0; virtual void UnbindMaterial( IMaterial *pMaterial ) = 0; - virtual uint GetRenderThreadId() const = 0 ; + virtual ThreadId_t GetRenderThreadId() const = 0 ; virtual IMaterialProxy *DetermineProxyReplacements( IMaterial *pMaterial, KeyValues *pFallbackKeyValues ) = 0; }; diff --git a/materialsystem/texturemanager.cpp b/materialsystem/texturemanager.cpp index 9719db4f..ae2838b1 100644 --- a/materialsystem/texturemanager.cpp +++ b/materialsystem/texturemanager.cpp @@ -1819,7 +1819,7 @@ void CTextureManager::RestoreTexture( ITextureInternal* pTexture ) //----------------------------------------------------------------------------- void CTextureManager::CleanupPossiblyUnreferencedTextures() { - if ( !ThreadInMainThread() || MaterialSystem()->GetRenderThreadId() != 0xFFFFFFFF ) + if ( !ThreadInMainThread() || MaterialSystem()->GetRenderThreadId() != (uintp)-1 ) { Assert( !"CTextureManager::CleanupPossiblyUnreferencedTextures should never be called here" ); // This is catastrophically bad, don't do this. Someone needs to fix this. See JohnS or McJohn @@ -2368,7 +2368,7 @@ void CTextureManager::RemoveTexture( ITextureInternal *pTexture ) Assert( pTexture->GetReferenceCount() <= 0 ); - if ( !ThreadInMainThread() || MaterialSystem()->GetRenderThreadId() != 0xFFFFFFFF ) + if ( !ThreadInMainThread() || MaterialSystem()->GetRenderThreadId() != (uintp)-1 ) { Assert( !"CTextureManager::RemoveTexture should never be called here"); // This is catastrophically bad, don't do this. Someone needs to fix this. diff --git a/public/XZip.cpp b/public/XZip.cpp index f576f1d4..d5d8f02a 100644 --- a/public/XZip.cpp +++ b/public/XZip.cpp @@ -125,7 +125,7 @@ static ZRESULT lasterrorZ=ZR_OK; #else #include "tier0/threadtools.h" -static CThreadLocalInt lasterrorZ; +static CTHREADLOCALINTEGER(ZRESULT) lasterrorZ; #endif typedef unsigned char uch; // unsigned 8-bit value diff --git a/public/builddisp.cpp b/public/builddisp.cpp index da749e2c..948b4f45 100644 --- a/public/builddisp.cpp +++ b/public/builddisp.cpp @@ -840,9 +840,9 @@ void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, void CCoreDispInfo::InitDispInfo( int power, int minTess, float smoothingAngle, const CDispVert *pVerts, const CDispTri *pTris ) { - Vector vectors[MAX_DISPVERTS]; - float dists[MAX_DISPVERTS]; - float alphas[MAX_DISPVERTS]; + static Vector vectors[MAX_DISPVERTS]; + static float dists[MAX_DISPVERTS]; + static float alphas[MAX_DISPVERTS]; int nVerts = NUM_DISP_POWER_VERTS( power ); for ( int i=0; i < nVerts; i++ ) diff --git a/public/datamap.h b/public/datamap.h index 7c664265..d4eee4ee 100644 --- a/public/datamap.h +++ b/public/datamap.h @@ -62,9 +62,10 @@ typedef enum _fieldtypes FIELD_INTERVAL, // a start and range floating point interval ( e.g., 3.2->3.6 == 3.2 and 0.4 ) FIELD_MODELINDEX, // a model index FIELD_MATERIALINDEX, // a material index (using the material precache string table) - + FIELD_VECTOR2D, // 2 floats - FIELD_INTEGER64, // 64bit integer + FIELD_INTEGER64, // 64bit integer + FIELD_POINTER, FIELD_TYPECOUNT, // MUST BE LAST } fieldtype_t; @@ -94,7 +95,7 @@ public: #define FIELD_BITS( _fieldType ) (FIELD_SIZE( _fieldType ) * 8) DECLARE_FIELD_SIZE( FIELD_FLOAT, sizeof(float) ) -DECLARE_FIELD_SIZE( FIELD_STRING, sizeof(int) ) + DECLARE_FIELD_SIZE( FIELD_VECTOR, 3 * sizeof(float) ) DECLARE_FIELD_SIZE( FIELD_VECTOR2D, 2 * sizeof(float) ) DECLARE_FIELD_SIZE( FIELD_QUATERNION, 4 * sizeof(float)) @@ -103,14 +104,16 @@ DECLARE_FIELD_SIZE( FIELD_BOOLEAN, sizeof(char)) DECLARE_FIELD_SIZE( FIELD_SHORT, sizeof(short)) DECLARE_FIELD_SIZE( FIELD_CHARACTER, sizeof(char)) DECLARE_FIELD_SIZE( FIELD_COLOR32, sizeof(int)) -DECLARE_FIELD_SIZE( FIELD_CLASSPTR, sizeof(int)) -DECLARE_FIELD_SIZE( FIELD_EHANDLE, sizeof(int)) +DECLARE_FIELD_SIZE( FIELD_STRING, sizeof(void*)) +DECLARE_FIELD_SIZE( FIELD_POINTER, sizeof(void*)) +DECLARE_FIELD_SIZE( FIELD_MODELNAME, sizeof(void*)) +DECLARE_FIELD_SIZE( FIELD_SOUNDNAME, sizeof(void*)) +DECLARE_FIELD_SIZE( FIELD_EHANDLE, sizeof(void*)) +DECLARE_FIELD_SIZE( FIELD_CLASSPTR, sizeof(void*)) DECLARE_FIELD_SIZE( FIELD_EDICT, sizeof(int)) DECLARE_FIELD_SIZE( FIELD_POSITION_VECTOR, 3 * sizeof(float)) DECLARE_FIELD_SIZE( FIELD_TIME, sizeof(float)) DECLARE_FIELD_SIZE( FIELD_TICK, sizeof(int)) -DECLARE_FIELD_SIZE( FIELD_MODELNAME, sizeof(int)) -DECLARE_FIELD_SIZE( FIELD_SOUNDNAME, sizeof(int)) DECLARE_FIELD_SIZE( FIELD_INPUT, sizeof(int)) #ifdef POSIX // pointer to members under gnuc are 8bytes if you have a virtual func diff --git a/public/dt_send.cpp b/public/dt_send.cpp index 27580f8f..211bc107 100644 --- a/public/dt_send.cpp +++ b/public/dt_send.cpp @@ -265,7 +265,7 @@ void SendProxy_UInt16ToInt32( const SendProp *pProp, const void *pStruct, const void SendProxy_UInt32ToInt32( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) { - memcpy( &pOut->m_Int, pData, sizeof(unsigned long) ); + memcpy( &pOut->m_Int, pData, sizeof(uint32) ); } #ifdef SUPPORTS_INT64 void SendProxy_UInt64ToInt64( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID) diff --git a/public/optimize.h b/public/optimize.h index 4a14bf31..2dae8174 100644 --- a/public/optimize.h +++ b/public/optimize.h @@ -48,7 +48,7 @@ struct Vertex_t // for sw skinned verts, these are indices into the global list of bones // for hw skinned verts, these are hardware bone indices - char boneID[MAX_NUM_BONES_PER_VERT]; + byte boneID[MAX_NUM_BONES_PER_VERT]; }; enum StripHeaderFlags_t { diff --git a/public/phyfile.h b/public/phyfile.h index c6b37720..c7b9747a 100644 --- a/public/phyfile.h +++ b/public/phyfile.h @@ -11,13 +11,14 @@ #include "datamap.h" + typedef struct phyheader_s { DECLARE_BYTESWAP_DATADESC(); int size; int id; int solidCount; - long checkSum; // checksum of source .mdl file + int checkSum; // checksum of source .mdl file } phyheader_t; #endif // PHYFILE_H diff --git a/public/studio.h b/public/studio.h index 3b0edc98..cf489807 100644 --- a/public/studio.h +++ b/public/studio.h @@ -2433,7 +2433,7 @@ struct studiohdr_t void* VertexBase() const { return pVertexBase; } void SetVertexBase( void* ptr ) { pVertexBase = ptr; } void* IndexBase() const { return pIndexBase; } - void SetIndexBase( void* ptr ) { pIndexBase = ptr; } } + void SetIndexBase( void* ptr ) { pIndexBase = ptr; } #endif // NOTE: No room to add stuff? Up the .mdl file format version diff --git a/public/tier0/platform.h b/public/tier0/platform.h index 2d770f69..0e46b970 100644 --- a/public/tier0/platform.h +++ b/public/tier0/platform.h @@ -1500,7 +1500,7 @@ inline void ConstructThreeArg( T* pMemory, P1 const& arg1, P2 const& arg2, P3 co template inline T* CopyConstruct( T* pMemory, T const& src ) { - return reinterpret_cast(::new( pMemory ) T(src)); + return ::new( pMemory ) T(src); } template diff --git a/public/tier0/threadtools.h b/public/tier0/threadtools.h index df481cc1..00a38422 100644 --- a/public/tier0/threadtools.h +++ b/public/tier0/threadtools.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========== Copyright 2005, Valve Corporation, All rights reserved. ======== // // Purpose: A collection of utility classes to simplify thread handling, and // as much as possible contain portability problems. Here avoiding @@ -9,51 +9,69 @@ #ifndef THREADTOOLS_H #define THREADTOOLS_H -#include "tier0/type_traits.h" - #include -#if defined( __arm__ ) || defined( __arm64__ ) -#include -#endif #include "tier0/platform.h" #include "tier0/dbg.h" -#include "tier0/vcrmode.h" -#include "tier0/vprof_telemetry.h" -#ifdef PLATFORM_WINDOWS_PC -#include -#endif - -#ifdef POSIX +#if defined( POSIX ) && !defined( _PS3 ) && !defined( _X360 ) #include #include -#include #define WAIT_OBJECT_0 0 #define WAIT_TIMEOUT 0x00000102 #define WAIT_FAILED -1 #define THREAD_PRIORITY_HIGHEST 2 #endif +#if !defined( _X360 ) && !defined( _PS3 ) && defined(COMPILER_MSVC) +// For _ReadWriteBarrier() +#include +#endif + +#if defined( _PS3 ) +#include +#include +#include +#include +#endif + +#ifdef OSX +// Add some missing defines +#define PTHREAD_MUTEX_TIMED_NP PTHREAD_MUTEX_NORMAL +#define PTHREAD_MUTEX_RECURSIVE_NP PTHREAD_MUTEX_RECURSIVE +#define PTHREAD_MUTEX_ERRORCHECK_NP PTHREAD_MUTEX_ERRORCHECK +#define PTHREAD_MUTEX_ADAPTIVE_NP 3 +#endif + +#ifdef _PS3 +#define PS3_SYS_PPU_THREAD_COMMON_STACK_SIZE ( 256 * 1024 ) +#endif + + #if defined( _WIN32 ) #pragma once #pragma warning(push) #pragma warning(disable:4251) #endif +#ifdef COMPILER_MSVC64 +#include +#endif + // #define THREAD_PROFILER 1 -#ifndef _RETAIL #define THREAD_MUTEX_TRACING_SUPPORTED -#if defined(_WIN32) && defined(_DEBUG) +#if defined(_WIN32) && defined(_DEBUG) && !defined(THREAD_MUTEX_TRACING_ENABLED) #define THREAD_MUTEX_TRACING_ENABLED #endif -#endif #ifdef _WIN32 typedef void *HANDLE; #endif +// maximum number of threads that can wait on one object +#define CTHREADEVENT_MAX_WAITING_THREADS 4 + // Start thread running - error if already running enum ThreadPriorityEnum_t { @@ -85,29 +103,71 @@ enum ThreadPriorityEnum_t #endif // PLATFORM_PS3 }; +#if defined( PLATFORM_LINUX ) +#define TP_IS_PRIORITY_HIGHER( a, b ) ( ( a ) < ( b ) ) +#else +#define TP_IS_PRIORITY_HIGHER( a, b ) ( ( a ) > ( b ) ) +#endif + +#if (defined( PLATFORM_WINDOWS_PC ) || defined( PLATFORM_X360 )) && !defined( STEAM ) && !defined( _CERT ) +//Thread parent stack trace linkage requires ALL executing binaries to disable frame pointer omission to operate speedily/successfully. (/Oy-) "vpc /nofpo" +#define THREAD_PARENT_STACK_TRACE_SUPPORTED 1 //uncomment to support joining the root of a thread's stack trace to its parent's at time of invocation. Must also set ENABLE_THREAD_PARENT_STACK_TRACING in stacktools.h +#endif + +#if defined( THREAD_PARENT_STACK_TRACE_SUPPORTED ) +#include "tier0/stacktools.h" +# if defined( ENABLE_THREAD_PARENT_STACK_TRACING ) //stacktools.h opted in +# define THREAD_PARENT_STACK_TRACE_ENABLED 1 //both threadtools.h and stacktools.h have opted into the feature, enable it +# endif +#endif + +extern bool gbCheckNotMultithreaded; + +#ifdef _PS3 + +#define USE_INTRINSIC_INTERLOCKED + +#define CHECK_NOT_MULTITHREADED() \ +{ \ + static int init = 0; \ + static sys_ppu_thread_t threadIDPrev; \ + \ + if (!init) \ + { \ + sys_ppu_thread_get_id(&threadIDPrev); \ + init = 1; \ + } \ + else if (gbCheckNotMultithreaded) \ + { \ + sys_ppu_thread_t threadID; \ + sys_ppu_thread_get_id(&threadID); \ + if (threadID != threadIDPrev) \ + { \ + printf("CHECK_NOT_MULTITHREADED: prev thread = %x, cur thread = %x\n", \ + (uint)threadIDPrev, (uint)threadID); \ + *(int*)0 = 0; \ + } \ + } \ +} + +#else // _PS3 + #define CHECK_NOT_MULTITHREADED() +#endif // _PS3 + +#if defined( _X360 ) || defined( _PS3 ) +#define MAX_THREADS_SUPPORTED 16 +#else +#define MAX_THREADS_SUPPORTED 32 +#endif + + + //----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- const unsigned TT_INFINITE = 0xffffffff; - -#ifndef NO_THREAD_LOCAL - -#ifndef THREAD_LOCAL -#ifdef _WIN32 -#define THREAD_LOCAL __declspec(thread) -#elif POSIX -#define THREAD_LOCAL __thread -#endif -#endif - -#endif // NO_THREAD_LOCAL - -#ifdef PLATFORM_64BITS -typedef uint64 ThreadId_t; -#else -typedef uint32 ThreadId_t; -#endif +typedef uintp ThreadId_t; //----------------------------------------------------------------------------- // @@ -115,73 +175,83 @@ typedef uint32 ThreadId_t; // in that it accepts a standard C function rather than compiler specific one. // //----------------------------------------------------------------------------- +#ifdef COMPILER_SNC +typedef uint64 ThreadHandle_t; +#else // COMPILER_SNC FORWARD_DECLARE_HANDLE( ThreadHandle_t ); +#endif // !COMPILER_SNC typedef uintp (*ThreadFunc_t)( void *pParam ); +#if defined( _PS3 ) +PLATFORM_OVERLOAD ThreadHandle_t CreateSimpleThread( ThreadFunc_t, void *pParam, ThreadId_t *pID, unsigned stackSize = 0x10000 /*64*/ ); +PLATFORM_INTERFACE ThreadHandle_t CreateSimpleThread( ThreadFunc_t, void *pParam, unsigned stackSize = 0x10000 /*64*/ ); +#else //_PS3 PLATFORM_OVERLOAD ThreadHandle_t CreateSimpleThread( ThreadFunc_t, void *pParam, ThreadId_t *pID, unsigned stackSize = 0 ); PLATFORM_INTERFACE ThreadHandle_t CreateSimpleThread( ThreadFunc_t, void *pParam, unsigned stackSize = 0 ); +#endif //_PS3 PLATFORM_INTERFACE bool ReleaseThreadHandle( ThreadHandle_t ); //----------------------------------------------------------------------------- PLATFORM_INTERFACE void ThreadSleep(unsigned duration = 0); +PLATFORM_INTERFACE void ThreadNanoSleep(unsigned ns); PLATFORM_INTERFACE ThreadId_t ThreadGetCurrentId(); PLATFORM_INTERFACE ThreadHandle_t ThreadGetCurrentHandle(); PLATFORM_INTERFACE int ThreadGetPriority( ThreadHandle_t hThread = NULL ); PLATFORM_INTERFACE bool ThreadSetPriority( ThreadHandle_t hThread, int priority ); inline bool ThreadSetPriority( int priority ) { return ThreadSetPriority( NULL, priority ); } +#ifndef _X360 PLATFORM_INTERFACE bool ThreadInMainThread(); PLATFORM_INTERFACE void DeclareCurrentThreadIsMainThread(); +#else +PLATFORM_INTERFACE byte *g_pBaseMainStack; +PLATFORM_INTERFACE byte *g_pLimitMainStack; +inline bool ThreadInMainThread() +{ + byte b; + byte *p = &b; + return ( p < g_pBaseMainStack && p >= g_pLimitMainStack ); +} +#endif // NOTE: ThreadedLoadLibraryFunc_t needs to return the sleep time in milliseconds or TT_INFINITE typedef int (*ThreadedLoadLibraryFunc_t)(); PLATFORM_INTERFACE void SetThreadedLoadLibraryFunc( ThreadedLoadLibraryFunc_t func ); PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc(); -#if defined( _WIN32 ) && !defined( _WIN64 ) && !defined( _X360 ) -extern "C" unsigned long __declspec(dllimport) __stdcall GetCurrentThreadId(); +#if defined( PLATFORM_WINDOWS_PC32 ) +DLL_IMPORT unsigned long STDCALL GetCurrentThreadId(); #define ThreadGetCurrentId GetCurrentThreadId #endif inline void ThreadPause() { -#if defined( PLATFORM_WINDOWS_PC ) - // Intrinsic for __asm pause; from - _mm_pause(); -#elif POSIX && ( defined( __i386__ ) || defined( __x86_64__ ) ) +#if defined( COMPILER_PS3 ) + __db16cyc(); +#elif defined( COMPILER_GCC ) __asm __volatile( "pause" ); -#elif defined( _X360 ) -#elif defined(__arm__) || defined(__arm64__) - sched_yield(); +#elif defined ( COMPILER_MSVC64 ) + _mm_pause(); +#elif defined( COMPILER_MSVC32 ) + __asm pause; +#elif defined( COMPILER_MSVCX360 ) + YieldProcessor(); + __asm { or r0,r0,r0 } + YieldProcessor(); + __asm { or r1,r1,r1 } #else #error "implement me" #endif } PLATFORM_INTERFACE bool ThreadJoin( ThreadHandle_t, unsigned timeout = TT_INFINITE ); -// If you're not calling ThreadJoin, you need to call ThreadDetach so pthreads on Linux knows it can -// free the memory for this thread. Otherwise you wind up leaking threads until you run out and -// CreateSimpleThread() will fail. -PLATFORM_INTERFACE void ThreadDetach( ThreadHandle_t ); -PLATFORM_INTERFACE void ThreadSetDebugName( ThreadId_t id, const char *pszName ); -inline void ThreadSetDebugName( const char *pszName ) { ThreadSetDebugName( (ThreadId_t)-1, pszName ); } +PLATFORM_INTERFACE void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName ); +inline void ThreadSetDebugName( const char *pszName ) { ThreadSetDebugName( NULL, pszName ); } PLATFORM_INTERFACE void ThreadSetAffinity( ThreadHandle_t hThread, int nAffinityMask ); -//----------------------------------------------------------------------------- - -enum ThreadWaitResult_t -{ - TW_FAILED = 0xffffffff, // WAIT_FAILED - TW_TIMEOUT = 0x00000102, // WAIT_TIMEOUT -}; - -#ifdef _WIN32 -PLATFORM_INTERFACE int ThreadWaitForObjects( int nEvents, const HANDLE *pHandles, bool bWaitAll = true, unsigned timeout = TT_INFINITE ); -inline int ThreadWaitForObject( HANDLE handle, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) { return ThreadWaitForObjects( 1, &handle, bWaitAll, timeout ); } -#endif //----------------------------------------------------------------------------- // @@ -192,44 +262,83 @@ inline int ThreadWaitForObject( HANDLE handle, bool bWaitAll = true, unsigned ti #ifdef _WIN32 #define NOINLINE -#elif POSIX +#elif defined( _PS3 ) +#define NOINLINE __attribute__ ((noinline)) +#elif defined(POSIX) #define NOINLINE __attribute__ ((noinline)) #endif -// ThreadMemoryBarrier is a fence/barrier sufficient for most uses. It prevents reads -// from moving past reads, and writes moving past writes. It is sufficient for -// read-acquire and write-release barriers. It is not a full barrier and it does -// not prevent reads from moving past writes -- that would require a full __sync() -// on PPC and is significantly more expensive. #if defined( _X360 ) || defined( _PS3 ) - #define ThreadMemoryBarrier() __lwsync() - -#elif defined(_MSC_VER) - // Prevent compiler reordering across this barrier. This is - // sufficient for most purposes on x86/x64. - - #if _MSC_VER < 1500 - // !KLUDGE! For VC 2005 - // http://connect.microsoft.com/VisualStudio/feedback/details/100051 - #pragma intrinsic(_ReadWriteBarrier) - #endif - #define ThreadMemoryBarrier() _ReadWriteBarrier() -#elif defined(GNUC) - // Prevent compiler reordering across this barrier. This is - // sufficient for most purposes on x86/x64. - // http://preshing.com/20120625/memory-ordering-at-compile-time - #define ThreadMemoryBarrier() asm volatile("" ::: "memory") +#define ThreadMemoryBarrier() __lwsync() +#elif defined(COMPILER_MSVC) +// Prevent compiler reordering across this barrier. This is +// sufficient for most purposes on x86/x64. +#define ThreadMemoryBarrier() _ReadWriteBarrier() +#elif defined(COMPILER_GCC) +// Prevent compiler reordering across this barrier. This is +// sufficient for most purposes on x86/x64. +// http://preshing.com/20120625/memory-ordering-at-compile-time +#define ThreadMemoryBarrier() asm volatile("" ::: "memory") #else - #error Every platform needs to define ThreadMemoryBarrier to at least prevent compiler reordering +#error Every platform needs to define ThreadMemoryBarrier to at least prevent compiler reordering #endif -#if defined(_WIN32) && !defined(_X360) - #if ( _MSC_VER >= 1310 ) - #define USE_INTRINSIC_INTERLOCKED - #endif -#endif +#if defined( _LINUX ) || defined( _OSX ) +#define USE_INTRINSIC_INTERLOCKED +// linux implementation +inline int32 ThreadInterlockedIncrement( int32 volatile *p ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_fetch_and_add( p, 1 ) + 1; +} + +inline int32 ThreadInterlockedDecrement( int32 volatile *p ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_fetch_and_add( p, -1 ) - 1; +} + +inline int32 ThreadInterlockedExchange( int32 volatile *p, int32 value ) +{ + Assert( (size_t)p % 4 == 0 ); + int32 nRet; + + // Note: The LOCK instruction prefix is assumed on the XCHG instruction and GCC gets very confused on the Mac when we use it. + __asm __volatile( + "xchgl %2,(%1)" + : "=r" (nRet) + : "r" (p), "0" (value) + : "memory"); + return nRet; +} + +inline int32 ThreadInterlockedExchangeAdd( int32 volatile *p, int32 value ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_fetch_and_add( p, value ); +} +inline int64 ThreadInterlockedExchangeAdd64( int64 volatile *p, int64 value ) +{ + Assert( ( (size_t)p ) % 8 == 0 ); + return __sync_fetch_and_add( p, value ); +} +inline int32 ThreadInterlockedCompareExchange( int32 volatile *p, int32 value, int32 comperand ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_val_compare_and_swap( p, comperand, value ); +} + + +inline bool ThreadInterlockedAssignIf( int32 volatile *p, int32 value, int32 comperand ) +{ + Assert( (size_t)p % 4 == 0 ); + return __sync_bool_compare_and_swap( p, comperand, value ); +} + +#elif ( defined( COMPILER_MSVC32 ) && ( _MSC_VER >= 1310 ) ) +// windows 32 implemnetation using compiler intrinsics +#define USE_INTRINSIC_INTERLOCKED -#ifdef USE_INTRINSIC_INTERLOCKED extern "C" { long __cdecl _InterlockedIncrement(volatile long*); @@ -245,72 +354,106 @@ extern "C" #pragma intrinsic( _InterlockedExchangeAdd ) #pragma intrinsic( _InterlockedIncrement ) -inline int32 ThreadInterlockedIncrement( int32 volatile *p ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedIncrement( p ); } -inline int32 ThreadInterlockedDecrement( int32 volatile *p ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedDecrement( p ); } -inline int32 ThreadInterlockedExchange( int32 volatile *p, int32 value ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedExchange( p, value ); } -inline int32 ThreadInterlockedExchangeAdd( int32 volatile *p, int32 value ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedExchangeAdd( p, value ); } -inline int32 ThreadInterlockedCompareExchange( int32 volatile *p, int32 value, int32 comperand ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedCompareExchange( p, value, comperand ); } -inline bool ThreadInterlockedAssignIf( int32 volatile *p, int32 value, int32 comperand ) { Assert( (size_t)p % 4 == 0 ); return ( _InterlockedCompareExchange( p, value, comperand ) == comperand ); } +inline int32 ThreadInterlockedIncrement( int32 volatile *p ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedIncrement( (volatile long*)p ); } +inline int32 ThreadInterlockedDecrement( int32 volatile *p ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedDecrement( (volatile long*)p ); } +inline int32 ThreadInterlockedExchange( int32 volatile *p, int32 value ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedExchange( (volatile long*)p, value ); } +inline int32 ThreadInterlockedExchangeAdd( int32 volatile *p, int32 value ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedExchangeAdd( (volatile long*)p, value ); } +inline int32 ThreadInterlockedCompareExchange( int32 volatile *p, int32 value, int32 comperand ) { Assert( (size_t)p % 4 == 0 ); return _InterlockedCompareExchange( (volatile long*)p, value, comperand ); } +inline bool ThreadInterlockedAssignIf( int32 volatile *p, int32 value, int32 comperand ) { Assert( (size_t)p % 4 == 0 ); return ( _InterlockedCompareExchange( (volatile long*)p, value, comperand ) == comperand ); } +#elif defined( _PS3 ) +PLATFORM_INTERFACE inline int32 ThreadInterlockedIncrement( int32 volatile * ea ) { return cellAtomicIncr32( (uint32_t*)ea ) + 1; } +PLATFORM_INTERFACE inline int32 ThreadInterlockedDecrement( int32 volatile * ea ) { return cellAtomicDecr32( (uint32_t*)ea ) - 1; } +PLATFORM_INTERFACE inline int32 ThreadInterlockedExchange( int32 volatile * ea, int32 value ) { return cellAtomicStore32( ( uint32_t* )ea, value); } +PLATFORM_INTERFACE inline int32 ThreadInterlockedExchangeAdd( int32 volatile * ea, int32 value ) { return cellAtomicAdd32( ( uint32_t* )ea, value ); } +PLATFORM_INTERFACE inline int32 ThreadInterlockedCompareExchange( int32 volatile * ea, int32 value, int32 comperand ) { return cellAtomicCompareAndSwap32( (uint32_t*)ea, comperand, value ) ; } +PLATFORM_INTERFACE inline bool ThreadInterlockedAssignIf( int32 volatile * ea, int32 value, int32 comperand ) { return ( cellAtomicCompareAndSwap32( (uint32_t*)ea, comperand, value ) == ( uint32_t ) comperand ); } + +PLATFORM_INTERFACE inline int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand ) { return cellAtomicCompareAndSwap64( ( uint64_t* ) pDest, comperand, value ); } +PLATFORM_INTERFACE inline bool ThreadInterlockedAssignIf64( volatile int64 *pDest, int64 value, int64 comperand ) { return ( cellAtomicCompareAndSwap64( ( uint64_t* ) pDest, comperand, value ) == ( uint64_t ) comperand ); } + +#elif defined( _X360 ) +#define TO_INTERLOCK_PARAM(p) ((volatile long *)p) +#define TO_INTERLOCK_PTR_PARAM(p) ((void **)p) +FORCEINLINE int32 ThreadInterlockedIncrement( int32 volatile *pDest ) { Assert( (size_t)pDest % 4 == 0 ); return InterlockedIncrement( TO_INTERLOCK_PARAM(pDest) ); } +FORCEINLINE int32 ThreadInterlockedDecrement( int32 volatile *pDest ) { Assert( (size_t)pDest % 4 == 0 ); return InterlockedDecrement( TO_INTERLOCK_PARAM(pDest) ); } +FORCEINLINE int32 ThreadInterlockedExchange( int32 volatile *pDest, int32 value ) { Assert( (size_t)pDest % 4 == 0 ); return InterlockedExchange( TO_INTERLOCK_PARAM(pDest), value ); } +FORCEINLINE int32 ThreadInterlockedExchangeAdd( int32 volatile *pDest, int32 value ) { Assert( (size_t)pDest % 4 == 0 ); return InterlockedExchangeAdd( TO_INTERLOCK_PARAM(pDest), value ); } +FORCEINLINE int32 ThreadInterlockedCompareExchange( int32 volatile *pDest, int32 value, int32 comperand ) { Assert( (size_t)pDest % 4 == 0 ); return InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand ); } +FORCEINLINE bool ThreadInterlockedAssignIf( int32 volatile *pDest, int32 value, int32 comperand ) { Assert( (size_t)pDest % 4 == 0 ); return ( InterlockedCompareExchange( TO_INTERLOCK_PARAM(pDest), value, comperand ) == comperand ); } #else -PLATFORM_INTERFACE int32 ThreadInterlockedIncrement( int32 volatile * ); -PLATFORM_INTERFACE int32 ThreadInterlockedDecrement( int32 volatile * ); -PLATFORM_INTERFACE int32 ThreadInterlockedExchange( int32 volatile *, int32 value ); -PLATFORM_INTERFACE int32 ThreadInterlockedExchangeAdd( int32 volatile *, int32 value ); -PLATFORM_INTERFACE int32 ThreadInterlockedCompareExchange( int32 volatile *, int32 value, int32 comperand ); -PLATFORM_INTERFACE bool ThreadInterlockedAssignIf( int32 volatile *, int32 value, int32 comperand ); +// non 32-bit windows and 360 implementation +PLATFORM_INTERFACE int32 ThreadInterlockedIncrement( int32 volatile * ) NOINLINE; +PLATFORM_INTERFACE int32 ThreadInterlockedDecrement( int32 volatile * ) NOINLINE; +PLATFORM_INTERFACE int32 ThreadInterlockedExchange( int32 volatile *, int32 value ) NOINLINE; +PLATFORM_INTERFACE int32 ThreadInterlockedExchangeAdd( int32 volatile *, int32 value ) NOINLINE; +PLATFORM_INTERFACE int32 ThreadInterlockedCompareExchange( int32 volatile *, int32 value, int32 comperand ) NOINLINE; +PLATFORM_INTERFACE bool ThreadInterlockedAssignIf( int32 volatile *, int32 value, int32 comperand ) NOINLINE; #endif -inline unsigned ThreadInterlockedExchangeSubtract( int32 volatile *p, int32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, -value ); } -#if defined( USE_INTRINSIC_INTERLOCKED ) && !defined( _WIN64 ) +#if defined( USE_INTRINSIC_INTERLOCKED ) && !defined( PLATFORM_64BITS ) #define TIPTR() -inline void *ThreadInterlockedExchangePointer( void * volatile *p, void *value ) { return (void *)_InterlockedExchange( reinterpret_cast(p), reinterpret_cast(value) ); } -inline void *ThreadInterlockedCompareExchangePointer( void * volatile *p, void *value, void *comperand ) { return (void *)_InterlockedCompareExchange( reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comperand) ); } -inline bool ThreadInterlockedAssignPointerIf( void * volatile *p, void *value, void *comperand ) { return ( _InterlockedCompareExchange( reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comperand) ) == reinterpret_cast(comperand) ); } +inline void *ThreadInterlockedExchangePointer( void * volatile *p, void *value ) { return (void *)( ( intp )ThreadInterlockedExchange( reinterpret_cast(p), reinterpret_cast(value) ) ); } +inline void *ThreadInterlockedCompareExchangePointer( void * volatile *p, void *value, void *comperand ) { return (void *)( ( intp )ThreadInterlockedCompareExchange( reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comperand) ) ); } +inline bool ThreadInterlockedAssignPointerIf( void * volatile *p, void *value, void *comperand ) { return ( ThreadInterlockedCompareExchange( reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comperand) ) == reinterpret_cast(comperand) ); } #else PLATFORM_INTERFACE void *ThreadInterlockedExchangePointer( void * volatile *, void *value ) NOINLINE; PLATFORM_INTERFACE void *ThreadInterlockedCompareExchangePointer( void * volatile *, void *value, void *comperand ) NOINLINE; PLATFORM_INTERFACE bool ThreadInterlockedAssignPointerIf( void * volatile *, void *value, void *comperand ) NOINLINE; #endif + +inline unsigned ThreadInterlockedExchangeSubtract( int32 volatile *p, int32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, -value ); } + inline void const *ThreadInterlockedExchangePointerToConst( void const * volatile *p, void const *value ) { return ThreadInterlockedExchangePointer( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ) ); } inline void const *ThreadInterlockedCompareExchangePointerToConst( void const * volatile *p, void const *value, void const *comperand ) { return ThreadInterlockedCompareExchangePointer( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ), const_cast < void * > ( comperand ) ); } inline bool ThreadInterlockedAssignPointerToConstIf( void const * volatile *p, void const *value, void const *comperand ) { return ThreadInterlockedAssignPointerIf( const_cast < void * volatile * > ( p ), const_cast < void * > ( value ), const_cast < void * > ( comperand ) ); } -#if defined( PLATFORM_64BITS ) -#if defined (_WIN32) -typedef __m128i int128; -inline int128 int128_zero() { return _mm_setzero_si128(); } -#else -typedef __int128_t int128; -#define int128_zero() int128() + +#ifndef _PS3 +PLATFORM_INTERFACE int64 ThreadInterlockedCompareExchange64( int64 volatile *, int64 value, int64 comperand ) NOINLINE; +PLATFORM_INTERFACE bool ThreadInterlockedAssignIf64( volatile int64 *pDest, int64 value, int64 comperand ) NOINLINE; #endif -PLATFORM_INTERFACE bool ThreadInterlockedAssignIf128( volatile int128 *pDest, const int128 &value, const int128 &comperand ) NOINLINE; - -#endif +PLATFORM_INTERFACE int64 ThreadInterlockedExchange64( int64 volatile *, int64 value ) NOINLINE; +#ifdef COMPILER_MSVC32 PLATFORM_INTERFACE int64 ThreadInterlockedIncrement64( int64 volatile * ) NOINLINE; PLATFORM_INTERFACE int64 ThreadInterlockedDecrement64( int64 volatile * ) NOINLINE; -PLATFORM_INTERFACE int64 ThreadInterlockedCompareExchange64( int64 volatile *, int64 value, int64 comperand ) NOINLINE; -PLATFORM_INTERFACE int64 ThreadInterlockedExchange64( int64 volatile *, int64 value ) NOINLINE; PLATFORM_INTERFACE int64 ThreadInterlockedExchangeAdd64( int64 volatile *, int64 value ) NOINLINE; -PLATFORM_INTERFACE bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand ) NOINLINE; +#elif defined(POSIX) -inline uint32 ThreadInterlockedExchangeSubtract( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } -inline uint32 ThreadInterlockedIncrement( uint32 volatile *p ) { return ThreadInterlockedIncrement( (int32 volatile *)p ); } -inline uint32 ThreadInterlockedDecrement( uint32 volatile *p ) { return ThreadInterlockedDecrement( (int32 volatile *)p ); } -inline uint32 ThreadInterlockedExchange( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchange( (int32 volatile *)p, value ); } -inline uint32 ThreadInterlockedExchangeAdd( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } -inline uint32 ThreadInterlockedCompareExchange( uint32 volatile *p, uint32 value, uint32 comperand ) { return ThreadInterlockedCompareExchange( (int32 volatile *)p, value, comperand ); } -inline bool ThreadInterlockedAssignIf( uint32 volatile *p, uint32 value, uint32 comperand ) { return ThreadInterlockedAssignIf( (int32 volatile *)p, value, comperand ); } +inline int64 ThreadInterlockedIncrement64( int64 volatile *p ) +{ + Assert( (size_t)p % 8 == 0 ); + return __sync_fetch_and_add( p, 1 ) + 1; +} -inline uint64 ThreadInterlockedIncrement64( uint64 volatile *p ) { return ThreadInterlockedIncrement64( (int64 volatile *)p ); } -inline uint64 ThreadInterlockedDecrement64( uint64 volatile *p ) { return ThreadInterlockedDecrement64( (int64 volatile *)p ); } -inline uint64 ThreadInterlockedCompareExchange64( uint64 volatile *p, uint64 value, uint64 comperand ) { return ThreadInterlockedCompareExchange64( (int64 volatile *)p, value, comperand ); } -inline uint64 ThreadInterlockedExchange64( uint64 volatile *p, uint64 value ) { return ThreadInterlockedExchange64( (int64 volatile *)p, value ); } -inline uint64 ThreadInterlockedExchangeAdd64( uint64 volatile *p, uint64 value ) { return ThreadInterlockedExchangeAdd64( (int64 volatile *)p, value ); } -inline bool ThreadInterlockedAssignIf64( uint64 volatile *p, uint64 value, uint64 comperand ) { return ThreadInterlockedAssignIf64( (int64 volatile *)p, value, comperand ); } +inline int64 ThreadInterlockedDecrement64( int64 volatile *p ) +{ + Assert( (size_t)p % 8 == 0 ); + return __sync_fetch_and_add( p, -1 ) - 1; +} + +#endif + +#ifdef COMPILER_MSVC64 +// 64 bit windows can use intrinsics for these, 32-bit can't +#pragma intrinsic( _InterlockedCompareExchange64 ) +#pragma intrinsic( _InterlockedExchange64 ) +#pragma intrinsic( _InterlockedExchangeAdd64 ) +inline int64 ThreadInterlockedCompareExchange64( int64 volatile *p, int64 value, int64 comparand ) { AssertDbg( (size_t)p % 8 == 0 ); return _InterlockedCompareExchange64( (volatile int64*)p, value, comparand ); } +inline int64 ThreadInterlockedExchangeAdd64( int64 volatile *p, int64 value ) { AssertDbg( (size_t)p % 8 == 0 ); return _InterlockedExchangeAdd64( (volatile int64*)p, value ); } +#endif + +inline unsigned ThreadInterlockedExchangeSubtract( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } + +inline unsigned ThreadInterlockedIncrement( uint32 volatile *p ) { return ThreadInterlockedIncrement( (int32 volatile *)p ); } +inline unsigned ThreadInterlockedDecrement( uint32 volatile *p ) { return ThreadInterlockedDecrement( (int32 volatile *)p ); } +inline unsigned ThreadInterlockedExchange( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchange( (int32 volatile *)p, value ); } +inline unsigned ThreadInterlockedExchangeAdd( uint32 volatile *p, uint32 value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } +inline unsigned ThreadInterlockedCompareExchange( uint32 volatile *p, uint32 value, uint32 comperand ) { return ThreadInterlockedCompareExchange( (int32 volatile *)p, value, comperand ); } +inline bool ThreadInterlockedAssignIf( uint32 volatile *p, uint32 value, uint32 comperand ) { return ThreadInterlockedAssignIf( (int32 volatile *)p, value, comperand ); } //inline int ThreadInterlockedExchangeSubtract( int volatile *p, int value ) { return ThreadInterlockedExchangeAdd( (int32 volatile *)p, value ); } //inline int ThreadInterlockedIncrement( int volatile *p ) { return ThreadInterlockedIncrement( (int32 volatile *)p ); } @@ -320,6 +463,13 @@ inline bool ThreadInterlockedAssignIf64( uint64 volatile *p, uint64 value, uint6 //inline int ThreadInterlockedCompareExchange( int volatile *p, int value, int comperand ) { return ThreadInterlockedCompareExchange( (int32 volatile *)p, value, comperand ); } //inline bool ThreadInterlockedAssignIf( int volatile *p, int value, int comperand ) { return ThreadInterlockedAssignIf( (int32 volatile *)p, value, comperand ); } + +#if defined( _WIN64 ) +typedef __m128i int128; +inline int128 int128_zero() { return _mm_setzero_si128(); } +PLATFORM_INTERFACE bool ThreadInterlockedAssignIf128( volatile int128 *pDest, const int128 &value, const int128 &comperand ) NOINLINE; +#endif + //----------------------------------------------------------------------------- // Access to VTune thread profiling //----------------------------------------------------------------------------- @@ -342,7 +492,8 @@ PLATFORM_INTERFACE void ThreadNotifySyncReleasing(void *p); #ifndef NO_THREAD_LOCAL -#if defined(_LINUX) && !defined(OSX) + +#if ( defined(_LINUX) && defined(DEDICATED) ) && !defined(OSX) // linux totally supports compiler thread locals, even across dll's. #define PLAT_COMPILER_SUPPORTED_THREADLOCALS 1 #define CTHREADLOCALINTEGER( typ ) __thread int @@ -350,24 +501,38 @@ PLATFORM_INTERFACE void ThreadNotifySyncReleasing(void *p); #define CTHREADLOCALPTR( typ ) __thread typ * #define CTHREADLOCAL( typ ) __thread typ #define GETLOCAL( x ) ( x ) -#endif // _LINUX && !OSX +#ifndef TIER0_DLL_EXPORT +DLL_IMPORT __thread int g_nThreadID; +#endif +#endif -#if defined(WIN32) || defined(OSX) + +#if defined(WIN32) || defined(OSX) || defined( _PS3 ) || ( defined (_LINUX) && !defined(DEDICATED) ) #ifndef __AFXTLS_H__ // not compatible with some Windows headers + +#if defined(_PS3) #define CTHREADLOCALINT CThreadLocalInt #define CTHREADLOCALINTEGER( typ ) CThreadLocalInt #define CTHREADLOCALPTR( typ ) CThreadLocalPtr #define CTHREADLOCAL( typ ) CThreadLocal #define GETLOCAL( x ) ( x.Get() ) +#else +#define CTHREADLOCALINT GenericThreadLocals::CThreadLocalInt +#define CTHREADLOCALINTEGER( typ ) GenericThreadLocals::CThreadLocalInt +#define CTHREADLOCALPTR( typ ) GenericThreadLocals::CThreadLocalPtr +#define CTHREADLOCAL( typ ) GenericThreadLocals::CThreadLocal +#define GETLOCAL( x ) ( x.Get() ) + #endif -#endif // WIN32 || OSX -#endif // NO_THREAD_LOCALS +#if !defined(_PS3) +namespace GenericThreadLocals +{ +#endif + // a (not so efficient) implementation of thread locals for compilers without full support (i.e. visual c). + // don't use this explicity - instead, use the CTHREADxxx macros above. -#ifndef __AFXTLS_H__ // not compatible with some Windows headers -#ifndef NO_THREAD_LOCAL - -class PLATFORM_CLASS CThreadLocalBase + class PLATFORM_CLASS CThreadLocalBase { public: CThreadLocalBase(); @@ -377,17 +542,15 @@ public: void Set(void *); private: -#ifdef _WIN32 - uint32 m_index; -#elif POSIX +#if defined(POSIX) && !defined( _GAMECONSOLE ) pthread_key_t m_index; +#else + uint32 m_index; #endif }; //--------------------------------------------------------- -#ifndef __AFXTLS_H__ - template class CThreadLocal : public CThreadLocalBase { @@ -395,19 +558,21 @@ private: CThreadLocal() { #ifdef PLATFORM_64BITS - COMPILE_TIME_ASSERT( sizeof(T) <= sizeof(void *) ); + COMPILE_TIME_ASSERT( sizeof(T) <= sizeof(void *) ); #else - COMPILE_TIME_ASSERT( sizeof(T) == sizeof(void *) ); + COMPILE_TIME_ASSERT( sizeof(T) == sizeof(void *) ); #endif } + void operator=( T i ) { Set( i ); } + T Get() const { #ifdef PLATFORM_64BITS - void *pData = CThreadLocalBase::Get(); - return *reinterpret_cast( &pData ); + void *pData = CThreadLocalBase::Get(); + return *reinterpret_cast( &pData ); #else - #ifdef COMPILER_MSVC + #ifdef COMPILER_MSVC #pragma warning ( disable : 4311 ) #endif return reinterpret_cast( CThreadLocalBase::Get() ); @@ -420,11 +585,11 @@ private: void Set(T val) { #ifdef PLATFORM_64BITS - void* pData = 0; - *reinterpret_cast( &pData ) = val; - CThreadLocalBase::Set( pData ); + void* pData = 0; + *reinterpret_cast( &pData ) = val; + CThreadLocalBase::Set( pData ); #else - #ifdef COMPILER_MSVC + #ifdef COMPILER_MSVC #pragma warning ( disable : 4312 ) #endif CThreadLocalBase::Set( reinterpret_cast(val) ); @@ -435,27 +600,27 @@ private: } }; -#endif //--------------------------------------------------------- -template + template class CThreadLocalInt : public CThreadLocal { public: - CThreadLocalInt() - { - COMPILE_TIME_ASSERT( sizeof(T) >= sizeof(int) ); - } + operator const T() const { return this->Get(); } + int operator=( T i ) { this->Set( i ); return i; } - operator int() const { return (int)this->Get(); } - int operator=( int i ) { this->Set( (intp)i ); return i; } + T operator++() { T i = this->Get(); this->Set( ++i ); return i; } + T operator++(int) { T i = this->Get(); this->Set( i + 1 ); return i; } - int operator++() { T i = this->Get(); this->Set( ++i ); return (int)i; } - int operator++(int) { T i = this->Get(); this->Set( i + 1 ); return (int)i; } + T operator--() { T i = this->Get(); this->Set( --i ); return i; } + T operator--(int) { T i = this->Get(); this->Set( i - 1 ); return i; } - int operator--() { T i = this->Get(); this->Set( --i ); return (int)i; } - int operator--(int) { T i = this->Get(); this->Set( i - 1 ); return (int)i; } + inline CThreadLocalInt( ) { } + inline CThreadLocalInt( const T &initialvalue ) + { + this->Set( initialvalue ); + } }; @@ -467,26 +632,30 @@ template public: CThreadLocalPtr() {} - operator const void *() const { return (T *)Get(); } + operator const void *() const { return (const T *)Get(); } operator void *() { return (T *)Get(); } - operator const T *() const { return (T *)Get(); } - operator const T *() { return (T *)Get(); } + operator const T *() const { return (const T *)Get(); } + operator const T *() { return (const T *)Get(); } operator T *() { return (T *)Get(); } T * operator=( T *p ) { Set( p ); return p; } bool operator !() const { return (!Get()); } + bool operator!=( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (Get() != NULL); } + bool operator==( int i ) const { AssertMsg( i == 0, "Only NULL allowed on integer compare" ); return (Get() == NULL); } bool operator==( const void *p ) const { return (Get() == p); } bool operator!=( const void *p ) const { return (Get() != p); } + bool operator==( const T *p ) const { return operator==((const void*)p); } + bool operator!=( const T *p ) const { return operator!=((const void*)p); } T * operator->() { return (T *)Get(); } T & operator *() { return *((T *)Get()); } - const T * operator->() const { return (T *)Get(); } - const T & operator *() const { return *((T *)Get()); } + const T * operator->() const { return (const T *)Get(); } + const T & operator *() const { return *((const T *)Get()); } - const T & operator[]( int i ) const { return *((T *)Get() + i); } + const T & operator[]( int i ) const { return *((const T *)Get() + i); } T & operator[]( int i ) { return *((T *)Get() + i); } private: @@ -499,9 +668,37 @@ template bool operator==( const CThreadLocalPtr &p ) const; bool operator!=( const CThreadLocalPtr &p ) const; }; +#if !defined(_PS3) +} +using namespace GenericThreadLocals; +#endif + + +#ifdef _OSX +PLATFORM_INTERFACE GenericThreadLocals::CThreadLocalInt g_nThreadID; +#else // _OSX +#ifndef TIER0_DLL_EXPORT + +#ifndef _PS3 +DLL_GLOBAL_IMPORT CTHREADLOCALINT g_nThreadID; +#endif // !_PS3 + +#endif // TIER0_DLL_EXPORT +#endif // _OSX + +#endif /// afx32 +#endif //__win32 #endif // NO_THREAD_LOCAL -#endif // !__AFXTLS_H__ + +#ifdef _WIN64 +// 64 bit windows can use intrinsics for these, 32-bit can't +#pragma intrinsic( _InterlockedCompareExchange64 ) +#pragma intrinsic( _InterlockedExchange64 ) +#pragma intrinsic( _InterlockedExchangeAdd64 ) +inline int64 ThreadInterlockedIncrement64(int64 volatile *p) { AssertDbg((size_t)p % 8 == 0); return _InterlockedIncrement64((volatile int64*)p); } +inline int64 ThreadInterlockedDecrement64(int64 volatile *p) { AssertDbg((size_t)p % 8 == 0); return _InterlockedDecrement64((volatile int64*)p); } +#endif //----------------------------------------------------------------------------- // @@ -516,45 +713,67 @@ template class CInterlockedIntT { public: - CInterlockedIntT() : m_value( 0 ) { COMPILE_TIME_ASSERT( sizeof(T) == sizeof(int) ); } + CInterlockedIntT() : m_value( 0 ) { COMPILE_TIME_ASSERT( ( sizeof(T) == sizeof(int32) ) || ( sizeof(T) == sizeof(int64) ) ); } + CInterlockedIntT( T value ) : m_value( value ) {} - T GetRaw() const { return m_value; } - + T operator()( void ) const { return m_value; } operator T() const { return m_value; } bool operator!() const { return ( m_value == 0 ); } bool operator==( T rhs ) const { return ( m_value == rhs ); } bool operator!=( T rhs ) const { return ( m_value != rhs ); } - -#if defined( __arm__ ) || defined( __arm64__ ) - CInterlockedIntT( const CInterlockedIntT &rhs ) : m_value( rhs ) {} - CInterlockedIntT &operator=( const CInterlockedIntT &rhs ) { m_value.store(rhs.m_value.load()); return *this; } - T operator++() { return m_value.fetch_add(1) + 1; } - T operator++(int) { return m_value.fetch_add(1); } - - T operator--() { return m_value.fetch_sub(1) - 1; } - T operator--(int) { return m_value.fetch_sub(1); } - - bool AssignIf( T conditionValue, T newValue ) { return m_value.compare_exchange_strong(conditionValue, newValue); } - - T operator=( T newValue ) { m_value.store(newValue); return newValue; } - - void operator+=( T add ) { m_value.fetch_add(add); } -#else - T operator++() { return (T)ThreadInterlockedIncrement( (int *)&m_value ); } + T operator++() { + if ( sizeof(T) == sizeof(int32) ) + return (T)ThreadInterlockedIncrement( (int32 *)&m_value ); + else + return (T)ThreadInterlockedIncrement64( (int64 *)&m_value ); + } T operator++(int) { return operator++() - 1; } - T operator--() { return (T)ThreadInterlockedDecrement( (int *)&m_value ); } + T operator--() { + if ( sizeof(T) == sizeof(int32) ) + return (T)ThreadInterlockedDecrement( (int32 *)&m_value ); + else + return (T)ThreadInterlockedDecrement64( (int64 *)&m_value ); + } + T operator--(int) { return operator--() + 1; } - bool AssignIf( T conditionValue, T newValue ) { return ThreadInterlockedAssignIf( (int *)&m_value, (int)newValue, (int)conditionValue ); } + bool AssignIf( T conditionValue, T newValue ) + { + if ( sizeof(T) == sizeof(int32) ) + return ThreadInterlockedAssignIf( (int32 *)&m_value, (int32)newValue, (int32)conditionValue ); + else + return ThreadInterlockedAssignIf64( (int64 *)&m_value, (int64)newValue, (int64)conditionValue ); + } - T operator=( T newValue ) { ThreadInterlockedExchange((int *)&m_value, newValue); return m_value; } - void operator+=( T add ) { ThreadInterlockedExchangeAdd( (int *)&m_value, (int)add ); } -#endif + T operator=( T newValue ) { + if ( sizeof(T) == sizeof(int32) ) + ThreadInterlockedExchange((int32 *)&m_value, newValue); + else + ThreadInterlockedExchange64((int64 *)&m_value, newValue); + return m_value; + } + + // Atomic add is like += except it returns the previous value as its return value + T AtomicAdd( T add ) { + if ( sizeof(T) == sizeof(int32) ) + return (T)ThreadInterlockedExchangeAdd( (int32 *)&m_value, (int32)add ); + else + return (T)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, (int64)add ); + } + + + void operator+=( T add ) { + if ( sizeof(T) == sizeof(int32) ) + ThreadInterlockedExchangeAdd( (int32 *)&m_value, (int32)add ); + else + ThreadInterlockedExchangeAdd64( (int64 *)&m_value, (int64)add ); + } + void operator-=( T subtract ) { operator+=( -subtract ); } void operator*=( T multiplier ) { T original, result; @@ -576,19 +795,22 @@ public: T operator+( T rhs ) const { return m_value + rhs; } T operator-( T rhs ) const { return m_value - rhs; } + T InterlockedExchange(T newValue) { + if (sizeof(T) == sizeof(int32)) + return (T)ThreadInterlockedExchange((int32*)&m_value, newValue); + else + return (T)ThreadInterlockedExchange64((int64*)&m_value, newValue); + } + private: -#if defined( __arm__ ) || defined( __arm64__ ) - std::atomic m_value; -#else volatile T m_value; -#endif }; typedef CInterlockedIntT CInterlockedInt; typedef CInterlockedIntT CInterlockedUInt; //----------------------------------------------------------------------------- - +#ifdef _M_X64 template class CInterlockedPtr { @@ -601,50 +823,23 @@ public: bool operator!() const { return ( m_value == 0 ); } bool operator==( T *rhs ) const { return ( m_value == rhs ); } bool operator!=( T *rhs ) const { return ( m_value != rhs ); } -#if defined( __arm__ ) || defined( __arm64__ ) - CInterlockedPtr( const CInterlockedPtr &rhs ) : m_value( rhs ) {} - CInterlockedPtr &operator=( const CInterlockedPtr &rhs ) { m_value.store(rhs.m_value.load()); return *this; } - T *operator++() { return m_value.fetch_add(1) + 1; } - T *operator++(int) { return m_value.fetch_add(1); } - T *operator--() { return m_value.fetch_sub(1) - 1; } - T *operator--(int) { return m_value.fetch_sub(1); } + T *operator++() { return ((T *)_InterlockedExchangeAdd64( (volatile __int64 *)&m_value, sizeof(T) )) + 1; } + T *operator++(int) { return (T *)_InterlockedExchangeAdd64( (volatile __int64 *)&m_value, sizeof(T) ); } - bool AssignIf( T *conditionValue, T *newValue ) { return m_value.compare_exchange_strong(conditionValue, newValue); } + T *operator--() { return ((T *)_InterlockedExchangeAdd64( (volatile __int64 *)&m_value, -sizeof(T) )) - 1; } + T *operator--(int) { return (T *)_InterlockedExchangeAdd64( (volatile __int64 *)&m_value, -sizeof(T) ); } - T *operator=( T *newValue ) { m_value.store(newValue); return newValue; } + bool AssignIf( T *conditionValue, T *newValue ) { return _InterlockedCompareExchangePointer( (void * volatile *)&m_value, newValue, conditionValue ) == conditionValue; } - void operator+=( int add ) { m_value.fetch_add(add); } -#else -#if defined( PLATFORM_64BITS ) - T *operator++() { return ((T *)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, sizeof(T) )) + 1; } - T *operator++(int) { return (T *)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, sizeof(T) ); } - - T *operator--() { return ((T *)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, -sizeof(T) )) - 1; } - T *operator--(int) { return (T *)ThreadInterlockedExchangeAdd64( (int64 *)&m_value, -sizeof(T) ); } - - bool AssignIf( T *conditionValue, T *newValue ) { return ThreadInterlockedAssignPointerToConstIf( (void const **) &m_value, (void const *) newValue, (void const *) conditionValue ); } - - T *operator=( T *newValue ) { ThreadInterlockedExchangePointerToConst( (void const **) &m_value, (void const *) newValue ); return newValue; } - - void operator+=( int add ) { ThreadInterlockedExchangeAdd64( (int64 *)&m_value, add * sizeof(T) ); } -#else - T *operator++() { return ((T *)ThreadInterlockedExchangeAdd( (long *)&m_value, sizeof(T) )) + 1; } - T *operator++(int) { return (T *)ThreadInterlockedExchangeAdd( (long *)&m_value, sizeof(T) ); } - - T *operator--() { return ((T *)ThreadInterlockedExchangeAdd( (long *)&m_value, -sizeof(T) )) - 1; } - T *operator--(int) { return (T *)ThreadInterlockedExchangeAdd( (long *)&m_value, -sizeof(T) ); } - - bool AssignIf( T *conditionValue, T *newValue ) { return ThreadInterlockedAssignPointerToConstIf( (void const **) &m_value, (void const *) newValue, (void const *) conditionValue ); } - - T *operator=( T *newValue ) { ThreadInterlockedExchangePointerToConst( (void const **) &m_value, (void const *) newValue ); return newValue; } - - void operator+=( int add ) { ThreadInterlockedExchangeAdd( (long *)&m_value, add * sizeof(T) ); } -#endif -#endif + T *operator=( T *newValue ) { _InterlockedExchangePointer( (void * volatile *) &m_value, newValue ); return newValue; } + void operator+=( int add ) { _InterlockedExchangeAdd64( (volatile __int64 *)&m_value, add * sizeof(T) ); } void operator-=( int subtract ) { operator+=( -subtract ); } + // Atomic add is like += except it returns the previous value as its return value + T *AtomicAdd( int add ) { return ( T * )_InterlockedExchangeAdd64( (volatile __int64 *)&m_value, add * sizeof(T) ); } + T *operator+( int rhs ) const { return m_value + rhs; } T *operator-( int rhs ) const { return m_value - rhs; } T *operator+( unsigned rhs ) const { return m_value + rhs; } @@ -653,47 +848,61 @@ public: size_t operator-( const CInterlockedPtr &p ) const { return m_value - p.m_value; } private: -#if defined( __arm__ ) || defined( __arm64__ ) - std::atomic m_value; -#else T * volatile m_value; -#endif }; - -//----------------------------------------------------------------------------- -// -// Platform independent verification that multiple threads aren't getting into the same code at the same time. -// Note: This is intended for use to identify problems, it doesn't provide any sort of thread safety. -// -//----------------------------------------------------------------------------- -class ReentrancyVerifier +#else +template +class CInterlockedPtr { public: - inline ReentrancyVerifier(CInterlockedInt* counter, int sleepTimeMS) - : mCounter(counter) + CInterlockedPtr() : m_value( 0 ) { - Assert(mCounter != NULL); - - if (++(*mCounter) != 1) { - DebuggerBreakIfDebugging_StagingOnly(); - } - - if (sleepTimeMS > 0) - { - ThreadSleep(sleepTimeMS); - } +#ifdef PLATFORM_64BITS + COMPILE_TIME_ASSERT( sizeof(T *) == sizeof(int64) ); +#define THREADINTERLOCKEDEXCHANGEADD( _dest, _value ) ThreadInterlockedExchangeAdd64( (int64 *)(_dest), _value ) +#else // PLATFORM_64BITS + COMPILE_TIME_ASSERT( sizeof(T *) == sizeof(int32) ); +#define THREADINTERLOCKEDEXCHANGEADD( _dest, _value ) ThreadInterlockedExchangeAdd( (int32 *)_dest, _value ) +#endif // PLATFORM_64BITS } - inline ~ReentrancyVerifier() - { - if (--(*mCounter) != 0) { - DebuggerBreakIfDebugging_StagingOnly(); - } - } + CInterlockedPtr( T *value ) : m_value( value ) {} + + operator T *() const { return m_value; } + + bool operator!() const { return ( m_value == 0 ); } + bool operator==( T *rhs ) const { return ( m_value == rhs ); } + bool operator!=( T *rhs ) const { return ( m_value != rhs ); } + + T *operator++() { return ((T *)THREADINTERLOCKEDEXCHANGEADD( (int32 *)&m_value, sizeof(T) )) + 1; } + T *operator++(int) { return (T *)THREADINTERLOCKEDEXCHANGEADD( (int32 *)&m_value, sizeof(T) ); } + + T *operator--() { return ((T *)THREADINTERLOCKEDEXCHANGEADD( (int32 *)&m_value, -sizeof(T) )) - 1; } + T *operator--(int) { return (T *)THREADINTERLOCKEDEXCHANGEADD( (int32 *)&m_value, -sizeof(T) ); } + + bool AssignIf( T *conditionValue, T *newValue ) { return ThreadInterlockedAssignPointerToConstIf( (void const **) &m_value, (void const *) newValue, (void const *) conditionValue ); } + + T *operator=( T *newValue ) { ThreadInterlockedExchangePointerToConst( (void const **) &m_value, (void const *) newValue ); return newValue; } + + void operator+=( int add ) { THREADINTERLOCKEDEXCHANGEADD( (int32 *)&m_value, add * sizeof(T) ); } + void operator-=( int subtract ) { operator+=( -subtract ); } + + // Atomic add is like += except it returns the previous value as its return value + T *AtomicAdd( int add ) { return ( T * ) THREADINTERLOCKEDEXCHANGEADD( (int32 *)&m_value, add * sizeof(T) ); } + + T *operator+( int rhs ) const { return m_value + rhs; } + T *operator-( int rhs ) const { return m_value - rhs; } + T *operator+( unsigned rhs ) const { return m_value + rhs; } + T *operator-( unsigned rhs ) const { return m_value - rhs; } + size_t operator-( T *p ) const { return m_value - p; } + size_t operator-( const CInterlockedPtr &p ) const { return m_value - p.m_value; } private: - CInterlockedInt* mCounter; + T * volatile m_value; + +#undef THREADINTERLOCKEDEXCHANGEADD }; +#endif //----------------------------------------------------------------------------- @@ -719,12 +928,21 @@ public: bool TryLock(); bool TryLock() const { return (const_cast(this))->TryLock(); } + void LockSilent(); // A Lock() operation which never spews. Required by the logging system to prevent badness. + void UnlockSilent(); // An Unlock() operation which never spews. Required by the logging system to prevent badness. + //------------------------------------------------------ // Use this to make deadlocks easier to track by asserting // when it is expected that the current thread owns the mutex //------------------------------------------------------ bool AssertOwnedByCurrentThread(); + //------------------------------------------------------ + // On windows with THREAD_MUTEX_TRACING_ENABLED defined, this returns + // true if the mutex is owned by the current thread. + //------------------------------------------------------ + bool IsOwnedByCurrentThread_DebugOnly(); + //------------------------------------------------------ // Enable tracing to track deadlock problems //------------------------------------------------------ @@ -744,9 +962,11 @@ private: #define TT_SIZEOF_CRITICALSECTION 24 #else #define TT_SIZEOF_CRITICALSECTION 28 -#endif // !_XBOX +#endif // !_X360 #endif // _WIN64 byte m_CriticalSection[TT_SIZEOF_CRITICALSECTION]; +#elif defined( _PS3 ) + sys_mutex_t m_Mutex; #elif defined(POSIX) pthread_mutex_t m_Mutex; pthread_mutexattr_t m_Attr; @@ -755,7 +975,7 @@ private: #endif #ifdef THREAD_MUTEX_TRACING_SUPPORTED - // Debugging (always here to allow mixed debug/release builds w/o changing size) + // Debugging (always herge to allow mixed debug/release builds w/o changing size) uint m_currentOwnerID; uint16 m_lockCount; bool m_bTrace; @@ -784,13 +1004,9 @@ public: } private: - FORCEINLINE bool TryLockInline( const uintp threadId ) volatile + FORCEINLINE bool TryLockInline( const uint32 threadId ) volatile { -#if PLATFORM_64BITS - if ( threadId != m_ownerID && !ThreadInterlockedAssignIf64( &m_ownerID, threadId, 0 ) ) -#else - if ( threadId != m_ownerID && !ThreadInterlockedAssignIf( &m_ownerID, threadId, 0 ) ) -#endif + if ( threadId != m_ownerID && !ThreadInterlockedAssignIf( (volatile int32 *)&m_ownerID, (int32)threadId, 0 ) ) return false; ThreadMemoryBarrier(); @@ -798,12 +1014,12 @@ private: return true; } - bool TryLock( const uintp threadId ) volatile + bool TryLock( const uint32 threadId ) volatile { return TryLockInline( threadId ); } - PLATFORM_CLASS void Lock( const uintp threadId, unsigned nSpinSleepTime ) volatile; + PLATFORM_CLASS void Lock( const uint32 threadId, unsigned nSpinSleepTime ) volatile; public: bool TryLock() volatile @@ -823,7 +1039,7 @@ public: #endif void Lock( unsigned int nSpinSleepTime = 0 ) volatile { - const uintp threadId = ThreadGetCurrentId(); + const uint32 threadId = ThreadGetCurrentId(); if ( !TryLockInline( threadId ) ) { @@ -831,7 +1047,7 @@ public: Lock( threadId, nSpinSleepTime ); } #ifdef _DEBUG - if ( m_ownerID != ThreadGetCurrentId() ) + if ( m_ownerID != (int32)ThreadGetCurrentId() ) DebuggerBreak(); if ( m_depth == INT_MAX ) @@ -848,7 +1064,7 @@ public: void Unlock() volatile { #ifdef _DEBUG - if ( m_ownerID != ThreadGetCurrentId() ) + if ( m_ownerID != (int32)ThreadGetCurrentId() ) DebuggerBreak(); if ( m_depth <= 0 ) @@ -859,35 +1075,25 @@ public: if ( !m_depth ) { ThreadMemoryBarrier(); -#if PLATFORM_64BITS - ThreadInterlockedExchange64( &m_ownerID, 0 ); -#else - ThreadInterlockedExchange( &m_ownerID, 0 ); -#endif - } - } + ThreadInterlockedExchange( &m_ownerID, 0 ); + } + } -#ifdef WIN32 bool TryLock() const volatile { return (const_cast(this))->TryLock(); } - void Lock(unsigned nSpinSleepTime = 1 ) const volatile { (const_cast(this))->Lock( nSpinSleepTime ); } + void Lock(unsigned nSpinSleepTime = 0 ) const volatile { (const_cast(this))->Lock( nSpinSleepTime ); } void Unlock() const volatile { (const_cast(this))->Unlock(); } -#endif + // To match regular CThreadMutex: bool AssertOwnedByCurrentThread() { return true; } void SetTrace( bool ) {} - uintp GetOwnerId() const { return m_ownerID; } + uint32 GetOwnerId() const { return m_ownerID; } int GetDepth() const { return m_depth; } private: - volatile uintp m_ownerID; + volatile uint32 m_ownerID; int m_depth; }; -#ifdef COMPILER_CLANG -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wunused-private-field" -#endif // Q_CC_CLANG - class ALIGN128 CAlignedThreadFastMutex : public CThreadFastMutex { public: @@ -898,14 +1104,65 @@ public: private: uint8 pad[128-sizeof(CThreadFastMutex)]; -} ALIGN128_POST; - -#ifdef COMPILER_CLANG -# pragma clang diagnostic pop -#endif +}; #else +#ifdef _PS3 + +class CThreadFastMutex +{ +public: + CThreadFastMutex(); + ~CThreadFastMutex(); + + //------------------------------------------------------ + // Mutex acquisition/release. Const intentionally defeated. + //------------------------------------------------------ + void Lock(); + void Lock() const { (const_cast(this))->Lock(); } + void Unlock(); + void Unlock() const { (const_cast(this))->Unlock(); } + + bool TryLock(); + bool TryLock() const { return (const_cast(this))->TryLock(); } + + //------------------------------------------------------ + // Use this to make deadlocks easier to track by asserting + // when it is expected that the current thread owns the mutex + //------------------------------------------------------ + bool AssertOwnedByCurrentThread(); + + //------------------------------------------------------ + // Enable tracing to track deadlock problems + //------------------------------------------------------ + void SetTrace( bool ); + +private: + // Disallow copying + CThreadFastMutex( const CThreadFastMutex & ); + //CThreadFastMutex &operator=( const CThreadFastMutex & ); + sys_lwmutex_t m_Mutex; + sys_mutex_t m_SlowMutex; +}; + +#else + typedef CThreadMutex CThreadFastMutex; + +#endif + +class ALIGN128 CAlignedThreadFastMutex : public CThreadFastMutex +{ +public: + CAlignedThreadFastMutex() + { + Assert( (size_t)this % 128 == 0 && sizeof(*this) == 128 ); + } + +private: + uint8 pad[128-sizeof(CThreadFastMutex)]; +}; + #endif //----------------------------------------------------------------------------- @@ -922,7 +1179,7 @@ public: static bool AssertOwnedByCurrentThread() { return true; } static void SetTrace( bool b ) {} - static uintp GetOwnerId() { return 0; } + static uint32 GetOwnerId() { return 0; } static int GetDepth() { return 0; } }; @@ -974,75 +1231,71 @@ template class CAutoLockT { public: - FORCEINLINE CAutoLockT( MUTEX_TYPE &lock, const char* pMutexName, const char* pFilename, int nLineNum, uint64 minReportDurationUs ) - : m_lock( const_cast< typename V_remove_const< MUTEX_TYPE >::type & >( lock ) ) - , m_pMutexName( pMutexName ) - , m_pFilename( pFilename ) - , m_nLineNum( nLineNum ) - , m_bOwned( true ) + FORCEINLINE CAutoLockT( MUTEX_TYPE &lock) + : m_lock(lock) { - tmTryLockEx( TELEMETRY_LEVEL0, &m_uLockMatcher, minReportDurationUs, pFilename, nLineNum, &m_lock, pMutexName ); m_lock.Lock(); - tmEndTryLockEx( TELEMETRY_LEVEL0, m_uLockMatcher, pFilename, nLineNum, &m_lock, TMLR_SUCCESS ); - tmSetLockStateEx( TELEMETRY_LEVEL0, pFilename, nLineNum, &m_lock, TMLS_LOCKED, pMutexName ); } - FORCEINLINE CAutoLockT( CAutoLockT && rhs ) - : m_lock( const_cast< typename V_remove_const< MUTEX_TYPE >::type &>( rhs.m_lock ) ) + FORCEINLINE CAutoLockT(const MUTEX_TYPE &lock) + : m_lock(const_cast(lock)) { - m_pMutexName = rhs.m_pMutexName; - m_pFilename = rhs.m_pFilename; - m_nLineNum = rhs.m_nLineNum; - #ifdef RAD_TELEMETRY_ENABLED - m_uLockMatcher = rhs.m_uLockMatcher; - #endif - m_bOwned = true; - rhs.m_bOwned = false; + m_lock.Lock(); } FORCEINLINE ~CAutoLockT() { - if ( m_bOwned ) - { - m_lock.Unlock(); - tmSetLockStateEx( TELEMETRY_LEVEL0, m_pFilename, m_nLineNum, &m_lock, TMLS_RELEASED, m_pMutexName ); - } + m_lock.Unlock(); } -private: - typename V_remove_const< MUTEX_TYPE >::type &m_lock; - const char* m_pMutexName; - const char* m_pFilename; - int m_nLineNum; - bool m_bOwned; // Did owenership of the lock pass to another instance? -#ifdef RAD_TELEMETRY_ENABLED - TmU64 m_uLockMatcher; -#endif +private: + MUTEX_TYPE &m_lock; // Disallow copying CAutoLockT( const CAutoLockT & ); CAutoLockT &operator=( const CAutoLockT & ); - - // No move assignment because no default construction. - CAutoLockT &operator=( CAutoLockT && ); }; typedef CAutoLockT CAutoLock; -template < typename MUTEX_TYPE > -inline CAutoLockT make_auto_lock( MUTEX_TYPE& lock, const char* pMutexname, const char* pFilename, int nLineNum, int nMinReportDurationUs = 1 ) -{ - return CAutoLockT( lock, pMutexname, pFilename, nLineNum, nMinReportDurationUs ); -} - //--------------------------------------------------------- -#define AUTO_LOCK( mutex ) \ - auto UNIQUE_ID = make_auto_lock( mutex, #mutex, __FILE__, __LINE__ ); +template struct CAutoLockTypeDeducer {}; +template <> struct CAutoLockTypeDeducer { typedef CThreadMutex Type_t; }; +template <> struct CAutoLockTypeDeducer { typedef CThreadNullMutex Type_t; }; +#if !defined(THREAD_PROFILER) +template <> struct CAutoLockTypeDeducer { typedef CThreadFastMutex Type_t; }; +template <> struct CAutoLockTypeDeducer { typedef CAlignedThreadFastMutex Type_t; }; +#else +template <> struct CAutoLockTypeDeducer { typedef CAlignedThreadFastMutex Type_t; }; +#endif -#define AUTO_LOCK_D( mutex, minDurationUs ) \ - auto UNIQUE_ID = make_auto_lock( mutex, #mutex, __FILE__, __LINE__, minDurationUs ); + +#define AUTO_LOCK_( type, mutex ) \ + CAutoLockT< type > UNIQUE_ID( static_cast( mutex ) ) + +#if defined(GNUC) + +template T strip_cv_quals_for_mutex(T&); +template T strip_cv_quals_for_mutex(const T&); +template T strip_cv_quals_for_mutex(volatile T&); +template T strip_cv_quals_for_mutex(const volatile T&); + +#define AUTO_LOCK( mutex ) \ + AUTO_LOCK_( decltype(::strip_cv_quals_for_mutex(mutex)), mutex ) + +#elif defined( __clang__ ) +#define AUTO_LOCK( mutex ) \ + AUTO_LOCK_( typename CAutoLockTypeDeducer::Type_t, mutex ) +#else +#define AUTO_LOCK( mutex ) \ + AUTO_LOCK_( CAutoLockTypeDeducer::Type_t, mutex ) +#endif + + +#define AUTO_LOCK_FM( mutex ) \ + AUTO_LOCK_( CThreadFastMutex, mutex ) #define LOCAL_THREAD_LOCK_( tag ) \ ; \ @@ -1058,6 +1311,11 @@ inline CAutoLockT make_auto_lock( MUTEX_TYPE& lock, const char* pMut // //----------------------------------------------------------------------------- +// TW_TIMEOUT must match WAIT_TIMEOUT definition +#define TW_TIMEOUT 0x00000102 +// TW_FAILED must match WAIT_FAILED definition +#define TW_FAILED 0xFFFFFFFF + class PLATFORM_CLASS CThreadSyncObject { public: @@ -1080,6 +1338,21 @@ public: //----------------------------------------------------- bool Wait( uint32 dwTimeout = TT_INFINITE ); + //----------------------------------------------------- + // Wait for a signal from any of the specified objects. + // + // Returns the index of the object that signaled the event + // or THREADSYNC_TIMEOUT if the timeout was hit before the wait condition was met. + // + // Returns TW_FAILED if an incoming object is invalid. + // + // If bWaitAll=true, then it'll return 0 if all the objects were set. + //----------------------------------------------------- + static uint32 WaitForMultiple( int nObjects, CThreadSyncObject **ppObjects, bool bWaitAll, uint32 dwTimeout = TT_INFINITE ); + + // This builds a list of pointers and calls straight through to the other WaitForMultiple. + static uint32 WaitForMultiple( int nObjects, CThreadSyncObject *ppObjects, bool bWaitAll, uint32 dwTimeout = TT_INFINITE ); + protected: CThreadSyncObject(); void AssertUseable(); @@ -1087,6 +1360,10 @@ protected: #ifdef _WIN32 HANDLE m_hSyncObject; bool m_bCreatedHandle; +#elif defined( _PS3 ) + static sys_lwmutex_t m_staticMutex; + static uint32_t m_bstaticMutexInitialized; + static uint32_t m_bstaticMutexInitializing; #elif defined(POSIX) pthread_mutex_t m_Mutex; pthread_cond_t m_Condition; @@ -1110,7 +1387,6 @@ private: // //----------------------------------------------------------------------------- -#if defined( _WIN32 ) //----------------------------------------------------------------------------- // @@ -1121,19 +1397,30 @@ private: class PLATFORM_CLASS CThreadSemaphore : public CThreadSyncObject { public: - CThreadSemaphore(long initialValue, long maxValue); + CThreadSemaphore(int32 initialValue, int32 maxValue); //----------------------------------------------------- // Increases the count of the semaphore object by a specified // amount. Wait() decreases the count by one on return. //----------------------------------------------------- - bool Release(long releaseCount = 1, long * pPreviousCount = NULL ); + bool Release(int32 releaseCount = 1, int32 * pPreviousCount = NULL ); + bool Wait( uint32 dwTimeout = TT_INFINITE ); private: CThreadSemaphore(const CThreadSemaphore &); CThreadSemaphore &operator=(const CThreadSemaphore &); +#ifdef _PS3 + bool AddWaitingThread(); + void RemoveWaitingThread(); + sys_semaphore_t m_Semaphore; + sys_semaphore_value_t m_sema_max_val; + uint32_t m_numWaitingThread; + uint32_t m_bInitalized; + uint32_t m_semaCount; +#endif }; +#if defined( _WIN32 ) //----------------------------------------------------------------------------- // @@ -1164,12 +1451,54 @@ private: }; #endif +enum NamedEventResult_t +{ + TT_EventDoesntExist = 0, + TT_EventNotSignaled, + TT_EventSignaled +}; +#if defined( _PS3 ) +//--------------------------------------------------------------------------- +// CThreadEventWaitObject - the purpose of this class is to help implement +// WaitForMultipleObejcts on PS3. +// +// Each event maintains a linked list of CThreadEventWaitObjects. When a +// thread wants to wait on an event it passes the event a semaphore that +// ptr to see the index of the event that triggered it +// +// The thread-specific mutex is to ensure that setting the index and setting the +// semaphore are atomic +//--------------------------------------------------------------------------- + +class CThreadEventWaitObject +{ +public: + CThreadEventWaitObject *m_pPrev, *m_pNext; + sys_semaphore_t *m_pSemaphore; + int m_index; + int *m_pFlag; + + CThreadEventWaitObject() {} + + void Init(sys_semaphore_t *pSem, int index, int *pFlag) + { + m_pSemaphore = pSem; + m_index = index; + m_pFlag = pFlag; + } + + void Set(); +}; +#endif //_PS3 class PLATFORM_CLASS CThreadEvent : public CThreadSyncObject { public: CThreadEvent( bool fManualReset = false ); -#ifdef WIN32 +#ifdef PLATFORM_WINDOWS + CThreadEvent( const char *name, bool initialState = false, bool bManualReset = false ); + static NamedEventResult_t CheckNamedEvent( const char *name, uint32 dwTimeout = 0 ); + CThreadEvent( HANDLE hHandle ); #endif //----------------------------------------------------- @@ -1185,13 +1514,88 @@ public: //----------------------------------------------------- // Check if the event is signaled //----------------------------------------------------- - bool Check(); + bool Check(); // Please, use for debugging only! bool Wait( uint32 dwTimeout = TT_INFINITE ); + // See CThreadSyncObject for definitions of these functions. + static uint32 WaitForMultiple( int nObjects, CThreadEvent **ppObjects, bool bWaitAll, uint32 dwTimeout = TT_INFINITE ); + // To implement these, I need to check that casts are safe + static uint32 WaitForMultiple( int nObjects, CThreadEvent *ppObjects, bool bWaitAll, uint32 dwTimeout = TT_INFINITE ); + +#ifdef _PS3 + void RegisterWaitingThread(sys_semaphore_t *pSemaphore, int index, int *flag); + void UnregisterWaitingThread(sys_semaphore_t *pSemaphore); +#endif + +protected: +#ifdef _PS3 + // These virtual functions need to be inline in order for the class to be exported from tier0.prx + virtual bool AddWaitingThread() + { + //This checks if the event is already signaled and if not creates a semaphore which will be signaled + //when the event is finally signaled. + bool result; + + sys_lwmutex_lock(&m_staticMutex, 0); + + if (m_bSet) + result=false; + else + { + result=true; + + m_numWaitingThread++; + + if ( m_numWaitingThread == 1 ) + { + sys_semaphore_attribute_t semAttr; + sys_semaphore_attribute_initialize( semAttr ); + int err = sys_semaphore_create( &m_Semaphore, &semAttr, 0, 256 ); + Assert( err == CELL_OK ); + m_bInitalized = true; + } + } + + sys_lwmutex_unlock(&m_staticMutex); + return result; + } + + virtual void RemoveWaitingThread() + { + sys_lwmutex_lock(&m_staticMutex, 0); + + m_numWaitingThread--; + + if ( m_numWaitingThread == 0) + { + int err = sys_semaphore_destroy( m_Semaphore ); + Assert( err == CELL_OK ); + m_bInitalized = false; + } + + sys_lwmutex_unlock(&m_staticMutex); + } +#endif private: CThreadEvent( const CThreadEvent & ); CThreadEvent &operator=( const CThreadEvent & ); +#if defined( _PS3 ) + uint32_t m_bSet; + bool m_bManualReset; + + sys_semaphore_t m_Semaphore; + uint32_t m_numWaitingThread; + uint32_t m_bInitalized; + + CThreadEventWaitObject m_waitObjects[CTHREADEVENT_MAX_WAITING_THREADS+2]; + CThreadEventWaitObject *m_pWaitObjectsPool; + CThreadEventWaitObject *m_pWaitObjectsList; + + CThreadEventWaitObject* LLUnlinkNode(CThreadEventWaitObject *node); + CThreadEventWaitObject* LLLinkNode(CThreadEventWaitObject* list, CThreadEventWaitObject *node); + +#endif }; // Hard-wired manual event for use in array declarations @@ -1204,21 +1608,8 @@ public: } }; -inline int ThreadWaitForEvents( int nEvents, CThreadEvent * const *pEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) -{ -#ifdef POSIX - Assert( nEvents == 1); - if ( pEvents[0]->Wait( timeout ) ) - return WAIT_OBJECT_0; - else - return WAIT_TIMEOUT; -#else - HANDLE handles[64]; - for ( int i = 0; i < min( nEvents, (int)ARRAYSIZE(handles) ); i++ ) - handles[i] = pEvents[i]->GetHandle(); - return ThreadWaitForObjects( nEvents, handles, bWaitAll, timeout ); -#endif -} +PLATFORM_INTERFACE int ThreadWaitForObjects( int nEvents, const HANDLE *pHandles, bool bWaitAll = true, unsigned timeout = TT_INFINITE ); +inline int ThreadWaitForEvents( int nEvents, const CThreadEvent *pEvents, bool bWaitAll = true, unsigned timeout = TT_INFINITE ) { return ThreadWaitForObjects( nEvents, (const HANDLE *)pEvents, bWaitAll, timeout ); } //----------------------------------------------------------------------------- // @@ -1263,10 +1654,80 @@ private: // //----------------------------------------------------------------------------- +#ifndef OLD_SPINRWLOCK class ALIGN8 PLATFORM_CLASS CThreadSpinRWLock { public: - CThreadSpinRWLock() { Assert( (intp)this % 8 == 0 ); memset( this, 0, sizeof( *this ) ); } + CThreadSpinRWLock() + { + m_lockInfo.m_i32 = 0; + m_writerId = 0; +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + m_iWriteDepth = 0; +#endif + } + + bool IsLockedForWrite(); + bool IsLockedForRead(); + + FORCEINLINE bool TryLockForWrite(); + bool TryLockForWrite_UnforcedInline(); + + void LockForWrite(); + void SpinLockForWrite(); + + FORCEINLINE bool TryLockForRead(); + bool TryLockForRead_UnforcedInline(); + + void LockForRead(); + void SpinLockForRead(); + + void UnlockWrite(); + void UnlockRead(); + + bool TryLockForWrite() const { return const_cast(this)->TryLockForWrite(); } + bool TryLockForRead() const { return const_cast(this)->TryLockForRead(); } + void LockForRead() const { const_cast(this)->LockForRead(); } + void UnlockRead() const { const_cast(this)->UnlockRead(); } + void LockForWrite() const { const_cast(this)->LockForWrite(); } + void UnlockWrite() const { const_cast(this)->UnlockWrite(); } + +private: + enum + { + THREAD_SPIN = (8*1024) + }; + + union LockInfo_t + { + struct + { +#if PLAT_LITTLE_ENDIAN + uint16 m_nReaders; + uint16 m_fWriting; +#else + uint16 m_fWriting; + uint16 m_nReaders; +#endif + }; + uint32 m_i32; + }; + + LockInfo_t m_lockInfo; + ThreadId_t m_writerId; +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + int m_iWriteDepth; + uint32 pad; +#endif +} ALIGN8_POST; + +#else + +/* (commented out to reduce distraction in colorized editor, remove entirely when new implementation settles) +class ALIGN8 PLATFORM_CLASS CThreadSpinRWLock +{ +public: + CThreadSpinRWLock() { COMPILE_TIME_ASSERT( sizeof( LockInfo_t ) == sizeof( int64 ) ); Assert( (intp)this % 8 == 0 ); memset( this, 0, sizeof( *this ) ); } bool TryLockForWrite(); bool TryLockForRead(); @@ -1284,32 +1745,43 @@ public: void UnlockWrite() const { const_cast(this)->UnlockWrite(); } private: - - struct LockInfo_t + // This structure is used as an atomic & exchangeable 64-bit value. It would probably be better to just have one 64-bit value + // and accessor functions that make/break it, but at this late stage of development, I'm just wrapping it into union + // Beware of endianness: on Xbox/PowerPC m_writerId is high-word of m_i64; on PC, it's low-dword of m_i64 + union LockInfo_t { - LockInfo_t(uint32 thread_id = 0, int readers = 0) + struct { - m_writerId = thread_id; - m_nReaders = readers; - } - - uint32 m_writerId; - int m_nReaders; + uint32 m_writerId; + int m_nReaders; + }; + int64 m_i64; }; bool AssignIf( const LockInfo_t &newValue, const LockInfo_t &comperand ); - bool TryLockForWrite( const uintp threadId ); - void SpinLockForWrite( const uintp threadId ); + bool TryLockForWrite( const uint32 threadId ); + void SpinLockForWrite( const uint32 threadId ); volatile LockInfo_t m_lockInfo; CInterlockedInt m_nWriters; } ALIGN8_POST; +*/ +#endif //----------------------------------------------------------------------------- // // A thread wrapper similar to a Java thread. // //----------------------------------------------------------------------------- +#ifdef _PS3 +// Everything must be inline for this to work across PRX boundaries + +class CThread; +PLATFORM_INTERFACE CThread *GetCurThreadPS3(); +PLATFORM_INTERFACE void SetCurThreadPS3( CThread * ); +PLATFORM_INTERFACE void AllocateThreadID( void ); +PLATFORM_INTERFACE void FreeThreadID( void ); +#endif class PLATFORM_CLASS CThread { @@ -1320,7 +1792,7 @@ public: //----------------------------------------------------- const char *GetName(); - void SetName( const char * ); + void SetName( const char *pszName ); size_t CalcStackDepth( void *pStackVariable ) { return ((byte *)m_pStackBase - (byte *)pStackVariable); } @@ -1329,7 +1801,7 @@ public: //----------------------------------------------------- // Start thread running - error if already running - virtual bool Start( unsigned nBytesStack = 0 ); + virtual bool Start( unsigned nBytesStack = 0, ThreadPriorityEnum_t nPriority = TP_PRIORITY_DEFAULT ); // Returns true if thread has been created and hasn't yet exited bool IsAlive(); @@ -1338,11 +1810,10 @@ public: // is no longer alive. bool Join( unsigned timeout = TT_INFINITE ); -#ifdef _WIN32 // Access the thread handle directly - HANDLE GetThreadHandle(); - uint GetThreadId(); -#elif defined( LINUX ) + ThreadHandle_t GetThreadHandle(); + +#ifdef _WIN32 uint GetThreadId(); #endif @@ -1361,26 +1832,16 @@ public: int GetPriority() const; // Set the priority - bool SetPriority( int ); + bool SetPriority( int priority ); - // Request a thread to suspend, this must ONLY be called from the thread itself, not the main thread - // This suspend variant causes the thread in question to suspend at a known point in its execution - // which means you don't risk the global deadlocks/hangs potentially caused by the raw Suspend() call - void SuspendCooperative(); + // Suspend a thread, can only call from the thread itself + unsigned Suspend(); - // Resume a previously suspended thread from the Cooperative call - void ResumeCooperative(); + // Resume a suspended thread + unsigned Resume(); - // wait for a thread to execute its SuspendCooperative call - void BWaitForThreadSuspendCooperative(); - -#ifndef LINUX - // forcefully Suspend a thread - unsigned int Suspend(); - - // forcefully Resume a previously suspended thread - unsigned int Resume(); -#endif + // Check if thread is suspended + bool IsSuspended() { return !m_NotSuspendedEvent.Check(); } // Force hard-termination of thread. Used for critical failures. bool Terminate( int exitCode = 0 ); @@ -1402,7 +1863,7 @@ public: // This method causes the current thread to yield and not to be // scheduled for further execution until a certain amount of real - // time has elapsed, more or less. + // time has elapsed, more or less. Duration is in milliseconds static void Sleep( unsigned duration ); protected: @@ -1415,27 +1876,29 @@ protected: // derived class, performs the intended action of the thread. virtual int Run() = 0; - // Called when the thread is about to exit, by the about-to-exit thread. + // Called when the thread exits virtual void OnExit(); - // Called after OnExit when a thread finishes or is killed. Not virtual because no inherited classes - // override it and we don't want to change the vtable from the published SDK version. - void Cleanup(); + // Allow for custom start waiting + virtual bool WaitForCreateComplete( CThreadEvent *pEvent ); + const ThreadId_t GetThreadID() const { return (ThreadId_t)m_threadId; } - bool WaitForCreateComplete( CThreadEvent *pEvent ); +#ifdef PLATFORM_WINDOWS + const ThreadHandle_t GetThreadHandle() const { return (ThreadHandle_t)m_hThread; } + + static unsigned long __stdcall ThreadProc( void * pv ); + typedef unsigned long (__stdcall *ThreadProc_t)( void * ); +#else + static void* ThreadProc( void * pv ); + typedef void* (*ThreadProc_t)( void * pv ); +#endif + static void ThreadProcRunWithMinidumpHandler( void *pv ); - // "Virtual static" facility - typedef unsigned (__stdcall *ThreadProc_t)( void * ); virtual ThreadProc_t GetThreadProc(); virtual bool IsThreadRunning(); CThreadMutex m_Lock; - -#ifdef WIN32 - ThreadHandle_t GetThreadID() const { return (ThreadHandle_t)m_hThread; } -#else - ThreadId_t GetThreadID() const { return (ThreadId_t)m_threadId; } -#endif + CThreadEvent m_ExitEvent; // Set right before the thread's function exits. private: enum Flags @@ -1450,10 +1913,11 @@ private: CThread * pThread; CThreadEvent *pInitCompleteEvent; bool * pfInitSuccess; +#if defined( THREAD_PARENT_STACK_TRACE_ENABLED ) + void * ParentStackTrace[THREAD_PARENT_STACK_TRACE_LENGTH]; +#endif }; - static unsigned __stdcall ThreadProc( void * pv ); - // make copy constructor and assignment operator inaccessible CThread( const CThread & ); CThread &operator=( const CThread & ); @@ -1461,18 +1925,37 @@ private: #ifdef _WIN32 HANDLE m_hThread; ThreadId_t m_threadId; +#elif defined( _PS3 ) + sys_ppu_thread_t m_threadId; + volatile sys_ppu_thread_t m_threadZombieId; + + // Mutex and condition variable used by the Suspend / Resume logic + sys_mutex_t m_mutexSuspend; + sys_cond_t m_condSuspend; + + //EAPS3 Event to indicate that a thread has terminated. This helps with the replacing of WaitForMultipleObjects + // on the PS3, since it waits for a thread to finish. + CThreadEvent m_threadEnd; #elif defined(POSIX) pthread_t m_threadId; + volatile pthread_t m_threadZombieId; + //lwss add - Thread params. These were previously allocated on the heap and leaked. + ThreadInit_t m_threadInit; + //lwss end #endif - CInterlockedInt m_nSuspendCount; - CThreadEvent m_SuspendEvent; - CThreadEvent m_SuspendEventSignal; int m_result; char m_szName[32]; void * m_pStackBase; unsigned m_flags; + CThreadManualEvent m_NotSuspendedEvent; }; +// The CThread implementation needs to be inlined for performance on the PS3 - It makes a difference of more than 1ms/frame +// Since the dependency checker isn't smart enough to take an #ifdef _PS3 into account, all platforms will inline it. +#ifdef _PS3 +#include "threadtools.inl" +#endif + //----------------------------------------------------------------------------- // // A helper class to let you sleep a thread for memory validation, you need to handle @@ -1503,7 +1986,6 @@ protected: // synchronized communication. //----------------------------------------------------------------------------- - // These are internal reserved error results from a call attempt enum WTCallResult_t { @@ -1512,7 +1994,6 @@ enum WTCallResult_t WTCR_THREAD_GONE = -3, }; -class CFunctor; class PLATFORM_CLASS CWorkerThread : public CThread { public: @@ -1528,7 +2009,7 @@ public: //----------------------------------------------------- // Master: Signal the thread, and block for a response - int CallWorker( unsigned, unsigned timeout = TT_INFINITE, bool fBoostWorkerPriorityToMaster = true, CFunctor *pParamFunctor = NULL ); + int CallWorker( unsigned, unsigned timeout = TT_INFINITE, bool fBoostWorkerPriorityToMaster = true ); // Worker: Signal the thread, and block for a response int CallMaster( unsigned, unsigned timeout = TT_INFINITE ); @@ -1538,7 +2019,7 @@ public: bool WaitForCall( unsigned *pResult = NULL ); // Is there a request? - bool PeekCall( unsigned *pParam = NULL, CFunctor **ppParamFunctor = NULL ); + bool PeekCall( unsigned *pParam = NULL ); // Reply to the request void Reply( unsigned ); @@ -1548,20 +2029,17 @@ public: // If you want to do WaitForMultipleObjects you'll need to include // this handle in your wait list or you won't be responsive - CThreadEvent &GetCallHandle(); + CThreadEvent& GetCallHandle(); // (returns m_EventSend) + // Find out what the request was - unsigned GetCallParam( CFunctor **ppParamFunctor = NULL ) const; + unsigned GetCallParam() const; // Boost the worker thread to the master thread, if worker thread is lesser, return old priority int BoostPriority(); protected: -#ifndef _WIN32 -#define __stdcall -#endif - typedef uint32 (__stdcall *WaitFunc_t)( int nEvents, CThreadEvent * const *pEvents, int bWaitAll, uint32 timeout ); - - int Call( unsigned, unsigned timeout, bool fBoost, WaitFunc_t = NULL, CFunctor *pParamFunctor = NULL ); + typedef uint32 ( *WaitFunc_t)( uint32 nHandles, CThreadEvent** ppHandles, int bWaitAll, uint32 timeout ); + int Call( unsigned, unsigned timeout, bool fBoost, WaitFunc_t = NULL ); int WaitForReply( unsigned timeout, WaitFunc_t ); private: @@ -1572,7 +2050,6 @@ private: CThreadEvent m_EventComplete; unsigned m_Param; - CFunctor *m_pParamFunctor; int m_ReturnVal; }; @@ -1660,7 +2137,7 @@ public: // //----------------------------------------------------------------------------- -#ifdef _WIN32 +#ifdef MSVC typedef struct _RTL_CRITICAL_SECTION RTL_CRITICAL_SECTION; typedef RTL_CRITICAL_SECTION CRITICAL_SECTION; @@ -1673,18 +2150,25 @@ extern "C" void __declspec(dllimport) __stdcall DeleteCriticalSection(CRITICAL_SECTION *); }; #endif +#endif //--------------------------------------------------------- +#if !defined(POSIX) || defined( _GAMECONSOLE ) inline void CThreadMutex::Lock() { -#ifdef THREAD_MUTEX_TRACING_ENABLED +#if defined(_PS3) + #ifndef NO_THREAD_SYNC + sys_mutex_lock( m_Mutex, 0 ); + #endif +#else + #if defined( THREAD_MUTEX_TRACING_ENABLED ) uint thisThreadID = ThreadGetCurrentId(); if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) ) - Msg( "Thread %u about to wait for lock %p owned by %u\n", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); + Msg( _T( "Thread %u about to wait for lock %p owned by %u\n" ), ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); #endif - VCRHook_EnterCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); + LockSilent(); #ifdef THREAD_MUTEX_TRACING_ENABLED if (m_lockCount == 0) @@ -1692,27 +2176,58 @@ inline void CThreadMutex::Lock() // we now own it for the first time. Set owner information m_currentOwnerID = thisThreadID; if ( m_bTrace ) - Msg( "Thread %u now owns lock %p\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); + Msg( _T( "Thread %u now owns lock 0x%p\n" ), m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); } m_lockCount++; #endif +#endif } //--------------------------------------------------------- inline void CThreadMutex::Unlock() { +#if defined( _PS3 ) + + #ifndef NO_THREAD_SYNC + sys_mutex_unlock( m_Mutex ); + #endif + +#else #ifdef THREAD_MUTEX_TRACING_ENABLED AssertMsg( m_lockCount >= 1, "Invalid unlock of thread lock" ); m_lockCount--; if (m_lockCount == 0) { if ( m_bTrace ) - Msg( "Thread %u releasing lock %p\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); + Msg( _T( "Thread %u releasing lock 0x%p\n" ), m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); m_currentOwnerID = 0; } #endif + UnlockSilent(); +#endif +} + +//--------------------------------------------------------- + +inline void CThreadMutex::LockSilent() +{ + #ifdef MSVC + EnterCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); + #else + DebuggerBreak(); // should not be called - not defined for this platform/compiler!!! + #endif +} + +//--------------------------------------------------------- + +inline void CThreadMutex::UnlockSilent() +{ + #ifdef MSVC LeaveCriticalSection((CRITICAL_SECTION *)&m_CriticalSection); + #else + DebuggerBreak(); // should not be called - not defined for this platform/compiler!!! + #endif } //--------------------------------------------------------- @@ -1720,10 +2235,23 @@ inline void CThreadMutex::Unlock() inline bool CThreadMutex::AssertOwnedByCurrentThread() { #ifdef THREAD_MUTEX_TRACING_ENABLED +#ifdef _WIN32 if (ThreadGetCurrentId() == m_currentOwnerID) return true; - AssertMsg3( 0, "Expected thread %u as owner of lock %p, but %u owns", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); + AssertMsg3( 0, "Expected thread %u as owner of lock 0x%p, but %u owns", ThreadGetCurrentId(), (CRITICAL_SECTION *)&m_CriticalSection, m_currentOwnerID ); return false; +#elif defined( _PS3 ) + return true; +#endif +#else + return true; +#endif +} + +inline bool CThreadMutex::IsOwnedByCurrentThread_DebugOnly() +{ +#if defined ( THREAD_MUTEX_TRACING_ENABLED ) && defined ( _WIN32 ) + return ThreadGetCurrentId() == m_currentOwnerID; #else return true; #endif @@ -1733,14 +2261,19 @@ inline bool CThreadMutex::AssertOwnedByCurrentThread() inline void CThreadMutex::SetTrace( bool bTrace ) { +#ifdef _WIN32 #ifdef THREAD_MUTEX_TRACING_ENABLED m_bTrace = bTrace; #endif +#elif defined _PS3 + //EAPS3 +#endif + } //--------------------------------------------------------- -#elif defined(POSIX) +#elif defined(POSIX) && !defined( _GAMECONSOLE ) inline CThreadMutex::CThreadMutex() { @@ -1773,6 +2306,20 @@ inline void CThreadMutex::Unlock() //--------------------------------------------------------- +inline void CThreadMutex::LockSilent() +{ + pthread_mutex_lock( &m_Mutex ); +} + +//--------------------------------------------------------- + +inline void CThreadMutex::UnlockSilent() +{ + pthread_mutex_unlock( &m_Mutex ); +} + +//--------------------------------------------------------- + inline bool CThreadMutex::AssertOwnedByCurrentThread() { return true; @@ -1784,6 +2331,8 @@ inline void CThreadMutex::SetTrace(bool fTrace) { } +#else +#error #endif // POSIX //----------------------------------------------------------------------------- @@ -1829,18 +2378,165 @@ inline void CThreadRWLock::UnlockRead() // //----------------------------------------------------------------------------- -inline bool CThreadSpinRWLock::AssignIf( const LockInfo_t &newValue, const LockInfo_t &comperand ) -{ -#if PLATFORM_64BITS - COMPILE_TIME_ASSERT(sizeof(LockInfo_t) == 16); - return ThreadInterlockedAssignIf128( (int128 *)&m_lockInfo, *((int128 *)&newValue), *((int128 *)&comperand) ); +#ifndef OLD_SPINRWLOCK + +#if defined(TEST_THREAD_SPIN_RW_LOCK) +#define RWLAssert( exp ) if ( exp ) ; else DebuggerBreak(); #else - COMPILE_TIME_ASSERT(sizeof(LockInfo_t) == 8); - return ThreadInterlockedAssignIf64( (int64 *)&m_lockInfo, *((int64 *)&newValue), *((int64 *)&comperand) ); +#define RWLAssert( exp ) ((void)0) +#endif + +inline bool CThreadSpinRWLock::IsLockedForWrite() +{ + return ( m_lockInfo.m_fWriting == 1 ); +} + +inline bool CThreadSpinRWLock::IsLockedForRead() +{ + return ( m_lockInfo.m_nReaders > 0 ); +} + +FORCEINLINE bool CThreadSpinRWLock::TryLockForWrite() +{ + volatile LockInfo_t &curValue = m_lockInfo; + if ( !( curValue.m_i32 & 0x00010000 ) && ThreadInterlockedAssignIf( &curValue.m_i32, 0x00010000, 0 ) ) + { + ThreadMemoryBarrier(); + RWLAssert( m_iWriteDepth == 0 && m_writerId == 0 ); + m_writerId = ThreadGetCurrentId(); +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + m_iWriteDepth++; +#endif + return true; + } + + return false; +} + +inline bool CThreadSpinRWLock::TryLockForWrite_UnforcedInline() +{ + if ( TryLockForWrite() ) + { + return true; + } + +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + if ( m_writerId != ThreadGetCurrentId() ) + { + return false; + } + m_iWriteDepth++; + return true; +#else + return false; #endif } -inline bool CThreadSpinRWLock::TryLockForWrite( const uintp threadId ) +FORCEINLINE void CThreadSpinRWLock::LockForWrite() +{ + if ( !TryLockForWrite() ) + { + SpinLockForWrite(); + } +} + +FORCEINLINE bool CThreadSpinRWLock::TryLockForRead() +{ + volatile LockInfo_t &curValue = m_lockInfo; + if ( !( curValue.m_i32 & 0x00010000 ) ) // !m_lockInfo.m_fWriting + { + LockInfo_t oldValue; + LockInfo_t newValue; + oldValue.m_i32 = ( curValue.m_i32 & 0xffff ); + newValue.m_i32 = oldValue.m_i32 + 1; + + if ( ThreadInterlockedAssignIf( &m_lockInfo.m_i32, newValue.m_i32, oldValue.m_i32 ) ) + { + ThreadMemoryBarrier(); + RWLAssert( m_lockInfo.m_fWriting == 0 ); + return true; + } + } + return false; +} + +inline bool CThreadSpinRWLock::TryLockForRead_UnforcedInline() +{ +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + if ( m_lockInfo.m_i32 & 0x00010000 ) // m_lockInfo.m_fWriting + { + if ( m_writerId == ThreadGetCurrentId() ) + { + m_lockInfo.m_nReaders++; + return true; + } + + return false; + } +#endif + return TryLockForRead(); +} + +FORCEINLINE void CThreadSpinRWLock::LockForRead() +{ + if ( !TryLockForRead() ) + { + SpinLockForRead(); + } +} + +FORCEINLINE void CThreadSpinRWLock::UnlockWrite() +{ + RWLAssert( m_writerId == ThreadGetCurrentId() ); +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + if ( --m_iWriteDepth == 0 ) +#endif + { + m_writerId = 0; + ThreadMemoryBarrier(); + m_lockInfo.m_i32 = 0; + } +} + +#ifndef REENTRANT_THREAD_SPIN_RW_LOCK +FORCEINLINE +#else +inline +#endif +void CThreadSpinRWLock::UnlockRead() +{ + RWLAssert( m_writerId == 0 || ( m_writerId == ThreadGetCurrentId() && m_lockInfo.m_fWriting ) ); +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + if ( !( m_lockInfo.m_i32 & 0x00010000 ) ) // !m_lockInfo.m_fWriting +#endif + { + ThreadMemoryBarrier(); + ThreadInterlockedDecrement( &m_lockInfo.m_i32 ); + RWLAssert( m_writerId == 0 && !m_lockInfo.m_fWriting ); + } +#ifdef REENTRANT_THREAD_SPIN_RW_LOCK + else if ( m_writerId == ThreadGetCurrentId() ) + { + m_lockInfo.m_nReaders--; + } + else + { + RWLAssert( 0 ); + } +#endif +} + +#else +/* (commented out to reduce distraction in colorized editor, remove entirely when new implementation settles) +inline bool CThreadSpinRWLock::AssignIf( const LockInfo_t &newValue, const LockInfo_t &comperand ) +{ + // Note: using unions guarantees no aliasing bugs. Casting structures through *(int64*)& + // may create hard-to-catch bugs because when you do that, compiler doesn't know that the newly computed pointer + // is actually aliased with LockInfo_t structure. It's rarely a problem in practice, but when it is, it's a royal pain to debug. + return ThreadInterlockedAssignIf64( &m_lockInfo.m_i64, newValue.m_i64, comperand.m_i64 ); +} + +FORCEINLINE bool CThreadSpinRWLock::TryLockForWrite( const uint32 threadId ) { // In order to grab a write lock, there can be no readers and no owners of the write lock if ( m_lockInfo.m_nReaders > 0 || ( m_lockInfo.m_writerId && m_lockInfo.m_writerId != threadId ) ) @@ -1848,16 +2544,14 @@ inline bool CThreadSpinRWLock::TryLockForWrite( const uintp threadId ) return false; } - static const LockInfo_t oldValue( 0, 0 ); - LockInfo_t newValue( threadId, 0 ); - const bool bSuccess = AssignIf( newValue, oldValue ); -#if defined(_X360) - if ( bSuccess ) + static const LockInfo_t oldValue = { {0, 0} }; + LockInfo_t newValue = { { threadId, 0 } }; + if ( AssignIf( newValue, oldValue ) ) { - // X360TBD: Serious perf implications. Not Yet. __sync(); + ThreadMemoryBarrier(); + return true; } -#endif - return bSuccess; + return false; } inline bool CThreadSpinRWLock::TryLockForWrite() @@ -1871,7 +2565,7 @@ inline bool CThreadSpinRWLock::TryLockForWrite() return true; } -inline bool CThreadSpinRWLock::TryLockForRead() +FORCEINLINE bool CThreadSpinRWLock::TryLockForRead() { if ( m_nWriters != 0 ) { @@ -1881,24 +2575,33 @@ inline bool CThreadSpinRWLock::TryLockForRead() LockInfo_t oldValue; LockInfo_t newValue; + if( IsX360() || IsPS3() ) + { + // this is the code equivalent to original code (see below) that doesn't cause LHS on Xbox360 + // WARNING: This code assumes BIG Endian CPU + oldValue.m_i64 = uint32( m_lockInfo.m_nReaders ); + newValue.m_i64 = oldValue.m_i64 + 1; // NOTE: when we have -1 (or 0xFFFFFFFF) readers, this will result in non-equivalent code + } + else + { + // this is the original code that worked here for a while oldValue.m_nReaders = m_lockInfo.m_nReaders; oldValue.m_writerId = 0; newValue.m_nReaders = oldValue.m_nReaders + 1; newValue.m_writerId = 0; - - const bool bSuccess = AssignIf( newValue, oldValue ); -#if defined(_X360) - if ( bSuccess ) - { - // X360TBD: Serious perf implications. Not Yet. __sync(); } -#endif - return bSuccess; + + if ( AssignIf( newValue, oldValue ) ) + { + ThreadMemoryBarrier(); + return true; + } + return false; } inline void CThreadSpinRWLock::LockForWrite() { - const uintp threadId = ThreadGetCurrentId(); + const uint32 threadId = ThreadGetCurrentId(); m_nWriters++; @@ -1908,6 +2611,8 @@ inline void CThreadSpinRWLock::LockForWrite() SpinLockForWrite( threadId ); } } +*/ +#endif // read data from a memory address template FORCEINLINE T ReadVolatileMemory( T const *pPtr ) @@ -1916,10 +2621,16 @@ template FORCEINLINE T ReadVolatileMemory( T const *pPtr ) return *pVolatilePtr; } + //----------------------------------------------------------------------------- #if defined( _WIN32 ) #pragma warning(pop) #endif +#if defined( _PS3 ) +BOOL SetEvent( CThreadEvent *pEvent ); +BOOL ResetEvent( CThreadEvent *pEvent ); +DWORD WaitForMultipleObjects(DWORD nCount, CThreadEvent **lppHandles, BOOL bWaitAll, DWORD dwMilliseconds ); +#endif // _PS3 #endif // THREADTOOLS_H diff --git a/public/tier0/threadtools.inl b/public/tier0/threadtools.inl new file mode 100644 index 00000000..037a64ed --- /dev/null +++ b/public/tier0/threadtools.inl @@ -0,0 +1,653 @@ +#ifndef THREADTOOLS_INL +#define THREADTOOLS_INL + +// This file is included in threadtools.h for PS3 and threadtools.cpp for all other platforms +// +// Do not #include other files here + +#ifndef _PS3 +// this is defined in the .cpp for the PS3 to avoid introducing a dependency for files including the header +CTHREADLOCALPTR(CThread) g_pCurThread; + +#define INLINE_ON_PS3 +#else +// Inlining these functions on PS3 (which are called across PRX boundaries) saves us over 1ms per frame +#define INLINE_ON_PS3 inline +#endif + +INLINE_ON_PS3 CThread::CThread() : +#ifdef _WIN32 +m_hThread( NULL ), +m_threadId( 0 ), +#elif defined( _PS3 ) || defined(_POSIX) +m_threadId( 0 ), +m_threadZombieId( 0 ) , +#endif +m_result( 0 ), +m_flags( 0 ) +{ + m_szName[0] = 0; + m_NotSuspendedEvent.Set(); +} + +//--------------------------------------------------------- + +INLINE_ON_PS3 CThread::~CThread() +{ +#ifdef MSVC + if (m_hThread) +#elif defined(POSIX) && !defined( _PS3 ) + if ( m_threadId ) +#endif + { + if ( IsAlive() ) + { + Msg( "Illegal termination of worker thread! Threads must negotiate an end to the thread before the CThread object is destroyed.\n" ); +#ifdef _WIN32 + + DoNewAssertDialog( __FILE__, __LINE__, "Illegal termination of worker thread! Threads must negotiate an end to the thread before the CThread object is destroyed.\n" ); +#endif + if ( GetCurrentCThread() == this ) + { + Stop(); // BUGBUG: Alfred - this doesn't make sense, this destructor fires from the hosting thread not the thread itself!! + } + } + } +#if defined(POSIX) || defined( _PS3 ) + if ( m_threadZombieId ) + { + // just clean up zombie threads immediately (the destructor is fired from the hosting thread) + Join(); + } +#endif +} + + +//--------------------------------------------------------- + +INLINE_ON_PS3 const char *CThread::GetName() +{ + AUTO_LOCK( m_Lock ); + if ( !m_szName[0] ) + { +#if defined( _WIN32 ) + _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p/%p)", this, m_hThread ); +#elif defined( _PS3 ) + snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p)", this ); +#elif defined( POSIX ) + _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p/0x%p)", this, m_threadId ); +#endif + m_szName[sizeof(m_szName) - 1] = 0; + } + return m_szName; +} + +//--------------------------------------------------------- + +INLINE_ON_PS3 void CThread::SetName(const char *pszName) +{ + AUTO_LOCK( m_Lock ); + strncpy( m_szName, pszName, sizeof(m_szName) - 1 ); + m_szName[sizeof(m_szName) - 1] = 0; +} + +//----------------------------------------------------- +// Functions for the other threads +//----------------------------------------------------- + +// Start thread running - error if already running +INLINE_ON_PS3 bool CThread::Start( unsigned nBytesStack, ThreadPriorityEnum_t nPriority ) +{ + AUTO_LOCK( m_Lock ); + + if ( IsAlive() ) + { + AssertMsg( 0, "Tried to create a thread that has already been created!" ); + return false; + } + + bool bInitSuccess = false; + CThreadEvent createComplete; + ThreadInit_t init = { this, &createComplete, &bInitSuccess }; + +#if defined( THREAD_PARENT_STACK_TRACE_ENABLED ) + { + int iValidEntries = GetCallStack_Fast( init.ParentStackTrace, ARRAYSIZE( init.ParentStackTrace ), 0 ); + for( int i = iValidEntries; i < ARRAYSIZE( init.ParentStackTrace ); ++i ) + { + init.ParentStackTrace[i] = NULL; + } + } +#endif + +#ifdef PLATFORM_WINDOWS + m_hThread = (HANDLE)CreateThread( NULL, + nBytesStack, + (LPTHREAD_START_ROUTINE)GetThreadProc(), + new ThreadInit_t(init), + nBytesStack ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0, + (LPDWORD)&m_threadId ); + + if( nPriority != TP_PRIORITY_DEFAULT ) + { + SetThreadPriority( m_hThread, nPriority ); + } + + if ( !m_hThread ) + { + AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() ); + return false; + } +#elif PLATFORM_PS3 + // On the PS3, a stack size of 0 doesn't imply a default stack size, so we need to force it to our + // own default size. + if ( nBytesStack == 0 ) + { + nBytesStack = PS3_SYS_PPU_THREAD_COMMON_STACK_SIZE; + } + + //The thread is about to begin + m_threadEnd.Reset(); + + // sony documentation: + // "If the PPU thread is not joined by sys_ppu_thread_join() after exit, + // it should always be created as non-joinable (not specifying + // SYS_PPU_THREAD_CREATE_JOINABLE). Otherwise, some resources are left + // allocated after termination of the PPU thread as if memory leaks." + const char* threadName=m_szName; + if ( sys_ppu_thread_create( &m_threadId, + (void(*)(uint64_t))GetThreadProc(), + (uint64_t)(new ThreadInit_t( init )), + nPriority, + nBytesStack, + SYS_PPU_THREAD_CREATE_JOINABLE , + threadName ) != CELL_OK ) + { + AssertMsg1( 0, "Failed to create thread (error 0x%x)", errno ); + return false; + } + + bInitSuccess = true; +#elif PLATFORM_POSIX + pthread_attr_t attr; + pthread_attr_init( &attr ); + pthread_attr_setstacksize( &attr, MAX( nBytesStack, 1024u*1024 ) ); + //lwss - fix memory leak here + m_threadInit = ThreadInit_t( init ); + //if ( pthread_create( &m_threadId, &attr, (void *(*)(void *))GetThreadProc(), new ThreadInit_t( init ) ) != 0 ) + if ( pthread_create( &m_threadId, &attr, (void *(*)(void *))GetThreadProc(), &m_threadInit ) != 0 ) + //lwss end + { + AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() ); + return false; + } + bInitSuccess = true; +#endif + + + + if ( !WaitForCreateComplete( &createComplete ) ) + { + Msg( "Thread failed to initialize\n" ); +#ifdef _WIN32 + CloseHandle( m_hThread ); + m_hThread = NULL; +#elif defined( _PS3 ) + m_threadEnd.Set(); + m_threadId = NULL; + m_threadZombieId = 0; +#endif + + return false; + } + + if ( !bInitSuccess ) + { + Msg( "Thread failed to initialize\n" ); +#ifdef _WIN32 + CloseHandle( m_hThread ); + m_hThread = NULL; +#elif defined(POSIX) && !defined( _PS3 ) + m_threadId = 0; + m_threadZombieId = 0; +#endif + return false; + } + +#ifdef _WIN32 + if ( !m_hThread ) + { + Msg( "Thread exited immediately\n" ); + } +#endif + +#ifdef _WIN32 + AddThreadHandleToIDMap( m_hThread, m_threadId ); + return !!m_hThread; +#elif defined(POSIX) + return !!m_threadId; +#endif +} + +//--------------------------------------------------------- +// +// Return true if the thread has been created and hasn't yet exited +// + +INLINE_ON_PS3 bool CThread::IsAlive() +{ +#ifdef PLATFORM_WINDOWS + DWORD dwExitCode; + return ( + m_hThread + && GetExitCodeThread(m_hThread, &dwExitCode) + && dwExitCode == STILL_ACTIVE ); +#elif defined(POSIX) + return !!m_threadId; +#endif +} + +// This method causes the current thread to wait until this thread +// is no longer alive. +INLINE_ON_PS3 bool CThread::Join( unsigned timeout ) +{ +#ifdef _WIN32 + if ( m_hThread ) +#elif defined(POSIX) + if ( m_threadId || m_threadZombieId ) +#endif + { + AssertMsg(GetCurrentCThread() != this, _T("Thread cannot be joined with self")); + +#ifdef _WIN32 + return ThreadJoin( (ThreadHandle_t)m_hThread, timeout ); +#elif defined(POSIX) + bool ret = ThreadJoin( (ThreadHandle_t)(m_threadId ? m_threadId : m_threadZombieId), timeout ); + m_threadZombieId = 0; + return ret; +#endif + } + return true; +} + +//--------------------------------------------------------- + +INLINE_ON_PS3 ThreadHandle_t CThread::GetThreadHandle() +{ +#ifdef _WIN32 + return (ThreadHandle_t)m_hThread; +#else + return (ThreadHandle_t)m_threadId; +#endif +} + + +//--------------------------------------------------------- + +INLINE_ON_PS3 int CThread::GetResult() +{ + return m_result; +} + +//----------------------------------------------------- +// Functions for both this, and maybe, and other threads +//----------------------------------------------------- + +// Forcibly, abnormally, but relatively cleanly stop the thread +// + +INLINE_ON_PS3 void CThread::Stop(int exitCode) +{ + if ( !IsAlive() ) + return; + + if ( GetCurrentCThread() == this ) + { +#if !defined( _PS3 ) + m_result = exitCode; + if ( !( m_flags & SUPPORT_STOP_PROTOCOL ) ) + { + OnExit(); + g_pCurThread = NULL; + +#ifdef _WIN32 + CloseHandle( m_hThread ); + RemoveThreadHandleToIDMap( m_hThread ); + m_hThread = NULL; +#else + m_threadId = 0; + m_threadZombieId = 0; +#endif + } + else + { + throw exitCode; + } +#else + AssertMsg( false, "Called CThread::Stop() for a platform that doesn't have it!\n"); +#endif + } + else + AssertMsg( 0, "Only thread can stop self: Use a higher-level protocol"); +} + +//--------------------------------------------------------- + +// Get the priority +INLINE_ON_PS3 int CThread::GetPriority() const +{ +#ifdef _WIN32 + return GetThreadPriority(m_hThread); +#elif defined( _PS3 ) + return ThreadGetPriority( (ThreadHandle_t) m_threadId ); +#elif defined(POSIX) + struct sched_param thread_param; + int policy; + pthread_getschedparam( m_threadId, &policy, &thread_param ); + return thread_param.sched_priority; +#endif +} + +//--------------------------------------------------------- + +// Set the priority +INLINE_ON_PS3 bool CThread::SetPriority(int priority) +{ +#ifdef WIN32 + return ThreadSetPriority( (ThreadHandle_t)m_hThread, priority ); +#else + return ThreadSetPriority( (ThreadHandle_t)m_threadId, priority ); +#endif +} + +//--------------------------------------------------------- + +// Suspend a thread +INLINE_ON_PS3 unsigned CThread::Suspend() +{ + AssertMsg( ThreadGetCurrentId() == (ThreadId_t)m_threadId, "Cannot call CThread::Suspend from outside thread" ); + + if ( ThreadGetCurrentId() != (ThreadId_t)m_threadId ) + { + DebuggerBreakIfDebugging(); + } + + m_NotSuspendedEvent.Reset(); + m_NotSuspendedEvent.Wait(); + + return 0; +} + + +//--------------------------------------------------------- + +INLINE_ON_PS3 unsigned CThread::Resume() +{ + if ( m_NotSuspendedEvent.Check() ) + { + DevWarning( "Called Resume() on a thread that is not suspended!\n" ); + } + m_NotSuspendedEvent.Set(); + return 0; +} + +//--------------------------------------------------------- + +// Force hard-termination of thread. Used for critical failures. +INLINE_ON_PS3 bool CThread::Terminate(int exitCode) +{ +#if defined( _X360 ) + AssertMsg( 0, "Cannot terminate a thread on the Xbox!" ); + return false; +#elif defined( _WIN32 ) + // I hope you know what you're doing! + if (!TerminateThread(m_hThread, exitCode)) + return false; + CloseHandle( m_hThread ); + RemoveThreadHandleToIDMap( m_hThread ); + m_hThread = NULL; +#elif defined( _PS3 ) + m_threadEnd.Set(); + m_threadId = NULL; +#elif defined(POSIX) + pthread_kill( m_threadId, SIGKILL ); + m_threadId = 0; +#endif + return true; +} + +//----------------------------------------------------- +// Global methods +//----------------------------------------------------- + +// Get the Thread object that represents the current thread, if any. +// Can return NULL if the current thread was not created using +// CThread +// + +INLINE_ON_PS3 CThread *CThread::GetCurrentCThread() +{ +#ifdef _PS3 + return GetCurThreadPS3(); +#else + return g_pCurThread; +#endif +} + +//--------------------------------------------------------- +// +// Offer a context switch. Under Win32, equivalent to Sleep(0) +// + +#ifdef Yield +#undef Yield +#endif +INLINE_ON_PS3 void CThread::Yield() +{ +#ifdef _WIN32 + ::Sleep(0); +#elif defined( _PS3 ) + // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead. + sys_timer_usleep( 60 ); +#elif defined(POSIX) + sched_yield(); +#endif +} + +//--------------------------------------------------------- +// +// This method causes the current thread to yield and not to be +// scheduled for further execution until a certain amount of real +// time has elapsed, more or less. Duration is in milliseconds + +INLINE_ON_PS3 void CThread::Sleep( unsigned duration ) +{ +#ifdef _WIN32 + ::Sleep(duration); +#elif defined (_PS3) + sys_timer_usleep( duration * 1000 ); +#elif defined(POSIX) + usleep( duration * 1000 ); +#endif +} + +//--------------------------------------------------------- + +// Optional pre-run call, with ability to fail-create. Note Init() +// is forced synchronous with Start() +INLINE_ON_PS3 bool CThread::Init() +{ + return true; +} + +//--------------------------------------------------------- + +#if defined( _PS3 ) +INLINE_ON_PS3 int CThread::Run() +{ + return -1; +} +#endif // _PS3 + +// Called when the thread exits +INLINE_ON_PS3 void CThread::OnExit() { } + +// Allow for custom start waiting +INLINE_ON_PS3 bool CThread::WaitForCreateComplete( CThreadEvent *pEvent ) +{ + // Force serialized thread creation... + if (!pEvent->Wait(60000)) + { + AssertMsg( 0, "Probably deadlock or failure waiting for thread to initialize." ); + return false; + } + return true; +} + +INLINE_ON_PS3 bool CThread::IsThreadRunning() +{ +#ifdef _PS3 + // ThreadIsThreadIdRunning() doesn't work on PS3 if the thread is in a zombie state + return m_eventTheadExit.Check(); +#else + return ThreadIsThreadIdRunning( (ThreadId_t)m_threadId ); +#endif +} + +//--------------------------------------------------------- +INLINE_ON_PS3 CThread::ThreadProc_t CThread::GetThreadProc() +{ + return ThreadProc; +} + +INLINE_ON_PS3 void CThread::ThreadProcRunWithMinidumpHandler( void *pv ) +{ + ThreadInit_t *pInit = reinterpret_cast(pv); + pInit->pThread->m_result = pInit->pThread->Run(); +} + +#ifdef PLATFORM_WINDOWS +unsigned long STDCALL CThread::ThreadProc(LPVOID pv) +#else +INLINE_ON_PS3 void* CThread::ThreadProc(LPVOID pv) +#endif +{ +#if defined( POSIX ) || defined( _PS3 ) + ThreadInit_t *pInit = reinterpret_cast(pv); +#else + std::auto_ptr pInit((ThreadInit_t *)pv); +#endif + +#ifdef _X360 + // Make sure all threads are consistent w.r.t floating-point math + SetupFPUControlWord(); +#endif + AllocateThreadID(); + + CThread *pThread = pInit->pThread; +#ifdef _PS3 + SetCurThreadPS3( pThread ); +#else + g_pCurThread = pThread; +#endif + + pThread->m_pStackBase = AlignValue( &pThread, 4096 ); + + pInit->pThread->m_result = -1; + +#if defined( THREAD_PARENT_STACK_TRACE_ENABLED ) + CStackTop_ReferenceParentStack stackTop( pInit->ParentStackTrace, ARRAYSIZE( pInit->ParentStackTrace ) ); +#endif + + bool bInitSuccess = true; + if ( pInit->pfInitSuccess ) + *(pInit->pfInitSuccess) = false; + +#ifdef _PS3 + *(pInit->pfInitSuccess) = pInit->pThread->Init(); +#else + try + { + bInitSuccess = pInit->pThread->Init(); + } + + catch (...) + { + pInit->pInitCompleteEvent->Set(); + throw; + } +#endif // _PS3 + + if ( pInit->pfInitSuccess ) + *(pInit->pfInitSuccess) = bInitSuccess; + pInit->pInitCompleteEvent->Set(); + if (!bInitSuccess) + return 0; + + if ( !Plat_IsInDebugSession() && (pInit->pThread->m_flags & SUPPORT_STOP_PROTOCOL) ) + { +#ifndef _PS3 + try +#endif + { + pInit->pThread->m_result = pInit->pThread->Run(); + } + +#ifndef _PS3 + catch (...) + { + } +#endif + } + else + { +#if defined( _WIN32 ) + CatchAndWriteMiniDumpForVoidPtrFn( ThreadProcRunWithMinidumpHandler, pv, false ); +#else + pInit->pThread->m_result = pInit->pThread->Run(); +#endif + } + + pInit->pThread->OnExit(); +#ifdef _PS3 + SetCurThreadPS3( NULL ); +#else + g_pCurThread = NULL; +#endif + FreeThreadID(); + + AUTO_LOCK( pThread->m_Lock ); +#ifdef _WIN32 + CloseHandle( pThread->m_hThread ); + RemoveThreadHandleToIDMap( pThread->m_hThread ); + pThread->m_hThread = NULL; +#elif defined( _PS3 ) + pThread->m_threadZombieId = pThread->m_threadId; + pThread->m_threadEnd.Set(); + pThread->m_threadId = 0; +#elif defined(POSIX) + pThread->m_threadZombieId = pThread->m_threadId; + pThread->m_threadId = 0; +#else +#error +#endif + + pThread->m_ExitEvent.Set(); +#ifdef _PS3 + { + pThread->m_Lock.Unlock(); + sys_ppu_thread_exit( pInit->pThread->m_result ); + // reacquire the lock in case thread exit didn't actually exit the thread, so that + // AUTO_LOCK won't double-unlock the lock (to keep it paired) + pThread->m_Lock.Lock(); + } +#endif + +#if defined( POSIX )|| defined( _PS3 ) + return (void*)(uintp)pInit->pThread->m_result; +#else + return pInit->pThread->m_result; +#endif +} + +#endif // THREADTOOLS_INL diff --git a/public/tier0/tslist.h b/public/tier0/tslist.h index ab530c59..09c03177 100644 --- a/public/tier0/tslist.h +++ b/public/tier0/tslist.h @@ -36,15 +36,38 @@ #if defined( PLATFORM_64BITS ) +#if defined (PLATFORM_WINDOWS) +//typedef __m128i int128; +//inline int128 int128_zero() { return _mm_setzero_si128(); } +#else // PLATFORM_WINDOWS +typedef __int128_t int128; +#define int128_zero() 0 +#endif// PLATFORM_WINDOWS + #define TSLIST_HEAD_ALIGNMENT 16 #define TSLIST_NODE_ALIGNMENT 16 + +#ifdef POSIX +inline bool ThreadInterlockedAssignIf128( int128 volatile * pDest, const int128 &value, const int128 &comparand ) +{ + // We do not want the original comparand modified by the swap + // so operate on a local copy. + int128 local_comparand = comparand; + return __sync_bool_compare_and_swap( pDest, local_comparand, value ); +} +#endif + inline bool ThreadInterlockedAssignIf64x128( volatile int128 *pDest, const int128 &value, const int128 &comperand ) - { return ThreadInterlockedAssignIf128( pDest, value, comperand ); } +{ + return ThreadInterlockedAssignIf128( pDest, value, comperand ); +} #else #define TSLIST_HEAD_ALIGNMENT 8 #define TSLIST_NODE_ALIGNMENT 8 inline bool ThreadInterlockedAssignIf64x128( volatile int64 *pDest, const int64 value, const int64 comperand ) - { return ThreadInterlockedAssignIf64( pDest, value, comperand ); } +{ + return ThreadInterlockedAssignIf64( pDest, value, comperand ); +} #endif #ifdef _MSC_VER @@ -99,13 +122,13 @@ union TSLIST_HEAD_ALIGN TSLHead_t // because Sequence can be pretty much random. We could operate on both of them separately, // but it could perhaps (?) lead to problems with store forwarding. I don't know 'cause I didn't // performance-test or design original code, I'm just making it work on PowerPC. - #ifdef VALVE_BIG_ENDIAN +#ifdef VALVE_BIG_ENDIAN int16 Sequence; int16 Depth; - #else +#else int16 Depth; int16 Sequence; - #endif +#endif #ifdef PLATFORM_64BITS int32 Padding; #endif @@ -132,33 +155,33 @@ class CTSListBase public: // override new/delete so we can guarantee 8-byte aligned allocs - static void * operator new( size_t size ) + static void * operator new(size_t size) { - CTSListBase *pNode = (CTSListBase *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); + CTSListBase *pNode = (CTSListBase *)MemAlloc_AllocAlignedFileLine( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); return pNode; } - static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) + static void * operator new(size_t size, int nBlockUse, const char *pFileName, int nLine) { - CTSListBase *pNode = (CTSListBase *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); + CTSListBase *pNode = (CTSListBase *)MemAlloc_AllocAlignedFileLine( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); return pNode; } - static void operator delete( void *p) + static void operator delete(void *p) { MemAlloc_FreeAligned( p ); } - static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine ) + static void operator delete(void *p, int nBlockUse, const char *pFileName, int nLine) { MemAlloc_FreeAligned( p ); } private: // These ain't gonna work - static void * operator new[] ( size_t size ); - static void operator delete [] ( void *p); - + static void * operator new[]( size_t size ); + static void operator delete[]( void *p ); + public: CTSListBase() @@ -204,22 +227,22 @@ public: TSLHead_t oldHead; TSLHead_t newHead; - #if defined( PLATFORM_PS3 ) || defined( PLATFORM_X360 ) +#if defined( PLATFORM_PS3 ) || defined( PLATFORM_X360 ) __lwsync(); // write-release barrier - #endif +#endif #ifdef PLATFORM_64BITS newHead.value.Padding = 0; #endif - for (;;) + for ( ;; ) { oldHead.value64x128 = m_Head.value64x128; pNode->Next = oldHead.value.Next; newHead.value.Next = pNode; - + newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence + 0x10001; - - + + if ( ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) ) { break; @@ -231,7 +254,7 @@ public: #endif } - __attribute__((no_sanitize("address"))) TSLNodeBase_t *Pop() + TSLNodeBase_t *Pop() { #ifdef USE_NATIVE_SLIST #ifdef _X360 @@ -248,21 +271,21 @@ public: #ifdef PLATFORM_64BITS newHead.value.Padding = 0; #endif - for (;;) + for ( ;; ) { oldHead.value64x128 = m_Head.value64x128; if ( !oldHead.value.Next ) return NULL; newHead.value.Next = oldHead.value.Next->Next; - newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence - 1; + newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence - 1; if ( ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) ) { - #if defined( PLATFORM_PS3 ) || defined( PLATFORM_X360 ) - __lwsync(); // read-acquire barrier - #endif +#if defined( PLATFORM_PS3 ) || defined( PLATFORM_X360 ) + __lwsync(); // read-acquire barrier +#endif break; } ThreadPause(); @@ -301,7 +324,7 @@ public: // I didn't construct this code. In any case, leaving it as is on big-endian newHead.value32.DepthAndSequence = oldHead.value32.DepthAndSequence & 0xffff0000; - } while( !ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) ); + } while ( !ThreadInterlockedAssignIf64x128( &m_Head.value64x128, newHead.value64x128, oldHead.value64x128 ) ); return (TSLNodeBase_t *)oldHead.value.Next; #endif @@ -315,7 +338,7 @@ public: int Count() const { #ifdef USE_NATIVE_SLIST - return QueryDepthSList( const_cast( &m_Head ) ); + return QueryDepthSList( const_cast(&m_Head) ); #else return m_Head.value.Depth; #endif @@ -349,7 +372,7 @@ public: // similar to CTSSimpleList except that it allocates it's own pool objects // and frees them on destruct. Also it does not overlay the TSNodeBase_t memory // on T's memory -template< class T > +template< class T > class TSLIST_HEAD_ALIGN CTSPool : public CTSListBase { // packs the node and the item (T) into a single struct and pools those @@ -380,7 +403,7 @@ public: void PutObject( T *pInfo ) { char *pElem = (char *)pInfo; - pElem -= offsetof(simpleTSPoolStruct_t,elem); + pElem -= offsetof( simpleTSPoolStruct_t, elem ); simpleTSPoolStruct_t *pNode = (simpleTSPoolStruct_t *)pElem; CTSListBase::Push( pNode ); @@ -414,25 +437,25 @@ public: Node_t( const T &init ) : elem( init ) {} T elem; - // override new/delete so we can guarantee 8-byte aligned allocs - static void * operator new( size_t size ) - { - Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_NODE_ALIGNMENT, __FILE__, __LINE__ ); - return pNode; - } - // override new/delete so we can guarantee 8-byte aligned allocs - static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) + static void * operator new(size_t size) { - Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_NODE_ALIGNMENT, pFileName, nLine ); + Node_t *pNode = (Node_t *)MemAlloc_AllocAlignedFileLine( size, TSLIST_NODE_ALIGNMENT, __FILE__, __LINE__ ); return pNode; } - static void operator delete( void *p) - { + // override new/delete so we can guarantee 8-byte aligned allocs + static void * operator new(size_t size, int nBlockUse, const char *pFileName, int nLine) + { + Node_t *pNode = (Node_t *)MemAlloc_AllocAlignedFileLine( size, TSLIST_NODE_ALIGNMENT, pFileName, nLine ); + return pNode; + } + + static void operator delete(void *p) + { MemAlloc_FreeAligned( p ); - } - static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine ) + } + static void operator delete(void *p, int nBlockUse, const char *pFileName, int nLine) { MemAlloc_FreeAligned( p ); } @@ -476,7 +499,7 @@ public: Push( new Node_t( init ) ); } - bool PopItem( T *pResult) + bool PopItem( T *pResult ) { Node_t *pNode = Pop(); if ( !pNode ) @@ -564,7 +587,7 @@ public: Push( pNode ); } - bool PopItem( T *pResult) + bool PopItem( T *pResult ) { Node_t *pNode = Pop(); if ( !pNode ) @@ -608,37 +631,37 @@ class TSLIST_HEAD_ALIGN CTSQueue public: // override new/delete so we can guarantee 8-byte aligned allocs - static void * operator new( size_t size ) + static void * operator new(size_t size) { - CTSQueue *pNode = (CTSQueue *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); + CTSQueue *pNode = (CTSQueue *)MemAlloc_AllocAlignedFileLine( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); return pNode; } // override new/delete so we can guarantee 8-byte aligned allocs - static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) + static void * operator new(size_t size, int nBlockUse, const char *pFileName, int nLine) { - CTSQueue *pNode = (CTSQueue *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); + CTSQueue *pNode = (CTSQueue *)MemAlloc_AllocAlignedFileLine( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); return pNode; } - static void operator delete( void *p) + static void operator delete(void *p) { MemAlloc_FreeAligned( p ); } - static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine ) + static void operator delete(void *p, int nBlockUse, const char *pFileName, int nLine) { MemAlloc_FreeAligned( p ); } private: // These ain't gonna work - static void * operator new[] ( size_t size ) throw() + static void * operator new[]( size_t size ) throw() { return NULL; } - static void operator delete [] ( void *p) + static void operator delete []( void *p ) { } @@ -647,24 +670,24 @@ public: struct TSLIST_NODE_ALIGN Node_t { // override new/delete so we can guarantee 8-byte aligned allocs - static void * operator new( size_t size ) + static void * operator new(size_t size) { - Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); + Node_t *pNode = (Node_t *)MemAlloc_AllocAlignedFileLine( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); return pNode; } - static void * operator new( size_t size, int nBlockUse, const char *pFileName, int nLine ) + static void * operator new(size_t size, int nBlockUse, const char *pFileName, int nLine) { - Node_t *pNode = (Node_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); + Node_t *pNode = (Node_t *)MemAlloc_AllocAlignedFileLine( size, TSLIST_HEAD_ALIGNMENT, pFileName, nLine ); return pNode; } - static void operator delete( void *p) + static void operator delete(void *p) { MemAlloc_FreeAligned( p ); } - static void operator delete( void *p, int nBlockUse, const char *pFileName, int nLine ) + static void operator delete(void *p, int nBlockUse, const char *pFileName, int nLine) { MemAlloc_FreeAligned( p ); } @@ -679,13 +702,13 @@ public: union TSLIST_HEAD_ALIGN NodeLink_t { // override new/delete so we can guarantee 8-byte aligned allocs - static void * operator new( size_t size ) + static void * operator new(size_t size) { - NodeLink_t *pNode = (NodeLink_t *)MemAlloc_AllocAligned( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); + NodeLink_t *pNode = (NodeLink_t *)MemAlloc_AllocAlignedFileLine( size, TSLIST_HEAD_ALIGNMENT, __FILE__, __LINE__ ); return pNode; } - static void operator delete( void *p) + static void operator delete(void *p) { MemAlloc_FreeAligned( p ); } @@ -740,12 +763,12 @@ public: } Node_t *pNode; - while ( ( pNode = Pop() ) != NULL ) + while ( (pNode = Pop()) != NULL ) { delete pNode; } - while ( ( pNode = (Node_t *)m_FreeNodes.Pop() ) != NULL ) + while ( (pNode = (Node_t *)m_FreeNodes.Pop()) != NULL ) { delete pNode; } @@ -765,7 +788,7 @@ public: } Node_t *pNode; - while ( ( pNode = Pop() ) != NULL ) + while ( (pNode = Pop()) != NULL ) { m_FreeNodes.Push( (TSLNodeBase_t *)pNode ); } @@ -846,7 +869,7 @@ public: pNode->pNext = End(); - for (;;) + for ( ;; ) { oldTail.value.sequence = m_Tail.value.sequence; oldTail.value.pNode = m_Tail.value.pNode; @@ -870,7 +893,7 @@ public: Node_t *Pop() { - #define TSQUEUE_BAD_NODE_LINK ( (Node_t *)INT_TO_POINTER( 0xdeadbeef ) ) +#define TSQUEUE_BAD_NODE_LINK ( (Node_t *)INT_TO_POINTER( 0xdeadbeef ) ) NodeLink_t * volatile pHead = &m_Head; NodeLink_t * volatile pTail = &m_Tail; Node_t * volatile * pHeadNode = &m_Head.value.pNode; @@ -883,17 +906,17 @@ public: intp tailSequence; T elem; - for (;;) + for ( ;; ) { head.value.sequence = *pHeadSequence; // must grab sequence first, which allows condition below to ensure pNext is valid ThreadMemoryBarrier(); // need a barrier to prevent reordering of these assignments - head.value.pNode = *pHeadNode; - tailSequence = pTail->value.sequence; - pNext = head.value.pNode->pNext; + head.value.pNode = *pHeadNode; + tailSequence = pTail->value.sequence; + pNext = head.value.pNode->pNext; // Checking pNext only to force optimizer to not reorder the assignment // to pNext and the compare of the sequence - if ( !pNext || head.value.sequence != *pHeadSequence ) + if ( !pNext || head.value.sequence != *pHeadSequence ) continue; if ( bTestOptimizer ) @@ -916,7 +939,7 @@ public: FinishPush( pNext, oldTail ); continue; } - + if ( pNext != End() ) { elem = pNext->elem; // NOTE: next could be a freed node here, by design @@ -991,7 +1014,7 @@ private: NodeLink_t m_Tail; CInterlockedInt m_Count; - + CTSListBase m_FreeNodes; } TSLIST_NODE_ALIGN_POST; diff --git a/public/tier1/stringpool.h b/public/tier1/stringpool.h index 6b1dfaef..903a1cb5 100644 --- a/public/tier1/stringpool.h +++ b/public/tier1/stringpool.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: // @@ -14,21 +14,33 @@ #include "utlrbtree.h" #include "utlvector.h" +#include "utlbuffer.h" +#include "generichash.h" //----------------------------------------------------------------------------- // Purpose: Allocates memory for strings, checking for duplicates first, // reusing exising strings if duplicate found. //----------------------------------------------------------------------------- +enum StringPoolCase_t +{ + StringPoolCaseInsensitive, + StringPoolCaseSensitive +}; + class CStringPool { public: - CStringPool(); + CStringPool( StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive ); ~CStringPool(); unsigned int Count() const; const char * Allocate( const char *pszValue ); + // This feature is deliberately not supported because it's pretty dangerous + // given current uses of CStringPool, which assume they can copy string pointers without + // any refcounts. + //void Free( const char *pszValue ); void FreeAll(); // searches for a string already in the pool @@ -48,14 +60,15 @@ protected: // // At some point this should replace CStringPool //----------------------------------------------------------------------------- -class CCountedStringPool +template +class CCountedStringPoolBase { public: // HACK, hash_item_t structure should not be public. struct hash_item_t { char* pString; - unsigned short nNextElement; + T nNextElement; unsigned char nReferenceCount; unsigned char pad; }; @@ -67,13 +80,14 @@ public: // HACK, hash_item_t structure should not be public. HASH_TABLE_SIZE = 1024 }; - CUtlVector m_HashTable; // Points to each element + CUtlVector m_HashTable; // Points to each element CUtlVector m_Elements; - unsigned short m_FreeListStart; + T m_FreeListStart; + StringPoolCase_t m_caseSensitivity; public: - CCountedStringPool(); - virtual ~CCountedStringPool(); + CCountedStringPoolBase( StringPoolCase_t caseSensitivity = StringPoolCaseInsensitive ); + virtual ~CCountedStringPoolBase(); void FreeAll(); @@ -82,10 +96,416 @@ public: void DereferenceString( const char* pIntrinsic ); // These are only reliable if there are less than 64k strings in your string pool - unsigned short FindStringHandle( const char* pIntrinsic ); - unsigned short ReferenceStringHandle( const char* pIntrinsic ); - char *HandleToString( unsigned short handle ); + T FindStringHandle( const char* pIntrinsic ); + T ReferenceStringHandle( const char* pIntrinsic ); + char *HandleToString( T handle ); void SpewStrings(); + unsigned Hash( const char *pszKey ); + + bool SaveToBuffer( CUtlBuffer &buffer ); + bool RestoreFromBuffer( CUtlBuffer &buffer ); + + // Debug helper method to validate that we didn't overflow + void VerifyNotOverflowed( unsigned int value ); }; +typedef CCountedStringPoolBase CCountedStringPool; + +template +inline CCountedStringPoolBase::CCountedStringPoolBase( StringPoolCase_t caseSensitivity ) +{ + MEM_ALLOC_CREDIT(); + m_HashTable.EnsureCount(HASH_TABLE_SIZE); + + for( int i = 0; i < m_HashTable.Count(); i++ ) + { + m_HashTable[i] = INVALID_ELEMENT; + } + + m_FreeListStart = INVALID_ELEMENT; + m_Elements.AddToTail(); + m_Elements[0].pString = NULL; + m_Elements[0].nReferenceCount = 0; + m_Elements[0].nNextElement = INVALID_ELEMENT; + + m_caseSensitivity = caseSensitivity; +} + +template +inline CCountedStringPoolBase::~CCountedStringPoolBase() +{ + FreeAll(); +} + +template +inline void CCountedStringPoolBase::FreeAll() +{ + int i; + + // Reset the hash table: + for( i = 0; i < m_HashTable.Count(); i++ ) + { + m_HashTable[i] = INVALID_ELEMENT; + } + + // Blow away the free list: + m_FreeListStart = INVALID_ELEMENT; + + for( i = 0; i < m_Elements.Count(); i++ ) + { + if( m_Elements[i].pString ) + { + delete [] m_Elements[i].pString; + m_Elements[i].pString = NULL; + m_Elements[i].nReferenceCount = 0; + m_Elements[i].nNextElement = INVALID_ELEMENT; + } + } + + // Remove all but the invalid element: + m_Elements.RemoveAll(); + m_Elements.AddToTail(); + m_Elements[0].pString = NULL; + m_Elements[0].nReferenceCount = 0; + m_Elements[0].nNextElement = INVALID_ELEMENT; +} + +template +inline unsigned CCountedStringPoolBase::Hash( const char *pszKey ) +{ + if ( m_caseSensitivity == StringPoolCaseInsensitive ) + { + return HashStringCaseless( pszKey ); + } + return HashString( pszKey ); +} + +template +inline T CCountedStringPoolBase::FindStringHandle( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return INVALID_ELEMENT; + + T nHashBucketIndex = ( Hash( pIntrinsic ) %HASH_TABLE_SIZE); + T nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // Does the bucket already exist? + if( nCurrentBucket != INVALID_ELEMENT ) + { + for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) + { + if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) + { + return nCurrentBucket; + } + } + } + + return 0; + +} + +template +inline char* CCountedStringPoolBase::FindString( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return NULL; + + // Yes, this will be NULL on failure. + return m_Elements[FindStringHandle(pIntrinsic)].pString; +} + +template +inline T CCountedStringPoolBase::ReferenceStringHandle( const char* pIntrinsic ) +{ + if( pIntrinsic == NULL ) + return INVALID_ELEMENT; + + T nHashBucketIndex = ( Hash( pIntrinsic ) % HASH_TABLE_SIZE); + T nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // Does the bucket already exist? + if( nCurrentBucket != INVALID_ELEMENT ) + { + for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) + { + if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) + { + // Anyone who hits 65k references is permanant + if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE ) + { + m_Elements[nCurrentBucket].nReferenceCount ++ ; + } + return nCurrentBucket; + } + } + } + + if( m_FreeListStart != INVALID_ELEMENT ) + { + nCurrentBucket = m_FreeListStart; + m_FreeListStart = m_Elements[nCurrentBucket].nNextElement; + } + else + { + unsigned int newElement = m_Elements.AddToTail(); + VerifyNotOverflowed( newElement ); + nCurrentBucket = newElement; + } + + m_Elements[nCurrentBucket].nReferenceCount = 1; + + // Insert at the beginning of the bucket: + m_Elements[nCurrentBucket].nNextElement = m_HashTable[ nHashBucketIndex ]; + m_HashTable[ nHashBucketIndex ] = nCurrentBucket; + + m_Elements[nCurrentBucket].pString = new char[Q_strlen( pIntrinsic ) + 1]; + Q_strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic ); + + return nCurrentBucket; +} + +template<> +inline void CCountedStringPoolBase::VerifyNotOverflowed( unsigned int value ) { Assert( value < 0xffff ); } + +template<> +inline void CCountedStringPoolBase::VerifyNotOverflowed( unsigned int value ) {} + +template +inline char* CCountedStringPoolBase::ReferenceString( const char* pIntrinsic ) +{ + if(!pIntrinsic) + return NULL; + + return m_Elements[ReferenceStringHandle( pIntrinsic)].pString; +} + +template +inline void CCountedStringPoolBase::DereferenceString( const char* pIntrinsic ) +{ + // If we get a NULL pointer, just return + if (!pIntrinsic) + return; + + T nHashBucketIndex = (Hash( pIntrinsic ) % m_HashTable.Count()); + T nCurrentBucket = m_HashTable[ nHashBucketIndex ]; + + // If there isn't anything in the bucket, just return. + if ( nCurrentBucket == INVALID_ELEMENT ) + return; + + for( T previous = INVALID_ELEMENT; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) + { + if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) + { + // Anyone who hits 65k references is permanant + if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE ) + { + m_Elements[nCurrentBucket].nReferenceCount --; + } + + if( m_Elements[nCurrentBucket].nReferenceCount == 0 ) + { + if( previous == INVALID_ELEMENT ) + { + m_HashTable[nHashBucketIndex] = m_Elements[nCurrentBucket].nNextElement; + } + else + { + m_Elements[previous].nNextElement = m_Elements[nCurrentBucket].nNextElement; + } + + delete [] m_Elements[nCurrentBucket].pString; + m_Elements[nCurrentBucket].pString = NULL; + m_Elements[nCurrentBucket].nReferenceCount = 0; + + m_Elements[nCurrentBucket].nNextElement = m_FreeListStart; + m_FreeListStart = nCurrentBucket; + break; + + } + } + + previous = nCurrentBucket; + } +} + +template +inline char* CCountedStringPoolBase::HandleToString( T handle ) +{ + return m_Elements[handle].pString; +} + +template +inline void CCountedStringPoolBase::SpewStrings() +{ + int i; + for ( i = 0; i < m_Elements.Count(); i++ ) + { + char* string = m_Elements[i].pString; + + Msg("String %d: ref:%d %s\n", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string); + } + + Msg("\n%d total counted strings.", m_Elements.Count()); +} + +#define STRING_POOL_VERSION MAKEID( 'C', 'S', 'P', '1' ) +#define MAX_STRING_SAVE 1024 + +template<> +inline bool CCountedStringPoolBase::SaveToBuffer( CUtlBuffer &buffer ) +{ + if ( m_Elements.Count() <= 1 ) + { + // pool is empty, saving nothing + // caller can check put position of buffer to detect + return true; + } + + // signature/version + buffer.PutInt( STRING_POOL_VERSION ); + + buffer.PutUnsignedShort( m_FreeListStart ); + + buffer.PutInt( m_HashTable.Count() ); + for ( int i = 0; i < m_HashTable.Count(); i++ ) + { + buffer.PutUnsignedShort( m_HashTable[i] ); + } + + buffer.PutInt( m_Elements.Count() ); + for ( int i = 1; i < m_Elements.Count(); i++ ) + { + buffer.PutUnsignedShort( m_Elements[i].nNextElement ); + buffer.PutUnsignedChar( m_Elements[i].nReferenceCount ); + + const char *pString = m_Elements[i].pString; + if ( strlen( pString ) >= MAX_STRING_SAVE ) + { + return false; + } + buffer.PutString( pString ? pString : "" ); + } + + return buffer.IsValid(); +} + +template<> +inline bool CCountedStringPoolBase::RestoreFromBuffer( CUtlBuffer &buffer ) +{ + int signature = buffer.GetInt(); + if ( signature != STRING_POOL_VERSION ) + { + // wrong version + return false; + } + + FreeAll(); + + m_FreeListStart = buffer.GetUnsignedShort(); + + int hashCount = buffer.GetInt(); + m_HashTable.SetCount( hashCount ); + + for ( int i = 0; i < hashCount; i++ ) + { + m_HashTable[i] = buffer.GetUnsignedShort(); + } + + int tableCount = buffer.GetInt(); + if ( tableCount > 1 ) + { + m_Elements.AddMultipleToTail( tableCount-1 ); + } + + char tempString[MAX_STRING_SAVE]; + for ( int i = 1; i < tableCount; i++ ) + { + m_Elements[i].nNextElement = buffer.GetUnsignedShort(); + m_Elements[i].nReferenceCount = buffer.GetUnsignedChar(); + buffer.GetString( tempString, sizeof( tempString ) ); + m_Elements[i].pString = strdup( tempString ); + } + + return buffer.IsValid(); +} + +template<> +inline bool CCountedStringPoolBase::SaveToBuffer( CUtlBuffer &buffer ) +{ + if ( m_Elements.Count() <= 1 ) + { + // pool is empty, saving nothing + // caller can check put position of buffer to detect + return true; + } + + // signature/version + buffer.PutInt( STRING_POOL_VERSION ); + + buffer.PutUnsignedInt( m_FreeListStart ); + + buffer.PutInt( m_HashTable.Count() ); + for ( int i = 0; i < m_HashTable.Count(); i++ ) + { + buffer.PutUnsignedInt( m_HashTable[i] ); + } + + buffer.PutInt( m_Elements.Count() ); + for ( int i = 1; i < m_Elements.Count(); i++ ) + { + buffer.PutUnsignedInt( m_Elements[i].nNextElement ); + buffer.PutUnsignedChar( m_Elements[i].nReferenceCount ); + + const char *pString = m_Elements[i].pString; + if ( strlen( pString ) >= MAX_STRING_SAVE ) + { + return false; + } + buffer.PutString( pString ? pString : "" ); + } + + return buffer.IsValid(); +} + +template<> +inline bool CCountedStringPoolBase::RestoreFromBuffer( CUtlBuffer &buffer ) +{ + int signature = buffer.GetInt(); + if ( signature != STRING_POOL_VERSION ) + { + // wrong version + return false; + } + + FreeAll(); + + m_FreeListStart = buffer.GetUnsignedInt(); + + int hashCount = buffer.GetInt(); + m_HashTable.SetCount( hashCount ); + + for ( int i = 0; i < hashCount; i++ ) + { + m_HashTable[i] = buffer.GetUnsignedInt(); + } + + int tableCount = buffer.GetInt(); + if ( tableCount > 1 ) + { + m_Elements.AddMultipleToTail( tableCount-1 ); + } + + char tempString[MAX_STRING_SAVE]; + for ( int i = 1; i < tableCount; i++ ) + { + m_Elements[i].nNextElement = buffer.GetUnsignedInt(); + m_Elements[i].nReferenceCount = buffer.GetUnsignedChar(); + buffer.GetString( tempString, sizeof( tempString ) ); + m_Elements[i].pString = strdup( tempString ); + } + + return buffer.IsValid(); +} #endif // STRINGPOOL_H diff --git a/public/tier1/utlbuffer.h b/public/tier1/utlbuffer.h index ec1b1188..59909410 100644 --- a/public/tier1/utlbuffer.h +++ b/public/tier1/utlbuffer.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//====== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. =======// // // Purpose: // @@ -14,6 +14,8 @@ #pragma once #endif +#include "unitlib/unitlib.h" // just here for tests - remove before checking in!!! + #include "tier1/utlmemory.h" #include "tier1/byteswap.h" #include @@ -102,11 +104,48 @@ CUtlCharConversion *GetNoEscCharConversion(); SetOverflowFuncs( static_cast ( _get ), static_cast ( _put ) ) + +typedef unsigned short ushort; + +template < class A > +static const char *GetFmtStr( int nRadix = 10, bool bPrint = true ) { Assert( 0 ); return ""; } +#if defined( LINUX ) || defined( __clang__ ) || ( defined( _MSC_VER ) && _MSC_VER >= 1900 ) +template <> const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } +template <> const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } +template <> const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } +template <> const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } +template <> const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%lld"; } +template <> const char *GetFmtStr< float > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%f"; } +template <> const char *GetFmtStr< double > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return bPrint ? "%.15lf" : "%lf"; } // force Printf to print DBL_DIG=15 digits of precision for doubles - defaults to FLT_DIG=6 +#else +template <> static const char *GetFmtStr< short > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hd"; } +template <> static const char *GetFmtStr< ushort > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%hu"; } +template <> static const char *GetFmtStr< int > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%d"; } +template <> static const char *GetFmtStr< uint > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 || nRadix == 16 ); return nRadix == 16 ? "%x" : "%u"; } +template <> static const char *GetFmtStr< int64 > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%lld"; } +template <> static const char *GetFmtStr< float > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return "%f"; } +template <> static const char *GetFmtStr< double > ( int nRadix, bool bPrint ) { Assert( nRadix == 10 ); return bPrint ? "%.15lf" : "%lf"; } // force Printf to print DBL_DIG=15 digits of precision for doubles - defaults to FLT_DIG=6 +#endif //----------------------------------------------------------------------------- // Command parsing.. //----------------------------------------------------------------------------- class CUtlBuffer { +// Brian has on his todo list to revisit this as there are issues in some cases with CUtlVector using operator = instead of copy construtor in InsertMultiple, etc. +// The unsafe case is something like this: +// CUtlVector< CUtlBuffer > vecFoo; +// +// CUtlBuffer buf; +// buf.Put( xxx ); +// vecFoo.Insert( buf ); +// +// This will cause memory corruption when vecFoo is cleared +// +//private: +// // Disallow copying +// CUtlBuffer( const CUtlBuffer & );// { Assert( 0 ); } +// CUtlBuffer &operator=( const CUtlBuffer & );// { Assert( 0 ); return *this; } + public: enum SeekType_t { @@ -132,7 +171,19 @@ public: CUtlBuffer( int growSize = 0, int initSize = 0, int nFlags = 0 ); CUtlBuffer( const void* pBuffer, int size, int nFlags = 0 ); // This one isn't actually defined so that we catch contructors that are trying to pass a bool in as the third param. - CUtlBuffer( const void *pBuffer, int size, bool crap ); + CUtlBuffer( const void *pBuffer, int size, bool crap ) = delete; + + // UtlBuffer objects should not be copyable; we do a slow copy if you use this but it asserts. + // (REI: I'd like to delete these but we have some python bindings that currently rely on being able to copy these objects) + CUtlBuffer( const CUtlBuffer& ); // = delete; + CUtlBuffer& operator= ( const CUtlBuffer& ); // = delete; + +#if VALVE_CPP11 + // UtlBuffer is non-copyable (same as CUtlMemory), but it is moveable. We would like to declare these with '= default' + // but unfortunately VS2013 isn't fully C++11 compliant, so we have to manually declare these in the boilerplate way. + CUtlBuffer( CUtlBuffer&& moveFrom ); // = default; + CUtlBuffer& operator= ( CUtlBuffer&& moveFrom ); // = default; +#endif unsigned char GetFlags() const; @@ -143,11 +194,15 @@ public: // Makes sure we've got at least this much memory void EnsureCapacity( int num ); + // Access for direct read into buffer + void * AccessForDirectRead( int nBytes ); + // Attaches the buffer to external memory.... void SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, int nFlags = 0 ); bool IsExternallyAllocated() const; - // Takes ownership of the passed memory, including freeing it when this buffer is destroyed. void AssumeMemory( void *pMemory, int nSize, int nInitialPut, int nFlags = 0 ); + void *Detach(); + void* DetachMemory(); // copies data from another buffer void CopyBuffer( const CUtlBuffer &buffer ); @@ -156,9 +211,10 @@ public: void Swap( CUtlBuffer &buf ); void Swap( CUtlMemory &mem ); + FORCEINLINE void ActivateByteSwappingIfBigEndian( void ) { - if ( IsX360() ) + if ( ( IsX360() || IsPS3() ) ) ActivateByteSwapping( true ); } @@ -174,6 +230,9 @@ public: // Clears out the buffer; frees memory void Purge(); + // Dump the buffer to stdout + void Spew( ); + // Read stuff out. // Binary mode: it'll just read the bits directly in, and characters will be // read for strings until a null character is reached. @@ -185,28 +244,25 @@ public: unsigned short GetUnsignedShort( ); int GetInt( ); int64 GetInt64( ); - int GetIntHex( ); + unsigned int GetIntHex( ); unsigned int GetUnsignedInt( ); + uint64 GetUnsignedInt64( ); float GetFloat( ); double GetDouble( ); - void * GetPtr(); - template void GetString( char( &pString )[maxLenInChars] ) - { - GetStringInternal( pString, maxLenInChars ); - } - - void GetString( char *pString, size_t maxLenInChars ) - { - GetStringInternal( pString, maxLenInChars ); - } + void * GetPtr(); + void GetString( char* pString, int nMaxChars ); + bool Get( void* pMem, int size ); + void GetLine( char* pLine, int nMaxChars ); void GetStringManualCharCount( char *pString, size_t maxLenInChars ) { - GetStringInternal( pString, maxLenInChars ); + GetString( pString, maxLenInChars ); } - void Get( void* pMem, int size ); - void GetLine( char* pLine, int nMaxChars = 0 ); + template void GetString( char( &pString )[maxLenInChars] ) + { + GetString( pString, maxLenInChars ); + } // Used for getting objects that have a byteswap datadesc defined template void GetObjects( T *dest, int count = 1 ); @@ -238,7 +294,7 @@ public: // Just like scanf, but doesn't work in binary mode int Scanf( SCANF_FORMAT_STRING const char* pFmt, ... ); - int VaScanf( const char* pFmt, va_list list ); + int VaScanf( const char* pFmt, va_list list ); // Eats white space, advances Get index void EatWhiteSpace(); @@ -270,16 +326,16 @@ public: // PutString will not write a terminating character void PutChar( char c ); void PutUnsignedChar( unsigned char uc ); - void PutUint64( uint64 ub ); - void PutInt16( int16 s16 ); void PutShort( short s ); void PutUnsignedShort( unsigned short us ); void PutInt( int i ); void PutInt64( int64 i ); void PutUnsignedInt( unsigned int u ); + void PutUnsignedInt64( uint64 u ); + void PutUint64( uint64 u ); void PutFloat( float f ); void PutDouble( double d ); - void PutPtr( void * ); // Writes the pointer, not the pointed to + void PutPtr( void * ); // Writes the pointer, not the pointed to void PutString( const char* pString ); void Put( const void* pMem, int size ); @@ -318,8 +374,8 @@ public: // Buffer base const void* Base() const; void* Base(); - // Returns the base as a const char*, only valid in text mode. - const char *String() const; + + const void* String() const; // memory allocation size, does *not* reflect size written or read, // use TellPut or TellGet for that @@ -352,6 +408,12 @@ public: // Temporarily disables pretty print void EnableTabs( bool bEnable ); +#if !defined( _GAMECONSOLE ) + // Swap my internal memory with another buffer, + // and copy all of its other members + void SwapCopy( CUtlBuffer &other ) ; +#endif + protected: // error flags enum @@ -371,7 +433,10 @@ protected: bool CheckPut( int size ); bool CheckGet( int size ); + // NOTE: Pass in nPut here even though it is just a copy of m_Put. This is almost always called immediately + // after modifying m_Put and this lets it stay in a register void AddNullTermination( ); + void AddNullTermination( int nPut ); // Methods to help with pretty-printing bool WasLastCharacterCR(); @@ -400,16 +465,18 @@ protected: // Call this to peek arbitrarily long into memory. It doesn't fail unless // it can't read *anything* new bool CheckArbitraryPeekGet( int nOffset, int &nIncrement ); - void GetStringInternal( char *pString, size_t maxLenInChars ); - template void GetType( T& dest, const char *pszFmt ); + template void GetType( T& dest ); template void GetTypeBin( T& dest ); + template bool GetTypeText( T &value, int nRadix = 10 ); template void GetObject( T *src ); - template void PutType( T src, const char *pszFmt ); + template void PutType( T src ); template void PutTypeBin( T src ); template void PutObject( T *src ); + // be sure to also update the copy constructor + // and SwapCopy() when adding members. CUtlMemory m_Memory; int m_Get; int m_Put; @@ -417,7 +484,7 @@ protected: unsigned char m_Error; unsigned char m_Flags; unsigned char m_Reserved; -#if defined( _X360 ) +#if defined( _GAMECONSOLE ) unsigned char pad; #endif @@ -605,7 +672,7 @@ inline void CUtlBuffer::GetObject( T *dest ) { if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) { - Q_memcpy( dest, PeekGet(), sizeof( T ) ); + *dest = *(T *)PeekGet(); } else { @@ -615,7 +682,7 @@ inline void CUtlBuffer::GetObject( T *dest ) } else { - Q_memset( dest, 0, sizeof(T) ); + Q_memset( &dest, 0, sizeof(T) ); } } @@ -637,18 +704,18 @@ inline void CUtlBuffer::GetTypeBin( T &dest ) { if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) { - Q_memcpy(&dest, PeekGet(), sizeof(T) ); + dest = *(T *)PeekGet(); } else { m_Byteswap.SwapBufferToTargetEndian( &dest, (T*)PeekGet() ); } - m_Get += sizeof(T); - } + m_Get += sizeof(T); + } else { dest = 0; - } + } } template <> @@ -656,8 +723,8 @@ inline void CUtlBuffer::GetTypeBin< float >( float &dest ) { if ( CheckGet( sizeof( float ) ) ) { - uintptr_t pData = (uintptr_t)PeekGet(); - if ( IsX360() && ( pData & 0x03 ) ) + uintp pData = (uintp)PeekGet(); + if ( ( IsX360() || IsPS3() ) && ( pData & 0x03 ) ) { // handle unaligned read ((unsigned char*)&dest)[0] = ((unsigned char*)pData)[0]; @@ -668,22 +735,148 @@ inline void CUtlBuffer::GetTypeBin< float >( float &dest ) else { // aligned read - Q_memcpy( &dest, (void*)pData, sizeof(float) ); + dest = *(float *)pData; } if ( m_Byteswap.IsSwappingBytes() ) { m_Byteswap.SwapBufferToTargetEndian< float >( &dest, &dest ); } - m_Get += sizeof( float ); - } + m_Get += sizeof( float ); + } else { dest = 0; + } +} + +template <> +inline void CUtlBuffer::GetTypeBin< double >( double &dest ) +{ + if ( CheckGet( sizeof( double ) ) ) + { + uintp pData = (uintp)PeekGet(); + if ( ( IsX360() || IsPS3() ) && ( pData & 0x07 ) ) + { + // handle unaligned read + ((unsigned char*)&dest)[0] = ((unsigned char*)pData)[0]; + ((unsigned char*)&dest)[1] = ((unsigned char*)pData)[1]; + ((unsigned char*)&dest)[2] = ((unsigned char*)pData)[2]; + ((unsigned char*)&dest)[3] = ((unsigned char*)pData)[3]; + ((unsigned char*)&dest)[4] = ((unsigned char*)pData)[4]; + ((unsigned char*)&dest)[5] = ((unsigned char*)pData)[5]; + ((unsigned char*)&dest)[6] = ((unsigned char*)pData)[6]; + ((unsigned char*)&dest)[7] = ((unsigned char*)pData)[7]; + } + else + { + // aligned read + dest = *(double *)pData; + } + if ( m_Byteswap.IsSwappingBytes() ) + { + m_Byteswap.SwapBufferToTargetEndian< double >( &dest, &dest ); + } + m_Get += sizeof( double ); + } + else + { + dest = 0; + } +} + +template < class T > +inline T StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + Assert( 0 ); + *ppEnd = pString; + return 0; +} + +template <> +inline int8 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( int8 )strtol( pString, ppEnd, nRadix ); +} + +template <> +inline uint8 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( uint8 )strtoul( pString, ppEnd, nRadix ); +} + +template <> +inline int16 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( int16 )strtol( pString, ppEnd, nRadix ); +} + +template <> +inline uint16 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( uint16 )strtoul( pString, ppEnd, nRadix ); +} + +template <> +inline int32 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( int32 )strtol( pString, ppEnd, nRadix ); +} + +template <> +inline uint32 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + return ( uint32 )strtoul( pString, ppEnd, nRadix ); +} + +template <> +inline int64 StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ +#if defined(_PS3) || defined(POSIX) + return ( int64 )strtoll( pString, ppEnd, nRadix ); +#else // !_PS3 + return ( int64 )_strtoi64( pString, ppEnd, nRadix ); +#endif // _PS3 +} + +template <> +inline float StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + NOTE_UNUSED( nRadix ); + return ( float )strtod( pString, ppEnd ); +} + +template <> +inline double StringToNumber( char *pString, char **ppEnd, int nRadix ) +{ + NOTE_UNUSED( nRadix ); + return ( double )strtod( pString, ppEnd ); +} + +template +inline bool CUtlBuffer::GetTypeText( T &value, int nRadix /*= 10*/ ) +{ + // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters + int nLength = 128; + if ( !CheckArbitraryPeekGet( 0, nLength ) ) + { + value = 0; + return false; } + + char *pStart = (char*)PeekGet(); + char* pEnd = pStart; + value = StringToNumber< T >( pStart, &pEnd, nRadix ); + + int nBytesRead = (int)( pEnd - pStart ); + if ( nBytesRead == 0 ) + return false; + + m_Get += nBytesRead; + return true; } template -inline void CUtlBuffer::GetType( T &dest, const char *pszFmt ) +inline void CUtlBuffer::GetType( T &dest ) { if (!IsText()) { @@ -691,93 +884,115 @@ inline void CUtlBuffer::GetType( T &dest, const char *pszFmt ) } else { - dest = 0; - Scanf( pszFmt, &dest ); + GetTypeText( dest ); } } inline char CUtlBuffer::GetChar( ) { + // LEGACY WARNING: this behaves differently than GetUnsignedChar() char c; - GetType( c, "%c" ); + GetTypeBin( c ); // always reads as binary return c; } inline unsigned char CUtlBuffer::GetUnsignedChar( ) { + // LEGACY WARNING: this behaves differently than GetChar() unsigned char c; - GetType( c, "%u" ); + if (!IsText()) + { + GetTypeBin( c ); + } + else + { + c = ( unsigned char )GetUnsignedShort(); + } return c; } inline short CUtlBuffer::GetShort( ) { short s; - GetType( s, "%d" ); + GetType( s ); return s; } inline unsigned short CUtlBuffer::GetUnsignedShort( ) { unsigned short s; - GetType( s, "%u" ); + GetType( s ); return s; } inline int CUtlBuffer::GetInt( ) { int i; - GetType( i, "%d" ); + GetType( i ); return i; } inline int64 CUtlBuffer::GetInt64( ) { int64 i; - GetType( i, "%lld" ); + GetType( i ); return i; } -inline int CUtlBuffer::GetIntHex( ) +inline unsigned int CUtlBuffer::GetIntHex( ) { - int i; - GetType( i, "%x" ); + uint i; + if (!IsText()) + { + GetTypeBin( i ); + } + else + { + GetTypeText( i, 16 ); + } return i; } inline unsigned int CUtlBuffer::GetUnsignedInt( ) { - unsigned int u; - GetType( u, "%u" ); - return u; + unsigned int i; + GetType( i ); + return i; } +inline uint64 CUtlBuffer::GetUnsignedInt64() +{ + uint64 i; + GetType( i ); + return i; +} + + inline float CUtlBuffer::GetFloat( ) { float f; - GetType( f, "%f" ); + GetType( f ); return f; } -inline void *CUtlBuffer::GetPtr( ) -{ - void *p; - // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal -#ifndef PLATFORM_64BITS - p = ( void* )GetUnsignedInt(); -#else - p = ( void* )GetInt64(); -#endif - return p; -} - inline double CUtlBuffer::GetDouble( ) { double d; - GetType( d, "%f" ); + GetType( d ); return d; } +inline void *CUtlBuffer::GetPtr( ) +{ + void *p; + // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal +#if !defined(X64BITS) && !defined(PLATFORM_64BITS) + p = ( void* )GetUnsignedInt(); +#else + p = ( void* )GetInt64(); +#endif + return p; +} //----------------------------------------------------------------------------- // Where am I writing? @@ -835,14 +1050,14 @@ inline void CUtlBuffer::PutObject( T *src ) { if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) { - Q_memcpy( PeekPut(), src, sizeof( T ) ); + *(T *)PeekPut() = *src; } else { m_Byteswap.SwapFieldsToTargetEndian( (T*)PeekPut(), src ); } m_Put += sizeof(T); - AddNullTermination(); + AddNullTermination( m_Put ); } } @@ -864,19 +1079,93 @@ inline void CUtlBuffer::PutTypeBin( T src ) { if ( !m_Byteswap.IsSwappingBytes() || ( sizeof( T ) == 1 ) ) { - Q_memcpy( PeekPut(), &src, sizeof( T ) ); + *(T *)PeekPut() = src; } else { m_Byteswap.SwapBufferToTargetEndian( (T*)PeekPut(), &src ); } m_Put += sizeof(T); - AddNullTermination(); + AddNullTermination( m_Put ); } } +#if defined( _GAMECONSOLE ) +template <> +inline void CUtlBuffer::PutTypeBin< float >( float src ) +{ + if ( CheckPut( sizeof( src ) ) ) + { + if ( m_Byteswap.IsSwappingBytes() ) + { + m_Byteswap.SwapBufferToTargetEndian( &src, &src ); + } + + // + // Write the data + // + unsigned pData = (unsigned)PeekPut(); + if ( pData & 0x03 ) + { + // handle unaligned write + byte* dst = (byte*)pData; + byte* srcPtr = (byte*)&src; + dst[0] = srcPtr[0]; + dst[1] = srcPtr[1]; + dst[2] = srcPtr[2]; + dst[3] = srcPtr[3]; + } + else + { + *(float *)pData = src; + } + + m_Put += sizeof(float); + AddNullTermination( m_Put ); + } +} + +template <> +inline void CUtlBuffer::PutTypeBin< double >( double src ) +{ + if ( CheckPut( sizeof( src ) ) ) + { + if ( m_Byteswap.IsSwappingBytes() ) + { + m_Byteswap.SwapBufferToTargetEndian( &src, &src ); + } + + // + // Write the data + // + unsigned pData = (unsigned)PeekPut(); + if ( pData & 0x07 ) + { + // handle unaligned write + byte* dst = (byte*)pData; + byte* srcPtr = (byte*)&src; + dst[0] = srcPtr[0]; + dst[1] = srcPtr[1]; + dst[2] = srcPtr[2]; + dst[3] = srcPtr[3]; + dst[4] = srcPtr[4]; + dst[5] = srcPtr[5]; + dst[6] = srcPtr[6]; + dst[7] = srcPtr[7]; + } + else + { + *(double *)pData = src; + } + + m_Put += sizeof(double); + AddNullTermination( m_Put ); + } +} +#endif + template -inline void CUtlBuffer::PutType( T src, const char *pszFmt ) +inline void CUtlBuffer::PutType( T src ) { if (!IsText()) { @@ -884,7 +1173,7 @@ inline void CUtlBuffer::PutType( T src, const char *pszFmt ) } else { - Printf( pszFmt, src ); + Printf( GetFmtStr< T >(), src ); } } @@ -952,68 +1241,74 @@ inline void CUtlBuffer::PutChar( char c ) inline void CUtlBuffer::PutUnsignedChar( unsigned char c ) { - PutType( c, "%u" ); -} - -inline void CUtlBuffer::PutUint64( uint64 ub ) -{ - PutType( ub, "%llu" ); -} - -inline void CUtlBuffer::PutInt16( int16 s16 ) -{ - PutType( s16, "%d" ); + if (!IsText()) + { + PutTypeBin( c ); + } + else + { + PutUnsignedShort( c ); + } } inline void CUtlBuffer::PutShort( short s ) { - PutType( s, "%d" ); + PutType( s ); } inline void CUtlBuffer::PutUnsignedShort( unsigned short s ) { - PutType( s, "%u" ); + PutType( s ); } inline void CUtlBuffer::PutInt( int i ) { - PutType( i, "%d" ); + PutType( i ); } inline void CUtlBuffer::PutInt64( int64 i ) { - PutType( i, "%llu" ); + PutType( i ); } inline void CUtlBuffer::PutUnsignedInt( unsigned int u ) { - PutType( u, "%u" ); + PutType( u ); +} + +inline void CUtlBuffer::PutUnsignedInt64( uint64 i ) +{ + PutType( i ); +} + +inline void CUtlBuffer::PutUint64( uint64 i ) +{ + PutType( i ); } inline void CUtlBuffer::PutFloat( float f ) { - PutType( f, "%f" ); + PutType( f ); } inline void CUtlBuffer::PutDouble( double d ) { - PutType( d, "%f" ); + PutType( d ); } inline void CUtlBuffer::PutPtr( void *p ) { - // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal - if (!IsText()) - { - PutTypeBin( p ); - } - else - { - Printf( "0x%p", p ); - } + // LEGACY WARNING: in text mode, PutPtr writes 32 bit pointers in hex, while GetPtr reads 32 or 64 bit pointers in decimal + if (!IsText()) + { + PutTypeBin( p ); + } + else + { + Printf( "0x%p", p ); + } } - //----------------------------------------------------------------------------- // Am I a text buffer? //----------------------------------------------------------------------------- @@ -1062,26 +1357,25 @@ inline bool CUtlBuffer::IsReadOnly() const //----------------------------------------------------------------------------- // Buffer base and size //----------------------------------------------------------------------------- -inline const void* CUtlBuffer::Base() const -{ - return m_Memory.Base(); +inline const void* CUtlBuffer::Base() const +{ + return m_Memory.Base(); } inline void* CUtlBuffer::Base() { - return m_Memory.Base(); + return m_Memory.Base(); } -// Returns the base as a const char*, only valid in text mode. -inline const char *CUtlBuffer::String() const +inline const void* CUtlBuffer::String() const { Assert( IsText() ); return reinterpret_cast( m_Memory.Base() ); } -inline int CUtlBuffer::Size() const -{ - return m_Memory.NumAllocated(); +inline int CUtlBuffer::Size() const +{ + return m_Memory.NumAllocated(); } @@ -1095,7 +1389,7 @@ inline void CUtlBuffer::Clear() m_Error = 0; m_nOffset = 0; m_nMaxPut = -1; - AddNullTermination(); + AddNullTermination( m_Put ); } inline void CUtlBuffer::Purge() @@ -1108,6 +1402,58 @@ inline void CUtlBuffer::Purge() m_Memory.Purge(); } +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +inline void *CUtlBuffer::AccessForDirectRead( int nBytes ) +{ + Assert( m_Get == 0 && m_Put == 0 && m_nMaxPut == 0 ); + EnsureCapacity( nBytes ); + m_nMaxPut = nBytes; + return Base(); +} + +inline void *CUtlBuffer::Detach() +{ + void *p = m_Memory.Detach(); + Clear(); + return p; +} + +//----------------------------------------------------------------------------- + +inline void CUtlBuffer::Spew( ) +{ + SeekGet( CUtlBuffer::SEEK_HEAD, 0 ); + + char pTmpLine[1024]; + while( IsValid() && GetBytesRemaining() ) + { + V_memset( pTmpLine, 0, sizeof(pTmpLine) ); + Get( pTmpLine, MIN( ( size_t )GetBytesRemaining(), sizeof(pTmpLine)-1 ) ); + Msg( _T( "%s" ), pTmpLine ); + } +} + +#if !defined(_GAMECONSOLE) +inline void CUtlBuffer::SwapCopy( CUtlBuffer &other ) +{ + m_Get = other.m_Get; + m_Put = other.m_Put; + m_Error = other.m_Error; + m_Flags = other.m_Flags; + m_Reserved = other.m_Reserved; + m_nTab = other.m_nTab; + m_nMaxPut = other.m_nMaxPut; + m_nOffset = other.m_nOffset; + m_GetOverflowFunc = other.m_GetOverflowFunc; + m_PutOverflowFunc = other.m_PutOverflowFunc; + m_Byteswap = other.m_Byteswap; + + m_Memory.Swap( other.m_Memory ); +} +#endif + inline void CUtlBuffer::CopyBuffer( const CUtlBuffer &buffer ) { CopyBuffer( buffer.Base(), buffer.TellPut() ); diff --git a/public/tier1/utllinkedlist.h b/public/tier1/utllinkedlist.h index 2e201805..d0f4249b 100644 --- a/public/tier1/utllinkedlist.h +++ b/public/tier1/utllinkedlist.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Linked list container class // @@ -26,6 +26,9 @@ #define FOR_EACH_LL( listName, iteratorName ) \ for( auto iteratorName=(listName).Head(); (listName).IsUtlLinkedList && iteratorName != (listName).InvalidIndex(); iteratorName = (listName).Next( iteratorName ) ) +#define FOR_EACH_LL_BACK( listName, iteratorName ) \ + for( auto iteratorName=(listName).Tail(); (listName).IsUtlLinkedList && iteratorName != (listName).InvalidIndex(); iteratorName = (listName).Previous( iteratorName ) ) + //----------------------------------------------------------------------------- // class CUtlLinkedList: // description: @@ -65,12 +68,15 @@ public: typedef S IndexType_t; // should really be called IndexStorageType_t, but that would be a huge change typedef I IndexLocalType_t; typedef M MemoryAllocator_t; - static const bool IsUtlLinkedList = true; // Used to match this at compiletime + enum { IsUtlLinkedList = true }; // Used to match this at compiletime // constructor, destructor CUtlLinkedList( int growSize = 0, int initSize = 0 ); ~CUtlLinkedList(); + CUtlLinkedList( const CUtlLinkedList& ) = delete; + CUtlLinkedList& operator=( const CUtlLinkedList& ) = delete; + // gets particular elements T& Element( I i ); T const& Element( I i ) const; @@ -115,6 +121,9 @@ public: I Alloc( bool multilist = false ); void Free( I elem ); + // Identify the owner of this linked list's memory: + void SetAllocOwner( const char *pszAllocOwner ); + // list modification void LinkBefore( I before, I elem ); void LinkAfter( I after, I elem ); @@ -348,16 +357,13 @@ protected: typedef UtlLinkedListElem_t ListElem_t; // constructs the class - I AllocInternal( bool multilist = false ); + I AllocInternal( bool multilist = false ) RESTRICT; void ConstructList(); // Gets at the list element.... ListElem_t& InternalElement( I i ) { return m_Memory[i]; } ListElem_t const& InternalElement( I i ) const { return m_Memory[i]; } - // copy constructors not allowed - CUtlLinkedList( CUtlLinkedList const& list ) { Assert(0); } - M m_Memory; I m_Head; I m_Tail; @@ -379,17 +385,10 @@ protected: { m_pElements = m_Memory.Base(); } - -private: - // Faster version of Next that can only be used from tested code internal - // to this class, such as Find(). It avoids the cost of checking the index - // validity, which is a big win on debug builds. - I PrivateNext( I i ) const; }; // this is kind of ugly, but until C++ gets templatized typedefs in C++0x, it's our only choice -// MoeMod : CUtlFixedMemory uses intp as index type template < class T > class CUtlFixedLinkedList : public CUtlLinkedList< T, intp, true, intp, CUtlFixedMemory< UtlLinkedListElem_t< T, intp > > > { @@ -397,25 +396,24 @@ public: CUtlFixedLinkedList( int growSize = 0, int initSize = 0 ) : CUtlLinkedList< T, intp, true, intp, CUtlFixedMemory< UtlLinkedListElem_t< T, intp > > >( growSize, initSize ) {} - typedef CUtlLinkedList< T, intp, true, intp, CUtlFixedMemory< UtlLinkedListElem_t< T, intp > > > BaseClass; bool IsValidIndex( intp i ) const { - if ( !BaseClass::Memory().IsIdxValid( i ) ) + if ( !this->Memory().IsIdxValid( i ) ) return false; #ifdef _DEBUG // it's safe to skip this here, since the only way to get indices after m_LastAlloc is to use MaxElementIndex - if ( BaseClass::Memory().IsIdxAfter( i, this->m_LastAlloc ) ) + if ( this->Memory().IsIdxAfter( i, this->m_LastAlloc ) ) { Assert( 0 ); return false; // don't read values that have been allocated, but not constructed } #endif - return ( BaseClass::Memory()[ i ].m_Previous != i ) || ( BaseClass::Memory()[ i ].m_Next == i ); + return ( this->Memory()[ i ].m_Previous != i ) || ( this->Memory()[ i ].m_Next == i ); } private: - intp MaxElementIndex() const { Assert( 0 ); return BaseClass::InvalidIndex(); } // fixedmemory containers don't support iteration from 0..maxelements-1 + int MaxElementIndex() const { Assert( 0 ); return this->InvalidIndex(); } // fixedmemory containers don't support iteration from 0..maxelements-1 void ResetDbgInfo() {} }; @@ -439,8 +437,10 @@ template CUtlLinkedList::CUtlLinkedList( int growSize, int initSize ) : m_Memory( growSize, initSize ), m_LastAlloc( m_Memory.InvalidIterator() ) { +#if !defined( PLATFORM_WINDOWS_PC64 ) && !defined( PLATFORM_64BITS ) // Prevent signed non-int datatypes - COMPILE_TIME_ASSERT( sizeof(S) == sizeof(M::InvalidIndex()) || ( ( (S)-1 ) > 0 ) ); + COMPILE_TIME_ASSERT( sizeof(S) == 4 || ( ( (S)-1 ) > 0 ) ); +#endif ConstructList(); ResetDbgInfo(); } @@ -540,21 +540,13 @@ inline I CUtlLinkedList::Next( I i ) const return InternalElement(i).m_Next; } -template -inline I CUtlLinkedList::PrivateNext( I i ) const -{ - return InternalElement(i).m_Next; -} - //----------------------------------------------------------------------------- // Are nodes in the list or valid? //----------------------------------------------------------------------------- -#ifdef _WIN32 #pragma warning(push) #pragma warning( disable: 4310 ) // Allows "(I)(S)M::INVALID_INDEX" below -#endif template inline bool CUtlLinkedList::IndexInRange( I index ) // Static method { @@ -565,17 +557,17 @@ inline bool CUtlLinkedList::IndexInRange( I index ) // Static method // Do some static checks here: // 'I' needs to be able to store 'S' - COMPILE_TIME_ASSERT( sizeof(I) >= sizeof(S) ); + // These COMPILE_TIME_ASSERT checks need to be in individual scopes to avoid build breaks + // on MacOS and Linux due to a gcc bug. + { COMPILE_TIME_ASSERT( sizeof(I) >= sizeof(S) ); } // 'S' should be unsigned (to avoid signed arithmetic errors for plausibly exhaustible ranges) - COMPILE_TIME_ASSERT( ( sizeof(S) > 2 ) || ( ( (S)-1 ) > 0 ) ); + { COMPILE_TIME_ASSERT( ( sizeof(S) > 2 ) || ( ( (S)-1 ) > 0 ) ); } // M::INVALID_INDEX should be storable in S to avoid ambiguities (e.g. with 65536) - COMPILE_TIME_ASSERT( ( M::INVALID_INDEX == -1 ) || ( M::INVALID_INDEX == (S)M::INVALID_INDEX ) ); + { COMPILE_TIME_ASSERT( ( M::INVALID_INDEX == -1 ) || ( M::INVALID_INDEX == (S)M::INVALID_INDEX ) ); } return ( ( (S)index == index ) && ( (S)index != InvalidIndex() ) ); } -#ifdef _WIN32 #pragma warning(pop) -#endif template inline bool CUtlLinkedList::IsValidIndex( I i ) const @@ -626,6 +618,12 @@ void CUtlLinkedList::SetGrowSize( int growSize ) ResetDbgInfo(); } +template< class T, class S, bool ML, class I, class M > +void CUtlLinkedList::SetAllocOwner( const char *pszAllocOwner ) +{ + m_Memory.SetAllocOwner( pszAllocOwner ); +} + //----------------------------------------------------------------------------- // Deallocate memory @@ -665,7 +663,7 @@ void CUtlLinkedList::PurgeAndDeleteElements() // Node allocation/deallocation //----------------------------------------------------------------------------- template -I CUtlLinkedList::AllocInternal( bool multilist ) +I CUtlLinkedList::AllocInternal( bool multilist ) RESTRICT { Assert( !multilist || ML ); #ifdef MULTILIST_PEDANTIC_ASSERTS @@ -798,7 +796,7 @@ inline I CUtlLinkedList::AddToHead( ) template inline I CUtlLinkedList::AddToTail( ) { - return InsertBefore( InvalidIndex() ); + return InsertBefore( InvalidIndex() ); } @@ -860,9 +858,7 @@ inline I CUtlLinkedList::AddToTail( T const& src ) template I CUtlLinkedList::Find( const T &src ) const { - // Cache the invalidIndex to avoid two levels of function calls on each iteration. - I invalidIndex = InvalidIndex(); - for ( I i=Head(); i != invalidIndex; i = PrivateNext( i ) ) + for ( I i=Head(); i != InvalidIndex(); i = Next( i ) ) { if ( Element( i ) == src ) return i; diff --git a/public/tier1/utlmemory.h b/public/tier1/utlmemory.h index 66c1f28d..a3a70fff 100644 --- a/public/tier1/utlmemory.h +++ b/public/tier1/utlmemory.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // @@ -17,22 +17,21 @@ #include "tier0/dbg.h" #include #include "tier0/platform.h" -#include "mathlib/mathlib.h" #include "tier0/memalloc.h" +#include "mathlib/mathlib.h" #include "tier0/memdbgon.h" -#ifdef _WIN32 #pragma warning (disable:4100) #pragma warning (disable:4514) -#endif + //----------------------------------------------------------------------------- #ifdef UTLMEMORY_TRACK -#define UTLMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "Sum of all UtlMemory", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) -#define UTLMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "Sum of all UtlMemory", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) +#define UTLMEMORY_TRACK_ALLOC() MemAlloc_RegisterAllocation( "||Sum of all UtlMemory||", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) +#define UTLMEMORY_TRACK_FREE() if ( !m_pMemory ) ; else MemAlloc_RegisterDeallocation( "||Sum of all UtlMemory||", 0, m_nAllocationCount * sizeof(T), m_nAllocationCount * sizeof(T), 0 ) #else #define UTLMEMORY_TRACK_ALLOC() ((void)0) #define UTLMEMORY_TRACK_FREE() ((void)0) @@ -46,6 +45,8 @@ template< class T, class I = int > class CUtlMemory { + template< class A, class B> friend class CUtlVector; + template< class A, size_t B> friend class CUtlVectorFixedGrowableCompat; public: // constructor, destructor CUtlMemory( int nGrowSize = 0, int nInitSize = 0 ); @@ -53,6 +54,12 @@ public: CUtlMemory( const T* pMemory, int numElements ); ~CUtlMemory(); + CUtlMemory( const CUtlMemory& ) = delete; + CUtlMemory& operator=( const CUtlMemory& ) = delete; + + CUtlMemory( CUtlMemory&& moveFrom ); + CUtlMemory& operator=( CUtlMemory&& moveFrom ); + // Set the size by which the memory grows void Init( int nGrowSize = 0, int nInitSize = 0 ); @@ -92,8 +99,9 @@ public: // Attaches the buffer to external memory.... void SetExternalBuffer( T* pMemory, int numElements ); void SetExternalBuffer( const T* pMemory, int numElements ); - // Takes ownership of the passed memory, including freeing it when this buffer is destroyed. void AssumeMemory( T *pMemory, int nSize ); + T* Detach(); + void *DetachMemory(); // Fast swap void Swap( CUtlMemory< T, I > &mem ); @@ -212,8 +220,7 @@ public: CUtlMemoryFixed( T* pMemory, int numElements ) { Assert( 0 ); } // Can we use this index? - // Use unsigned math to improve performance - bool IsIdxValid( int i ) const { return (size_t)i < SIZE; } + bool IsIdxValid( int i ) const { return (i >= 0) && (i < SIZE); } // Specify the invalid ('null') index that we'll only return on failure static const int INVALID_INDEX = -1; // For use with COMPILE_TIME_ASSERT @@ -224,11 +231,10 @@ public: const T* Base() const { if ( nAlignment == 0 ) return (T*)(&m_Memory[0]); else return (T*)AlignValue( &m_Memory[0], nAlignment ); } // element access - // Use unsigned math and inlined checks to improve performance. - T& operator[]( int i ) { Assert( (size_t)i < SIZE ); return Base()[i]; } - const T& operator[]( int i ) const { Assert( (size_t)i < SIZE ); return Base()[i]; } - T& Element( int i ) { Assert( (size_t)i < SIZE ); return Base()[i]; } - const T& Element( int i ) const { Assert( (size_t)i < SIZE ); return Base()[i]; } + T& operator[]( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; } + const T& operator[]( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } + T& Element( int i ) { Assert( IsIdxValid(i) ); return Base()[i]; } + const T& Element( int i ) const { Assert( IsIdxValid(i) ); return Base()[i]; } // Attaches the buffer to external memory.... void SetExternalBuffer( T* pMemory, int numElements ) { Assert( 0 ); } @@ -274,12 +280,7 @@ private: char m_Memory[ SIZE*sizeof(T) + nAlignment ]; }; -#if defined(POSIX) -// From Chris Green: Memory is a little fuzzy but I believe this class did -// something fishy with respect to msize and alignment that was OK under our -// allocator, the glibc allocator, etc but not the valgrind one (which has no -// padding because it detects all forms of head/tail overwrite, including -// writing 1 byte past a 1 byte allocation). +#ifdef _LINUX #define REMEMBER_ALLOC_SIZE_FOR_VALGRIND 1 #endif @@ -445,6 +446,44 @@ template< class T, class I > CUtlMemory::~CUtlMemory() { Purge(); + +#ifdef _DEBUG + m_pMemory = reinterpret_cast< T* >( 0xFEFEBAAD ); + m_nAllocationCount = 0x7BADF00D; +#endif +} + +template< class T, class I > +CUtlMemory::CUtlMemory( CUtlMemory&& moveFrom ) +: m_pMemory(moveFrom.m_pMemory) +, m_nAllocationCount(moveFrom.m_nAllocationCount) +, m_nGrowSize(moveFrom.m_nGrowSize) +{ + moveFrom.m_pMemory = nullptr; + moveFrom.m_nAllocationCount = 0; + moveFrom.m_nGrowSize = 0; +} + +template< class T, class I > +CUtlMemory& CUtlMemory::operator=( CUtlMemory&& moveFrom ) +{ + // Copy member variables to locals before purge to handle self-assignment + T* pMemory = moveFrom.m_pMemory; + int nAllocationCount = moveFrom.m_nAllocationCount; + int nGrowSize = moveFrom.m_nGrowSize; + + moveFrom.m_pMemory = nullptr; + moveFrom.m_nAllocationCount = 0; + moveFrom.m_nGrowSize = 0; + + // If this is a self-assignment, Purge() is a no-op here + Purge(); + + m_pMemory = pMemory; + m_nAllocationCount = nAllocationCount; + m_nGrowSize = nGrowSize; + + return *this; } template< class T, class I > @@ -493,7 +532,7 @@ void CUtlMemory::ConvertToGrowableMemory( int nGrowSize ) int nNumBytes = m_nAllocationCount * sizeof(T); T *pMemory = (T*)malloc( nNumBytes ); - memcpy( (void*)pMemory, (void*)m_pMemory, nNumBytes ); + memcpy( pMemory, m_pMemory, nNumBytes ); m_pMemory = pMemory; } else @@ -543,6 +582,24 @@ void CUtlMemory::AssumeMemory( T* pMemory, int numElements ) m_nAllocationCount = numElements; } +template< class T, class I > +void *CUtlMemory::DetachMemory() +{ + if ( IsExternallyAllocated() ) + return NULL; + + void *pMemory = m_pMemory; + m_pMemory = 0; + m_nAllocationCount = 0; + return pMemory; +} + +template< class T, class I > +inline T* CUtlMemory::Detach() +{ + return (T*)DetachMemory(); +} + //----------------------------------------------------------------------------- // element access @@ -550,35 +607,31 @@ void CUtlMemory::AssumeMemory( T* pMemory, int numElements ) template< class T, class I > inline T& CUtlMemory::operator[]( I i ) { - // Avoid function calls in the asserts to improve debug build performance - Assert( m_nGrowSize != EXTERNAL_CONST_BUFFER_MARKER ); //Assert( !IsReadOnly() ); - Assert( (uint32)i < (uint32)m_nAllocationCount ); - return m_pMemory[(uint32)i]; + Assert( !IsReadOnly() ); + Assert( IsIdxValid(i) ); + return m_pMemory[i]; } template< class T, class I > inline const T& CUtlMemory::operator[]( I i ) const { - // Avoid function calls in the asserts to improve debug build performance - Assert( (uint32)i < (uint32)m_nAllocationCount ); - return m_pMemory[(uint32)i]; + Assert( IsIdxValid(i) ); + return m_pMemory[i]; } template< class T, class I > inline T& CUtlMemory::Element( I i ) { - // Avoid function calls in the asserts to improve debug build performance - Assert( m_nGrowSize != EXTERNAL_CONST_BUFFER_MARKER ); //Assert( !IsReadOnly() ); - Assert( (uint32)i < (uint32)m_nAllocationCount ); - return m_pMemory[(uint32)i]; + Assert( !IsReadOnly() ); + Assert( IsIdxValid(i) ); + return m_pMemory[i]; } template< class T, class I > inline const T& CUtlMemory::Element( I i ) const { - // Avoid function calls in the asserts to improve debug build performance - Assert( (uint32)i < (uint32)m_nAllocationCount ); - return m_pMemory[(uint32)i]; + Assert( IsIdxValid(i) ); + return m_pMemory[i]; } @@ -651,10 +704,10 @@ inline int CUtlMemory::Count() const template< class T, class I > inline bool CUtlMemory::IsIdxValid( I i ) const { - // If we always cast 'i' and 'm_nAllocationCount' to unsigned then we can - // do our range checking with a single comparison instead of two. This gives - // a modest speedup in debug builds. - return (uint32)i < (uint32)m_nAllocationCount; + // GCC warns if I is an unsigned type and we do a ">= 0" against it (since the comparison is always 0). + // We get the warning even if we cast inside the expression. It only goes away if we assign to another variable. + long x = i; + return ( x >= 0 ) && ( x < m_nAllocationCount ); } //----------------------------------------------------------------------------- @@ -672,6 +725,11 @@ inline int UtlMemory_CalcNewAllocationCount( int nAllocationCount, int nGrowSize { // Compute an allocation which is at least as big as a cache line... nAllocationCount = (31 + nBytesItem) / nBytesItem; + // If the requested amount is larger then compute an allocation which + // is exactly the right size. Otherwise we can end up with wasted memory + // when CUtlVector::EnsureCount(n) is called. + if ( nAllocationCount < nNewSize ) + nAllocationCount = nNewSize; } while (nAllocationCount < nNewSize) diff --git a/public/tier1/utlsymbol.h b/public/tier1/utlsymbol.h index e90be466..7eb89775 100644 --- a/public/tier1/utlsymbol.h +++ b/public/tier1/utlsymbol.h @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//===== Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: Defines a symbol table // @@ -13,9 +13,13 @@ #pragma once #endif +#include "tier0/platform.h" #include "tier0/threadtools.h" #include "tier1/utlrbtree.h" #include "tier1/utlvector.h" +#include "tier1/utlbuffer.h" +#include "tier1/utllinkedlist.h" +#include "tier1/stringpool.h" //----------------------------------------------------------------------------- @@ -24,6 +28,7 @@ class CUtlSymbolTable; class CUtlSymbolTableMT; +#define FILENAMEHANDLE_INVALID 0 //----------------------------------------------------------------------------- // This is a symbol, which is a easier way of dealing with strings. @@ -52,14 +57,19 @@ public: bool IsValid() const { return m_Id != UTL_INVAL_SYMBOL; } // Gets at the symbol - operator UtlSymId_t const() const { return m_Id; } + operator UtlSymId_t () const { return m_Id; } // Gets the string associated with the symbol const char* String( ) const; // Modules can choose to disable the static symbol table so to prevent accidental use of them. static void DisableStaticSymbolTable(); - + + // Methods with explicit locking mechanism. Only use for optimization reasons. + static void LockTableForRead(); + static void UnlockTableForRead(); + const char * StringNoLock() const; + protected: UtlSymId_t m_Id; @@ -85,13 +95,17 @@ protected: // of strings to symbols and back. The symbol class itself contains // a static version of this class for creating global strings, but this // class can also be instanced to create local symbol tables. +// +// This class stores the strings in a series of string pools. The first +// two bytes of each string are decorated with a hash to speed up +// comparisons. //----------------------------------------------------------------------------- class CUtlSymbolTable { public: // constructor, destructor - CUtlSymbolTable( int growSize = 0, int initSize = 32, bool caseInsensitive = false ); + CUtlSymbolTable( int growSize = 0, int initSize = 16, bool caseInsensitive = false ); ~CUtlSymbolTable(); // Finds and/or creates a symbol based on the string @@ -102,6 +116,11 @@ public: // Look up the string associated with a particular symbol const char* String( CUtlSymbol id ) const; + + inline bool HasElement(const char* pStr) const + { + return Find(pStr) != UTL_INVAL_SYMBOL; + } // Remove all symbols in the table. void RemoveAll(); @@ -111,6 +130,10 @@ public: return m_Lookup.Count(); } + // We store one of these at the beginning of every string to speed + // up comparisons. + typedef unsigned short hashDecoration_t; + protected: class CStringPoolIndex { @@ -120,10 +143,8 @@ protected: } inline CStringPoolIndex( unsigned short iPool, unsigned short iOffset ) - { - m_iPool = iPool; - m_iOffset = iOffset; - } + : m_iPool(iPool), m_iOffset(iOffset) + {} inline bool operator==( const CStringPoolIndex &other ) const { @@ -158,7 +179,9 @@ protected: }; CTree m_Lookup; + bool m_bInsensitive; + mutable unsigned short m_nUserSearchStringHash; mutable const char* m_pUserSearchString; // stores the string data @@ -167,11 +190,14 @@ protected: private: int FindPoolWithSpace( int len ) const; const char* StringFromIndex( const CStringPoolIndex &index ) const; + const char* DecoratedStringFromIndex( const CStringPoolIndex &index ) const; friend class CLess; + friend class CSymbolHash; + }; -class CUtlSymbolTableMT : private CUtlSymbolTable +class CUtlSymbolTableMT : public CUtlSymbolTable { public: CUtlSymbolTableMT( int growSize = 0, int initSize = 32, bool caseInsensitive = false ) @@ -189,9 +215,9 @@ public: CUtlSymbol Find( const char* pString ) const { - m_lock.LockForRead(); + m_lock.LockForWrite(); CUtlSymbol result = CUtlSymbolTable::Find( pString ); - m_lock.UnlockRead(); + m_lock.UnlockWrite(); return result; } @@ -202,9 +228,24 @@ public: m_lock.UnlockRead(); return pszResult; } - + + const char * StringNoLock( CUtlSymbol id ) const + { + return CUtlSymbolTable::String( id ); + } + + void LockForRead() + { + m_lock.LockForRead(); + } + + void UnlockForRead() + { + m_lock.UnlockRead(); + } + private: -#if defined(WIN32) || defined(_WIN32) +#ifdef WIN32 mutable CThreadSpinRWLock m_lock; #else mutable CThreadRWLock m_lock; @@ -225,7 +266,6 @@ private: // The handle is a CUtlSymbol for the dirname and the same for the filename, the accessor // copies them into a static char buffer for return. typedef void* FileNameHandle_t; -#define FILENAMEHANDLE_INVALID 0 // Symbol table for more efficiently storing filenames by breaking paths and filenames apart. // Refactored from BaseFileSystem.h @@ -238,32 +278,104 @@ class CUtlFilenameSymbolTable { FileNameHandleInternal_t() { - path = 0; - file = 0; + COMPILE_TIME_ASSERT( sizeof( *this ) == sizeof( FileNameHandle_t ) ); + COMPILE_TIME_ASSERT( sizeof( value ) == 4 ); + value = 0; + +#ifdef PLATFORM_64BITS + pad = 0; +#endif } + // We pack the path and file values into a single 32 bit value. We were running + // out of space with the two 16 bit values (more than 64k files) so instead of increasing + // the total size we split the underlying pool into two (paths and files) and + // use a smaller path string pool and a larger file string pool. + unsigned int value; + +#ifdef PLATFORM_64BITS + // some padding to make sure we are the same size as FileNameHandle_t on 64 bit. + unsigned int pad; +#endif + + static const unsigned int cNumBitsInPath = 12; + static const unsigned int cNumBitsInFile = 32 - cNumBitsInPath; + + static const unsigned int cMaxPathValue = 1 << cNumBitsInPath; + static const unsigned int cMaxFileValue = 1 << cNumBitsInFile; + + static const unsigned int cPathBitMask = cMaxPathValue - 1; + static const unsigned int cFileBitMask = cMaxFileValue - 1; + // Part before the final '/' character - unsigned short path; + unsigned int GetPath() const { return ((value >> cNumBitsInFile) & cPathBitMask); } + void SetPath( unsigned int path ) { Assert( path < cMaxPathValue ); value = ((value & cFileBitMask) | ((path & cPathBitMask) << cNumBitsInFile)); } + // Part after the final '/', including extension - unsigned short file; + unsigned int GetFile() const { return (value & cFileBitMask); } + void SetFile( unsigned int file ) { Assert( file < cMaxFileValue ); value = ((value & (cPathBitMask << cNumBitsInFile)) | (file & cFileBitMask)); } }; - class HashTable; - public: - CUtlFilenameSymbolTable(); - ~CUtlFilenameSymbolTable(); FileNameHandle_t FindOrAddFileName( const char *pFileName ); FileNameHandle_t FindFileName( const char *pFileName ); - int PathIndex(const FileNameHandle_t &handle) { return (( const FileNameHandleInternal_t * )&handle)->path; } + int PathIndex( const FileNameHandle_t &handle ) { return (( const FileNameHandleInternal_t * )&handle)->GetPath(); } bool String( const FileNameHandle_t& handle, char *buf, int buflen ); void RemoveAll(); + void SpewStrings(); + bool SaveToBuffer( CUtlBuffer &buffer ); + bool RestoreFromBuffer( CUtlBuffer &buffer ); private: - //CCountedStringPool m_StringPool; - HashTable* m_Strings; + CCountedStringPoolBase m_PathStringPool; + CCountedStringPoolBase m_FileStringPool; mutable CThreadSpinRWLock m_lock; }; +// This creates a simple class that includes the underlying CUtlSymbol +// as a private member and then instances a private symbol table to +// manage those symbols. Avoids the possibility of the code polluting the +// 'global'/default symbol table, while letting the code look like +// it's just using = and .String() to look at CUtlSymbol type objects +// +// NOTE: You can't pass these objects between .dlls in an interface (also true of CUtlSymbol of course) +// +#define DECLARE_PRIVATE_SYMBOLTYPE( typename ) \ + class typename \ + { \ + public: \ + typename(); \ + typename( const char* pStr ); \ + typename& operator=( typename const& src ); \ + bool operator==( typename const& src ) const; \ + const char* String( ) const; \ + private: \ + CUtlSymbol m_SymbolId; \ + }; + +// Put this in the .cpp file that uses the above typename +#define IMPLEMENT_PRIVATE_SYMBOLTYPE( typename ) \ + static CUtlSymbolTable g_##typename##SymbolTable; \ + typename::typename() \ + { \ + m_SymbolId = UTL_INVAL_SYMBOL; \ + } \ + typename::typename( const char* pStr ) \ + { \ + m_SymbolId = g_##typename##SymbolTable.AddString( pStr ); \ + } \ + typename& typename::operator=( typename const& src ) \ + { \ + m_SymbolId = src.m_SymbolId; \ + return *this; \ + } \ + bool typename::operator==( typename const& src ) const \ + { \ + return ( m_SymbolId == src.m_SymbolId ); \ + } \ + const char* typename::String( ) const \ + { \ + return g_##typename##SymbolTable.String( m_SymbolId ); \ + } #endif // UTLSYMBOL_H diff --git a/public/vgui_controls/BuildGroup.h b/public/vgui_controls/BuildGroup.h index c6ce0f4e..a0fcf352 100644 --- a/public/vgui_controls/BuildGroup.h +++ b/public/vgui_controls/BuildGroup.h @@ -94,7 +94,6 @@ public: virtual const char *GetResourceName(void) { return m_pResourceName; } virtual void PanelAdded(Panel* panel); - virtual void PanelRemoved(Panel* panel); virtual bool MousePressed(MouseCode code,Panel* panel); virtual bool MouseReleased(MouseCode code,Panel* panel); diff --git a/public/vstdlib/jobthread.h b/public/vstdlib/jobthread.h index 306e1308..9b34efe9 100644 --- a/public/vstdlib/jobthread.h +++ b/public/vstdlib/jobthread.h @@ -191,7 +191,7 @@ public: // and execute or execute pFunctor right after completing current job and // before looking for another job. //----------------------------------------------------- - virtual void ExecuteHighPriorityFunctor( CFunctor *pFunctor ) = 0; + // virtual void ExecuteHighPriorityFunctor( CFunctor *pFunctor ) = 0; //----------------------------------------------------- // Add an function object to the queue (master thread) diff --git a/studiorender/r_studiodecal.cpp b/studiorender/r_studiodecal.cpp index 53524cde..c48702d0 100644 --- a/studiorender/r_studiodecal.cpp +++ b/studiorender/r_studiodecal.cpp @@ -115,7 +115,7 @@ StudioDecalHandle_t CStudioRender::CreateDecalList( studiohwdata_t *pHardwareDat // NOTE: This function is called directly without queueing m_DecalMutex.Lock(); - int handle = m_DecalList.AddToTail(); + intp handle = m_DecalList.AddToTail(); m_DecalMutex.Unlock(); m_DecalList[handle].m_pHardwareData = pHardwareData; diff --git a/tier0/cpu.cpp b/tier0/cpu.cpp index a8a0814c..9b0a9d6a 100644 --- a/tier0/cpu.cpp +++ b/tier0/cpu.cpp @@ -20,11 +20,23 @@ const tchar* GetProcessorVendorId(); -static bool cpuid(unsigned long function, unsigned long& out_eax, unsigned long& out_ebx, unsigned long& out_ecx, unsigned long& out_edx) +static bool cpuid(uint32 function, uint32& out_eax, uint32& out_ebx, uint32& out_ecx, uint32& out_edx) { #if defined (__arm__) || defined (__arm64__) || defined( _X360 ) return false; #elif defined(GNUC) + +#if defined(PLATFORM_64BITS) + asm("mov %%rbx, %%rsi\n\t" + "cpuid\n\t" + "xchg %%rsi, %%rbx" + : "=a" (out_eax), + "=S" (out_ebx), + "=c" (out_ecx), + "=d" (out_edx) + : "a" (function) + ); +#else asm("mov %%ebx, %%esi\n\t" "cpuid\n\t" "xchg %%esi, %%ebx" @@ -34,7 +46,9 @@ static bool cpuid(unsigned long function, unsigned long& out_eax, unsigned long& "=d" (out_edx) : "a" (function) ); +#endif return true; + #elif defined(_WIN64) int pCPUInfo[4]; __cpuid( pCPUInfo, (int)function ); @@ -45,7 +59,7 @@ static bool cpuid(unsigned long function, unsigned long& out_eax, unsigned long& return true; #else bool retval = true; - unsigned long local_eax, local_ebx, local_ecx, local_edx; + uint32 local_eax, local_ebx, local_ecx, local_edx; _asm pushad; __try @@ -83,7 +97,7 @@ static bool CheckMMXTechnology(void) #if defined( _X360 ) || defined( _PS3 ) return true; #else - unsigned long eax,ebx,edx,unused; + uint32 eax,ebx,edx,unused; if ( !cpuid(1,eax,ebx,unused,edx) ) return false; @@ -151,7 +165,7 @@ static bool CheckSSETechnology(void) return false; } - unsigned long eax,ebx,edx,unused; + uint32 eax,ebx,edx,unused; if ( !cpuid(1,eax,ebx,unused,edx) ) { return false; } @@ -165,7 +179,7 @@ static bool CheckSSE2Technology(void) #if defined( _X360 ) || defined( _PS3 ) || defined(__SANITIZE_ADDRESS__) || defined (__arm__) return false; #else - unsigned long eax,ebx,edx,unused; + uint32 eax,ebx,edx,unused; if ( !cpuid(1,eax,ebx,unused,edx) ) return false; @@ -178,7 +192,7 @@ bool CheckSSE3Technology(void) #if defined( _X360 ) || defined( _PS3 ) || defined(__SANITIZE_ADDRESS__) || defined (__arm__) return false; #else - unsigned long eax,ebx,edx,ecx; + uint32 eax,ebx,edx,ecx; if( !cpuid(1,eax,ebx,ecx,edx) ) return false; @@ -193,7 +207,7 @@ bool CheckSSSE3Technology(void) #else // SSSE 3 is implemented by both Intel and AMD // detection is done the same way for both vendors - unsigned long eax,ebx,edx,ecx; + uint32 eax,ebx,edx,ecx; if( !cpuid(1,eax,ebx,ecx,edx) ) return false; @@ -209,7 +223,7 @@ bool CheckSSE41Technology(void) // SSE 4.1 is implemented by both Intel and AMD // detection is done the same way for both vendors - unsigned long eax,ebx,edx,ecx; + uint32 eax,ebx,edx,ecx; if( !cpuid(1,eax,ebx,ecx,edx) ) return false; @@ -228,7 +242,7 @@ bool CheckSSE42Technology(void) if ( 0 != V_tier0_stricmp( pchVendor, "GenuineIntel" ) ) return false; - unsigned long eax,ebx,edx,ecx; + uint32 eax,ebx,edx,ecx; if( !cpuid(1,eax,ebx,ecx,edx) ) return false; @@ -248,7 +262,7 @@ bool CheckSSE4aTechnology( void ) if ( 0 != V_tier0_stricmp( pchVendor, "AuthenticAMD" ) ) return false; - unsigned long eax,ebx,edx,ecx; + uint32 eax,ebx,edx,ecx; if( !cpuid( 0x80000001,eax,ebx,ecx,edx) ) return false; @@ -262,7 +276,7 @@ static bool Check3DNowTechnology(void) #if defined( _X360 ) || defined( _PS3 ) || defined (__arm__) || defined(__SANITIZE_ADDRESS__) return false; #else - unsigned long eax, unused; + uint32 eax, unused; if ( !cpuid(0x80000000,eax,unused,unused,unused) ) return false; @@ -282,7 +296,7 @@ static bool CheckCMOVTechnology() #if defined( _X360 ) || defined( _PS3 ) || defined (__arm__) || defined(__SANITIZE_ADDRESS__) return false; #else - unsigned long eax,ebx,edx,unused; + uint32 eax,ebx,edx,unused; if ( !cpuid(1,eax,ebx,unused,edx) ) return false; @@ -295,7 +309,7 @@ static bool CheckFCMOVTechnology(void) #if defined( _X360 ) || defined( _PS3 ) || defined (__arm__) || defined(__SANITIZE_ADDRESS__) return false; #else - unsigned long eax,ebx,edx,unused; + uint32 eax,ebx,edx,unused; if ( !cpuid(1,eax,ebx,unused,edx) ) return false; @@ -308,7 +322,7 @@ static bool CheckRDTSCTechnology(void) #if defined( _X360 ) || defined( _PS3 ) || defined (__arm__) || defined(__SANITIZE_ADDRESS__) return false; #else - unsigned long eax,ebx,edx,unused; + uint32 eax,ebx,edx,unused; if ( !cpuid(1,eax,ebx,unused,edx) ) return false; @@ -324,7 +338,7 @@ const tchar* GetProcessorVendorId() #elif defined ( __arm__ ) return "ARM"; #else - unsigned long unused, VendorIDRegisters[3]; + uint32 unused, VendorIDRegisters[3]; static tchar VendorID[13]; @@ -365,7 +379,7 @@ static bool HTSupported(void) const unsigned int EXT_FAMILY_ID = 0x0f00000; // EAX[23:20] - Bit 23 thru 20 contains extended family processor id const unsigned int PENTIUM4_ID = 0x0f00; // Pentium 4 family processor id - unsigned long unused, + uint32 unused, reg_eax = 0, reg_edx = 0, vendor_id[3] = {0, 0, 0}; @@ -393,7 +407,7 @@ static uint8 LogicalProcessorsPerPackage(void) // EBX[23:16] indicate number of logical processors per package const unsigned NUM_LOGICAL_BITS = 0x00FF0000; - unsigned long unused, reg_ebx = 0; + uint32 unused, reg_ebx = 0; if ( !HTSupported() ) return 1; @@ -582,7 +596,7 @@ const CPUInformation* GetCPUInformation() pi.m_szProcessorID = (tchar*)GetProcessorVendorId(); pi.m_bHT = HTSupported(); - unsigned long eax, ebx, edx, ecx; + uint32 eax, ebx, edx, ecx; if (cpuid(1, eax, ebx, ecx, edx)) { pi.m_nModel = eax; // full CPU model info diff --git a/tier0/dbg.cpp b/tier0/dbg.cpp index 2a64e77c..9d64da4a 100644 --- a/tier0/dbg.cpp +++ b/tier0/dbg.cpp @@ -49,7 +49,6 @@ // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" - //----------------------------------------------------------------------------- // internal structures //----------------------------------------------------------------------------- @@ -152,7 +151,7 @@ struct SpewInfo_t int m_nSpewOutputLevel; }; -CThreadLocalPtr g_pSpewInfo; +CTHREADLOCALPTR(SpewInfo_t) g_pSpewInfo; // Standard groups diff --git a/tier0/threadtools.cpp b/tier0/threadtools.cpp index 8f9ff285..2d00d706 100644 --- a/tier0/threadtools.cpp +++ b/tier0/threadtools.cpp @@ -1,35 +1,45 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========== Copyright 2005, Valve Corporation, All rights reserved. ======== // // Purpose: // //============================================================================= -#include "pch_tier0.h" +#include "tier0/platform.h" -#include "tier1/strtools.h" -#include "tier0/dynfunction.h" -#if defined( _WIN32 ) && !defined( _X360 ) +#if defined( PLATFORM_WINDOWS_PC ) #define WIN32_LEAN_AND_MEAN +#define _WIN32_WINNT 0x0403 #include #endif -#ifdef _WIN32 + +#ifdef PLATFORM_WINDOWS #include - -#ifdef IS_WINDOWS_PC - #include - #pragma comment(lib, "winmm.lib") -#endif // IS_WINDOWS_PC - -#elif defined(POSIX) - -#if !defined(OSX) -#if defined(ANDROID) - #include + #ifdef PLATFORM_WINDOWS_PC + #include + #pragma comment(lib, "winmm.lib") + #endif +#elif PLATFORM_PS3 + #include #include -#else + #include + #include + #include + #include + #include + #define GetLastError() errno + typedef void *LPVOID; +#elif PLATFORM_POSIX + #include + #include + #include + #include + #include + #include + #define GetLastError() errno + typedef void *LPVOID; +#if !defined(OSX) #include #include -#endif #define sem_unlink( arg ) #define OS_TO_PTHREAD(x) (x) #else @@ -39,43 +49,39 @@ #define OS_TO_PTHREAD(x) pthread_from_mach_thread_np( x ) #endif // !OSX -#ifdef LINUX -#include // RTLD_NEXT #endif -typedef int (*PTHREAD_START_ROUTINE)( - void *lpThreadParameter - ); -typedef PTHREAD_START_ROUTINE LPTHREAD_START_ROUTINE; -#include -#include -#include -#include -#include -#include -#include -#define GetLastError() errno -typedef void *LPVOID; +#ifndef _PS3 +#include #endif - -#include "tier0/valve_minmax_off.h" -#include -#include "tier0/valve_minmax_on.h" - +#include "tier0/minidump.h" #include "tier0/threadtools.h" -#include "tier0/vcrmode.h" +#include "tier0/dynfunction.h" #ifdef _X360 #include "xbox/xbox_win32stubs.h" #endif -#include "tier0/vprof_telemetry.h" +#include // Must be last header... #include "tier0/memdbgon.h" +#ifdef _PS3 +#include "ps3/ps3_win32stubs.h" +#define NEW_WAIT_FOR_MULTIPLE_OBJECTS +bool gbCheckNotMultithreaded = true; + +extern "C" void(*g_pfnPushMarker)( const char * pName ); +extern "C" void(*g_pfnPopMarker)(); + + +#endif + #define THREADS_DEBUG 1 +#define DEBUG_ERROR(XX) Assert(0) + // Need to ensure initialized before other clients call in for main thread ID #ifdef _WIN32 #pragma warning(disable:4073) @@ -87,6 +93,50 @@ ASSERT_INVARIANT(TT_SIZEOF_CRITICALSECTION == sizeof(CRITICAL_SECTION)); ASSERT_INVARIANT(TT_INFINITE == INFINITE); #endif +// thread creation counter. +// this is used to provide a unique threadid for each running thread in g_nThreadID ( a thread local variable ). + +const int MAX_THREAD_IDS = 128; + +static volatile bool s_bThreadIDAllocated[MAX_THREAD_IDS]; + +#if defined(_LINUX) && defined(DEDICATED) + +DLL_CLASS_EXPORT __thread int g_nThreadID; + +#elif defined(_PS3) + #include "tls_ps3.h" +#else + DLL_CLASS_EXPORT CTHREADLOCALINT g_nThreadID; +#endif + + +static CThreadFastMutex s_ThreadIDMutex; + +PLATFORM_INTERFACE void AllocateThreadID( void ) +{ + AUTO_LOCK( s_ThreadIDMutex ); + for( int i = 1; i < MAX_THREAD_IDS; i++ ) + { + if ( ! s_bThreadIDAllocated[i] ) + { + g_nThreadID = i; + s_bThreadIDAllocated[i] = true; + return; + } + } + Error( "Out of thread ids. Decrease the number of threads or increase MAX_THREAD_IDS\n" ); +} + +PLATFORM_INTERFACE void FreeThreadID( void ) +{ + AUTO_LOCK( s_ThreadIDMutex ); + int nThread = g_nThreadID; + if ( nThread ) + s_bThreadIDAllocated[nThread] = false; +} + + //----------------------------------------------------------------------------- // Simple thread functions. // Because _beginthreadex uses stdcall, we need to convert to cdecl @@ -105,85 +155,330 @@ struct ThreadProcInfo_t //--------------------------------------------------------- -#ifdef _WIN32 -static unsigned __stdcall ThreadProcConvert( void *pParam ) -#elif defined(POSIX) -static void *ThreadProcConvert( void *pParam ) -#else -#error -#endif +#ifdef PLATFORM_WINDOWS +static DWORD WINAPI ThreadProcConvert( void *pParam ) { ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam); + AllocateThreadID(); delete ((ThreadProcInfo_t *)pParam); -#ifdef _WIN32 - return (*info.pfnThread)(info.pParam); -#elif defined(POSIX) - return (void *)(*info.pfnThread)(info.pParam); -#else -#error -#endif + unsigned nRet = (*info.pfnThread)(info.pParam); + FreeThreadID(); + return nRet; } +#elif defined( PLATFORM_PS3 ) +union ThreadProcInfoUnion_t +{ + struct Val_t + { + ThreadFunc_t pfnThread; + void * pParam; + } + val; + uint64_t val64; +}; +static void ThreadProcConvertUnion( uint64_t param ) +{ + COMPILE_TIME_ASSERT( sizeof( ThreadProcInfoUnion_t ) == 8 ); + ThreadProcInfoUnion_t info; + info.val64 = param; + AllocateThreadID(); + unsigned nRet = (*info.val.pfnThread)(info.val.pParam); + FreeThreadID(); + sys_ppu_thread_exit( nRet ); +} +static void* ThreadProcConvert( void *pParam ) +{ + ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam); + AllocateThreadID(); + delete ((ThreadProcInfo_t *)pParam); + unsigned nRet = (*info.pfnThread)(info.pParam); + FreeThreadID(); + return ( void * ) nRet; +} + +#else +static void* ThreadProcConvert( void *pParam ) +{ + ThreadProcInfo_t info = *((ThreadProcInfo_t *)pParam); + AllocateThreadID(); + delete ((ThreadProcInfo_t *)pParam); + unsigned nRet = (*info.pfnThread)(info.pParam); + FreeThreadID(); + return ( void * ) (uintp) nRet; +} +#endif + + + +#if defined( _PS3 ) + +/******************************************************************************* +* Thread Local Storage globals and functions +*******************************************************************************/ +#ifndef _PS3 +__thread void *gTLSValues[ MAX_TLS_VALUES ] = { NULL }; +__thread bool gTLSFlags[ MAX_TLS_VALUES ] = { false }; +__thread bool gbWaitObjectsCreated = false; +__thread sys_semaphore_t gWaitObjectsSemaphore; +#endif // !_PS3 + +static char gThreadName[28] = ""; + +// Simple TLS allocator. Linearly searches for a free slot. +uint32 TlsAlloc() +{ + for ( int i = 0; i < MAX_TLS_VALUES; ++i ) + { + if ( !gTLSFlags[i] ) + { + gTLSFlags[i] = true; + return i; + } + } + +#ifdef _PS3 + DEBUG_ERROR("TlsAlloc(): Out of TLS\n"); +#endif + + return 0xFFFFFFFF; +} + +void TlsFree( uint32 index ) +{ + gTLSValues[ index ] = NULL; + gTLSFlags[ index ] = false; +} + +void *TlsGetValue( uint32 index ) +{ + return gTLSValues[ index ]; +} + +void TlsSetValue( uint32 index, void *pValue ) +{ + gTLSValues[ index ] = pValue; +} +#endif //_PS3 + + + +#ifdef PLATFORM_WINDOWS +class CThreadHandleToIDMap +{ +public: + HANDLE m_hThread; + uint m_ThreadID; + CThreadHandleToIDMap *m_pNext; +}; +static CThreadHandleToIDMap *g_pThreadHandleToIDMaps = NULL; +static CThreadMutex g_ThreadHandleToIDMapMutex; +static volatile int g_nThreadHandleToIDMaps = 0; + +static void AddThreadHandleToIDMap( HANDLE hThread, uint threadID ) +{ + if ( !hThread ) + return; + + // Remember this handle/id combo. + CThreadHandleToIDMap *pMap = new CThreadHandleToIDMap; + pMap->m_hThread = hThread; + pMap->m_ThreadID = threadID; + + // Add it to the global list. + g_ThreadHandleToIDMapMutex.Lock(); + pMap->m_pNext = g_pThreadHandleToIDMaps; + g_pThreadHandleToIDMaps = pMap; + ++g_nThreadHandleToIDMaps; + + g_ThreadHandleToIDMapMutex.Unlock(); + + if ( g_nThreadHandleToIDMaps > 500 ) + Error( "ThreadHandleToIDMap overflow." ); +} + +// This assumes you've got g_ThreadHandleToIDMapMutex locked!! +static bool InternalLookupHandleToThreadIDMap( HANDLE hThread, CThreadHandleToIDMap* &pMap, CThreadHandleToIDMap** &ppPrev ) +{ + ppPrev = &g_pThreadHandleToIDMaps; + for ( pMap=g_pThreadHandleToIDMaps; pMap; pMap=pMap->m_pNext ) + { + if ( pMap->m_hThread == hThread ) + return true; + + ppPrev = &pMap->m_pNext; + } + + return false; +} + +static void RemoveThreadHandleToIDMap( HANDLE hThread ) +{ + if ( !hThread ) + return; + + CThreadHandleToIDMap *pMap, **ppPrev; + + g_ThreadHandleToIDMapMutex.Lock(); + + if ( g_nThreadHandleToIDMaps <= 0 ) + Error( "ThreadHandleToIDMap underflow." ); + + if ( InternalLookupHandleToThreadIDMap( hThread, pMap, ppPrev ) ) + { + *ppPrev = pMap->m_pNext; + delete pMap; + --g_nThreadHandleToIDMaps; + } + + g_ThreadHandleToIDMapMutex.Unlock(); +} + +static uint LookupThreadIDFromHandle( HANDLE hThread ) +{ + if ( hThread == NULL || hThread == GetCurrentThread() ) + return GetCurrentThreadId(); + + float flStartTime = Plat_FloatTime(); + while ( Plat_FloatTime() - flStartTime < 2 ) + { + CThreadHandleToIDMap *pMap, **ppPrev; + + g_ThreadHandleToIDMapMutex.Lock(); + bool bRet = InternalLookupHandleToThreadIDMap( hThread, pMap, ppPrev ); + g_ThreadHandleToIDMapMutex.Unlock(); + + if ( bRet ) + return pMap->m_ThreadID; + + // We should only get here if a thread that is just starting up is currently in AddThreadHandleToIDMap. + // Give up the timeslice and try again. + ThreadSleep( 1 ); + } + + Assert( !"LookupThreadIDFromHandle failed!" ); + Warning( "LookupThreadIDFromHandle couldn't find thread ID for handle." ); + return 0; +} +#endif //--------------------------------------------------------- -ThreadHandle_t CreateSimpleThread( ThreadFunc_t pfnThread, void *pParam, ThreadId_t *pID, unsigned stackSize ) +ThreadHandle_t * CreateTestThreads( ThreadFunc_t fnThread, int numThreads, int nProcessorsToDistribute ) { -#ifdef _WIN32 - ThreadId_t idIgnored; - if ( !pID ) - pID = &idIgnored; - HANDLE h = VCRHook_CreateThread(NULL, stackSize, (LPTHREAD_START_ROUTINE)ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ), CREATE_SUSPENDED, pID); - if ( h != INVALID_HANDLE_VALUE ) + ThreadHandle_t *pHandles = (new ThreadHandle_t[numThreads+1]) + 1; + pHandles[-1] = (ThreadHandle_t)INT_TO_POINTER( numThreads ); + for( int i = 0; i < numThreads; ++i ) { - Plat_ApplyHardwareDataBreakpointsToNewThread( *pID ); - ResumeThread( h ); - } - return (ThreadHandle_t)h; -#elif defined(POSIX) - pthread_t tid; + //TestThreads(); + ThreadHandle_t hThread; + const unsigned int nDefaultStackSize = 64 * 1024; // this stack size is used in case stackSize == 0 + hThread = CreateSimpleThread( fnThread, INT_TO_POINTER( i ), nDefaultStackSize ); - // If we need to create threads that are detached right out of the gate, we would need to do something like this: - // pthread_attr_t attr; - // int rc = pthread_attr_init(&attr); - // rc = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); - // ... pthread_create( &tid, &attr, ... ) ... - // rc = pthread_attr_destroy(&attr); - // ... pthread_join will now fail - - int ret = pthread_create( &tid, NULL, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ) ); - if ( ret ) - { - // There are only PTHREAD_THREADS_MAX number of threads, and we're probably leaking handles if ret == EAGAIN here? - Error( "CreateSimpleThread: pthread_create failed. Someone not calling pthread_detach() or pthread_join. Ret:%d\n", ret ); + if ( nProcessorsToDistribute ) + { + int32 mask = 1 << (i % nProcessorsToDistribute); + ThreadSetAffinity( hThread, mask ); + } + +/* + ThreadProcInfoUnion_t info; + info.val.pfnThread = fnThread; + info.val.pParam = (void*)(i); + if ( int nError = sys_ppu_thread_create( &hThread, ThreadProcConvertUnion, info.val64, 1001, nDefaultStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) != CELL_OK ) + { + printf( "PROBLEM!\n" ); + Error( "Cannot create thread, error %d\n", nError ); + return 0; + } +*/ + //ThreadHandle_t hThread = CreateSimpleThread( fnThread, (void*)i ); + pHandles[i] = hThread; } - if ( pID ) - *pID = (ThreadId_t)tid; - Plat_ApplyHardwareDataBreakpointsToNewThread( (long unsigned int)tid ); - return (ThreadHandle_t)tid; -#endif +// printf("Finishinged CreateTestThreads(%p,%d)\n", (void*)fnThread, numThreads ); + return pHandles; } +void JoinTestThreads( ThreadHandle_t *pHandles ) +{ + int nCount = POINTER_TO_INT( (uintp)pHandles[-1] ); +// printf("Joining test threads @%p[%d]:\n", pHandles, nCount ); +// for( int i = 0; i < nCount; ++i ) +// { +// printf(" %p,\n", (void*)pHandles[i] ); +// } + for( int i = 0; i < nCount; ++i ) + { +// printf( "Joining %p", (void*) pHandles[i] ); +// if( !i ) sys_timer_usleep(100000); + ThreadJoin( pHandles[i] ); + ReleaseThreadHandle( pHandles[i] ); + } + delete[]( pHandles - 1 ); +} + + + ThreadHandle_t CreateSimpleThread( ThreadFunc_t pfnThread, void *pParam, unsigned stackSize ) { - return CreateSimpleThread( pfnThread, pParam, NULL, stackSize ); -} - -PLATFORM_INTERFACE void ThreadDetach( ThreadHandle_t hThread ) -{ -#if defined( POSIX ) - // The resources of this thread will be freed immediately when it terminates, - // instead of waiting for another thread to perform PTHREAD_JOIN. - pthread_t tid = ( pthread_t )hThread; - - pthread_detach( tid ); +#ifdef PLATFORM_WINDOWS + DWORD threadID; + HANDLE hThread = (HANDLE)CreateThread( NULL, stackSize, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ), stackSize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0, &threadID ); + AddThreadHandleToIDMap( hThread, threadID ); + return (ThreadHandle_t)hThread; +#elif PLATFORM_PS3 + //TestThreads(); + ThreadHandle_t th; + ThreadProcInfoUnion_t info; + info.val.pfnThread = pfnThread; + info.val.pParam = pParam; + const unsigned int nDefaultStackSize = 64 * 1024; // this stack size is used in case stackSize == 0 + if ( sys_ppu_thread_create( &th, ThreadProcConvertUnion, info.val64, 1001, stackSize ? stackSize : nDefaultStackSize, SYS_PPU_THREAD_CREATE_JOINABLE, "SimpleThread" ) != CELL_OK ) + { + AssertMsg1( 0, "Failed to create thread (error 0x%x)", errno ); + return 0; + } + return th; +#elif PLATFORM_POSIX + pthread_t tid; + pthread_create( &tid, NULL, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ) ); + return ( ThreadHandle_t ) tid; +#else + Assert( 0 ); + DebuggerBreak(); + return 0; #endif } +ThreadHandle_t CreateSimpleThread( ThreadFunc_t pfnThread, void *pParam, ThreadId_t *pID, unsigned stackSize ) +{ +#ifdef PLATFORM_WINDOWS + DWORD threadID; + HANDLE hThread = (HANDLE)CreateThread( NULL, stackSize, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ), stackSize ? STACK_SIZE_PARAM_IS_A_RESERVATION : 0, &threadID ); + if( pID ) + *pID = (ThreadId_t)threadID; + AddThreadHandleToIDMap( hThread, threadID ); + return (ThreadHandle_t)hThread; +#elif PLATFORM_POSIX + pthread_t tid; + pthread_create( &tid, NULL, ThreadProcConvert, new ThreadProcInfo_t( pfnThread, pParam ) ); + if( pID ) + *pID = (ThreadId_t)tid; + return ( ThreadHandle_t ) tid; +#else + Assert( 0 ); + DebuggerBreak(); + return 0; +#endif +} + + bool ReleaseThreadHandle( ThreadHandle_t hThread ) { #ifdef _WIN32 - return ( CloseHandle( hThread ) != 0 ); + bool bRetVal = ( CloseHandle( hThread ) != 0 ); + RemoveThreadHandleToIDMap( (HANDLE)hThread ); + return bRetVal; #else return true; #endif @@ -199,7 +494,7 @@ void ThreadSleep(unsigned nMilliseconds) { #ifdef _WIN32 -#ifdef IS_WINDOWS_PC +#ifdef PLATFORM_WINDOWS_PC static bool bInitialized = false; if ( !bInitialized ) { @@ -210,14 +505,42 @@ void ThreadSleep(unsigned nMilliseconds) // rate. timeBeginPeriod( 1 ); } -#endif // IS_WINDOWS_PC +#endif Sleep( nMilliseconds ); +#elif PLATFORM_PS3 + if( nMilliseconds == 0 ) + { + // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead. +// sys_timer_usleep( 60 ); + sys_ppu_thread_yield(); + } + else + { + sys_timer_usleep( nMilliseconds * 1000 ); + } #elif defined(POSIX) usleep( nMilliseconds * 1000 ); #endif } +//----------------------------------------------------------------------------- +void ThreadNanoSleep(unsigned ns) +{ +#ifdef _WIN32 + // ceil + Sleep( ( ns + 999 ) / 1000 ); +#elif PLATFORM_PS3 + sys_timer_usleep( ns ); +#elif defined(POSIX) + struct timespec tm; + tm.tv_sec = 0; + tm.tv_nsec = ns; + nanosleep( &tm, NULL ); +#endif +} + + //----------------------------------------------------------------------------- #ifndef ThreadGetCurrentId @@ -225,8 +548,16 @@ ThreadId_t ThreadGetCurrentId() { #ifdef _WIN32 return GetCurrentThreadId(); +#elif defined( _PS3 ) + sys_ppu_thread_t th = 0; + sys_ppu_thread_get_id( &th ); + return th; #elif defined(POSIX) return (ThreadId_t)pthread_self(); +#else + Assert(0); + DebuggerBreak(); + return 0; #endif } #endif @@ -236,8 +567,16 @@ ThreadHandle_t ThreadGetCurrentHandle() { #ifdef _WIN32 return (ThreadHandle_t)GetCurrentThread(); +#elif defined( _PS3 ) + sys_ppu_thread_t th = 0; + sys_ppu_thread_get_id( &th ); + return th; #elif defined(POSIX) return (ThreadHandle_t)pthread_self(); +#else + Assert(0); + DebuggerBreak(); + return 0; #endif } @@ -261,24 +600,15 @@ bool ThreadIsThreadIdRunning( ThreadId_t uThreadId ) } return bRunning; #elif defined( _PS3 ) - + // will return CELL_OK for zombie threads int priority; return (sys_ppu_thread_get_priority( uThreadId, &priority ) == CELL_OK ); #elif defined(POSIX) - pthread_t thread = OS_TO_PTHREAD(uThreadId); - if ( thread ) - { - int iResult = pthread_kill( thread, 0 ); - if ( iResult == 0 ) - return true; - } - else - { - // We really ought not to be passing NULL in to here - AssertMsg( false, "ThreadIsThreadIdRunning received a null thread ID" ); - } + int iResult = pthread_kill( OS_TO_PTHREAD(uThreadId), 0 ); + if ( iResult == 0 ) + return true; return false; #endif @@ -295,11 +625,12 @@ int ThreadGetPriority( ThreadHandle_t hThread ) #ifdef _WIN32 return ::GetThreadPriority( (HANDLE)hThread ); +#elif defined( _PS3 ) + int iPri = 0; + sys_ppu_thread_get_priority( hThread, &iPri ); + return iPri; #else - struct sched_param thread_param; - int policy; - pthread_getschedparam( (pthread_t)hThread, &policy, &thread_param ); - return thread_param.sched_priority; + return 0; #endif } @@ -314,10 +645,13 @@ bool ThreadSetPriority( ThreadHandle_t hThread, int priority ) #ifdef _WIN32 return ( SetThreadPriority(hThread, priority) != 0 ); +#elif defined( _PS3 ) + int retval = sys_ppu_thread_set_priority( hThread, priority ); + return retval >= CELL_OK; #elif defined(POSIX) struct sched_param thread_param; thread_param.sched_priority = priority; - pthread_setschedparam( (pthread_t)hThread, SCHED_OTHER, &thread_param ); + //pthread_setschedparam( (pthread_t ) hThread, SCHED_RR, &thread_param ); return true; #endif } @@ -346,32 +680,12 @@ void ThreadSetAffinity( ThreadHandle_t hThread, int nAffinityMask ) //----------------------------------------------------------------------------- +#ifndef _X360 ThreadId_t InitMainThread() { -#ifndef LINUX - // Skip doing the setname on Linux for the main thread. Here is why... - - // From Pierre-Loup e-mail about why pthread_setname_np() on the main thread - // in Linux will cause some tools to display "MainThrd" as the executable name: - // - // You have two things in procfs, comm and cmdline. Each of the threads have - // a different `comm`, which is the value you set through pthread_setname_np - // or prctl(PR_SET_NAME). Top can either display cmdline or comm; it - // switched to display comm by default; htop still displays cmdline by - // default. Top -c will output cmdline rather than comm. - // - // If you press 'H' while top is running it will display each thread as a - // separate process, so you will have different entries for MainThrd, - // MatQueue0, etc with their own CPU usage. But when that mode isn't enabled - // it just displays the 'comm' name from the first thread. ThreadSetDebugName( "MainThrd" ); -#endif -#ifdef _WIN32 return ThreadGetCurrentId(); -#elif defined(POSIX) - return (ThreadId_t)pthread_self(); -#endif } ThreadId_t g_ThreadMainThreadID = InitMainThread(); @@ -381,24 +695,33 @@ bool ThreadInMainThread() return ( ThreadGetCurrentId() == g_ThreadMainThreadID ); } -//----------------------------------------------------------------------------- void DeclareCurrentThreadIsMainThread() { g_ThreadMainThreadID = ThreadGetCurrentId(); } +#else +byte *InitMainThread() +{ + byte b; + + return AlignValue( &b, 64*1024 ); +} +#define STACK_SIZE_360 327680 +byte *g_pBaseMainStack = InitMainThread(); +byte *g_pLimitMainStack = InitMainThread() - STACK_SIZE_360; +#endif + +//----------------------------------------------------------------------------- bool ThreadJoin( ThreadHandle_t hThread, unsigned timeout ) { - // You should really never be calling this with a NULL thread handle. If you - // are then that probably implies a race condition or threading misunderstanding. - Assert( hThread ); if ( !hThread ) { return false; } #ifdef _WIN32 - DWORD dwWait = VCRHook_WaitForSingleObject((HANDLE)hThread, timeout); + DWORD dwWait = WaitForSingleObject( (HANDLE)hThread, timeout ); if ( dwWait == WAIT_TIMEOUT) return false; if ( dwWait != WAIT_OBJECT_0 && ( dwWait != WAIT_FAILED && GetLastError() != 0 ) ) @@ -406,29 +729,24 @@ bool ThreadJoin( ThreadHandle_t hThread, unsigned timeout ) Assert( 0 ); return false; } +#elif defined( _PS3 ) + uint64 uiExitCode = 0; + int retval = sys_ppu_thread_join( hThread, &uiExitCode ); + return ( retval >= CELL_OK ); #elif defined(POSIX) if ( pthread_join( (pthread_t)hThread, NULL ) != 0 ) return false; +#else + Assert(0); + DebuggerBreak(); #endif return true; } -#ifdef RAD_TELEMETRY_ENABLED -void TelemetryThreadSetDebugName( ThreadId_t id, const char *pszName ); -#endif - //----------------------------------------------------------------------------- - -void ThreadSetDebugName( ThreadId_t id, const char *pszName ) +void ThreadSetDebugName( ThreadHandle_t hThread, const char *pszName ) { - if( !pszName ) - return; - -#ifdef RAD_TELEMETRY_ENABLED - TelemetryThreadSetDebugName( id, pszName ); -#endif - -#ifdef _WIN32 +#ifdef WIN32 if ( Plat_IsInDebugSession() ) { #define MS_VC_EXCEPTION 0x406d1388 @@ -444,59 +762,25 @@ void ThreadSetDebugName( ThreadId_t id, const char *pszName ) THREADNAME_INFO info; info.dwType = 0x1000; info.szName = pszName; - info.dwThreadID = id; - info.dwFlags = 0; + info.dwThreadID = LookupThreadIDFromHandle( hThread ); - __try + if ( info.dwThreadID != 0 ) { - RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info); - } - __except (EXCEPTION_CONTINUE_EXECUTION) - { - } - } -#elif defined( _LINUX ) - // As of glibc v2.12, we can use pthread_setname_np. - typedef int (pthread_setname_np_func)(pthread_t, const char *); - static pthread_setname_np_func *s_pthread_setname_np_func = (pthread_setname_np_func *)dlsym(RTLD_DEFAULT, "pthread_setname_np"); + info.dwFlags = 0; - if ( s_pthread_setname_np_func ) - { - if ( id == (uint32)-1 ) - id = pthread_self(); - - /* - pthread_setname_np() in phthread_setname.c has the following code: - - #define TASK_COMM_LEN 16 - size_t name_len = strlen (name); - if (name_len >= TASK_COMM_LEN) - return ERANGE; - - So we need to truncate the threadname to 16 or the call will just fail. - */ - char szThreadName[ 16 ]; - strncpy( szThreadName, pszName, ARRAYSIZE( szThreadName ) ); - szThreadName[ ARRAYSIZE( szThreadName ) - 1 ] = 0; - (*s_pthread_setname_np_func)( id, szThreadName ); + __try + { + RaiseException(MS_VC_EXCEPTION, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR *)&info); + } + __except (EXCEPTION_CONTINUE_EXECUTION) + { + } + } } #endif } -//----------------------------------------------------------------------------- - -#ifdef _WIN32 -ASSERT_INVARIANT( TW_FAILED == WAIT_FAILED ); -ASSERT_INVARIANT( TW_TIMEOUT == WAIT_TIMEOUT ); -ASSERT_INVARIANT( WAIT_OBJECT_0 == 0 ); - -int ThreadWaitForObjects( int nEvents, const HANDLE *pHandles, bool bWaitAll, unsigned timeout ) -{ - return VCRHook_WaitForMultipleObjects( nEvents, pHandles, bWaitAll, timeout ); -} -#endif - //----------------------------------------------------------------------------- // Used to thread LoadLibrary on the 360 @@ -515,15 +799,50 @@ PLATFORM_INTERFACE ThreadedLoadLibraryFunc_t GetThreadedLoadLibraryFunc() //----------------------------------------------------------------------------- // +// CThreadSyncObject (note nothing uses this directly (I think) ) +// //----------------------------------------------------------------------------- + +#ifdef _PS3 +uint32_t CThreadSyncObject::m_bstaticMutexInitialized = false; +uint32_t CThreadSyncObject::m_bstaticMutexInitializing = false; +sys_lwmutex_t CThreadSyncObject::m_staticMutex; +#endif + + CThreadSyncObject::CThreadSyncObject() #ifdef _WIN32 : m_hSyncObject( NULL ), m_bCreatedHandle(false) -#elif defined(POSIX) +#elif defined(POSIX) && !defined(PLATFORM_PS3) : m_bInitalized( false ) #endif { +#ifdef _PS3 + //Do we nee to initialise the staticMutex? + if (m_bstaticMutexInitialized) return; + + //If we are the first thread then create the mutex + if ( cellAtomicCompareAndSwap32(&m_bstaticMutexInitializing, false, true) == false ) + { + sys_lwmutex_attribute_t mutexAttr; + sys_lwmutex_attribute_initialize( mutexAttr ); + mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE; + int err = sys_lwmutex_create( &m_staticMutex, &mutexAttr ); + Assert(err == CELL_OK); + m_bstaticMutexInitialized = true; + } + else + { + //Another thread is already in the process of initialising the mutex, wait for it + while ( !m_bstaticMutexInitialized ) + { + // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead. +// sys_timer_usleep( 60 ); + sys_ppu_thread_yield(); + } + } +#endif } //--------------------------------------------------------- @@ -538,12 +857,12 @@ CThreadSyncObject::~CThreadSyncObject() Assert( 0 ); } } -#elif defined(POSIX) +#elif defined(POSIX) && !defined( PLATFORM_PS3 ) if ( m_bInitalized ) { - pthread_cond_destroy( &m_Condition ); + pthread_cond_destroy( &m_Condition ); pthread_mutex_destroy( &m_Mutex ); - m_bInitalized = false; + m_bInitalized = false; } #endif } @@ -552,7 +871,9 @@ CThreadSyncObject::~CThreadSyncObject() bool CThreadSyncObject::operator!() const { -#ifdef _WIN32 +#if PLATFORM_PS3 + return m_bstaticMutexInitialized; +#elif defined( _WIN32 ) return !m_hSyncObject; #elif defined(POSIX) return !m_bInitalized; @@ -564,7 +885,9 @@ bool CThreadSyncObject::operator!() const void CThreadSyncObject::AssertUseable() { #ifdef THREADS_DEBUG -#ifdef _WIN32 +#if PLATFORM_PS3 + AssertMsg( m_bstaticMutexInitialized, "Thread synchronization object is unuseable" ); +#elif defined( _WIN32 ) AssertMsg( m_hSyncObject, "Thread synchronization object is unuseable" ); #elif defined(POSIX) AssertMsg( m_bInitalized, "Thread synchronization object is unuseable" ); @@ -574,14 +897,15 @@ void CThreadSyncObject::AssertUseable() //--------------------------------------------------------- +#if defined(_WIN32) || ( defined(POSIX) && !defined( _PS3 ) ) bool CThreadSyncObject::Wait( uint32 dwTimeout ) { #ifdef THREADS_DEBUG AssertUseable(); #endif #ifdef _WIN32 - return ( VCRHook_WaitForSingleObject( m_hSyncObject, dwTimeout ) == WAIT_OBJECT_0 ); -#elif defined(POSIX) + return ( WaitForSingleObject( m_hSyncObject, dwTimeout ) == WAIT_OBJECT_0 ); +#elif defined( POSIX ) && !defined( PLATFORM_PS3 ) pthread_mutex_lock( &m_Mutex ); bool bRet = false; if ( m_cSet > 0 ) @@ -634,6 +958,147 @@ bool CThreadSyncObject::Wait( uint32 dwTimeout ) return bRet; #endif } +#endif + +uint32 CThreadSyncObject::WaitForMultiple( int nObjects, CThreadSyncObject **ppObjects, bool bWaitAll, uint32 dwTimeout ) +{ +#if defined( _WIN32 ) + + CThreadSyncObject *pHandles = (CThreadSyncObject*)stackalloc( sizeof(CThreadSyncObject) * nObjects ); + for ( int i=0; i < nObjects; i++ ) + { + pHandles[i].m_hSyncObject = ppObjects[i]->m_hSyncObject; + } + + return WaitForMultiple( nObjects, pHandles, bWaitAll, dwTimeout ); + +#else + + // TODO: Need a more efficient implementation of this. + uint32 dwStartTime = 0; + + if ( dwTimeout != TT_INFINITE ) + dwStartTime = Plat_MSTime(); + + // If bWaitAll = true, then we need to track which ones were triggered. + char *pWasTriggered = NULL; + int nTriggered = 0; + if ( bWaitAll ) + { + pWasTriggered = (char*)stackalloc( nObjects ); + memset( pWasTriggered, 0, nObjects ); + } + + while ( 1 ) + { + for ( int i=0; i < nObjects; i++ ) + { + if ( bWaitAll && pWasTriggered[i] ) + continue; + +#ifdef _PS3 + Assert( !"Not implemented!" ); + if ( false ) +#else + if ( ppObjects[i]->Wait( 0 ) ) +#endif + { + ++nTriggered; + if ( bWaitAll ) + { + if ( nTriggered == nObjects ) + return 0; + else + pWasTriggered[i] = 1; + } + else + { + return i; + } + } + } + + // Timeout? + if ( dwTimeout != TT_INFINITE ) + { + if ( Plat_MSTime() - dwStartTime >= dwTimeout ) + return TW_TIMEOUT; + } + + ThreadSleep( 0 ); + } + +#endif +} + +uint32 CThreadSyncObject::WaitForMultiple( int nObjects, CThreadSyncObject *pObjects, bool bWaitAll, uint32 dwTimeout ) +{ +#if defined(_WIN32 ) + + HANDLE *pHandles = (HANDLE*)stackalloc( sizeof(HANDLE) * nObjects ); + for ( int i=0; i < nObjects; i++ ) + { + pHandles[i] = pObjects[i].m_hSyncObject; + } + + DWORD ret = WaitForMultipleObjects( nObjects, pHandles, bWaitAll, dwTimeout ); + if ( ret == WAIT_TIMEOUT ) + return TW_TIMEOUT; + else if ( ret >= WAIT_OBJECT_0 && (ret-WAIT_OBJECT_0) < (uint32)nObjects ) + return (int)(ret - WAIT_OBJECT_0); + else if ( ret >= WAIT_ABANDONED_0 && (ret - WAIT_ABANDONED_0) < (uint32)nObjects ) + Error( "Unhandled WAIT_ABANDONED in WaitForMultipleObjects" ); + else if ( ret == WAIT_FAILED ) + return TW_FAILED; + else + Error( "Unknown return value (%lu) from WaitForMultipleObjects", ret ); + + // We'll never get here.. + return 0; + +#else + + CThreadSyncObject **ppObjects = (CThreadSyncObject**)stackalloc( sizeof( CThreadSyncObject* ) * nObjects ); + for ( int i=0; i < nObjects; i++ ) + { + ppObjects[i] = &pObjects[i]; + } + + return WaitForMultiple( nObjects, ppObjects, bWaitAll, dwTimeout ); + +#endif +} + +// To implement these, I need to check that casts are safe +uint32 CThreadEvent::WaitForMultiple( int nObjects, CThreadEvent *pObjects, bool bWaitAll, uint32 dwTimeout ) +{ + // If data ever gets added to CThreadEvent, then we need a different implementation. +#ifdef _PS3 + CThreadEvent **ppObjects = (CThreadEvent**)stackalloc( sizeof( CThreadEvent* ) * nObjects ); + for ( int i=0; i < nObjects; i++ ) + { + ppObjects[i] = &pObjects[i]; + } + return WaitForMultipleObjects( nObjects, ppObjects, bWaitAll, dwTimeout ); +#else + COMPILE_TIME_ASSERT( sizeof( CThreadSyncObject ) == 0 || sizeof( CThreadEvent ) == sizeof( CThreadSyncObject ) ); + return CThreadSyncObject::WaitForMultiple( nObjects, (CThreadSyncObject*)pObjects, bWaitAll, dwTimeout ); +#endif +} + + +uint32 CThreadEvent::WaitForMultiple( int nObjects, CThreadEvent **ppObjects, bool bWaitAll, uint32 dwTimeout ) +{ +#ifdef _PS3 + return WaitForMultipleObjects( nObjects, ppObjects, bWaitAll, dwTimeout ); +#else + // If data ever gets added to CThreadEvent, then we need a different implementation. + COMPILE_TIME_ASSERT( sizeof( CThreadSyncObject )== 0 || sizeof( CThreadEvent ) == sizeof( CThreadSyncObject ) ); + return CThreadSyncObject::WaitForMultiple( nObjects, (CThreadSyncObject**)ppObjects, bWaitAll, dwTimeout ); +#endif +} + + //----------------------------------------------------------------------------- // @@ -645,6 +1110,23 @@ CThreadEvent::CThreadEvent( bool bManualReset ) m_hSyncObject = CreateEvent( NULL, bManualReset, FALSE, NULL ); m_bCreatedHandle = true; AssertMsg1(m_hSyncObject, "Failed to create event (error 0x%x)", GetLastError() ); +#elif defined( _PS3 ) + + m_bManualReset = bManualReset; + m_bSet = 0; + m_bInitalized = false; + m_numWaitingThread = 0; + + // set up linked list of wait objects + + memset(&m_waitObjects[0], 0, sizeof(m_waitObjects)); + m_pWaitObjectsList = &m_waitObjects[0]; + m_pWaitObjectsPool = &m_waitObjects[1]; + + for (int i = 2; i < CTHREADEVENT_MAX_WAITING_THREADS + 2; i++) + { + LLLinkNode(m_pWaitObjectsPool, &m_waitObjects[i]); + } #elif defined( POSIX ) pthread_mutexattr_t Attr; pthread_mutexattr_init( &Attr ); @@ -660,14 +1142,146 @@ CThreadEvent::CThreadEvent( bool bManualReset ) #endif } -#ifdef _WIN32 -CThreadEvent::CThreadEvent( HANDLE hHandle ) + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- + +#ifdef _PS3 + +// +// linked list functionality +// + +//----------------------------------------------------------------------------- +// Purpose: Linked list implementation +//----------------------------------------------------------------------------- + +CThreadEventWaitObject* CThreadEvent::LLUnlinkNode(CThreadEventWaitObject *node) { - m_hSyncObject = hHandle; - m_bCreatedHandle = false; - AssertMsg(m_hSyncObject, "Null event passed into constructor" ); + // Note: if you have a null-access crash here, it may mean that CTHREADEVENT_MAX_WAITING_THREADS is not high enough + // and the linked list pool is simply exhausted + node->m_pPrev->m_pNext = node->m_pNext; + if (node->m_pNext) node->m_pNext->m_pPrev = node->m_pPrev; + node->m_pNext = node->m_pPrev = NULL; + + return node; } -#endif + +CThreadEventWaitObject* CThreadEvent::LLLinkNode(CThreadEventWaitObject* list, CThreadEventWaitObject *node) +{ + node->m_pNext = list->m_pNext; + if (node->m_pNext) + { + node->m_pNext->m_pPrev = node; + } + + list->m_pNext = node; + node->m_pPrev = list; + + return node; +} + +//----------------------------------------------------------------------------- +// Helper function to atomically write index into destination and set semaphore +// This is used by WaitForMultipleObjects(WAIT_ANY) because once the semaphore +// is set, the waiting thread also needs to know which event triggered it +// We do NOT need this to be atomic because if a number of events fire it doesn't +// matter which one of these we pick +//----------------------------------------------------------------------------- +void CThreadEventWaitObject::Set() +{ + *m_pFlag = m_index; + sys_semaphore_post(*m_pSemaphore, 1); +} + +// +// CThreadEvent::RegisterWaitingThread +// +void CThreadEvent::RegisterWaitingThread(sys_semaphore_t *pSemaphore, int index, int *flag) +{ + sys_lwmutex_lock(&m_staticMutex, 0); + + // if we are already set, then signal this semaphore + if (m_bSet) + { + CThreadEventWaitObject waitObject; + waitObject.Init(pSemaphore, index, flag); + waitObject.Set(); + + if (!m_bManualReset) + { + m_bSet = false; + } + } + else + { + if (!m_pWaitObjectsPool->m_pNext) + { + DEBUG_ERROR("CThreadEvent: Ran out of events; cannot register waiting thread\n"); + } + + // add this semaphore to linked list - can be added more than once it doesn't matter + + CThreadEventWaitObject *pWaitObject = LLUnlinkNode(m_pWaitObjectsPool->m_pNext); + + pWaitObject->Init(pSemaphore, index, flag); + + LLLinkNode(m_pWaitObjectsList, pWaitObject); + } + + sys_lwmutex_unlock(&m_staticMutex); +} + +// +// CThreadEvent::UnregisterWaitingThread +// +void CThreadEvent::UnregisterWaitingThread(sys_semaphore_t *pSemaphore) +{ + // remove all instances of this semaphore from linked list + + sys_lwmutex_lock(&m_staticMutex, 0); + + CThreadEventWaitObject *pWaitObject = m_pWaitObjectsList->m_pNext; + + while (pWaitObject) + { + CThreadEventWaitObject *pNext = pWaitObject->m_pNext; + + if (pWaitObject->m_pSemaphore == pSemaphore) + { + LLUnlinkNode(pWaitObject); + LLLinkNode(m_pWaitObjectsPool, pWaitObject); + } + + pWaitObject = pNext; + } + + sys_lwmutex_unlock(&m_staticMutex); +} + +#endif // _PS3 + + +#ifdef PLATFORM_WINDOWS + CThreadEvent::CThreadEvent( const char *name, bool initialState, bool bManualReset ) + { + m_hSyncObject = CreateEvent( NULL, bManualReset, (BOOL) initialState, name ); + AssertMsg1( m_hSyncObject, "Failed to create event (error 0x%x)", GetLastError() ); + } + + + NamedEventResult_t CThreadEvent::CheckNamedEvent( const char *name, uint32 dwTimeout ) + { + HANDLE eHandle = OpenEvent( SYNCHRONIZE, FALSE, name ); + + if ( eHandle == NULL ) return TT_EventDoesntExist; + + DWORD result = WaitForSingleObject( eHandle, dwTimeout ); + + return ( result == WAIT_OBJECT_0 ) ? TT_EventSignaled : TT_EventNotSignaled; + } +#endif //----------------------------------------------------------------------------- // @@ -678,17 +1292,92 @@ CThreadEvent::CThreadEvent( HANDLE hHandle ) bool CThreadEvent::Set() { +////////////////////////////////////////////////////////////// +#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS +////////////////////////////////////////////////////////////// AssertUseable(); #ifdef _WIN32 return ( SetEvent( m_hSyncObject ) != 0 ); +#elif defined( _PS3 ) + + sys_lwmutex_lock(&m_staticMutex, 0); + + if (m_bManualReset) + { + //Mark event as set + m_bSet = true; + + //If any threads are already waiting then signal them to run + if (m_bInitalized) + { + int err = sys_semaphore_post( m_Semaphore, m_numWaitingThread); + Assert(err == CELL_OK); + } + } + else + { + //If any threads are already waiting then signal ONE to run, else signal next to run + + if (m_numWaitingThread>0) + { + int err = sys_semaphore_post( m_Semaphore, 1); + Assert(err == CELL_OK); + } + else + { + m_bSet=true; + } + } + + sys_lwmutex_unlock(&m_staticMutex); + + return true; + + #elif defined(POSIX) - pthread_mutex_lock( &m_Mutex ); - m_cSet = 1; + pthread_mutex_lock( &m_Mutex ); + m_cSet = 1; m_bWakeForEvent = true; int ret = pthread_cond_signal( &m_Condition ); - pthread_mutex_unlock( &m_Mutex ); - return ret == 0; + pthread_mutex_unlock( &m_Mutex ); + return ret == 0; #endif + + +////////////////////////////////////////////////////////////// +#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS +////////////////////////////////////////////////////////////// + + sys_lwmutex_lock(&m_staticMutex, 0); + + //Mark event as set + m_bSet = true; + + // signal registered semaphores + while (m_pWaitObjectsList->m_pNext) + { + CThreadEventWaitObject *pWaitObject = LLUnlinkNode(m_pWaitObjectsList->m_pNext); + + pWaitObject->Set(); + + LLLinkNode(m_pWaitObjectsPool, pWaitObject); + + g_pfnPopMarker(); + + if (!m_bManualReset) + { + m_bSet = false; + break; + } + } + + sys_lwmutex_unlock(&m_staticMutex); + + return true; + +////////////////////////////////////////////////////////////// +#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS +////////////////////////////////////////////////////////////// } //--------------------------------------------------------- @@ -700,6 +1389,12 @@ bool CThreadEvent::Reset() #endif #ifdef _WIN32 return ( ResetEvent( m_hSyncObject ) != 0 ); +#elif defined( _PS3 ) + + //Just mark us as no longer signaled + m_bSet = 0; + + return true; #elif defined(POSIX) pthread_mutex_lock( &m_Mutex ); m_cSet = 0; @@ -713,6 +1408,9 @@ bool CThreadEvent::Reset() bool CThreadEvent::Check() { + #ifdef _PS3 + return m_bSet; // Please, use for debugging only! + #endif #ifdef THREADS_DEBUG AssertUseable(); #endif @@ -723,7 +1421,79 @@ bool CThreadEvent::Check() bool CThreadEvent::Wait( uint32 dwTimeout ) { +////////////////////////////////////////////////////////////// +#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS +////////////////////////////////////////////////////////////// + + +#if defined( _WIN32 ) || ( defined( POSIX ) && !defined( _PS3 ) ) return CThreadSyncObject::Wait( dwTimeout ); +#elif defined( _PS3 ) + + { + + if (dwTimeout == 0) + { + //If timeout is 0 then just test it now (and reset it if manual ) + if (m_bSet) + { + if ( !m_bManualReset ) m_bSet=false; + return true; + } + return false; + } + + if (!AddWaitingThread()) + { + //Waiting thread NOT added because m_bSet was already set + if ( !m_bManualReset ) m_bSet=false; + return true; + } + + uint32 timeout; + int countTimeout = 0; + int ret = ETIMEDOUT; + while ( timeout=MIN(1, dwTimeout) ) + { + // on the PS3, "infinite timeout" is specified by zero, not + // 0xFFFFFFFF, so we need to perform that ternary here. +//#error Untested code: + ret = sys_semaphore_wait( m_Semaphore, timeout == TT_INFINITE ? 0 : timeout * 1000 ); + Assert( (ret == CELL_OK) || (ret == ETIMEDOUT) ); + + if ( ret == CELL_OK ) + break; + + dwTimeout -= timeout; + countTimeout++; + if (countTimeout > 30) + { + // printf("WARNING: possible deadlock in CThreadEvent::Wait() !!!\n"); + } + } + + RemoveWaitingThread(); + + if ( !m_bManualReset ) m_bSet=false; + + return ret == CELL_OK; + } + +#endif + +////////////////////////////////////////////////////////////// +#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS +////////////////////////////////////////////////////////////// + + + CThreadEvent *pThis = this; + DWORD res = WaitForMultipleObjects(1, &pThis, true, dwTimeout); + return res == WAIT_OBJECT_0; + + +////////////////////////////////////////////////////////////// +#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS +////////////////////////////////////////////////////////////// } #ifdef _WIN32 @@ -735,8 +1505,9 @@ bool CThreadEvent::Wait( uint32 dwTimeout ) // //----------------------------------------------------------------------------- -CThreadSemaphore::CThreadSemaphore( long initialValue, long maxValue ) +CThreadSemaphore::CThreadSemaphore( int32 initialValue, int32 maxValue ) { +#ifdef _WIN32 if ( maxValue ) { AssertMsg( maxValue > 0, "Invalid max value for semaphore" ); @@ -750,16 +1521,135 @@ CThreadSemaphore::CThreadSemaphore( long initialValue, long maxValue ) { m_hSyncObject = NULL; } +#elif defined( _PS3 ) + if ( maxValue ) + { + m_sema_max_val = maxValue; + m_semaCount = initialValue; + } +#endif } + +#ifdef _PS3 +//--------------------------------------------------------- + +bool CThreadSemaphore::AddWaitingThread() +{ + bool result; + + sys_lwmutex_lock(&m_staticMutex, 0); + + if (cellAtomicTestAndDecr32(&m_semaCount) > 0) + { + result=false; + } + else + { + result=true; + m_numWaitingThread++; + + if ( m_numWaitingThread == 1 ) + { + sys_semaphore_attribute_t semAttr; + sys_semaphore_attribute_initialize( semAttr ); + Assert(m_semaCount == 0); + int err = sys_semaphore_create( &m_Semaphore, &semAttr, 0, m_sema_max_val ); + Assert( err == CELL_OK ); + m_bInitalized = true; + } + } + + sys_lwmutex_unlock(&m_staticMutex); + return result; +} + +void CThreadSemaphore::RemoveWaitingThread() +{ + sys_lwmutex_lock(&m_staticMutex, 0); + + m_numWaitingThread--; + + if ( m_numWaitingThread == 0) + { + int err = sys_semaphore_destroy( m_Semaphore ); + Assert( err == CELL_OK ); + m_bInitalized = false; + } + + sys_lwmutex_unlock(&m_staticMutex); +} + +#endif + +#ifdef _PS3 + +bool CThreadSemaphore::Wait( uint32 dwTimeout ) +{ +#ifdef THREADS_DEBUG + AssertUseable(); +#endif + + +#ifndef NO_THREAD_SYNC + if (!AddWaitingThread()) + { + //Waiting thread NOT added because semaphore was already in a signaled state + return true; + } + + int ret = sys_semaphore_wait( m_Semaphore, dwTimeout == TT_INFINITE ? 0 : dwTimeout * 1000 ); + Assert( (ret == CELL_OK) || (ret == ETIMEDOUT) ); + + RemoveWaitingThread(); + + int old = cellAtomicDecr32(&m_semaCount); + Assert(old>0); +#else + int ret = CELL_OK; +#endif + + // sys_ppu_thread_yield doesn't seem to function properly, so sleep instead. +// sys_timer_usleep( 60 ); + sys_ppu_thread_yield(); + + + + return ret == CELL_OK; +} + +#endif + //--------------------------------------------------------- -bool CThreadSemaphore::Release( long releaseCount, long *pPreviousCount ) +bool CThreadSemaphore::Release( int32 releaseCount, int32 *pPreviousCount ) { #ifdef THRDTOOL_DEBUG AssertUseable(); #endif - return ( ReleaseSemaphore( m_hSyncObject, releaseCount, pPreviousCount ) != 0 ); +#ifdef _WIN32 + return ( ReleaseSemaphore( m_hSyncObject, releaseCount, (LPLONG)pPreviousCount ) != 0 ); +#elif defined( _PS3 ) + +#ifndef NO_THREAD_SYNC + + if (m_bInitalized) + { + sys_semaphore_value_t previousVal; + sys_semaphore_get_value( m_Semaphore, &previousVal ); + + cellAtomicAdd32(&m_semaCount, releaseCount); + + *pPreviousCount = previousVal; + + int err = sys_semaphore_post( m_Semaphore, releaseCount ); + Assert(err == CELL_OK); + } + +#endif + + return true; +#endif } //----------------------------------------------------------------------------- @@ -789,15 +1679,20 @@ bool CThreadFullMutex::Release() // //----------------------------------------------------------------------------- +#if defined( WIN32 ) || defined( _PS3 ) || defined( _OSX ) || ( defined (_LINUX) && !defined(DEDICATED) ) +#if !defined(_PS3) +namespace GenericThreadLocals +{ +#endif CThreadLocalBase::CThreadLocalBase() { -#ifdef _WIN32 +#if defined(_WIN32) || defined(_PS3) m_index = TlsAlloc(); AssertMsg( m_index != 0xFFFFFFFF, "Bad thread local" ); if ( m_index == 0xFFFFFFFF ) Error( "Out of thread local storage!\n" ); #elif defined(POSIX) - if ( pthread_key_create( &m_index, NULL ) != 0 ) + if ( pthread_key_create( (pthread_key_t *)&m_index, NULL ) != 0 ) Error( "Out of thread local storage!\n" ); #endif } @@ -806,7 +1701,7 @@ CThreadLocalBase::CThreadLocalBase() CThreadLocalBase::~CThreadLocalBase() { -#ifdef _WIN32 +#if defined(_WIN32) || defined(_PS3) if ( m_index != 0xFFFFFFFF ) TlsFree( m_index ); m_index = 0xFFFFFFFF; @@ -819,7 +1714,7 @@ CThreadLocalBase::~CThreadLocalBase() void * CThreadLocalBase::Get() const { -#ifdef _WIN32 +#if defined(_WIN32) || defined(_PS3) if ( m_index != 0xFFFFFFFF ) return TlsGetValue( m_index ); AssertMsg( 0, "Bad thread local" ); @@ -834,7 +1729,7 @@ void * CThreadLocalBase::Get() const void CThreadLocalBase::Set( void *value ) { -#ifdef _WIN32 +#if defined(_WIN32) || defined(_PS3) if (m_index != 0xFFFFFFFF) TlsSetValue(m_index, value); else @@ -844,22 +1739,25 @@ void CThreadLocalBase::Set( void *value ) AssertMsg( 0, "Bad thread local" ); #endif } - -//----------------------------------------------------------------------------- - - -//----------------------------------------------------------------------------- - -#ifdef _WIN32 -#ifdef _X360 -#define TO_INTERLOCK_PARAM(p) ((long *)p) -#define TO_INTERLOCK_PTR_PARAM(p) ((void **)p) -#else -#define TO_INTERLOCK_PARAM(p) (p) -#define TO_INTERLOCK_PTR_PARAM(p) (p) +#if !defined(_PS3) +} // namespace GenericThreadLocals #endif +#endif // ( defined(WIN32) ) +//----------------------------------------------------------------------------- -#ifndef USE_INTRINSIC_INTERLOCKED + +//----------------------------------------------------------------------------- + +#ifdef MSVC +//#ifdef _X360 +#define TO_INTERLOCK_PARAM(p) ((volatile long *)p) +#define TO_INTERLOCK_PTR_PARAM(p) ((void **)p) +//#else +//#define TO_INTERLOCK_PARAM(p) (p) +//#define TO_INTERLOCK_PTR_PARAM(p) (p) +//#endif + +#if !defined(USE_INTRINSIC_INTERLOCKED) && !defined(_X360) int32 ThreadInterlockedIncrement( int32 volatile *pDest ) { Assert( (size_t)pDest % 4 == 0 ); @@ -915,7 +1813,7 @@ bool ThreadInterlockedAssignIf( int32 volatile *pDest, int32 value, int32 comper void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value ) { Assert( (size_t)pDest % 4 == 0 ); - return InterlockedExchangePointer( TO_INTERLOCK_PARAM(pDest), value ); + return InterlockedExchangePointer( TO_INTERLOCK_PTR_PARAM(pDest), value ); } void *ThreadInterlockedCompareExchangePointer( void * volatile *pDest, void *value, void *comperand ) @@ -943,13 +1841,11 @@ bool ThreadInterlockedAssignPointerIf( void * volatile *pDest, void *value, void } #endif +#ifdef COMPILER_MSVC32 int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand ) { Assert( (size_t)pDest % 8 == 0 ); -#if defined(_WIN64) || defined (_X360) - return InterlockedCompareExchange64( pDest, value, comperand ); -#else __asm { lea esi,comperand; @@ -962,14 +1858,16 @@ int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, in mov esi,pDest; lock CMPXCHG8B [esi]; } -#endif } +#endif bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand ) { Assert( (size_t)pDest % 8 == 0 ); -#if defined(PLATFORM_WINDOWS_PC32 ) +#if defined(_X360) || defined(_WIN64) + return ( ThreadInterlockedCompareExchange64( pDest, value, comperand ) == comperand ); +#else __asm { lea esi,comperand; @@ -984,25 +1882,15 @@ bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 compe mov eax,0; setz al; } -#else - return ( ThreadInterlockedCompareExchange64( pDest, value, comperand ) == comperand ); #endif } -#if defined( PLATFORM_64BITS ) - -#if _MSC_VER < 1500 -// This intrinsic isn't supported on VS2005. -extern "C" unsigned char _InterlockedCompareExchange128( int64 volatile * Destination, int64 ExchangeHigh, int64 ExchangeLow, int64 * ComparandResult ); -#endif - +#ifdef _WIN64 bool ThreadInterlockedAssignIf128( volatile int128 *pDest, const int128 &value, const int128 &comperand ) { - Assert( ( (size_t)pDest % 16 ) == 0 ); - - volatile int64 *pDest64 = ( volatile int64 * )pDest; - int64 *pValue64 = ( int64 * )&value; - int64 *pComperand64 = ( int64 * )&comperand; + DbgAssert( ( (size_t)pDest % 16 ) == 0 ); + // Must copy comperand to stack because the intrinsic uses it as an in/out param + int64 comperandInOut[2] = { comperand.m128i_i64[0], comperand.m128i_i64[1] }; // Description: // The CMPXCHG16B instruction compares the 128-bit value in the RDX:RAX and RCX:RBX registers @@ -1011,120 +1899,50 @@ bool ThreadInterlockedAssignIf128( volatile int128 *pDest, const int128 &value, // Otherwise, the ZF flag is cleared, and the memory value is copied to RDX:RAX. // _InterlockedCompareExchange128: http://msdn.microsoft.com/en-us/library/bb514094.aspx - return _InterlockedCompareExchange128( pDest64, pValue64[1], pValue64[0], pComperand64 ) == 1; -} - -#endif // PLATFORM_64BITS - -int64 ThreadInterlockedIncrement64( int64 volatile *pDest ) -{ - Assert( (size_t)pDest % 8 == 0 ); - - int64 Old; - - do - { - Old = *pDest; - } while (ThreadInterlockedCompareExchange64(pDest, Old + 1, Old) != Old); - - return Old + 1; -} - -int64 ThreadInterlockedDecrement64( int64 volatile *pDest ) -{ - Assert( (size_t)pDest % 8 == 0 ); - int64 Old; - - do - { - Old = *pDest; - } while (ThreadInterlockedCompareExchange64(pDest, Old - 1, Old) != Old); - - return Old - 1; -} - -int64 ThreadInterlockedExchange64( int64 volatile *pDest, int64 value ) -{ - Assert( (size_t)pDest % 8 == 0 ); - int64 Old; - - do - { - Old = *pDest; - } while (ThreadInterlockedCompareExchange64(pDest, value, Old) != Old); - - return Old; -} - -int64 ThreadInterlockedExchangeAdd64( int64 volatile *pDest, int64 value ) -{ - Assert( (size_t)pDest % 8 == 0 ); - int64 Old; - - do - { - Old = *pDest; - } while (ThreadInterlockedCompareExchange64(pDest, Old + value, Old) != Old); - - return Old; + if ( _InterlockedCompareExchange128( ( volatile int64 * )pDest, value.m128i_i64[1], value.m128i_i64[0], comperandInOut ) ) + return true; + return false; } +#endif #elif defined(GNUC) -int32 ThreadInterlockedIncrement( int32 volatile *pDest ) +#ifdef OSX +#include +#endif + + +long ThreadInterlockedIncrement( long volatile *pDest ) { return __sync_fetch_and_add( pDest, 1 ) + 1; } -int64 ThreadInterlockedIncrement64( int64 volatile *pDest ) -{ - return __sync_fetch_and_add( pDest, 1 ) + 1; -} - -int32 ThreadInterlockedDecrement( int32 volatile *pDest ) +long ThreadInterlockedDecrement( long volatile *pDest ) { return __sync_fetch_and_sub( pDest, 1 ) - 1; } -int64 ThreadInterlockedDecrement64( int64 volatile *pDest ) -{ - return __sync_fetch_and_sub( pDest, 1 ) - 1; -} - -int32 ThreadInterlockedExchange( int32 volatile *pDest, int32 value ) +long ThreadInterlockedExchange( long volatile *pDest, long value ) { return __sync_lock_test_and_set( pDest, value ); } -int64 ThreadInterlockedExchange64( int64 volatile *pDest, int64 value ) -{ - return __sync_lock_test_and_set( pDest, value ); -} - -int32 ThreadInterlockedExchangeAdd( int32 volatile *pDest, int32 value ) +long ThreadInterlockedExchangeAdd( long volatile *pDest, long value ) { return __sync_fetch_and_add( pDest, value ); } -int64 ThreadInterlockedExchangeAdd64( int64 volatile *pDest, int64 value ) -{ - return __sync_fetch_and_add( pDest, value ); -} - -int32 ThreadInterlockedCompareExchange( int32 volatile *pDest, int32 value, int32 comperand ) +long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand ) { return __sync_val_compare_and_swap( pDest, comperand, value ); } -bool ThreadInterlockedAssignIf( int32 volatile *pDest, int32 value, int32 comperand ) +bool ThreadInterlockedAssignIf( long volatile *pDest, long value, long comperand ) { return __sync_bool_compare_and_swap( pDest, comperand, value ); } -void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value ) -{ - return __sync_lock_test_and_set( pDest, value ); -} +#if !defined( USE_INTRINSIC_INTERLOCKED ) void *ThreadInterlockedCompareExchangePointer( void *volatile *pDest, void *value, void *comperand ) { @@ -1136,6 +1954,18 @@ bool ThreadInterlockedAssignPointerIf( void * volatile *pDest, void *value, void return __sync_bool_compare_and_swap( pDest, comperand, value ); } +#elif defined( PLATFORM_64BITS ) + +void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value ) +{ + return __sync_lock_test_and_set( pDest, value ); +} + +void *ThreadInterlockedCompareExchangePointer( void * volatile *p, void *value, void *comparand ) { + return (void *)( ( intp )ThreadInterlockedCompareExchange64( reinterpret_cast(p), reinterpret_cast(value), reinterpret_cast(comparand) ) ); +} +#endif + int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, int64 comperand ) { return __sync_val_compare_and_swap( pDest, comperand, value ); @@ -1146,35 +1976,32 @@ bool ThreadInterlockedAssignIf64( int64 volatile * pDest, int64 value, int64 com return __sync_bool_compare_and_swap( pDest, comperand, value ); } -#ifdef PLATFORM_64BITS -bool ThreadInterlockedAssignIf128( int128 volatile *pDest, const int128 &value, const int128 &comperand ) -{ - return __sync_bool_compare_and_swap( pDest, comperand, value ); -} -#endif +#elif defined( _PS3 ) + +// This is defined in the header! #else // This will perform horribly, #error "Falling back to mutexed interlocked operations, you really don't have intrinsics you can use?"ß CThreadMutex g_InterlockedMutex; -int32 ThreadInterlockedIncrement( int32 volatile *pDest ) +long ThreadInterlockedIncrement( long volatile *pDest ) { AUTO_LOCK( g_InterlockedMutex ); return ++(*pDest); } -int32 ThreadInterlockedDecrement( int32 volatile *pDest ) +long ThreadInterlockedDecrement( long volatile *pDest ) { AUTO_LOCK( g_InterlockedMutex ); return --(*pDest); } -int32 ThreadInterlockedExchange( int32 volatile *pDest, int32 value ) +long ThreadInterlockedExchange( long volatile *pDest, long value ) { AUTO_LOCK( g_InterlockedMutex ); - int32 retVal = *pDest; + long retVal = *pDest; *pDest = value; return retVal; } @@ -1187,18 +2014,18 @@ void *ThreadInterlockedExchangePointer( void * volatile *pDest, void *value ) return retVal; } -int32 ThreadInterlockedExchangeAdd( int32 volatile *pDest, int32 value ) +long ThreadInterlockedExchangeAdd( long volatile *pDest, long value ) { AUTO_LOCK( g_InterlockedMutex ); - int32 retVal = *pDest; + long retVal = *pDest; *pDest += value; return retVal; } -int32 ThreadInterlockedCompareExchange( int32 volatile *pDest, int32 value, int32 comperand ) +long ThreadInterlockedCompareExchange( long volatile *pDest, long value, long comperand ) { AUTO_LOCK( g_InterlockedMutex ); - int32 retVal = *pDest; + long retVal = *pDest; if ( *pDest == comperand ) *pDest = value; return retVal; @@ -1224,6 +2051,74 @@ int64 ThreadInterlockedCompareExchange64( int64 volatile *pDest, int64 value, in return retVal; } +#endif + +#ifdef COMPILER_MSVC32 + +PLATFORM_INTERFACE int64 ThreadInterlockedOr64( int64 volatile *pDest, int64 value ) +{ + int64 Old; + + do + { + Old = *pDest; + } while ( ThreadInterlockedCompareExchange64( pDest, Old | value, Old ) != Old ); + + return Old; +} + +PLATFORM_INTERFACE int64 ThreadInterlockedAnd64( int64 volatile *pDest, int64 value ) +{ + int64 Old; + + do + { + Old = *pDest; + } while ( ThreadInterlockedCompareExchange64( pDest, Old & value, Old ) != Old ); + + return Old; +} + +PLATFORM_INTERFACE int64 ThreadInterlockedIncrement64( int64 volatile *pDest ) +{ + int64 Old; + + do + { + Old = *pDest; + } while ( ThreadInterlockedCompareExchange64( pDest, Old + 1, Old ) != Old ); + + return Old + 1; +} + +PLATFORM_INTERFACE int64 ThreadInterlockedDecrement64( int64 volatile *pDest ) +{ + int64 Old; + + + do + { + Old = *pDest; + } while ( ThreadInterlockedCompareExchange64( pDest, Old - 1, Old ) != Old ); + + return Old - 1; +} + +PLATFORM_INTERFACE int64 ThreadInterlockedExchangeAdd64( int64 volatile *pDest, int64 value ) +{ + int64 Old; + + do + { + Old = *pDest; + } while ( ThreadInterlockedCompareExchange64( pDest, Old + value, Old ) != Old ); + + return Old; +} + + +#endif + int64 ThreadInterlockedExchange64( int64 volatile *pDest, int64 value ) { Assert( (size_t)pDest % 8 == 0 ); @@ -1237,19 +2132,6 @@ int64 ThreadInterlockedExchange64( int64 volatile *pDest, int64 value ) return Old; } -bool ThreadInterlockedAssignIf64(volatile int64 *pDest, int64 value, int64 comperand ) -{ - Assert( (size_t)pDest % 8 == 0 ); - return ( ThreadInterlockedCompareExchange64( pDest, value, comperand ) == comperand ); -} - -bool ThreadInterlockedAssignIf( int32 volatile *pDest, int32 value, int32 comperand ) -{ - Assert( (size_t)pDest % 4 == 0 ); - return ( ThreadInterlockedCompareExchange( pDest, value, comperand ) == comperand ); -} - -#endif //----------------------------------------------------------------------------- @@ -1276,7 +2158,20 @@ MAP_THREAD_PROFILER_CALL( ThreadNotifySyncReleasing, __itt_notify_sync_releasing // //----------------------------------------------------------------------------- -#ifndef POSIX +#ifdef _PS3 +CThreadMutex::CThreadMutex() +{ + // sys_mutex with recursion enabled is like a win32 critical section + sys_mutex_attribute_t mutexAttr; + sys_mutex_attribute_initialize( mutexAttr ); + mutexAttr.attr_recursive = SYS_SYNC_RECURSIVE; + sys_mutex_create( &m_Mutex, &mutexAttr ); +} +CThreadMutex::~CThreadMutex() +{ + sys_mutex_destroy( m_Mutex ); +} +#elif !defined( POSIX ) CThreadMutex::CThreadMutex() { #ifdef THREAD_MUTEX_TRACING_ENABLED @@ -1297,7 +2192,7 @@ CThreadMutex::~CThreadMutex() } #endif // !POSIX -#if defined( _WIN32 ) && !defined( _X360 ) +#ifdef IS_WINDOWS_PC typedef BOOL (WINAPI*TryEnterCriticalSectionFunc_t)(LPCRITICAL_SECTION); static CDynamicFunction DynTryEnterCriticalSection( "Kernel32.dll", "TryEnterCriticalSection" ); #elif defined( _X360 ) @@ -1306,8 +2201,7 @@ static CDynamicFunction DynTryEnterCriticalSectio bool CThreadMutex::TryLock() { - -#if defined( _WIN32 ) +#if defined( MSVC ) #ifdef THREAD_MUTEX_TRACING_ENABLED uint thisThreadID = ThreadGetCurrentId(); if ( m_bTrace && m_currentOwnerID && ( m_currentOwnerID != thisThreadID ) ) @@ -1323,7 +2217,7 @@ bool CThreadMutex::TryLock() // we now own it for the first time. Set owner information m_currentOwnerID = thisThreadID; if ( m_bTrace ) - Msg( "Thread %u now owns lock 0x%p\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); + Msg( "Thread %u now owns lock %p\n", m_currentOwnerID, (CRITICAL_SECTION *)&m_CriticalSection ); } m_lockCount++; #endif @@ -1333,8 +2227,18 @@ bool CThreadMutex::TryLock() } Lock(); return true; +#elif defined( _PS3 ) + +#ifndef NO_THREAD_SYNC + if ( sys_mutex_trylock( m_Mutex ) == CELL_OK ) +#endif + + return true; + + return false; // ?? moved from EA code + #elif defined( POSIX ) - return pthread_mutex_trylock( &m_Mutex ) == 0; + return pthread_mutex_trylock( &m_Mutex ) == 0; #else #error "Implement me!" return true; @@ -1347,10 +2251,59 @@ bool CThreadMutex::TryLock() // //----------------------------------------------------------------------------- +#ifdef THREAD_FAST_MUTEX_TIMINGS +// This is meant to be used in combination with breakpoints and in-debugee, so we turn the optimizer off +#pragma optimize( "", off ) +CThreadFastMutex *g_pIgnoredMutexes[256]; // Ignore noisy non-problem mutex. Probably could be an array. Right now needed only for sound thread +float g_MutexTimingTolerance = 5; +bool g_bMutexTimingOutput; + +void TrapMutexTimings( uint32 probableBlocker, uint32 thisThread, volatile CThreadFastMutex *pMutex, CFastTimer &spikeTimer, CAverageCycleCounter &sleepTimer ) +{ + spikeTimer.End(); + if ( spikeTimer.GetDuration().GetMillisecondsF() > g_MutexTimingTolerance ) + { + bool bIgnore = false; + for ( int j = 0; j < ARRAYSIZE( g_pIgnoredMutexes ) && g_pIgnoredMutexes[j]; j++ ) + { + if ( g_pIgnoredMutexes[j] == pMutex ) + { + bIgnore = true; + break; + } + } + + if ( !bIgnore && spikeTimer.GetDuration().GetMillisecondsF() < 100 ) + { + volatile float FastMutexDuration = spikeTimer.GetDuration().GetMillisecondsF(); + volatile float average = sleepTimer.GetAverageMilliseconds(); + volatile float peak = sleepTimer.GetPeakMilliseconds(); volatile int xx = 6; + if ( g_bMutexTimingOutput ) + { + char szBuf[256]; + Msg( "M (%.8x): [%.8x <-- %.8x] (%f,%f,%f)\n", pMutex, probableBlocker, thisThread, FastMutexDuration, average, peak ); + } + } + } +} + +#else +#define TrapMutexTimings( a, b, c, d, e ) ((void)0) +#endif + +//------------------------------------- + #define THREAD_SPIN (8*1024) -void CThreadFastMutex::Lock( const uintp threadId, unsigned nSpinSleepTime ) volatile +void CThreadFastMutex::Lock( const uint32 threadId, unsigned nSpinSleepTime ) volatile { +#ifdef THREAD_FAST_MUTEX_TIMINGS + CAverageCycleCounter sleepTimer; + CFastTimer spikeTimer; + uint32 currentOwner = m_ownerID; + spikeTimer.Start(); + sleepTimer.Init(); +#endif int i; if ( nSpinSleepTime != TT_INFINITE ) { @@ -1358,6 +2311,7 @@ void CThreadFastMutex::Lock( const uintp threadId, unsigned nSpinSleepTime ) vol { if ( TryLock( threadId ) ) { + TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer ); return; } ThreadPause(); @@ -1367,11 +2321,15 @@ void CThreadFastMutex::Lock( const uintp threadId, unsigned nSpinSleepTime ) vol { if ( TryLock( threadId ) ) { + TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer ); return; } ThreadPause(); if ( i % 1024 == 0 ) { +#ifdef THREAD_FAST_MUTEX_TIMINGS + CAverageTimeMarker marker( &sleepTimer ); +#endif ThreadSleep( 0 ); } } @@ -1381,15 +2339,18 @@ void CThreadFastMutex::Lock( const uintp threadId, unsigned nSpinSleepTime ) vol { nSpinSleepTime = 1; } - else #endif if ( nSpinSleepTime ) { for ( i = THREAD_SPIN; i != 0; --i ) { +#ifdef THREAD_FAST_MUTEX_TIMINGS + CAverageTimeMarker marker( &sleepTimer ); +#endif if ( TryLock( threadId ) ) { + TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer ); return; } @@ -1399,10 +2360,14 @@ void CThreadFastMutex::Lock( const uintp threadId, unsigned nSpinSleepTime ) vol } - for ( ;; ) // coded as for instead of while to make easy to breakpoint success + for ( ;; ) { +#ifdef THREAD_FAST_MUTEX_TIMINGS + CAverageTimeMarker marker( &sleepTimer ); +#endif if ( TryLock( threadId ) ) { + TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer ); return; } @@ -1412,10 +2377,11 @@ void CThreadFastMutex::Lock( const uintp threadId, unsigned nSpinSleepTime ) vol } else { - for ( ;; ) // coded as for instead of while to make easy to breakpoint success + for ( ;; ) { if ( TryLock( threadId ) ) { + TrapMutexTimings( currentOwner, threadId, this, spikeTimer, sleepTimer ); return; } @@ -1424,6 +2390,10 @@ void CThreadFastMutex::Lock( const uintp threadId, unsigned nSpinSleepTime ) vol } } +#ifdef THREAD_FAST_MUTEX_TIMINGS +#pragma optimize( "", on ) +#endif + //----------------------------------------------------------------------------- // // CThreadRWLock @@ -1483,12 +2453,122 @@ void CThreadRWLock::UnlockWrite() // CThreadSpinRWLock // //----------------------------------------------------------------------------- +#ifndef OLD_SPINRWLOCK -void CThreadSpinRWLock::SpinLockForWrite( const uintp threadId ) +void CThreadSpinRWLock::SpinLockForWrite() { int i; - for ( i = 1000; i != 0; --i ) + if ( TryLockForWrite_UnforcedInline() ) + { + return; + } + + for ( i = THREAD_SPIN; i != 0; --i ) + { + if ( TryLockForWrite_UnforcedInline() ) + { + return; + } + ThreadPause(); + } + + for ( i = THREAD_SPIN; i != 0; --i ) + { + if ( TryLockForWrite_UnforcedInline() ) + { + return; + } + ThreadPause(); + if ( i % 1024 == 0 ) + { + ThreadSleep( 0 ); + } + } + + for ( i = THREAD_SPIN * 4; i != 0; --i ) + { + if ( TryLockForWrite_UnforcedInline() ) + { + return; + } + + ThreadPause(); + ThreadSleep( 0 ); + } + + for ( ;; ) // coded as for instead of while to make easy to breakpoint success + { + if ( TryLockForWrite_UnforcedInline() ) + { + return; + } + + ThreadPause(); + ThreadSleep( 1 ); + } +} + +void CThreadSpinRWLock::SpinLockForRead() +{ + int i; + for ( i = THREAD_SPIN; i != 0; --i ) + { + if ( TryLockForRead_UnforcedInline() ) + { + return; + } + ThreadPause(); + } + + for ( i = THREAD_SPIN; i != 0; --i ) + { + if ( TryLockForRead_UnforcedInline() ) + { + return; + } + ThreadPause(); + if ( i % 1024 == 0 ) + { + ThreadSleep( 0 ); + } + } + + for ( i = THREAD_SPIN * 4; i != 0; --i ) + { + if ( TryLockForRead_UnforcedInline() ) + { + return; + } + + ThreadPause(); + ThreadSleep( 0 ); + } + + for ( ;; ) // coded as for instead of while to make easy to breakpoint success + { + if ( TryLockForRead_UnforcedInline() ) + { + return; + } + + ThreadPause(); + ThreadSleep( 1 ); + } +} + +#else +/* (commented out to reduce distraction in colorized editor, remove entirely when new implementation settles) +void CThreadSpinRWLock::SpinLockForWrite( const uint32 threadId ) +{ + int i; + + if ( TryLockForWrite( threadId ) ) + { + return; + } + + for ( i = THREAD_SPIN; i != 0; --i ) { if ( TryLockForWrite( threadId ) ) { @@ -1497,7 +2577,20 @@ void CThreadSpinRWLock::SpinLockForWrite( const uintp threadId ) ThreadPause(); } - for ( i = 20000; i != 0; --i ) + for ( i = THREAD_SPIN; i != 0; --i ) + { + if ( TryLockForWrite( threadId ) ) + { + return; + } + ThreadPause(); + if ( i % 1024 == 0 ) + { + ThreadSleep( 0 ); + } + } + + for ( i = THREAD_SPIN * 4; i != 0; --i ) { if ( TryLockForWrite( threadId ) ) { @@ -1523,49 +2616,53 @@ void CThreadSpinRWLock::SpinLockForWrite( const uintp threadId ) void CThreadSpinRWLock::LockForRead() { int i; - - // In order to grab a read lock, the number of readers must not change and no thread can own the write lock - LockInfo_t oldValue; - LockInfo_t newValue; - - oldValue.m_nReaders = m_lockInfo.m_nReaders; - oldValue.m_writerId = 0; - newValue.m_nReaders = oldValue.m_nReaders + 1; - newValue.m_writerId = 0; - - if( m_nWriters == 0 && AssignIf( newValue, oldValue ) ) - return; - ThreadPause(); - oldValue.m_nReaders = m_lockInfo.m_nReaders; - newValue.m_nReaders = oldValue.m_nReaders + 1; - - for ( i = 1000; i != 0; --i ) + if ( TryLockForRead() ) { - if( m_nWriters == 0 && AssignIf( newValue, oldValue ) ) - return; - ThreadPause(); - oldValue.m_nReaders = m_lockInfo.m_nReaders; - newValue.m_nReaders = oldValue.m_nReaders + 1; + return; } - for ( i = 20000; i != 0; --i ) + for ( i = THREAD_SPIN; i != 0; --i ) { - if( m_nWriters == 0 && AssignIf( newValue, oldValue ) ) + if ( TryLockForRead() ) + { return; + } + ThreadPause(); + } + + for ( i = THREAD_SPIN; i != 0; --i ) + { + if ( TryLockForRead() ) + { + return; + } + ThreadPause(); + if ( i % 1024 == 0 ) + { + ThreadSleep( 0 ); + } + } + + for ( i = THREAD_SPIN * 4; i != 0; --i ) + { + if ( TryLockForRead() ) + { + return; + } + ThreadPause(); ThreadSleep( 0 ); - oldValue.m_nReaders = m_lockInfo.m_nReaders; - newValue.m_nReaders = oldValue.m_nReaders + 1; } for ( ;; ) // coded as for instead of while to make easy to breakpoint success { - if( m_nWriters == 0 && AssignIf( newValue, oldValue ) ) + if ( TryLockForRead() ) + { return; + } + ThreadPause(); ThreadSleep( 1 ); - oldValue.m_nReaders = m_lockInfo.m_nReaders; - newValue.m_nReaders = oldValue.m_nReaders + 1; } } @@ -1574,21 +2671,35 @@ void CThreadSpinRWLock::UnlockRead() int i; Assert( m_lockInfo.m_nReaders > 0 && m_lockInfo.m_writerId == 0 ); + + //uint32 nLockInfoReaders = m_lockInfo.m_nReaders; LockInfo_t oldValue; LockInfo_t newValue; - - oldValue.m_nReaders = m_lockInfo.m_nReaders; - oldValue.m_writerId = 0; - newValue.m_nReaders = oldValue.m_nReaders - 1; - newValue.m_writerId = 0; - + + if( IsX360() ) + { + // this is the code equivalent to original code (see below) that doesn't cause LHS on Xbox360 + // WARNING: This code assumes BIG Endian CPU + oldValue.m_i64 = uint32( m_lockInfo.m_nReaders ); + newValue.m_i64 = oldValue.m_i64 - 1; // NOTE: when we have -1 (or 0xFFFFFFFF) readers, this will result in non-equivalent code + } + else + { + // this is the original code that worked here for a while + oldValue.m_nReaders = m_lockInfo.m_nReaders; + oldValue.m_writerId = 0; + newValue.m_nReaders = oldValue.m_nReaders - 1; + newValue.m_writerId = 0; + } + ThreadMemoryBarrier(); if( AssignIf( newValue, oldValue ) ) return; + ThreadPause(); oldValue.m_nReaders = m_lockInfo.m_nReaders; newValue.m_nReaders = oldValue.m_nReaders - 1; - for ( i = 500; i != 0; --i ) + for ( i = THREAD_SPIN; i != 0; --i ) { if( AssignIf( newValue, oldValue ) ) return; @@ -1597,7 +2708,20 @@ void CThreadSpinRWLock::UnlockRead() newValue.m_nReaders = oldValue.m_nReaders - 1; } - for ( i = 20000; i != 0; --i ) + for ( i = THREAD_SPIN; i != 0; --i ) + { + if( AssignIf( newValue, oldValue ) ) + return; + ThreadPause(); + if ( i % 512 == 0 ) + { + ThreadSleep( 0 ); + } + oldValue.m_nReaders = m_lockInfo.m_nReaders; + newValue.m_nReaders = oldValue.m_nReaders - 1; + } + + for ( i = THREAD_SPIN * 4; i != 0; --i ) { if( AssignIf( newValue, oldValue ) ) return; @@ -1621,563 +2745,33 @@ void CThreadSpinRWLock::UnlockRead() void CThreadSpinRWLock::UnlockWrite() { Assert( m_lockInfo.m_writerId == ThreadGetCurrentId() && m_lockInfo.m_nReaders == 0 ); - static const LockInfo_t newValue = { 0, 0 }; -#if defined(_X360) - // X360TBD: Serious Perf implications, not yet. __sync(); -#endif - int64 val; memcpy( &val, &newValue, sizeof( val ) ); - ThreadInterlockedExchange64( (int64 *)&m_lockInfo, val ); + static const LockInfo_t newValue = { { 0, 0 } }; + ThreadMemoryBarrier(); + ThreadInterlockedExchange64( (int64 *)&m_lockInfo, *((int64 *)&newValue) ); m_nWriters--; } - - - -//----------------------------------------------------------------------------- -// -// CThread -// -//----------------------------------------------------------------------------- - -CThreadLocalPtr g_pCurThread; - -//--------------------------------------------------------- - -CThread::CThread() -: -#ifdef _WIN32 - m_hThread( NULL ), -#endif - m_threadId( 0 ), - m_result( 0 ), - m_flags( 0 ) -{ - m_szName[0] = 0; -} - -//--------------------------------------------------------- - -CThread::~CThread() -{ -#ifdef _WIN32 - if (m_hThread) -#elif defined(POSIX) - if ( m_threadId ) -#endif - { - if ( IsAlive() ) - { - Msg( "Illegal termination of worker thread! Threads must negotiate an end to the thread before the CThread object is destroyed.\n" ); -#ifdef _WIN32 - - DoNewAssertDialog( __FILE__, __LINE__, "Illegal termination of worker thread! Threads must negotiate an end to the thread before the CThread object is destroyed.\n" ); -#endif - if ( GetCurrentCThread() == this ) - { - Stop(); // BUGBUG: Alfred - this doesn't make sense, this destructor fires from the hosting thread not the thread itself!! - } - } - -#ifdef _WIN32 - // Now that the worker thread has exited (which we know because we presumably waited - // on the thread handle for it to exit) we can finally close the thread handle. We - // cannot do this any earlier, and certainly not in CThread::ThreadProc(). - CloseHandle( m_hThread ); -#endif - } -} - - -//--------------------------------------------------------- - -const char *CThread::GetName() -{ - AUTO_LOCK( m_Lock ); - if ( !m_szName[0] ) - { -#ifdef _WIN32 - _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(%p/%p)", this, m_hThread ); -#elif defined(POSIX) - _snprintf( m_szName, sizeof(m_szName) - 1, "Thread(0x" PRIxPTR "/0x" PRIxPTR ")", (ThreadId_t)this, (ThreadId_t)m_threadId ); -#endif - m_szName[sizeof(m_szName) - 1] = 0; - } - return m_szName; -} - -//--------------------------------------------------------- - -void CThread::SetName(const char *pszName) -{ - AUTO_LOCK( m_Lock ); - strncpy( m_szName, pszName, sizeof(m_szName) - 1 ); - m_szName[sizeof(m_szName) - 1] = 0; -} - -//--------------------------------------------------------- - -bool CThread::Start( unsigned nBytesStack ) -{ - AUTO_LOCK( m_Lock ); - - if ( IsAlive() ) - { - AssertMsg( 0, "Tried to create a thread that has already been created!" ); - return false; - } - - bool bInitSuccess = false; - CThreadEvent createComplete; - ThreadInit_t init = { this, &createComplete, &bInitSuccess }; - -#ifdef _WIN32 - HANDLE hThread; - m_hThread = hThread = (HANDLE)VCRHook_CreateThread( NULL, - nBytesStack, - (LPTHREAD_START_ROUTINE)GetThreadProc(), - new ThreadInit_t(init), - CREATE_SUSPENDED, - &m_threadId ); - if ( !hThread ) - { - AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() ); - return false; - } - Plat_ApplyHardwareDataBreakpointsToNewThread( m_threadId ); - ResumeThread( hThread ); - -#elif defined(POSIX) - pthread_attr_t attr; - pthread_attr_init( &attr ); - // From http://www.kernel.org/doc/man-pages/online/pages/man3/pthread_attr_setstacksize.3.html - // A thread's stack size is fixed at the time of thread creation. Only the main thread can dynamically grow its stack. - pthread_attr_setstacksize( &attr, MAX( nBytesStack, 1024u*1024 ) ); - if ( pthread_create( &m_threadId, &attr, (void *(*)(void *))GetThreadProc(), new ThreadInit_t( init ) ) != 0 ) - { - AssertMsg1( 0, "Failed to create thread (error 0x%x)", GetLastError() ); - return false; - } - Plat_ApplyHardwareDataBreakpointsToNewThread( (long unsigned int)m_threadId ); - bInitSuccess = true; +*/ #endif -#if !defined( OSX ) - ThreadSetDebugName( m_threadId, m_szName ); -#endif +#if defined( _PS3 ) +// All CThread code is inline in the header for PS3 - if ( !WaitForCreateComplete( &createComplete ) ) - { - Msg( "Thread failed to initialize\n" ); -#ifdef _WIN32 - CloseHandle( m_hThread ); - m_hThread = NULL; - m_threadId = 0; -#elif defined(POSIX) - m_threadId = 0; -#endif - return false; - } - - if ( !bInitSuccess ) - { - Msg( "Thread failed to initialize\n" ); -#ifdef _WIN32 - CloseHandle( m_hThread ); - m_hThread = NULL; - m_threadId = 0; -#elif defined(POSIX) - m_threadId = 0; -#endif - return false; - } - -#ifdef _WIN32 - if ( !m_hThread ) - { - Msg( "Thread exited immediately\n" ); - } -#endif - -#ifdef _WIN32 - return !!m_hThread; -#elif defined(POSIX) - return !!m_threadId; -#endif +// This function is implemented here rather than the header because g_pCurThread resolves to GetCurThread() on PS3 +// and we don't want to create a dependency on the ELF stub for everyone who includes the header. +PLATFORM_INTERFACE CThread *GetCurThreadPS3() +{ + return (CThread*)g_pCurThread; } -//--------------------------------------------------------- -// -// Return true if the thread exists. false otherwise -// - -bool CThread::IsAlive() +PLATFORM_INTERFACE void SetCurThreadPS3( CThread *pThread ) { -#ifdef _WIN32 - DWORD dwExitCode; - - return ( m_hThread && - GetExitCodeThread( m_hThread, &dwExitCode ) && - dwExitCode == STILL_ACTIVE ); -#elif defined(POSIX) - return m_threadId; -#endif -} - -//--------------------------------------------------------- - -bool CThread::Join(unsigned timeout) -{ -#ifdef _WIN32 - if ( m_hThread ) -#elif defined(POSIX) - if ( m_threadId ) -#endif - { - AssertMsg(GetCurrentCThread() != this, _T("Thread cannot be joined with self")); - -#ifdef _WIN32 - return ThreadJoin( (ThreadHandle_t)m_hThread ); -#elif defined(POSIX) - return ThreadJoin( (ThreadHandle_t)m_threadId ); -#endif - } - return true; -} - -//--------------------------------------------------------- - -#ifdef _WIN32 - -HANDLE CThread::GetThreadHandle() -{ - return m_hThread; -} - -#endif - -#if defined( _WIN32 ) || defined( LINUX ) - -//--------------------------------------------------------- - -uint CThread::GetThreadId() -{ - return m_threadId; -} - -#endif - -//--------------------------------------------------------- - -int CThread::GetResult() -{ - return m_result; -} - -//--------------------------------------------------------- -// -// Forcibly, abnormally, but relatively cleanly stop the thread -// - -void CThread::Stop(int exitCode) -{ - if ( !IsAlive() ) - return; - - if ( GetCurrentCThread() == this ) - { - m_result = exitCode; - if ( !( m_flags & SUPPORT_STOP_PROTOCOL ) ) - { - OnExit(); - g_pCurThread = NULL; - -#ifdef _WIN32 - CloseHandle( m_hThread ); - m_hThread = NULL; -#endif - Cleanup(); - } - throw exitCode; - } - else - AssertMsg( 0, "Only thread can stop self: Use a higher-level protocol"); -} - -//--------------------------------------------------------- - -int CThread::GetPriority() const -{ -#ifdef _WIN32 - return GetThreadPriority(m_hThread); -#elif defined(POSIX) - struct sched_param thread_param; - int policy; - pthread_getschedparam( m_threadId, &policy, &thread_param ); - return thread_param.sched_priority; -#endif -} - -//--------------------------------------------------------- - -bool CThread::SetPriority(int priority) -{ -#ifdef _WIN32 - return ThreadSetPriority( (ThreadHandle_t)m_hThread, priority ); -#else - return ThreadSetPriority( (ThreadHandle_t)m_threadId, priority ); -#endif -} - - -//--------------------------------------------------------- - -void CThread::SuspendCooperative() -{ - if ( ThreadGetCurrentId() == (ThreadId_t)m_threadId ) - { - m_SuspendEventSignal.Set(); - m_nSuspendCount = 1; - m_SuspendEvent.Wait(); - m_nSuspendCount = 0; - } - else - { - Assert( !"Suspend not called from worker thread, this would be a bug" ); - } -} - -//--------------------------------------------------------- - -void CThread::ResumeCooperative() -{ - //Assert( m_nSuspendCount == 1 ); - m_SuspendEvent.Set(); -} - - -void CThread::BWaitForThreadSuspendCooperative() -{ - m_SuspendEventSignal.Wait(); -} - - -#ifndef LINUX -//--------------------------------------------------------- - -unsigned int CThread::Suspend() -{ -#ifdef _WIN32 - return ( SuspendThread(m_hThread) != 0 ); -#elif defined(OSX) - int susCount = m_nSuspendCount++; - while ( thread_suspend( pthread_mach_thread_np(m_threadId) ) != KERN_SUCCESS ) - { - }; - return ( susCount) != 0; -#else -#error -#endif -} - -//--------------------------------------------------------- - -unsigned int CThread::Resume() -{ -#ifdef _WIN32 - return ( ResumeThread(m_hThread) != 0 ); -#elif defined(OSX) - int susCount = m_nSuspendCount++; - while ( thread_resume( pthread_mach_thread_np(m_threadId) ) != KERN_SUCCESS ) - { - }; - return ( susCount - 1) != 0; -#else -#error -#endif -} -#endif - - -//--------------------------------------------------------- - -bool CThread::Terminate(int exitCode) -{ -#ifndef _X360 -#ifdef _WIN32 - // I hope you know what you're doing! - if (!TerminateThread(m_hThread, exitCode)) - return false; - CloseHandle( m_hThread ); - m_hThread = NULL; - Cleanup(); -#elif defined(POSIX) - pthread_kill( m_threadId, SIGKILL ); - Cleanup(); -#endif - - return true; -#else - AssertMsg( 0, "Cannot terminate a thread on the Xbox!" ); - return false; -#endif -} - -//--------------------------------------------------------- -// -// Get the Thread object that represents the current thread, if any. -// Can return NULL if the current thread was not created using -// CThread -// - -CThread *CThread::GetCurrentCThread() -{ - return g_pCurThread; -} - -//--------------------------------------------------------- -// -// Offer a context switch. Under Win32, equivalent to Sleep(0) -// - -void CThread::Yield() -{ -#ifdef _WIN32 - ::Sleep(0); -#elif defined(ANDROID) - sched_yield(); -#elif defined(POSIX) - pthread_yield(); -#endif -} - -//--------------------------------------------------------- -// -// This method causes the current thread to yield and not to be -// scheduled for further execution until a certain amount of real -// time has elapsed, more or less. -// - -void CThread::Sleep(unsigned duration) -{ -#ifdef _WIN32 - ::Sleep(duration); -#elif defined(POSIX) - usleep( duration * 1000 ); -#endif -} - -//--------------------------------------------------------- - -bool CThread::Init() -{ - return true; -} - -//--------------------------------------------------------- - -void CThread::OnExit() -{ -} - -//--------------------------------------------------------- - -void CThread::Cleanup() -{ - m_threadId = 0; -} - -//--------------------------------------------------------- -bool CThread::WaitForCreateComplete(CThreadEvent * pEvent) -{ - // Force serialized thread creation... - if (!pEvent->Wait(60000)) - { - AssertMsg( 0, "Probably deadlock or failure waiting for thread to initialize." ); - return false; - } - return true; -} - -//--------------------------------------------------------- - -bool CThread::IsThreadRunning() -{ -#ifdef _PS3 - // ThreadIsThreadIdRunning() doesn't work on PS3 if the thread is in a zombie state - return m_eventTheadExit.Check(); -#else - return ThreadIsThreadIdRunning( (ThreadId_t)m_threadId ); -#endif -} - -//--------------------------------------------------------- - -CThread::ThreadProc_t CThread::GetThreadProc() -{ - return ThreadProc; -} - -//--------------------------------------------------------- - -unsigned __stdcall CThread::ThreadProc(LPVOID pv) -{ - std::unique_ptr pInit((ThreadInit_t *)pv); - -#ifdef _X360 - // Make sure all threads are consistent w.r.t floating-point math - SetupFPUControlWord(); -#endif - - CThread *pThread = pInit->pThread; g_pCurThread = pThread; - - g_pCurThread->m_pStackBase = AlignValue( &pThread, 4096 ); - - pInit->pThread->m_result = -1; - - bool bInitSuccess = true; - if ( pInit->pfInitSuccess ) - *(pInit->pfInitSuccess) = false; - - try - { - bInitSuccess = pInit->pThread->Init(); - } - - catch (...) - { - pInit->pInitCompleteEvent->Set(); - throw; - } - - if ( pInit->pfInitSuccess ) - *(pInit->pfInitSuccess) = bInitSuccess; - pInit->pInitCompleteEvent->Set(); - if (!bInitSuccess) - return 0; - - if ( pInit->pThread->m_flags & SUPPORT_STOP_PROTOCOL ) - { - try - { - pInit->pThread->m_result = pInit->pThread->Run(); - } - - catch (...) - { - } - } - else - { - pInit->pThread->m_result = pInit->pThread->Run(); - } - - pInit->pThread->OnExit(); - g_pCurThread = NULL; - pInit->pThread->Cleanup(); - - return pInit->pThread->m_result; } - +#else +// The CThread implementation needs to be inlined for performance on the PS3 - It makes a difference of more than 1ms/frame +// for other platforms, we include the .inl in the .cpp file where it existed before +#include "../public/tier0/threadtools.inl" +#endif //----------------------------------------------------------------------------- // @@ -2186,16 +2780,15 @@ CWorkerThread::CWorkerThread() : m_EventSend(true), // must be manual-reset for PeekCall() m_EventComplete(true), // must be manual-reset to handle multiple wait with thread properly m_Param(0), - m_pParamFunctor(NULL), m_ReturnVal(0) { } //--------------------------------------------------------- -int CWorkerThread::CallWorker(unsigned dw, unsigned timeout, bool fBoostWorkerPriorityToMaster, CFunctor *pParamFunctor) +int CWorkerThread::CallWorker(unsigned dw, unsigned timeout, bool fBoostWorkerPriorityToMaster) { - return Call(dw, timeout, fBoostWorkerPriorityToMaster, NULL, pParamFunctor); + return Call(dw, timeout, fBoostWorkerPriorityToMaster); } //--------------------------------------------------------- @@ -2214,10 +2807,8 @@ CThreadEvent &CWorkerThread::GetCallHandle() //--------------------------------------------------------- -unsigned CWorkerThread::GetCallParam( CFunctor **ppParamFunctor ) const +unsigned CWorkerThread::GetCallParam() const { - if( ppParamFunctor ) - *ppParamFunctor = m_pParamFunctor; return m_Param; } @@ -2226,22 +2817,28 @@ unsigned CWorkerThread::GetCallParam( CFunctor **ppParamFunctor ) const int CWorkerThread::BoostPriority() { int iInitialPriority = GetPriority(); + +#ifdef WIN32 + const int iNewPriority = ThreadGetPriority( GetThreadHandle() ); + if (iNewPriority > iInitialPriority) + ThreadSetPriority( GetThreadHandle(), iNewPriority); +#elif !defined( _PS3 ) const int iNewPriority = ThreadGetPriority( (ThreadHandle_t)GetThreadID() ); if (iNewPriority > iInitialPriority) ThreadSetPriority( (ThreadHandle_t)GetThreadID(), iNewPriority); +#endif + return iInitialPriority; } //--------------------------------------------------------- - -static uint32 __stdcall DefaultWaitFunc( int nEvents, CThreadEvent * const *pEvents, int bWaitAll, uint32 timeout ) +static uint32 DefaultWaitFunc( uint32 nHandles, CThreadEvent** ppHandles, int bWaitAll, uint32 timeout ) { - return ThreadWaitForEvents( nEvents, pEvents, bWaitAll!=0, timeout ); -// return VCRHook_WaitForMultipleObjects( nHandles, (const void **)pHandles, bWaitAll, timeout ); + return CThreadEvent::WaitForMultiple( nHandles, ppHandles, bWaitAll!=0, timeout ) ; } -int CWorkerThread::Call(unsigned dwParam, unsigned timeout, bool fBoostPriority, WaitFunc_t pfnWait, CFunctor *pParamFunctor) +int CWorkerThread::Call(unsigned dwParam, unsigned timeout, bool fBoostPriority, WaitFunc_t waitFunc) { AssertMsg(!m_EventSend.Check(), "Cannot perform call if there's an existing call pending" ); @@ -2258,18 +2855,14 @@ int CWorkerThread::Call(unsigned dwParam, unsigned timeout, bool fBoostPriority, // set the parameter, signal the worker thread, wait for the completion to be signaled m_Param = dwParam; - m_pParamFunctor = pParamFunctor; m_EventComplete.Reset(); m_EventSend.Set(); - WaitForReply( timeout, pfnWait ); + WaitForReply( timeout, waitFunc ); - // MWD: Investigate why setting thread priorities is killing the 360 -#ifndef _X360 if (fBoostPriority) SetPriority(iInitialPriority); -#endif return m_ReturnVal; } @@ -2288,47 +2881,44 @@ int CWorkerThread::WaitForReply( unsigned timeout, WaitFunc_t pfnWait ) { if (!pfnWait) { - pfnWait = DefaultWaitFunc; + pfnWait = &DefaultWaitFunc; } -#ifdef WIN32 - CThreadEvent threadEvent( GetThreadHandle() ); -#endif - CThreadEvent *waits[] = { -#ifdef WIN32 - &threadEvent, -#endif - &m_EventComplete + &m_EventComplete, + &m_ExitEvent }; - unsigned result; bool bInDebugger = Plat_IsInDebugSession(); + uint32 dwActualTimeout = ( (timeout==TT_INFINITE) ? 30000 : timeout ); + do { #ifdef WIN32 // Make sure the thread handle hasn't been closed if ( !GetThreadHandle() ) { - result = WAIT_OBJECT_0 + 1; + result = 1; break; } #endif - result = (*pfnWait)((sizeof(waits) / sizeof(waits[0])), waits, false, - (timeout != TT_INFINITE) ? timeout : 30000); - AssertMsg(timeout != TT_INFINITE || result != WAIT_TIMEOUT, "Possible hung thread, call to thread timed out"); + result = (*pfnWait)( ARRAYSIZE( waits ), waits, false, dwActualTimeout ); - } while ( bInDebugger && ( timeout == TT_INFINITE && result == WAIT_TIMEOUT ) ); + AssertMsg(timeout != TT_INFINITE || result != TW_TIMEOUT, "Possible hung thread, call to thread timed out"); - if ( result != WAIT_OBJECT_0 + 1 ) + } while ( bInDebugger && ( timeout == TT_INFINITE && result == TW_TIMEOUT ) ); + + if ( result != 0 ) { - if (result == WAIT_TIMEOUT) + if (result == TW_TIMEOUT) + { m_ReturnVal = WTCR_TIMEOUT; - else if (result == WAIT_OBJECT_0) + } + else if (result == 1) { DevMsg( 2, "Thread failed to respond, probably exited\n"); m_EventSend.Reset(); @@ -2371,7 +2961,7 @@ bool CWorkerThread::WaitForCall(unsigned dwTimeout, unsigned * pResult) // is there a request? // -bool CWorkerThread::PeekCall(unsigned * pParam, CFunctor **ppParamFunctor) +bool CWorkerThread::PeekCall(unsigned * pParam) { if (!m_EventSend.Check()) { @@ -2383,10 +2973,6 @@ bool CWorkerThread::PeekCall(unsigned * pParam, CFunctor **ppParamFunctor) { *pParam = m_Param; } - if( ppParamFunctor ) - { - *ppParamFunctor = m_pParamFunctor; - } return true; } } @@ -2411,4 +2997,267 @@ void CWorkerThread::Reply(unsigned dw) m_EventComplete.Set(); } + //----------------------------------------------------------------------------- + + +#if defined( _PS3 ) + +/******************************************************************************* +* PS3 equivalent to Win32 function for setting events +*******************************************************************************/ +BOOL SetEvent( CThreadEvent *pEvent ) +{ + bool bRetVal = pEvent->Set(); + if ( !bRetVal ) + Assert(0); + + return bRetVal; +} + +/******************************************************************************* +* PS3 equivalent to Win32 function for resetting events +*******************************************************************************/ +BOOL ResetEvent( CThreadEvent *pEvent ) +{ + return pEvent->Reset(); +} + +#define MAXIMUM_WAIT_OBJECTS 64 + +/******************************************************************************* +* Wait for a selection of events to terminate +*******************************************************************************/ +DWORD WaitForMultipleObjects( DWORD nCount, CThreadEvent **lppHandles, BOOL bWaitAll, DWORD dwMilliseconds ) +{ + ////////////////////////////////////////////////////////////// +#ifndef NEW_WAIT_FOR_MULTIPLE_OBJECTS + ////////////////////////////////////////////////////////////// + + + // Support for a limited amount of events + if ( nCount >= MAXIMUM_WAIT_OBJECTS ) + { + Assert(0); + return false; + } + + bool bRunning = true; + unsigned int result = TW_FAILED; + + // For bWaitAll + int numEvent = 0; + int eventComplete[ MAXIMUM_WAIT_OBJECTS ] = {0}; + + uint64_t timeDiffMS = 0; + uint64_t startTimeMS = Plat_MSTime(); + uint64_t endTimeMS = 0; + + while ( bRunning ) + { + // Check for a timeout + if ( bRunning && ( dwMilliseconds != INFINITE ) && ( timeDiffMS > dwMilliseconds ) ) + { + result = TW_TIMEOUT; + bRunning = false; + } + + // Wait for all the events to be set + if ( bWaitAll ) + { + for ( int event = 0; event < nCount; ++event ) + { + if ( lppHandles[event]->Wait(1) ) + { + // If an event is complete, mark it as complete in our list + if ( eventComplete[ event ] == 0 ) + { + numEvent++; + eventComplete[ event ] = 1; + } + } + } + + // If all the events have been set, terminate the function + if ( numEvent >= nCount ) + { + result = WAIT_OBJECT_0; + bRunning = false; + } + } + + // Wait for one event to be set + else + { + for ( int event = 0; event < nCount; ++event ) + { + if ( lppHandles[event]->Wait(1) ) + { + result = WAIT_OBJECT_0 + event; + bRunning = false; + break; + } + } + } + + endTimeMS = Plat_MSTime(); + timeDiffMS = endTimeMS - startTimeMS; + } + + return result; + + + + ////////////////////////////////////////////////////////////// +#else // NEW_WAIT_FOR_MULTIPLE_OBJECTS // (expected PS3 only) + ////////////////////////////////////////////////////////////// +#ifndef _PS3 +#error This code was written expecting to be run on PS3. +#endif + + // check if we have a wait objects semaphore + if (!gbWaitObjectsCreated) + { + sys_semaphore_attribute_t semAttr; + sys_semaphore_attribute_initialize(semAttr); + sys_semaphore_create(&gWaitObjectsSemaphore, &semAttr, 0, 0xFFFF); + + gbWaitObjectsCreated = true; + } + + // Support for a limited amount of events + if ( nCount >= MAXIMUM_WAIT_OBJECTS ) + { + Assert(0); + return false; + } + + unsigned int result = WAIT_FAILED; + int res = CELL_OK; + int event = -1; + int numEvent = 0; + + // run through events registering this thread with each one + for (int i = 0; i < nCount; i++) + { + lppHandles[i]->RegisterWaitingThread(&gWaitObjectsSemaphore, i, &event); + } + + + // in the Source API, a timeOut of 0 means very short timeOut, not (as in the PS3 spec) an infinite timeout. + // TT_INFINITE is #defined to 2^31-1, which means "infinite timeout" on PC and "72 minutes, 35 seconds" on PS3. + // conversely, the code below (around deltaTime) expects to be able to compare against the timeout + // value given here, so we cannot just replace 0 with 1 and TT_INFINITE with 0. + // So, we replace 0 with 1, meaning "a very short time", and test for the special value TT_INFINITE + // at the moment of calling sys_semaphore_wait, where we replace it with the real "infinite timeout" + // value. It isn't safe to simply increase the declaration size of TT_INFINITE, because as you can + // see it is often assigned to uint32s. + // Also, Source timeouts are specified in milliseconds, and PS3 timeouts are in microseconds, + // so we need to multiply by one thousand. + uint32 timeOut = dwMilliseconds; + if ( timeOut == 0 ) + { + timeOut = 1; + } + else if ( timeOut != TT_INFINITE ) + { + timeOut *= 1000; + // note that it's impossible for dwMilliseconds * 1000 + // to coincidentally equal TT_INFINITE since TT_INFINITE + // is not divisible by 1000. + COMPILE_TIME_ASSERT( TT_INFINITE % 1000 != 0 ); + } + + COMPILE_TIME_ASSERT( TT_INFINITE != 0 ); // The code here was written expecting (working around) that TT_INFINITE is + // MAXINT, so if you changed this number, please read the comment above and + // carefully examine the code here to make sure that timeouts still work + // correctly on PS3. Be aware that in many places in Source, a timeout of + // 0 has some special meaning other than "infinite timeout", so track those + // down too. + + + // Wait for all the events to be set + if ( bWaitAll ) + { + while (numEvent < nCount) + { + uint64_t deltaTime = Plat_USTime(); + + res = sys_semaphore_wait(gWaitObjectsSemaphore, timeOut == TT_INFINITE ? 0 : timeOut ); + + deltaTime = Plat_USTime() - deltaTime; + + if (res == ETIMEDOUT) + { + result = TW_TIMEOUT; + break; + } + else if (res == CELL_OK) + { + numEvent++; + + if (deltaTime >= timeOut) + { + // note - if this is not truly a time out + // then it will be set to WAIT_OBJECT_0 + // after this loop + result = TW_TIMEOUT; + break; + } + else + { + timeOut -= deltaTime; + } + } + else + { + result = TW_FAILED; + break; + } + } + + if (numEvent >= nCount) + { + result = WAIT_OBJECT_0; + } + } + else // Wait for one event to be set + { + // no event fired yet, wait on semaphore + res = sys_semaphore_wait( gWaitObjectsSemaphore, timeOut == TT_INFINITE ? 0 : timeOut ); + + if (res == ETIMEDOUT) + { + result = TW_TIMEOUT; + } + else if (res == CELL_OK) + { + if ((event < 0) || (event >= nCount)) + { + DEBUG_ERROR("Bad event\n"); + } + + result = WAIT_OBJECT_0 + event; + } + } + + // run through events unregistering this thread, for benefit + // of those events that did not fire, or fired before semaphore + // was registered + for (int i = 0; i < nCount; i++) + { + lppHandles[i]->UnregisterWaitingThread(&gWaitObjectsSemaphore); + } + + // reset semaphore + while (sys_semaphore_trywait(gWaitObjectsSemaphore) != EBUSY); + + return result; + + + ////////////////////////////////////////////////////////////// +#endif // NEW_WAIT_FOR_MULTIPLE_OBJECTS + ////////////////////////////////////////////////////////////// +} + +#endif diff --git a/tier1/KeyValues.cpp b/tier1/KeyValues.cpp index a86cc530..ebf4d937 100644 --- a/tier1/KeyValues.cpp +++ b/tier1/KeyValues.cpp @@ -1425,7 +1425,7 @@ const char *KeyValues::GetString( const char *keyName, const char *defaultValue SetString( keyName, buf ); break; case TYPE_PTR: - Q_snprintf( buf, sizeof( buf ), "%lld", (int64)(size_t)dat->m_pValue ); + Q_snprintf( buf, sizeof( buf ), "%lld", (int64)dat->m_pValue ); SetString( keyName, buf ); break; case TYPE_INT: @@ -1478,7 +1478,7 @@ const wchar_t *KeyValues::GetWString( const char *keyName, const wchar_t *defaul SetWString( keyName, wbuf); break; case TYPE_PTR: - swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%lld", (int64)(size_t)dat->m_pValue ); + swprintf( wbuf, Q_ARRAYSIZE(wbuf), L"%lld", (int64)dat->m_pValue ); SetWString( keyName, wbuf ); break; case TYPE_INT: diff --git a/tier1/stringpool.cpp b/tier1/stringpool.cpp index 6b6b0ff9..2f0fbf6c 100644 --- a/tier1/stringpool.cpp +++ b/tier1/stringpool.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // Purpose: // @@ -18,16 +18,21 @@ // Purpose: Comparison function for string sorted associative data structures //----------------------------------------------------------------------------- -bool StrLess( const char * const &pszLeft, const char * const &pszRight ) +bool StrLessInsensitive( const char * const &pszLeft, const char * const &pszRight ) { return ( Q_stricmp( pszLeft, pszRight) < 0 ); } +bool StrLessSensitive( const char * const &pszLeft, const char * const &pszRight ) +{ + return ( Q_strcmp( pszLeft, pszRight) < 0 ); +} + //----------------------------------------------------------------------------- //----------------------------------------------------------------------------- -CStringPool::CStringPool() - : m_Strings( 32, 256, StrLess ) +CStringPool::CStringPool( StringPoolCase_t caseSensitivity ) + : m_Strings( 32, 256, caseSensitivity == StringPoolCaseInsensitive ? StrLessInsensitive : StrLessSensitive ) { } @@ -69,9 +74,7 @@ const char * CStringPool::Allocate( const char *pszValue ) return m_Strings[i]; pszNew = strdup( pszValue ); - - if ( bNew ) - m_Strings.Insert( pszNew ); + m_Strings.Insert( pszNew ); return pszNew; } @@ -94,217 +97,6 @@ void CStringPool::FreeAll() //----------------------------------------------------------------------------- -CCountedStringPool::CCountedStringPool() -{ - MEM_ALLOC_CREDIT(); - m_HashTable.EnsureCount(HASH_TABLE_SIZE); - - for( int i = 0; i < m_HashTable.Count(); i++ ) - { - m_HashTable[i] = INVALID_ELEMENT; - } - - m_FreeListStart = INVALID_ELEMENT; - m_Elements.AddToTail(); - m_Elements[0].pString = NULL; - m_Elements[0].nReferenceCount = 0; - m_Elements[0].nNextElement = INVALID_ELEMENT; -} - -CCountedStringPool::~CCountedStringPool() -{ - FreeAll(); -} - -void CCountedStringPool::FreeAll() -{ - int i; - - // Reset the hash table: - for( i = 0; i < m_HashTable.Count(); i++ ) - { - m_HashTable[i] = INVALID_ELEMENT; - } - - // Blow away the free list: - m_FreeListStart = INVALID_ELEMENT; - - for( i = 0; i < m_Elements.Count(); i++ ) - { - if( m_Elements[i].pString ) - { - delete [] m_Elements[i].pString; - m_Elements[i].pString = NULL; - m_Elements[i].nReferenceCount = 0; - m_Elements[i].nNextElement = INVALID_ELEMENT; - } - } - - // Remove all but the invalid element: - m_Elements.RemoveAll(); - m_Elements.AddToTail(); - m_Elements[0].pString = NULL; - m_Elements[0].nReferenceCount = 0; - m_Elements[0].nNextElement = INVALID_ELEMENT; -} - - -unsigned short CCountedStringPool::FindStringHandle( const char* pIntrinsic ) -{ - if( pIntrinsic == NULL ) - return INVALID_ELEMENT; - - unsigned short nHashBucketIndex = (HashStringCaseless(pIntrinsic ) %HASH_TABLE_SIZE); - unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ]; - - // Does the bucket already exist? - if( nCurrentBucket != INVALID_ELEMENT ) - { - for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) - { - if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) - { - return nCurrentBucket; - } - } - } - - return 0; - -} - -char* CCountedStringPool::FindString( const char* pIntrinsic ) -{ - if( pIntrinsic == NULL ) - return NULL; - - // Yes, this will be NULL on failure. - return m_Elements[FindStringHandle(pIntrinsic)].pString; -} - -unsigned short CCountedStringPool::ReferenceStringHandle( const char* pIntrinsic ) -{ - if( pIntrinsic == NULL ) - return INVALID_ELEMENT; - - unsigned short nHashBucketIndex = (HashStringCaseless( pIntrinsic ) % HASH_TABLE_SIZE); - unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ]; - - // Does the bucket already exist? - if( nCurrentBucket != INVALID_ELEMENT ) - { - for( ; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) - { - if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) - { - // Anyone who hits 65k references is permanant - if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE ) - { - m_Elements[nCurrentBucket].nReferenceCount ++ ; - } - return nCurrentBucket; - } - } - } - - if( m_FreeListStart != INVALID_ELEMENT ) - { - nCurrentBucket = m_FreeListStart; - m_FreeListStart = m_Elements[nCurrentBucket].nNextElement; - } - else - { - nCurrentBucket = m_Elements.AddToTail(); - } - - m_Elements[nCurrentBucket].nReferenceCount = 1; - - // Insert at the beginning of the bucket: - m_Elements[nCurrentBucket].nNextElement = m_HashTable[ nHashBucketIndex ]; - m_HashTable[ nHashBucketIndex ] = nCurrentBucket; - - m_Elements[nCurrentBucket].pString = new char[Q_strlen( pIntrinsic ) + 1]; - Q_strcpy( m_Elements[nCurrentBucket].pString, pIntrinsic ); - - return nCurrentBucket; -} - - -char* CCountedStringPool::ReferenceString( const char* pIntrinsic ) -{ - if(!pIntrinsic) - return NULL; - - return m_Elements[ReferenceStringHandle( pIntrinsic)].pString; -} - -void CCountedStringPool::DereferenceString( const char* pIntrinsic ) -{ - // If we get a NULL pointer, just return - if (!pIntrinsic) - return; - - unsigned short nHashBucketIndex = (HashStringCaseless( pIntrinsic ) % m_HashTable.Count()); - unsigned short nCurrentBucket = m_HashTable[ nHashBucketIndex ]; - - // If there isn't anything in the bucket, just return. - if ( nCurrentBucket == INVALID_ELEMENT ) - return; - - for( unsigned short previous = INVALID_ELEMENT; nCurrentBucket != INVALID_ELEMENT ; nCurrentBucket = m_Elements[nCurrentBucket].nNextElement ) - { - if( !Q_stricmp( pIntrinsic, m_Elements[nCurrentBucket].pString ) ) - { - // Anyone who hits 65k references is permanant - if( m_Elements[nCurrentBucket].nReferenceCount < MAX_REFERENCE ) - { - m_Elements[nCurrentBucket].nReferenceCount --; - } - - if( m_Elements[nCurrentBucket].nReferenceCount == 0 ) - { - if( previous == INVALID_ELEMENT ) - { - m_HashTable[nHashBucketIndex] = m_Elements[nCurrentBucket].nNextElement; - } - else - { - m_Elements[previous].nNextElement = m_Elements[nCurrentBucket].nNextElement; - } - - delete [] m_Elements[nCurrentBucket].pString; - m_Elements[nCurrentBucket].pString = NULL; - m_Elements[nCurrentBucket].nReferenceCount = 0; - - m_Elements[nCurrentBucket].nNextElement = m_FreeListStart; - m_FreeListStart = nCurrentBucket; - break; - - } - } - - previous = nCurrentBucket; - } -} - -char* CCountedStringPool::HandleToString( unsigned short handle ) -{ - return m_Elements[handle].pString; -} - -void CCountedStringPool::SpewStrings() -{ - int i; - for ( i = 0; i < m_Elements.Count(); i++ ) - { - char* string = m_Elements[i].pString; - - Msg("String %d: ref:%d %s", i, m_Elements[i].nReferenceCount, string == NULL? "EMPTY - ok for slot zero only!" : string); - } - - Msg("\n%d total counted strings.", m_Elements.Count()); -} - #ifdef _DEBUG CON_COMMAND( test_stringpool, "Tests the class CStringPool" ) { diff --git a/tier1/utlbuffer.cpp b/tier1/utlbuffer.cpp index ff03da06..ae29160f 100644 --- a/tier1/utlbuffer.cpp +++ b/tier1/utlbuffer.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//===== Copyright © 1996-2005, Valve Corporation, All rights reserved. ======// // // $Header: $ // $NoKeywords: $ @@ -92,14 +92,14 @@ CUtlCStringConversion::CUtlCStringConversion( char nEscapeChar, const char *pDel memset( m_pConversion, 0x0, sizeof(m_pConversion) ); for ( int i = 0; i < nCount; ++i ) { - m_pConversion[ (unsigned char) pArray[i].m_pReplacementString[0] ] = pArray[i].m_nActualChar; + m_pConversion[ (unsigned char)(pArray[i].m_pReplacementString[0]) ] = pArray[i].m_nActualChar; } } // Finds a conversion for the passed-in string, returns length char CUtlCStringConversion::FindConversion( const char *pString, int *pLength ) { - char c = m_pConversion[ (unsigned char) pString[0] ]; + char c = m_pConversion[ (unsigned char)( pString[0] ) ]; *pLength = (c != '\0') ? 1 : 0; return c; } @@ -114,7 +114,7 @@ CUtlCharConversion::CUtlCharConversion( char nEscapeChar, const char *pDelimiter m_nEscapeChar = nEscapeChar; m_pDelimiter = pDelimiter; m_nCount = nCount; - m_nDelimiterLength = Q_strlen( pDelimiter ); + m_nDelimiterLength = V_strlen( pDelimiter ); m_nMaxConversionLength = 0; memset( m_pReplacements, 0, sizeof(m_pReplacements) ); @@ -122,10 +122,10 @@ CUtlCharConversion::CUtlCharConversion( char nEscapeChar, const char *pDelimiter for ( int i = 0; i < nCount; ++i ) { m_pList[i] = pArray[i].m_nActualChar; - ConversionInfo_t &info = m_pReplacements[ (unsigned char) m_pList[i] ]; + ConversionInfo_t &info = m_pReplacements[ (unsigned char)( m_pList[i] ) ]; Assert( info.m_pReplacementString == 0 ); info.m_pReplacementString = pArray[i].m_pReplacementString; - info.m_nLength = Q_strlen( info.m_pReplacementString ); + info.m_nLength = V_strlen( info.m_pReplacementString ); if ( info.m_nLength > m_nMaxConversionLength ) { m_nMaxConversionLength = info.m_nLength; @@ -158,12 +158,12 @@ int CUtlCharConversion::GetDelimiterLength() const //----------------------------------------------------------------------------- const char *CUtlCharConversion::GetConversionString( char c ) const { - return m_pReplacements[ (unsigned char) c ].m_pReplacementString; + return m_pReplacements[ (unsigned char)c ].m_pReplacementString; } int CUtlCharConversion::GetConversionLength( char c ) const { - return m_pReplacements[ (unsigned char) c ].m_nLength; + return m_pReplacements[ (unsigned char)c ].m_nLength; } int CUtlCharConversion::MaxConversionLength() const @@ -179,9 +179,9 @@ char CUtlCharConversion::FindConversion( const char *pString, int *pLength ) { for ( int i = 0; i < m_nCount; ++i ) { - if ( !Q_strcmp( pString, m_pReplacements[ (unsigned char) m_pList[i] ].m_pReplacementString ) ) + if ( !V_strcmp( pString, m_pReplacements[ (unsigned char)( m_pList[i] ) ].m_pReplacementString ) ) { - *pLength = m_pReplacements[ (unsigned char) m_pList[i] ].m_nLength; + *pLength = m_pReplacements[ (unsigned char)( m_pList[i] ) ].m_nLength; return m_pList[i]; } } @@ -207,7 +207,7 @@ CUtlBuffer::CUtlBuffer( int growSize, int initSize, int nFlags ) : if ( (initSize != 0) && !IsReadOnly() ) { m_nMaxPut = -1; - AddNullTermination(); + AddNullTermination( m_Put ); } else { @@ -228,17 +228,115 @@ CUtlBuffer::CUtlBuffer( const void *pBuffer, int nSize, int nFlags ) : m_Flags = nFlags; if ( IsReadOnly() ) { - m_nMaxPut = nSize; + m_nMaxPut = m_Put = nSize; } else { m_nMaxPut = -1; - AddNullTermination(); + AddNullTermination( m_Put ); } SetOverflowFuncs( &CUtlBuffer::GetOverflow, &CUtlBuffer::PutOverflow ); } +CUtlBuffer::CUtlBuffer( const CUtlBuffer& copyFrom ) +: m_Get( copyFrom.m_Get ) +, m_Put( copyFrom.m_Put ) +, m_Error( copyFrom.m_Error ) +, m_Flags( copyFrom.m_Flags ) +, m_Reserved( copyFrom.m_Reserved ) +#if defined( _GAMECONSOLE ) +, pad( copyFrom.pad ) +#endif +, m_nTab( copyFrom.m_nTab ) +, m_nMaxPut( copyFrom.m_nMaxPut ) +, m_nOffset( copyFrom.m_nOffset ) +, m_GetOverflowFunc( copyFrom.m_GetOverflowFunc ) +, m_PutOverflowFunc( copyFrom.m_PutOverflowFunc ) +, m_Byteswap( copyFrom.m_Byteswap ) +{ + if(copyFrom.m_Memory.Count() > 0) + { + Assert( false ); // This is a slow path, don't do this. + + // copy memory + m_Memory.EnsureCapacity( copyFrom.m_Memory.Count() ); + memcpy( m_Memory.Base(), copyFrom.m_Memory.Base(), copyFrom.m_Memory.Count() ); + } +} + + +CUtlBuffer& CUtlBuffer::operator=( const CUtlBuffer& copyFrom ) +{ + if ( copyFrom.m_Memory.Count() > 0 ) + { + Assert( false ); // This is a slow path, don't do this. + if(this != ©From) + { + m_Memory.Purge(); + m_Memory.EnsureCapacity( copyFrom.m_Memory.Count() ); + memcpy( m_Memory.Base(), copyFrom.m_Memory.Base(), copyFrom.m_Memory.Count() ); + } + } + m_Get = copyFrom.m_Get; + m_Put = copyFrom.m_Put; + m_Error = copyFrom.m_Error; + m_Flags = copyFrom.m_Flags; + m_Reserved = copyFrom.m_Reserved; +#if defined( _GAMECONSOLE ) + pad = copyFrom.pad; +#endif + m_nTab = copyFrom.m_nTab; + m_nMaxPut = copyFrom.m_nMaxPut; + m_nOffset = copyFrom.m_nOffset; + m_GetOverflowFunc = copyFrom.m_GetOverflowFunc; + m_PutOverflowFunc = copyFrom.m_PutOverflowFunc; + m_Byteswap = copyFrom.m_Byteswap; + + return *this; +} + +#if VALVE_CPP11 +CUtlBuffer::CUtlBuffer( CUtlBuffer&& moveFrom ) // = default +: m_Memory( Move( moveFrom.m_Memory ) ) +, m_Get( Move( moveFrom.m_Get ) ) +, m_Put( Move( moveFrom.m_Put ) ) +, m_Error( Move( moveFrom.m_Error ) ) +, m_Flags( Move( moveFrom.m_Flags ) ) +, m_Reserved( Move( moveFrom.m_Reserved ) ) +#if defined( _GAMECONSOLE ) +, pad( Move( moveFrom.pad ) ) +#endif +, m_nTab( Move( moveFrom.m_nTab ) ) +, m_nMaxPut( Move( moveFrom.m_nMaxPut ) ) +, m_nOffset( Move( moveFrom.m_nOffset ) ) +, m_GetOverflowFunc( Move( moveFrom.m_GetOverflowFunc ) ) +, m_PutOverflowFunc( Move( moveFrom.m_PutOverflowFunc ) ) +, m_Byteswap( Move( moveFrom.m_Byteswap ) ) +{} + +CUtlBuffer& CUtlBuffer::operator=( CUtlBuffer&& moveFrom ) // = default +{ + m_Memory = Move( moveFrom.m_Memory ); + m_Get = Move( moveFrom.m_Get ); + m_Put = Move( moveFrom.m_Put ); + m_Error = Move( moveFrom.m_Error ); + m_Flags = Move( moveFrom.m_Flags ); + m_Reserved = Move( moveFrom.m_Reserved ); +#if defined( _GAMECONSOLE ) + pad = Move( moveFrom.pad ); +#endif + m_nTab = Move( moveFrom.m_nTab ); + m_nMaxPut = Move( moveFrom.m_nMaxPut ); + m_nOffset = Move( moveFrom.m_nOffset ); + m_GetOverflowFunc = Move( moveFrom.m_GetOverflowFunc ); + m_PutOverflowFunc = Move( moveFrom.m_PutOverflowFunc ); + m_Byteswap = Move( moveFrom.m_Byteswap ); + + return *this; +} +#endif + //----------------------------------------------------------------------------- // Modifies the buffer to be binary or text; Blows away the buffer and the CONTAINS_CRLF value. //----------------------------------------------------------------------------- @@ -303,7 +401,7 @@ void CUtlBuffer::SetExternalBuffer( void* pMemory, int nSize, int nInitialPut, i m_nOffset = 0; m_Flags = nFlags; m_nMaxPut = -1; - AddNullTermination(); + AddNullTermination( m_Put ); } //----------------------------------------------------------------------------- @@ -321,9 +419,25 @@ void CUtlBuffer::AssumeMemory( void *pMemory, int nSize, int nInitialPut, int nF m_nOffset = 0; m_Flags = nFlags; m_nMaxPut = -1; - AddNullTermination(); + AddNullTermination( m_Put ); } + +//----------------------------------------------------------------------------- +// Allows the caller to control memory +//----------------------------------------------------------------------------- +void* CUtlBuffer::DetachMemory() +{ + // Reset all indices; we just changed memory + m_Get = 0; + m_Put = 0; + m_nTab = 0; + m_Error = 0; + m_nOffset = 0; + return m_Memory.DetachMemory( ); +} + + //----------------------------------------------------------------------------- // Makes sure we've got at least this much memory //----------------------------------------------------------------------------- @@ -351,16 +465,15 @@ void CUtlBuffer::EnsureCapacity( int num ) //----------------------------------------------------------------------------- // Base get method from which all others derive //----------------------------------------------------------------------------- -void CUtlBuffer::Get( void* pMem, int size ) +bool CUtlBuffer::Get( void* pMem, int size ) { if ( size > 0 && CheckGet( size ) ) { - int Index = m_Get - m_nOffset; - Assert( m_Memory.IsIdxValid( Index ) && m_Memory.IsIdxValid( Index + size - 1 ) ); - - memcpy( pMem, &m_Memory[ Index ], size ); + memcpy( pMem, &m_Memory[m_Get - m_nOffset], size ); m_Get += size; + return true; } + return false; } @@ -372,10 +485,7 @@ int CUtlBuffer::GetUpTo( void *pMem, int nSize ) { if ( CheckArbitraryPeekGet( 0, nSize ) ) { - int Index = m_Get - m_nOffset; - Assert( m_Memory.IsIdxValid( Index ) && m_Memory.IsIdxValid( Index + nSize - 1 ) ); - - memcpy( pMem, &m_Memory[ Index ], nSize ); + memcpy( pMem, &m_Memory[m_Get - m_nOffset], nSize ); m_Get += nSize; return nSize; } @@ -392,7 +502,7 @@ void CUtlBuffer::EatWhiteSpace() { while ( CheckGet( sizeof(char) ) ) { - if ( !isspace( *(const unsigned char*)PeekGet() ) ) + if ( !V_isspace( *(const unsigned char*)PeekGet() ) ) break; m_Get += sizeof(char); } @@ -437,7 +547,7 @@ int CUtlBuffer::PeekWhiteSpace( int nOffset ) while ( CheckPeekGet( nOffset, sizeof(char) ) ) { - if ( !isspace( *(unsigned char*)PeekGet( nOffset ) ) ) + if ( !V_isspace( *(unsigned char*)PeekGet( nOffset ) ) ) break; nOffset += sizeof(char); } @@ -491,7 +601,7 @@ int CUtlBuffer::PeekStringLength() for ( int i = 0; i < nPeekAmount; ++i ) { // The +1 here is so we eat the terminating 0 - if ( isspace((unsigned char)pTest[i]) || (pTest[i] == 0) ) + if ( V_isspace((unsigned char)pTest[i]) || (pTest[i] == 0) ) return (i + nOffset - nStartingOffset + 1); } } @@ -550,7 +660,7 @@ bool CUtlBuffer::PeekStringMatch( int nOffset, const char *pString, int nLen ) { if ( !CheckPeekGet( nOffset, nLen ) ) return false; - return !Q_strncmp( (const char*)PeekGet(nOffset), pString, nLen ); + return !V_strncmp( (const char*)PeekGet(nOffset), pString, nLen ); } @@ -607,19 +717,16 @@ int CUtlBuffer::PeekDelimitedStringLength( CUtlCharConversion *pConv, bool bActu //----------------------------------------------------------------------------- // Reads a null-terminated string //----------------------------------------------------------------------------- -void CUtlBuffer::GetStringInternal( char *pString, size_t maxLenInChars ) +void CUtlBuffer::GetString( char* pString, int nMaxChars ) { - if ( !IsValid() ) + if (!IsValid()) { *pString = 0; return; } - // This can legitimately be zero if we were told that the buffer is zero length, and - // we're asking to duplicate the buffer, so let that pass, too. - Assert( maxLenInChars != 0 || PeekStringLength() == 0 ); - - if ( maxLenInChars == 0 ) + Assert( nMaxChars > 0 ); + if ( nMaxChars <= 0 ) { return; } @@ -640,14 +747,14 @@ void CUtlBuffer::GetStringInternal( char *pString, size_t maxLenInChars ) return; } - const size_t nCharsToRead = min( (size_t)nLen, maxLenInChars ) - 1; - + const int nCharsToRead = Min( nLen, nMaxChars ) - 1; + Get( pString, nCharsToRead ); - pString[nCharsToRead] = 0; + pString[ nCharsToRead ] = 0; - if ( (size_t)nLen > (nCharsToRead + 1) ) + if ( nLen > ( nCharsToRead + 1 ) ) { - SeekGet( SEEK_CURRENT, nLen - (nCharsToRead + 1) ); + SeekGet( SEEK_CURRENT, nLen - ( nCharsToRead + 1 ) ); } // Read the terminating NULL in binary formats @@ -663,7 +770,7 @@ void CUtlBuffer::GetStringInternal( char *pString, size_t maxLenInChars ) //----------------------------------------------------------------------------- void CUtlBuffer::GetLine( char* pLine, int nMaxChars ) { - Assert( IsText() && !ContainsCRLF() ); + //Assert( IsText() && !ContainsCRLF() ); if ( !IsValid() ) { @@ -732,7 +839,7 @@ void CUtlBuffer::GetDelimitedString( CUtlCharConversion *pConv, char *pString, i { if ( !IsText() || !pConv ) { - GetStringInternal( pString, nMaxChars ); + GetString( pString, nMaxChars ); return; } @@ -858,11 +965,7 @@ const void* CUtlBuffer::PeekGet( int nMaxSize, int nOffset ) { if ( !CheckPeekGet( nOffset, nMaxSize ) ) return NULL; - - int Index = m_Get + nOffset - m_nOffset; - Assert( m_Memory.IsIdxValid( Index ) && m_Memory.IsIdxValid( Index + nMaxSize - 1 ) ); - - return &m_Memory[ Index ]; + return &m_Memory[ m_Get + nOffset - m_nOffset ]; } @@ -914,10 +1017,8 @@ int CUtlBuffer::VaScanf( const char* pFmt, va_list list ) return 0; int numScanned = 0; - int nLength; char c; - char* pEnd; - while ( (c = *pFmt++) ) + while ( c = *pFmt++ ) { // Stop if we hit the end of the buffer if ( m_Get >= TellMaxPut() ) @@ -956,93 +1057,105 @@ int CUtlBuffer::VaScanf( const char* pFmt, va_list list ) return numScanned; } } - break; + break; + + case 'h': + { + if ( *pFmt == 'd' || *pFmt == 'i' ) + { + if ( !GetTypeText( *va_arg( list, int16 * ) ) ) + return numScanned; // only support short ints, don't bother with hex + } + else if ( *pFmt == 'u' ) + { + if ( !GetTypeText( *va_arg( list, uint16 * ) ) ) + return numScanned; + } + else + return numScanned; + ++pFmt; + } + break; + + case 'I': + { + if ( *pFmt++ != '6' || *pFmt++ != '4' ) + return numScanned; // only support "I64d" and "I64u" + + if ( *pFmt == 'd' ) + { + if ( !GetTypeText( *va_arg( list, int64 * ) ) ) + return numScanned; + } + else if ( *pFmt == 'u' ) + { + if ( !GetTypeText( *va_arg( list, uint64 * ) ) ) + return numScanned; + } + else + { + return numScanned; + } + + ++pFmt; + } + break; case 'i': case 'd': { - int* i = va_arg( list, int * ); - - // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters - nLength = 128; - if ( !CheckArbitraryPeekGet( 0, nLength ) ) - { - *i = 0; + int32 *pArg = va_arg( list, int32 * ); + if ( !GetTypeText( *pArg ) ) return numScanned; - } - - *i = strtol( (char*)PeekGet(), &pEnd, 10 ); - int nBytesRead = (int)( pEnd - (char*)PeekGet() ); - if ( nBytesRead == 0 ) - return numScanned; - m_Get += nBytesRead; } break; - + case 'x': { - int* i = va_arg( list, int * ); - - // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters - nLength = 128; - if ( !CheckArbitraryPeekGet( 0, nLength ) ) - { - *i = 0; + uint32 *pArg = va_arg( list, uint32 * ); + if ( !GetTypeText( *pArg, 16 ) ) return numScanned; - } - - *i = strtol( (char*)PeekGet(), &pEnd, 16 ); - int nBytesRead = (int)( pEnd - (char*)PeekGet() ); - if ( nBytesRead == 0 ) - return numScanned; - m_Get += nBytesRead; } break; - + case 'u': { - unsigned int* u = va_arg( list, unsigned int *); - - // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters - nLength = 128; - if ( !CheckArbitraryPeekGet( 0, nLength ) ) - { - *u = 0; + uint32 *pArg = va_arg( list, uint32 * ); + if ( !GetTypeText( *pArg ) ) return numScanned; - } - - *u = strtoul( (char*)PeekGet(), &pEnd, 10 ); - int nBytesRead = (int)( pEnd - (char*)PeekGet() ); - if ( nBytesRead == 0 ) - return numScanned; - m_Get += nBytesRead; } break; - + + case 'l': + { + // we currently support %lf and %lld + if ( *pFmt == 'f' ) + { + if ( !GetTypeText( *va_arg( list, double * ) ) ) + return numScanned; + } + else if ( *pFmt == 'l' && *++pFmt == 'd' ) + { + if ( !GetTypeText( *va_arg( list, int64 * ) ) ) + return numScanned; + } + else + return numScanned; + } + break; + case 'f': { - float* f = va_arg( list, float *); - - // NOTE: This is not bullet-proof; it assumes numbers are < 128 characters - nLength = 128; - if ( !CheckArbitraryPeekGet( 0, nLength ) ) - { - *f = 0.0f; + float *pArg = va_arg( list, float * ); + if ( !GetTypeText( *pArg ) ) return numScanned; - } - - *f = (float)strtod( (char*)PeekGet(), &pEnd ); - int nBytesRead = (int)( pEnd - (char*)PeekGet() ); - if ( nBytesRead == 0 ) - return numScanned; - m_Get += nBytesRead; } break; - + case 's': { char* s = va_arg( list, char * ); - GetStringInternal( s, 256 ); + GetString( s, 64 ); // [SECURITY EXPLOIT: Scanf %s should be deprecated as malicious data can overrun stack buffers! Here we'd assume that at least 64 bytes are available on the stack, and even if not this shouldn't give attracker much room for code execution] } break; @@ -1099,38 +1212,55 @@ bool CUtlBuffer::GetToken( const char *pToken ) Assert( pToken ); // Look for the token - int nLen = Q_strlen( pToken ); + int nLen = V_strlen( pToken ); - int nSizeToCheck = Size() - TellGet() - m_nOffset; + // First time through on streaming, check what we already have loaded + // if we have enough loaded to do the check + int nMaxSize = Size() - ( TellGet() - m_nOffset ); + if ( nMaxSize <= nLen ) + { + nMaxSize = Size(); + } + int nSizeRemaining = TellMaxPut() - TellGet(); int nGet = TellGet(); - do + while ( nSizeRemaining >= nLen ) { - int nMaxSize = TellMaxPut() - TellGet(); - if ( nMaxSize < nSizeToCheck ) - { - nSizeToCheck = nMaxSize; - } - if ( nLen > nSizeToCheck ) - break; - + bool bOverFlow = ( nSizeRemaining > nMaxSize ); + int nSizeToCheck = bOverFlow ? nMaxSize : nSizeRemaining; if ( !CheckPeekGet( 0, nSizeToCheck ) ) break; const char *pBufStart = (const char*)PeekGet(); - const char *pFoundEnd = Q_strnistr( pBufStart, pToken, nSizeToCheck ); - if ( pFoundEnd ) + const char *pFoundEnd = V_strnistr( pBufStart, pToken, nSizeToCheck ); + + // Time to be careful: if we are in a state of overflow + // (namely, there's more of the buffer beyond the current window) + // we could be looking for 'foo' for example, and find 'foobar' + // if 'foo' happens to be the last 3 characters of the current window + size_t nOffset = (size_t)pFoundEnd - (size_t)pBufStart; + bool bPotentialMismatch = ( bOverFlow && ( (int)nOffset == Size() - nLen ) ); + if ( !pFoundEnd || bPotentialMismatch ) { - size_t nOffset = (size_t)pFoundEnd - (size_t)pBufStart; - SeekGet( CUtlBuffer::SEEK_CURRENT, nOffset + nLen ); - return true; + nSizeRemaining -= nSizeToCheck; + if ( !pFoundEnd && ( nSizeRemaining < nLen ) ) + break; + + // Second time through, stream as much in as possible + // But keep the last portion of the current buffer + // since we couldn't check it against stuff outside the window + nSizeRemaining += nLen; + nMaxSize = Size(); + SeekGet( CUtlBuffer::SEEK_CURRENT, nSizeToCheck - nLen ); + continue; } - SeekGet( CUtlBuffer::SEEK_CURRENT, nSizeToCheck - nLen - 1 ); - nSizeToCheck = Size() - (nLen-1); - - } while ( true ); + // Seek past the end of the found string + SeekGet( CUtlBuffer::SEEK_CURRENT, (int)( nOffset + nLen ) ); + return true; + } + // Didn't find a match, leave the get index where it was to start with SeekGet( CUtlBuffer::SEEK_HEAD, nGet ); return false; } @@ -1161,7 +1291,7 @@ bool CUtlBuffer::ParseToken( const char *pStartingDelim, const char *pEndingDeli // Ending delimiter is not Assert( pEndingDelim && pEndingDelim[0] ); - nEndingDelimLen = Q_strlen( pEndingDelim ); + nEndingDelimLen = V_strlen( pEndingDelim ); int nStartGet = TellGet(); char nCurrChar; @@ -1170,7 +1300,7 @@ bool CUtlBuffer::ParseToken( const char *pStartingDelim, const char *pEndingDeli while ( *pStartingDelim ) { nCurrChar = *pStartingDelim++; - if ( !isspace((unsigned char)nCurrChar) ) + if ( !V_isspace((unsigned char)nCurrChar) ) { if ( tolower( GetChar() ) != tolower( nCurrChar ) ) goto parseFailed; @@ -1187,7 +1317,7 @@ bool CUtlBuffer::ParseToken( const char *pStartingDelim, const char *pEndingDeli goto parseFailed; nCurrentGet = TellGet(); - nCharsToCopy = (nCurrentGet - nEndingDelimLen) - nTokenStart; + nCharsToCopy = (int)( (nCurrentGet - nEndingDelimLen) - nTokenStart ); if ( nCharsToCopy >= nMaxLen ) { nCharsToCopy = nMaxLen - 1; @@ -1203,7 +1333,7 @@ bool CUtlBuffer::ParseToken( const char *pStartingDelim, const char *pEndingDeli // Eat trailing whitespace for ( ; nCharsToCopy > 0; --nCharsToCopy ) { - if ( !isspace( (unsigned char)pString[ nCharsToCopy-1 ] ) ) + if ( !V_isspace( (unsigned char)pString[ nCharsToCopy-1 ] ) ) break; } } @@ -1319,15 +1449,10 @@ void CUtlBuffer::Put( const void *pMem, int size ) { if ( size && CheckPut( size ) ) { - int Index = m_Put - m_nOffset; - Assert( m_Memory.IsIdxValid( Index ) && m_Memory.IsIdxValid( Index + size - 1 ) ); - if( Index >= 0 ) - { - memcpy( &m_Memory[ Index ], pMem, size ); - m_Put += size; + memcpy( &m_Memory[m_Put - m_nOffset], pMem, size ); + m_Put += size; - AddNullTermination(); - } + AddNullTermination( m_Put ); } } @@ -1342,7 +1467,7 @@ void CUtlBuffer::PutString( const char* pString ) if ( pString ) { // Not text? append a null at the end. - size_t nLen = Q_strlen( pString ) + 1; + int nLen = (int)V_strlen( pString ) + 1; Put( pString, nLen * sizeof(char) ); return; } @@ -1365,7 +1490,7 @@ void CUtlBuffer::PutString( const char* pString ) while ( pEndl ) { size_t nSize = (size_t)pEndl - (size_t)pString + sizeof(char); - Put( pString, nSize ); + Put( pString, (int)nSize ); pString = pEndl + 1; if ( *pString ) { @@ -1378,7 +1503,7 @@ void CUtlBuffer::PutString( const char* pString ) } } } - size_t nLen = Q_strlen( pString ); + int nLen = (int)V_strlen( pString ); if ( nLen ) { Put( pString, nLen * sizeof(char) ); @@ -1430,7 +1555,7 @@ void CUtlBuffer::PutDelimitedString( CUtlCharConversion *pConv, const char *pStr } Put( pConv->GetDelimiter(), pConv->GetDelimiterLength() ); - int nLen = pString ? Q_strlen( pString ) : 0; + int nLen = pString ? V_strlen( pString ) : 0; for ( int i = 0; i < nLen; ++i ) { PutDelimitedCharInternal( pConv, pString[i] ); @@ -1446,12 +1571,9 @@ void CUtlBuffer::PutDelimitedString( CUtlCharConversion *pConv, const char *pStr void CUtlBuffer::VaPrintf( const char* pFmt, va_list list ) { - char temp[2048]; -#ifdef DBGFLAG_ASSERT - int nLen = -#endif - Q_vsnprintf( temp, sizeof( temp ), pFmt, list ); - Assert( nLen < 2048 ); + char temp[8192]; + int nLen = V_vsnprintf( temp, sizeof( temp ), pFmt, list ); + ErrorIfNot( nLen < sizeof( temp ), ( "CUtlBuffer::VaPrintf: String overflowed buffer [%d]\n", sizeof( temp ) ) ); PutString( temp ); } @@ -1563,7 +1685,7 @@ void CUtlBuffer::SeekPut( SeekType_t type, int offset ) OnPutOverflow( -nNextPut-1 ); m_Put = nNextPut; - AddNullTermination(); + AddNullTermination( m_Put ); } @@ -1585,8 +1707,10 @@ bool CUtlBuffer::IsBigEndian( void ) //----------------------------------------------------------------------------- // null terminate the buffer +// NOTE: Pass in nPut here even though it is just a copy of m_Put. This is almost always called immediately +// after modifying m_Put and this lets it stay in a register and avoid LHS on PPC. //----------------------------------------------------------------------------- -void CUtlBuffer::AddNullTermination( void ) +void CUtlBuffer::AddNullTermination( ) { if ( m_Put > m_nMaxPut ) { @@ -1595,12 +1719,7 @@ void CUtlBuffer::AddNullTermination( void ) // Add null termination value if ( CheckPut( 1 ) ) { - int Index = m_Put - m_nOffset; - Assert( m_Memory.IsIdxValid( Index ) ); - if( Index >= 0 ) - { - m_Memory[ Index ] = 0; - } + m_Memory[m_Put - m_nOffset] = 0; } else { @@ -1613,6 +1732,29 @@ void CUtlBuffer::AddNullTermination( void ) } +void CUtlBuffer::AddNullTermination( int nPut ) +{ + if ( nPut > m_nMaxPut ) + { + if ( !IsReadOnly() && ((m_Error & PUT_OVERFLOW) == 0) ) + { + // Add null termination value + if ( CheckPut( 1 ) ) + { + m_Memory[nPut - m_nOffset] = 0; + } + else + { + // Restore the overflow state, it was valid before... + m_Error &= ~PUT_OVERFLOW; + } + } + m_nMaxPut = nPut; + } +} + + + //----------------------------------------------------------------------------- // Converts a buffer from a CRLF buffer to a CR buffer (and back) // Returns false if no conversion was necessary (and outBuf is left untouched) @@ -1640,21 +1782,21 @@ bool CUtlBuffer::ConvertCRLF( CUtlBuffer &outBuf ) int nPutDelta = 0; const char *pBase = (const char*)Base(); - int nCurrGet = 0; + intp nCurrGet = 0; while ( nCurrGet < nInCount ) { const char *pCurr = &pBase[nCurrGet]; if ( bFromCRLF ) { - const char *pNext = Q_strnistr( pCurr, "\r\n", nInCount - nCurrGet ); + const char *pNext = V_strnistr( pCurr, "\r\n", nInCount - nCurrGet ); if ( !pNext ) { outBuf.Put( pCurr, nInCount - nCurrGet ); break; } - int nBytes = (size_t)pNext - (size_t)pCurr; - outBuf.Put( pCurr, nBytes ); + intp nBytes = (intp)pNext - (intp)pCurr; + outBuf.Put( pCurr, (int)nBytes ); outBuf.PutChar( '\n' ); nCurrGet += nBytes + 2; if ( nGet >= nCurrGet - 1 ) @@ -1668,15 +1810,15 @@ bool CUtlBuffer::ConvertCRLF( CUtlBuffer &outBuf ) } else { - const char *pNext = Q_strnchr( pCurr, '\n', nInCount - nCurrGet ); + const char *pNext = V_strnchr( pCurr, '\n', nInCount - nCurrGet ); if ( !pNext ) { outBuf.Put( pCurr, nInCount - nCurrGet ); break; } - int nBytes = (size_t)pNext - (size_t)pCurr; - outBuf.Put( pCurr, nBytes ); + intp nBytes = (intp)pNext - (intp)pCurr; + outBuf.Put( pCurr, (int)nBytes ); outBuf.PutChar( '\r' ); outBuf.PutChar( '\n' ); nCurrGet += nBytes + 1; @@ -1793,4 +1935,3 @@ char * CUtlInplaceBuffer::InplaceGetLinePtr( void ) return pszLine; } - diff --git a/tier1/utlsymbol.cpp b/tier1/utlsymbol.cpp index d75eaa52..2f3e32cc 100644 --- a/tier1/utlsymbol.cpp +++ b/tier1/utlsymbol.cpp @@ -1,4 +1,4 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// +//========= Copyright � 1996-2005, Valve Corporation, All rights reserved. ============// // // Purpose: Defines a symbol table // @@ -9,29 +9,11 @@ #pragma warning (disable:4514) #include "utlsymbol.h" -#include "KeyValues.h" #include "tier0/threadtools.h" -#include "tier0/memdbgon.h" #include "stringpool.h" -#include "utlhashtable.h" -#include "utlstring.h" - -// Ensure that everybody has the right compiler version installed. The version -// number can be obtained by looking at the compiler output when you type 'cl' -// and removing the last two digits and the periods: 16.00.40219.01 becomes 160040219 -#ifdef _MSC_FULL_VER - #if _MSC_FULL_VER > 160000000 - // VS 2010 - #if _MSC_FULL_VER < 160040219 - #error You must install VS 2010 SP1 - #endif - #else - // VS 2005 - #if _MSC_FULL_VER < 140050727 - #error You must install VS 2005 SP1 - #endif - #endif -#endif +#include "generichash.h" +#include "tier0/vprof.h" +#include // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -68,6 +50,17 @@ void CUtlSymbol::Initialize() } } +void CUtlSymbol::LockTableForRead() +{ + Initialize(); + s_pSymbolTable->LockForRead(); +} + +void CUtlSymbol::UnlockTableForRead() +{ + s_pSymbolTable->UnlockForRead(); +} + //----------------------------------------------------------------------------- // Purpose: Singleton to delete table on exit from module //----------------------------------------------------------------------------- @@ -104,6 +97,11 @@ const char* CUtlSymbol::String( ) const return CurrTable()->String(m_Id); } +const char* CUtlSymbol::StringNoLock( ) const +{ + return CurrTable()->StringNoLock(m_Id); +} + void CUtlSymbol::DisableStaticSymbolTable() { s_bAllowStaticSymbolTable = false; @@ -125,16 +123,25 @@ bool CUtlSymbol::operator==( const char* pStr ) const //----------------------------------------------------------------------------- // symbol table stuff //----------------------------------------------------------------------------- - -inline const char* CUtlSymbolTable::StringFromIndex( const CStringPoolIndex &index ) const +inline const char* CUtlSymbolTable::DecoratedStringFromIndex( const CStringPoolIndex &index ) const { Assert( index.m_iPool < m_StringPools.Count() ); Assert( index.m_iOffset < m_StringPools[index.m_iPool]->m_TotalLen ); - return &m_StringPools[index.m_iPool]->m_Data[index.m_iOffset]; + // step over the hash decorating the beginning of the string + return (&m_StringPools[index.m_iPool]->m_Data[index.m_iOffset]); } +inline const char* CUtlSymbolTable::StringFromIndex( const CStringPoolIndex &index ) const +{ + // step over the hash decorating the beginning of the string + return DecoratedStringFromIndex(index)+sizeof(hashDecoration_t); +} +// The first two bytes of each string in the pool are actually the hash for that string. +// Thus we compare hashes rather than entire strings for a significant perf benefit. +// However since there is a high rate of hash collision we must still compare strings +// if the hashes match. bool CUtlSymbolTable::CLess::operator()( const CStringPoolIndex &i1, const CStringPoolIndex &i2 ) const { // Need to do pointer math because CUtlSymbolTable is used in CUtlVectors, and hence @@ -142,21 +149,79 @@ bool CUtlSymbolTable::CLess::operator()( const CStringPoolIndex &i1, const CStri // right now at least, because m_LessFunc is the first member of CUtlRBTree, and m_Lookup // is the first member of CUtlSymbolTabke, this == pTable CUtlSymbolTable *pTable = (CUtlSymbolTable *)( (byte *)this - offsetof(CUtlSymbolTable::CTree, m_LessFunc) ) - offsetof(CUtlSymbolTable, m_Lookup ); + +#if 1 // using the hashes + const char *str1, *str2; + hashDecoration_t hash1, hash2; + + if (i1 == INVALID_STRING_INDEX) + { + str1 = pTable->m_pUserSearchString; + hash1 = pTable->m_nUserSearchStringHash; + } + else + { + str1 = pTable->DecoratedStringFromIndex( i1 ); + hashDecoration_t storedHash = *reinterpret_cast(str1); + str1 += sizeof(hashDecoration_t); + AssertMsg2( storedHash == ( !pTable->m_bInsensitive ? HashString(str1) : HashStringCaseless(str1) ), + "The stored hash (%d) for symbol %s is not correct.", storedHash, str1 ); + hash1 = storedHash; + } + + if (i2 == INVALID_STRING_INDEX) + { + str2 = pTable->m_pUserSearchString; + hash2 = pTable->m_nUserSearchStringHash; + } + else + { + str2 = pTable->DecoratedStringFromIndex( i2 ); + hashDecoration_t storedHash = *reinterpret_cast(str2); + str2 += sizeof(hashDecoration_t); + AssertMsg2( storedHash == ( !pTable->m_bInsensitive ? HashString(str2) : HashStringCaseless(str2) ), + "The stored hash (%d) for symbol '%s' is not correct.", storedHash, str2 ); + hash2 = storedHash; + } + + // compare the hashes + if ( hash1 == hash2 ) + { + if ( !str1 && str2 ) + return 1; + if ( !str2 && str1 ) + return -1; + if ( !str1 && !str2 ) + return 0; + + // if the hashes match compare the strings + if ( !pTable->m_bInsensitive ) + return strcmp( str1, str2 ) < 0; + else + return V_stricmp( str1, str2 ) < 0; + } + else + { + return hash1 < hash2; + } + +#else // not using the hashes, just comparing strings const char* str1 = (i1 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString : - pTable->StringFromIndex( i1 ); + pTable->StringFromIndex( i1 ); const char* str2 = (i2 == INVALID_STRING_INDEX) ? pTable->m_pUserSearchString : - pTable->StringFromIndex( i2 ); + pTable->StringFromIndex( i2 ); if ( !str1 && str2 ) - return false; + return 1; if ( !str2 && str1 ) - return true; + return -1; if ( !str1 && !str2 ) - return false; + return 0; if ( !pTable->m_bInsensitive ) - return V_strcmp( str1, str2 ) < 0; + return strcmp( str1, str2 ) < 0; else - return V_stricmp( str1, str2 ) < 0; + return strcmpi( str1, str2 ) < 0; +#endif } @@ -177,11 +242,13 @@ CUtlSymbolTable::~CUtlSymbolTable() CUtlSymbol CUtlSymbolTable::Find( const char* pString ) const { + VPROF( "CUtlSymbol::Find" ); if (!pString) return CUtlSymbol(); // Store a special context used to help with insertion m_pUserSearchString = pString; + m_nUserSearchStringHash = m_bInsensitive ? HashStringCaseless(pString) : HashString(pString) ; // Passing this special invalid symbol makes the comparison function // use the string passed in the context @@ -189,6 +256,7 @@ CUtlSymbol CUtlSymbolTable::Find( const char* pString ) const #ifdef _DEBUG m_pUserSearchString = NULL; + m_nUserSearchStringHash = 0; #endif return CUtlSymbol( idx ); @@ -217,6 +285,7 @@ int CUtlSymbolTable::FindPoolWithSpace( int len ) const CUtlSymbol CUtlSymbolTable::AddString( const char* pString ) { + VPROF("CUtlSymbol::AddString"); if (!pString) return CUtlSymbol( UTL_INVAL_SYMBOL ); @@ -225,35 +294,47 @@ CUtlSymbol CUtlSymbolTable::AddString( const char* pString ) if (id.IsValid()) return id; - int len = V_strlen(pString) + 1; + int lenString = strlen(pString) + 1; // length of just the string + int lenDecorated = lenString + sizeof(hashDecoration_t); // and with its hash decoration + // make sure that all strings are aligned on 2-byte boundaries so the hashes will read correctly + COMPILE_TIME_ASSERT(sizeof(hashDecoration_t) == 2); + lenDecorated = (lenDecorated + 1) & (~0x01); // round up to nearest multiple of 2 // Find a pool with space for this string, or allocate a new one. - int iPool = FindPoolWithSpace( len ); + int iPool = FindPoolWithSpace( lenDecorated ); if ( iPool == -1 ) { // Add a new pool. - int newPoolSize = max( len, MIN_STRING_POOL_SIZE ); - StringPool_t *pPool = (StringPool_t*)malloc( sizeof( StringPool_t ) + newPoolSize - 1 ); - pPool->m_TotalLen = newPoolSize; + int newPoolSize = MAX( lenDecorated + sizeof( StringPool_t ), MIN_STRING_POOL_SIZE ); + StringPool_t *pPool = (StringPool_t*)malloc( newPoolSize ); + pPool->m_TotalLen = newPoolSize - sizeof( StringPool_t ); pPool->m_SpaceUsed = 0; iPool = m_StringPools.AddToTail( pPool ); } + // Compute a hash + hashDecoration_t hash = m_bInsensitive ? HashStringCaseless(pString) : HashString(pString) ; + // Copy the string in. StringPool_t *pPool = m_StringPools[iPool]; Assert( pPool->m_SpaceUsed < 0xFFFF ); // This should never happen, because if we had a string > 64k, it // would have been given its entire own pool. unsigned short iStringOffset = pPool->m_SpaceUsed; + const char *startingAddr = &pPool->m_Data[pPool->m_SpaceUsed]; - memcpy( &pPool->m_Data[pPool->m_SpaceUsed], pString, len ); - pPool->m_SpaceUsed += len; + // store the hash at the head of the string + *((hashDecoration_t *)(startingAddr)) = hash; + // and then the string's data + memcpy( (void *)(startingAddr + sizeof(hashDecoration_t)), pString, lenString ); + pPool->m_SpaceUsed += lenDecorated; - // didn't find, insert the string into the vector. + // insert the string into the vector. CStringPoolIndex index; index.m_iPool = iPool; index.m_iOffset = iStringOffset; + MEM_ALLOC_CREDIT(); UtlSymId_t idx = m_Lookup.Insert( index ); return CUtlSymbol( idx ); } @@ -288,22 +369,6 @@ void CUtlSymbolTable::RemoveAll() } - -class CUtlFilenameSymbolTable::HashTable : public CUtlStableHashtable -{ -}; - -CUtlFilenameSymbolTable::CUtlFilenameSymbolTable() -{ - m_Strings = new HashTable; -} - -CUtlFilenameSymbolTable::~CUtlFilenameSymbolTable() -{ - delete m_Strings; -} - - //----------------------------------------------------------------------------- // Purpose: // Input : *pFileName - @@ -328,7 +393,7 @@ FileNameHandle_t CUtlFilenameSymbolTable::FindOrAddFileName( const char *pFileNa Q_strncpy( fn, pFileName, sizeof( fn ) ); Q_RemoveDotSlashes( fn ); #ifdef _WIN32 - Q_strlower( fn ); + strlwr( fn ); #endif // Split the filename into constituent parts @@ -340,20 +405,18 @@ FileNameHandle_t CUtlFilenameSymbolTable::FindOrAddFileName( const char *pFileNa // not found, lock and look again FileNameHandleInternal_t handle; m_lock.LockForWrite(); - handle.path = m_Strings->Insert( basepath ) + 1; - handle.file = m_Strings->Insert( filename ) + 1; - //handle.path = m_StringPool.FindStringHandle( basepath ); - //handle.file = m_StringPool.FindStringHandle( filename ); - //if ( handle.path != m_Strings.InvalidHandle() && handle.file ) - //{ + handle.SetPath( m_PathStringPool.FindStringHandle( basepath ) ); + handle.SetFile( m_FileStringPool.FindStringHandle( filename ) ); + if ( handle.GetPath() && handle.GetFile() ) + { // found - // m_lock.UnlockWrite(); - // return *( FileNameHandle_t * )( &handle ); - //} + m_lock.UnlockWrite(); + return *( FileNameHandle_t * )( &handle ); + } // safely add it - //handle.path = m_StringPool.ReferenceStringHandle( basepath ); - //handle.file = m_StringPool.ReferenceStringHandle( filename ); + handle.SetPath( m_PathStringPool.ReferenceStringHandle( basepath ) ); + handle.SetFile( m_FileStringPool.ReferenceStringHandle( filename ) ); m_lock.UnlockWrite(); return *( FileNameHandle_t * )( &handle ); @@ -371,7 +434,7 @@ FileNameHandle_t CUtlFilenameSymbolTable::FindFileName( const char *pFileName ) Q_strncpy( fn, pFileName, sizeof( fn ) ); Q_RemoveDotSlashes( fn ); #ifdef _WIN32 - Q_strlower( fn ); + strlwr( fn ); #endif // Split the filename into constituent parts @@ -382,16 +445,13 @@ FileNameHandle_t CUtlFilenameSymbolTable::FindFileName( const char *pFileName ) FileNameHandleInternal_t handle; - Assert( (uint16)(m_Strings->InvalidHandle() + 1) == 0 ); - m_lock.LockForRead(); - handle.path = m_Strings->Find(basepath) + 1; - handle.file = m_Strings->Find(filename) + 1; - //handle.path = m_StringPool.FindStringHandle(basepath); - //handle.file = m_StringPool.FindStringHandle(filename); + handle.SetPath( m_PathStringPool.FindStringHandle( basepath ) ); + handle.SetFile( m_FileStringPool.FindStringHandle( filename ) ); m_lock.UnlockRead(); - if ( handle.path == 0 || handle.file == 0 ) + + if ( ( handle.GetPath() == 0 ) || ( handle.GetFile() == 0 ) ) return NULL; return *( FileNameHandle_t * )( &handle ); @@ -406,17 +466,15 @@ bool CUtlFilenameSymbolTable::String( const FileNameHandle_t& handle, char *buf, { buf[ 0 ] = 0; - FileNameHandleInternal_t *internal = ( FileNameHandleInternal_t * )&handle; - if ( !internal || !internal->file || !internal->path ) + FileNameHandleInternal_t *internalFileHandle = ( FileNameHandleInternal_t * )&handle; + if ( !internalFileHandle ) { return false; } m_lock.LockForRead(); - //const char *path = m_StringPool.HandleToString(internal->path); - //const char *fn = m_StringPool.HandleToString(internal->file); - const char *path = (*m_Strings)[ internal->path - 1 ].Get(); - const char *fn = (*m_Strings)[ internal->file - 1].Get(); + const char *path = m_PathStringPool.HandleToString( internalFileHandle->GetPath() ); + const char *fn = m_FileStringPool.HandleToString( internalFileHandle->GetFile() ); m_lock.UnlockRead(); if ( !path || !fn ) @@ -432,5 +490,34 @@ bool CUtlFilenameSymbolTable::String( const FileNameHandle_t& handle, char *buf, void CUtlFilenameSymbolTable::RemoveAll() { - m_Strings->Purge(); + m_PathStringPool.FreeAll(); + m_FileStringPool.FreeAll(); +} + +void CUtlFilenameSymbolTable::SpewStrings() +{ + m_lock.LockForRead(); + m_PathStringPool.SpewStrings(); + m_FileStringPool.SpewStrings(); + m_lock.UnlockRead(); +} + +bool CUtlFilenameSymbolTable::SaveToBuffer( CUtlBuffer &buffer ) +{ + m_lock.LockForRead(); + bool bResult = m_PathStringPool.SaveToBuffer( buffer ); + bResult = bResult && m_FileStringPool.SaveToBuffer( buffer ); + m_lock.UnlockRead(); + + return bResult; +} + +bool CUtlFilenameSymbolTable::RestoreFromBuffer( CUtlBuffer &buffer ) +{ + m_lock.LockForWrite(); + bool bResult = m_PathStringPool.RestoreFromBuffer( buffer ); + bResult = bResult && m_FileStringPool.RestoreFromBuffer( buffer ); + m_lock.UnlockWrite(); + + return bResult; } diff --git a/vgui2/vgui_controls/BuildGroup.cpp b/vgui2/vgui_controls/BuildGroup.cpp index a5001cbe..1905f7b1 100644 --- a/vgui2/vgui_controls/BuildGroup.cpp +++ b/vgui2/vgui_controls/BuildGroup.cpp @@ -41,6 +41,7 @@ #include "filesystem.h" #include "tier0/icommandline.h" #include "const.h" +#include "vprof.h" #if defined( _X360 ) #include "xbox/xbox_win32stubs.h" @@ -869,18 +870,6 @@ void BuildGroup::PanelAdded(Panel *panel) _panelDar.AddToTail(temp); } -//----------------------------------------------------------------------------- -// Purpose: Add panel the list of panels that are in the build group -//----------------------------------------------------------------------------- -void BuildGroup::PanelRemoved(Panel *panel) -{ - Assert(panel); - - PHandle temp; - temp = panel; - _panelDar.FindAndRemove(temp); -} - //----------------------------------------------------------------------------- // Purpose: loads the control settings from file //----------------------------------------------------------------------------- @@ -1553,4 +1542,4 @@ void BuildGroup::ClearResFileCache() nIndex = m_dictCachedResFiles.Next( nIndex ); } m_dictCachedResFiles.Purge(); -} \ No newline at end of file +} diff --git a/vgui2/vgui_controls/Panel.cpp b/vgui2/vgui_controls/Panel.cpp index 3c53f350..3adbf7ed 100644 --- a/vgui2/vgui_controls/Panel.cpp +++ b/vgui2/vgui_controls/Panel.cpp @@ -3800,17 +3800,11 @@ void Panel::SetTall(int tall) void Panel::SetBuildGroup(BuildGroup* buildGroup) { - if ( _buildGroup == buildGroup ) - return; - if ( _buildGroup.Get() ) - { - _buildGroup->PanelRemoved( this ); - } - _buildGroup = buildGroup; - if ( _buildGroup.Get() ) - { + //TODO: remove from old group + + Assert(buildGroup != NULL); + _buildGroup = buildGroup; _buildGroup->PanelAdded(this); - } } bool Panel::IsBuildGroupEnabled() @@ -5142,7 +5136,7 @@ void Panel::OnMessage(const KeyValues *params, VPANEL ifromPanel) { typedef void (Panel::*MessageFunc_HandleConstCharPtr_t)(VPANEL, VPANEL); VPANEL vp1 = ivgui()->HandleToPanel( param1->GetInt() ); - VPANEL vp2 = ivgui()->HandleToPanel( param1->GetInt() ); + VPANEL vp2 = ivgui()->HandleToPanel( param2->GetInt() ); (this->*((MessageFunc_HandleConstCharPtr_t)pMap->func))( vp1, vp2 ); } else diff --git a/vphysics/physics_virtualmesh.cpp b/vphysics/physics_virtualmesh.cpp index 7f447a80..f3c7bd4c 100644 --- a/vphysics/physics_virtualmesh.cpp +++ b/vphysics/physics_virtualmesh.cpp @@ -27,7 +27,7 @@ class CPhysCollideVirtualMesh; CTSPool< CUtlVector > g_MeshFrameLocksPool; -CThreadLocalPtr< CUtlVector > g_pMeshFrameLocks; +CTHREADLOCALPTR(CUtlVector) g_pMeshFrameLocks; // This is the surfacemanager class for IVP that implements the required functions by layering CPhysCollideVirtualMesh class IVP_SurfaceManager_VirtualMesh : public IVP_SurfaceManager diff --git a/vphysics/vphysics_saverestore.cpp b/vphysics/vphysics_saverestore.cpp index 4908f807..7cdff13e 100644 --- a/vphysics/vphysics_saverestore.cpp +++ b/vphysics/vphysics_saverestore.cpp @@ -57,7 +57,7 @@ bool CPhysicsEnvironment::Save( const physsaveparams_t ¶ms ) if ( type >= 0 && type < PIID_NUM_TYPES ) { - params.pSave->WriteInt( (int *)¶ms.pObject ); + params.pSave->WriteData( (char *)¶ms.pObject, sizeof(void*) ); return (*saveFuncs[type])( params, params.pObject ); } return false; @@ -105,7 +105,7 @@ bool CPhysicsEnvironment::Restore( const physrestoreparams_t ¶ms ) if ( type >= 0 && type < PIID_NUM_TYPES ) { void *pOldObject; - params.pRestore->ReadInt( (int *)&pOldObject ); + params.pRestore->ReadData( (char *)&pOldObject, sizeof(void*), 0 ); if ( (*restoreFuncs[type])( params, params.ppObject ) ) { AddPtrAssociation( pOldObject, *params.ppObject ); @@ -131,11 +131,11 @@ void CPhysicsEnvironment::PostRestore() void CVPhysPtrSaveRestoreOps::Save( const SaveRestoreFieldInfo_t &fieldInfo, ISave *pSave ) { - int *pField = (int *)fieldInfo.pField; + void *pField = (void *)fieldInfo.pField; int nObjects = fieldInfo.pTypeDesc->fieldSize; for ( int i = 0; i < nObjects; i++ ) { - pSave->WriteInt( pField ); + pSave->WriteData( (char*)pField, sizeof(void*) ); ++pField; } } @@ -153,9 +153,10 @@ void CVPhysPtrSaveRestoreOps::Restore( const SaveRestoreFieldInfo_t &fieldInfo, { void **ppField = (void **)fieldInfo.pField; int nObjects = fieldInfo.pTypeDesc->fieldSize; + for ( int i = 0; i < nObjects; i++ ) { - pRestore->ReadInt( (int *)ppField ); + pRestore->ReadData( (char *)ppField, sizeof(void*), 0 ); int iNewVal = s_VPhysPtrMap.Find( *ppField ); if ( iNewVal != s_VPhysPtrMap.InvalidIndex() ) @@ -188,10 +189,11 @@ void CVPhysPtrUtlVectorSaveRestoreOps::Save( const SaveRestoreFieldInfo_t &field VPhysPtrVector *pUtlVector = (VPhysPtrVector*)fieldInfo.pField; int nObjects = pUtlVector->Count(); + pSave->WriteInt( &nObjects ); for ( int i = 0; i < nObjects; i++ ) { - pSave->WriteInt( &pUtlVector->Element(i) ); + pSave->WriteData( (char*)&pUtlVector->Element(i), sizeof(void*) ); } } @@ -207,7 +209,8 @@ void CVPhysPtrUtlVectorSaveRestoreOps::Restore( const SaveRestoreFieldInfo_t &fi for ( int i = 0; i < nObjects; i++ ) { void **ppElem = (void**)(&pUtlVector->Element(i)); - pRestore->ReadInt( (int*)ppElem ); + + pRestore->ReadData( (char *)ppElem, sizeof(void*), 0 ); int iNewVal = s_VPhysPtrMap.Find( *ppElem ); if ( iNewVal != s_VPhysPtrMap.InvalidIndex() ) diff --git a/vstdlib/coroutine.cpp b/vstdlib/coroutine.cpp index a1bcfc1d..dc148a8c 100644 --- a/vstdlib/coroutine.cpp +++ b/vstdlib/coroutine.cpp @@ -585,7 +585,7 @@ private: CUtlVector m_VecCoroutineStack; }; -CThreadLocalPtr< CCoroutineMgr > g_ThreadLocalCoroutineMgr; +CTHREADLOCALPTR(CCoroutineMgr) g_ThreadLocalCoroutineMgr; CUtlVector< CCoroutineMgr * > g_VecPCoroutineMgr; CThreadMutex g_ThreadMutexCoroutineMgr; @@ -610,7 +610,7 @@ void Coroutine_ReleaseThreadMemory() { AUTO_LOCK( g_ThreadMutexCoroutineMgr ); - if ( g_ThreadLocalCoroutineMgr != NULL ) + if ( g_ThreadLocalCoroutineMgr != static_cast( nullptr ) ) { int iCoroutineMgr = g_VecPCoroutineMgr.Find( g_ThreadLocalCoroutineMgr ); delete g_VecPCoroutineMgr[iCoroutineMgr]; diff --git a/vstdlib/jobthread.cpp b/vstdlib/jobthread.cpp index 46d843e0..922b770f 100644 --- a/vstdlib/jobthread.cpp +++ b/vstdlib/jobthread.cpp @@ -227,7 +227,7 @@ public: // and execute or execute pFunctor right after completing current job and // before looking for another job. //----------------------------------------------------- - void ExecuteHighPriorityFunctor( CFunctor *pFunctor ); + // void ExecuteHighPriorityFunctor( CFunctor *pFunctor ); //----------------------------------------------------- // Add an function object to the queue (master thread) @@ -247,8 +247,6 @@ public: virtual void Reserved1() {} - void WaitForIdle( bool bAll = true ); - private: enum { @@ -418,7 +416,7 @@ private: CFunctor *pFunctor = NULL; tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s PeekCall():%d", __FUNCTION__, GetCallParam() ); - switch ( GetCallParam( &pFunctor ) ) + switch ( GetCallParam() ) { case TPM_EXIT: Reply( true ); @@ -427,10 +425,10 @@ private: case TPM_SUSPEND: Reply( true ); - SuspendCooperative(); + Suspend(); break; - case TPM_RUNFUNCTOR: +/* case TPM_RUNFUNCTOR: if( pFunctor ) { ( *pFunctor )(); @@ -441,7 +439,7 @@ private: Assert( pFunctor ); Reply( false ); } - break; + break;*/ default: AssertMsg( 0, "Unknown call to thread" ); @@ -535,7 +533,7 @@ int CThreadPool::NumIdleThreads() return m_nIdleThreads; } -void CThreadPool::ExecuteHighPriorityFunctor( CFunctor *pFunctor ) +/*void CThreadPool::ExecuteHighPriorityFunctor( CFunctor *pFunctor ) { int i; for ( i = 0; i < m_Threads.Count(); i++ ) @@ -547,7 +545,7 @@ void CThreadPool::ExecuteHighPriorityFunctor( CFunctor *pFunctor ) { m_Threads[i]->WaitForReply(); } -} +}*/ //--------------------------------------------------------- // Pause/resume processing jobs @@ -575,7 +573,10 @@ int CThreadPool::SuspendExecution() // here with the thread not actually suspended for ( i = 0; i < m_Threads.Count(); i++ ) { - m_Threads[i]->BWaitForThreadSuspendCooperative(); + while ( !m_Threads[i]->IsSuspended() ) + { + ThreadSleep(); + } } } @@ -593,7 +594,7 @@ int CThreadPool::ResumeExecution() { for ( int i = 0; i < m_Threads.Count(); i++ ) { - m_Threads[i]->ResumeCooperative(); + m_Threads[i]->Resume(); } } return result; @@ -601,13 +602,6 @@ int CThreadPool::ResumeExecution() //--------------------------------------------------------- -void CThreadPool::WaitForIdle( bool bAll ) -{ - ThreadWaitForEvents( m_IdleEvents.Count(), m_IdleEvents.Base(), bAll, 60000 ); -} - -//--------------------------------------------------------- - int CThreadPool::YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll, unsigned timeout ) { tmZone( TELEMETRY_LEVEL0, TMZF_IDLE, "%s(%d) SPINNING %t", __FUNCTION__, timeout, tmSendCallStack( TELEMETRY_LEVEL0, 0 ) ); @@ -618,7 +612,7 @@ int CThreadPool::YieldWait( CThreadEvent **pEvents, int nEvents, bool bWaitAll, CJob *pJob; // Always wait for zero milliseconds initially, to let us process jobs on this thread. timeout = 0; - while ( ( result = ThreadWaitForEvents( nEvents, pEvents, bWaitAll, timeout ) ) == WAIT_TIMEOUT ) + while ( ( result = CThreadEvent::WaitForMultiple( nEvents, pEvents, bWaitAll, timeout ) ) == TW_TIMEOUT ) { if ( !m_bExecOnThreadPoolThreadsOnly && m_SharedQueue.Pop( &pJob ) ) { diff --git a/wscript b/wscript index b3b87b0d..bdaab19a 100644 --- a/wscript +++ b/wscript @@ -158,11 +158,14 @@ def define_platform(conf): if conf.options.SDL: conf.define('USE_SDL', 1) + if conf.options.ALLOW64: + conf.define('PLATFORM_64BITS', 1) + if conf.env.DEST_OS == 'linux': conf.define('_GLIBCXX_USE_CXX11_ABI',0) conf.env.append_unique('DEFINES', [ 'LINUX=1', '_LINUX=1', - 'POSIX=1', '_POSIX=1', + 'POSIX=1', '_POSIX=1', 'PLATFORM_POSIX=1', 'GNUC', 'NO_HOOK_MALLOC', '_DLL_EXT=.so' @@ -265,6 +268,12 @@ def configure(conf): '-Wuninitialized', '-Winit-self', '-Wstrict-aliasing', + '-Wno-reorder', + '-Wno-unknown-pragmas', + '-Wno-unused-function', + '-Wno-unused-but-set-variable', + '-Wno-unused-value', + '-Wno-unused-variable', '-faligned-new', ] @@ -274,7 +283,7 @@ def configure(conf): cflags, linkflags = conf.get_optimization_flags() - flags = ['-fPIC', '-pipe'] #, '-fsanitize=undefined', '-fno-sanitize=vptr'] #, '-fno-sanitize=vptr,shift,shift-exponent,shift-base,signed-integer-overflow'] + flags = ['-pipe', '-fPIC'] #, '-fsanitize=undefined'] #, '-fsanitize=undefined'] #, '-fno-sanitize=vptr'] #, '-fno-sanitize=vptr,shift,shift-exponent,shift-base,signed-integer-overflow'] if conf.env.COMPILER_CC != 'msvc': flags += ['-pthread']